finish FFI scribbling

svn: r7944
This commit is contained in:
Matthew Flatt 2007-12-11 00:32:41 +00:00
parent 39866addc2
commit d7e8371254
13 changed files with 423 additions and 294 deletions

View File

@ -1,9 +1,10 @@
#lang scheme/base
;; Foreign Scheme interface
(module foreign mzscheme
(require '#%foreign (lib "dirs.ss" "setup"))
(require-for-syntax (lib "stx.ss" "syntax"))
(require '#%foreign
(lib "dirs.ss" "setup")
(for-syntax scheme/base
(lib "stx.ss" "syntax")))
;; This module is full of unsafe bindings that are not provided to requiring
;; modules. Instead, an `unsafe!' binding is provided that makes these unsafe
@ -28,8 +29,8 @@
(with-syntax ([(p ...) provides]) #'(provide p ...)))
(syntax-case (car ps) (unsafe)
[(unsafe u)
(syntax-case #'u (rename)
[(rename from to)
(syntax-case #'u (rename-out)
[(rename-out [from to])
(loop provides (cons (cons #'from #'to) unsafes) (cdr ps))]
[id (identifier? #'id)
(loop provides (cons (cons #'id #'id) unsafes) (cdr ps))]
@ -45,10 +46,10 @@
[(id ...) (generate-temporaries unsafe-bindings)])
(set! unsafe-bindings '())
#'(begin
(provide (protect unsafe))
(provide (protect-out unsafe))
(define-syntax (unsafe stx)
(syntax-case stx ()
[(_) (with-syntax ([(id ...) (list (datum->syntax-object
[(_) (with-syntax ([(id ...) (list (datum->syntax
stx 'to stx)
...)])
#'(begin (define-syntax id
@ -58,6 +59,7 @@
(provide* ctype-sizeof ctype-alignof compiler-sizeof
(unsafe malloc) (unsafe free) end-stubborn-change
cpointer? ptr-equal? ptr-add (unsafe ptr-ref) (unsafe ptr-set!)
ptr-offset ptr-add! offset-ptr? set-ptr-offset!
ctype? make-ctype make-cstruct-type make-sized-byte-string
_void _int8 _uint8 _int16 _uint16 _int32 _uint32 _int64 _uint64
_fixint _ufixint _fixnum _ufixnum
@ -136,7 +138,7 @@
(define lib-suffix (bytes->string/latin-1 (subbytes (system-type 'so-suffix) 1)))
(define lib-suffix-re (regexp (string-append "\\." lib-suffix "$")))
(provide (rename get-ffi-lib ffi-lib)
(provide (rename-out [get-ffi-lib ffi-lib])
ffi-lib? ffi-lib-name)
(define get-ffi-lib
(case-lambda
@ -161,9 +163,9 @@
(if (or (not v) (zero? (string-length v)))
"" (string-append "." v)))
versions)]
[fullpath (lambda (p) (path->complete-path (expand-path p)))]
[fullpath (lambda (p) (path->complete-path (cleanse-path p)))]
[absolute? (absolute-path? name)]
[name0 (path->string (expand-path name))] ; orig name
[name0 (path->string (cleanse-path name))] ; orig name
[names (map (if (regexp-match lib-suffix-re name0) ; name+suffix
(lambda (v) (string-append name0 v))
(lambda (v) (string-append name0 "." lib-suffix v)))
@ -371,7 +373,7 @@
body)))
(define (custom-type->keys type err)
(define stops (map (lambda (s) (datum->syntax-object type s #f))
(define stops (map (lambda (s) (datum->syntax type s #f))
'(#%app #%top #%datum)))
;; Expand `type' using expand-fun-syntax/fun
(define orig (expand-fun-syntax/fun type))
@ -639,7 +641,7 @@
#,output-expr)))]
;; if there is a string 'ffi-name property, use it as a name
[body (let ([n (cond [(syntax-property stx 'ffi-name)
=> syntax-object->datum]
=> syntax->datum]
[else #f])])
(if (string? n)
(syntax-property
@ -703,7 +705,7 @@
(provide _path)
;; `file' type: path-expands a path string, provide _path too.
(define* _file (make-ctype _path expand-path #f))
(define* _file (make-ctype _path cleanse-path #f))
;; `string/eof' type: converts an output #f (NULL) to an eof-object.
(define string-type->string/eof-type
@ -935,7 +937,7 @@
;; be just like _bytes since the string carries its size information (so there
;; is no real need for the `o', but it's there for consistency with the above
;; macros).
(provide (rename _bytes* _bytes))
(provide (rename-out [_bytes* _bytes]))
(define-fun-syntax _bytes*
(syntax-id-rules (o)
[(_ o n) (type: _bytes
@ -952,7 +954,7 @@
(provide* cvector? cvector-length cvector-type
;; make-cvector* is a dangerous operation
(unsafe (rename make-cvector make-cvector*)))
(unsafe (rename-out [make-cvector make-cvector*])))
(define _cvector* ; used only as input types
(make-ctype _pointer cvector-ptr
@ -976,13 +978,13 @@
[(_ . xs) (_cvector* . xs)]
[_ _cvector*]))
(provide (rename allocate-cvector make-cvector))
(provide (rename-out [allocate-cvector make-cvector]))
(define (allocate-cvector type len)
(make-cvector (if (zero? len) #f ; 0 => NULL
(malloc len type))
type len))
(provide (rename cvector-args cvector))
(provide (rename-out [cvector-args cvector]))
(define (cvector-args type . args)
(list->cvector args type))
@ -1019,13 +1021,13 @@
(syntax-case stx ()
[(_ TAG type more ...) (identifier? #'TAG)
(let ([name (string-append
(symbol->string (syntax-object->datum #'TAG))
(symbol->string (syntax->datum #'TAG))
"vector")])
(define (make-TAG-id prefix suffix)
(datum->syntax-object #'TAG
(string->symbol
(string-append prefix name suffix))
#'TAG))
(datum->syntax #'TAG
(string->symbol
(string-append prefix name suffix))
#'TAG))
(with-syntax ([TAG? (make-TAG-id "" "?")]
[TAG (make-TAG-id "" "")]
[make-TAG (make-TAG-id "make-" "")]
@ -1052,20 +1054,20 @@
bindings))
(syntax-case #'(more ...) ()
[(X? X-length make-X X X-ref X-set! X->list list->X _X)
#'(provide (rename X? TAG? )
(rename X-length TAG-length)
(rename make-X make-TAG )
(rename X TAG )
(rename X-ref TAG-ref )
(rename X-set! TAG-set! )
(rename X->list TAG->list )
(rename list->X list->TAG )
(rename _X _TAG ))]
#'(provide (rename-out [X? TAG? ]
[X-length TAG-length]
[make-X make-TAG ]
[X TAG ]
[X-ref TAG-ref ]
[X-set! TAG-set! ]
[X->list TAG->list ]
[list->X list->TAG ]
[_X _TAG ]))]
[()
#'(begin
(define-struct TAG (ptr length))
(provide TAG? TAG-length)
(provide (rename allocate-TAG make-TAG))
(provide (rename-out [allocate-TAG make-TAG]))
(define (allocate-TAG n . init)
(let* ([p (if (eq? n 0) #f (malloc n type))]
[v (make-TAG p n)])
@ -1076,7 +1078,7 @@
(ptr-set! p type i init)
(loop (sub1 i))))))
v))
(provide (rename TAG* TAG))
(provide (rename-out [TAG* TAG]))
(define (TAG* . vals)
(list->TAG vals))
(define* (TAG-ref v i)
@ -1245,7 +1247,7 @@
(let ([name (cadr (regexp-match #rx"^_(.+)$"
(symbol->string (syntax-e #'_TYPE))))])
(define (id . strings)
(datum->syntax-object
(datum->syntax
#'_TYPE (string->symbol (apply string-append strings)) #'_TYPE))
(with-syntax ([name-string name]
[TYPE? (id name "?")]
@ -1314,17 +1316,17 @@
(define 1st-type
(let ([xs (syntax->list slot-types-stx)]) (and (pair? xs) (car xs))))
(define (id . strings)
(datum->syntax-object
(datum->syntax
_TYPE-stx (string->symbol (apply string-append strings)) _TYPE-stx))
(define (ids name-func)
(map (lambda (s)
(datum->syntax-object
(datum->syntax
_TYPE-stx
(string->symbol (apply string-append (name-func s)))
_TYPE-stx))
slot-names))
(define (safe-id=? x y)
(and (identifier? x) (identifier? y) (module-identifier=? x y)))
(and (identifier? x) (identifier? y) (free-identifier=? x y)))
(with-syntax
([has-super? has-super?]
[name-string name]
@ -1468,7 +1470,7 @@
(make-syntax #'_TYPE #f #'(slot ...) #'(slot-type ...))]
[(_ (_TYPE _SUPER) ([slot slot-type] ...))
(and (_-identifier? #'_TYPE) (identifiers? #'(slot ...)))
(with-syntax ([super (datum->syntax-object #'_TYPE 'super #'_TYPE)])
(with-syntax ([super (datum->syntax #'_TYPE 'super #'_TYPE)])
(make-syntax #'_TYPE #t #'(super slot ...) #'(_SUPER slot-type ...)))]))
;; helper for the above: keep runtime information on structs
@ -1563,4 +1565,3 @@
(will-register killer-executor obj finalizer))
(define-unsafer unsafe!)
)

View File

@ -13,6 +13,9 @@ interface}. Furthermore, since most APIs consist mostly of functions,
the foreign interface is sometimes called a @defterm{foreign function
interface}, abbreviated @deftech{FFI}.
@bold{Important:} Most of the bindings documented here are available
only after an @scheme[(unsafe!)] declaration in the importing module.
@table-of-contents[]
@include-section["intro.scrbl"]
@ -22,5 +25,6 @@ interface}, abbreviated @deftech{FFI}.
@include-section["misc.scrbl"]
@include-section["derived.scrbl"]
@include-section["unexported.scrbl"]
@include-section["unsafe.scrbl"]
@index-section[]

View File

@ -1,4 +1,3 @@
(module info setup/infotab
(define name "Scribblings: FFI")
(define scribblings '(("foreign.scrbl" (multi-page main-doc)))))

View File

@ -28,13 +28,5 @@ macro should be considered as a declaration that your code is itself
unsafe, therefore can lead to serious problems in case of bugs: it is
your responsibility to provide a safe interface.
In rare cases, you might want to provide an @italic{unsafe} interface
hat builds on the unsafe features of the FFI. In such cases, use the
@indexed-scheme[provide*] macro with @scheme[unsafe] bindings, and use
@indexed-scheme[define-unsafer] to provide an @scheme[unsafe!]-like macro
that will make these bindings available to importers of your library.
Providing users with unsafe operations without using this facility
should be considered a bug in your code.
For examples of common FFI usage patterns, see the defined interfaces
in the @filepath{ffi} collection.

View File

@ -3,282 +3,363 @@
@title[#:tag "foreign:pointer-funcs"]{Pointer Functions}
@;{
@defproc[(cpointer? [v any/c]) boolean?]{
Returns @scheme[#t] if @scheme[v] is a C pointer or a value that can
be used as a pointer: @scheme[#f] (used as a @cpp{NULL} pointer), byte
strings (used as memory blocks), some additional internal objects
(@scheme[ffi-obj]s and callbacks, see @secref["c-only"]). Returns
@scheme[#f] for other values.}
(@scheme[ffi-obj]s and callbacks, see @secref["foreign:c-only"]).
Returns @scheme[#f] for other values.}
\scmutilsectionO{ptr-ref}{cptr ctype}{\Optional{'abs} offset}{procedure}
@defproc*[([(ptr-ref [cptr cpointer?]
[type ctype?]
[offset exact-nonnegative-integer? 0])
any]
[(ptr-ref [cptr cpointer?]
[type ctype?]
[abs-tag (one-of/c 'abs)]
[offset exact-nonnegative-integer?])
any]
[(ptr-set! [cptr cpointer?]
[type ctype?]
[val any/c])
void?]
[(ptr-set! [cptr cpointer?]
[type ctype?]
[offset exact-nonnegative-integer?]
[val any/c])
void?]
[(ptr-set! [cptr cpointer?]
[type ctype?]
[abs-tag (one-of/c 'abs)]
[offset exact-nonnegative-integer?]
[val any/c])
void?])]{
\scmutilsection{ptr-set!}{cptr ctype \Optional{\Optional{'abs} offset} value}{procedure}
The @scheme[ptr-ref] procedure returns the object referenced by
@scheme[cptr], using the given @scheme[type]. The @scheme[ptr-set!]
procedure stores the @scheme[val] in the memory @scheme[cptr] points
to, using the given @scheme[type] for the conversion.
The @scheme[pre-ref] procedure return the object referenced by
@var{cptr}, using the given @var{ctype}. The @scheme[ptr-set!]\
procedure stores the @var{value} in the memory @var{cptr} points to, using
the given @var{ctype} for the conversion, and returns @|void-const|.
In each case, @scheme[offset] defaults to @scheme[0] (which is the
only value that should be used with @scheme[ffi-obj] objects, see
@secref["foreign:c-only"]). If an @scheme[offset] index is
non-@scheme[0], the value is read or stored at that location,
considering the pointer as a vector of @scheme[type]s --- so the
actual address is the pointer plus the size of @scheme[type]
multiplied by @scheme[offset]. In addition, a @scheme['abs] flag can
be used to use the @scheme[offset] as counting bytes rather then
increments of the specified @scheme[type].
In each case, @var{offset} defaults to $0$ (which is the only value
that should be used with @scheme[ffi-obj] objects, see
section~\ref{foreign:c-only}). If an @var{offset} index is given, the
value is stored at that location, considering the pointer as a vector
of @var{ctype}s --- so the actual address is the pointer plus the size
of @var{ctype} multiplied by @var{offset}. In addition, a @var{'abs}
flag can be used to use the @var{offset} as counting bytes rather then
increments of the specified @var{ctype}.
Beware that the @scheme[ptr-ref] and @scheme[ptr-set!]\ procedure do
Beware that the @scheme[ptr-ref] and @scheme[ptr-set!] procedure do
not keep any meta-information on how pointers are used. It is the
programmer's responsibility to use this facility only when
appropriate. For example, on a little-endian machine:
%
@schemeblock[
> (define block (malloc _int 5))
> (ptr-set! block _int 0 196353)
> (map (lambda (i) (ptr-ref block _byte i)) '(0 1 2 3))
(1 255 2 0)
@,(schemeresultfont "(1 255 2 0)")
]
%
In addition, @scheme[ptr-ref] and @scheme[ptr-set!]\ cannot detect
when offsets are beyond an object's memory bounds; out-of-bounds
access can easily lead to a segmentation fault or memory corruption.
@defproc[(ptr-equal? [cptr$_1$ any/c][cptr$_2$ any/c]) any]
In addition, @scheme[ptr-ref] and @scheme[ptr-set!] cannot detect when
offsets are beyond an object's memory bounds; out-of-bounds access can
easily lead to a segmentation fault or memory corruption.}
Compares the values of the two pointers. (Note that two different Scheme
pointer objects can contain the same pointer.)
@defproc[(ptr-add [cptr any/c][offset-k nonnegative-exact-integer?][ctype any/c #f]) any]
@defproc[(ptr-equal? [cptr1 cpointer?][cptr2 cpointer?]) boolean?]{
Returns a cpointer that is like @var{cptr} offset by @var{offset-k}
instances of @var{ctype}. If @var{ctype} is not provided, @var{cptr}
is offset by @var{offset-k} bytes.
Compares the values of the two pointers. Two different Scheme
pointer objects can contain the same pointer.}
The resulting cpointer keeps the base pointer and offset separate. The two
pieces are combined at the last minute before any operation on the pointer,
such as supplying the pointer to a foreign function. In particular, the pointer
and offset are not combined until after all allocation leading up to a
foreign-function call; if the called function does not itself call anything
that can trigger a garbage collection, it can safey use pointers that are
offset into the middle of a GCable object.
@defproc[(offset-ptr? [cptr any/c]) any]
@defproc[(ptr-add [cptr cpointer?][offset exact-integer?][type ctype? _byte])
cpointer?]{
A predicate for cpointers that have an offset, such as pointers that were
created using @scheme[ptr-add]. Returns @scheme[#t] even if such an offset
happens to be 0. Returns @scheme[#f] for other cpointers and non-cpointers.
Returns a cpointer that is like @scheme[cptr] offset by
@scheme[offset] instances of @scheme[ctype].
@defproc[(ptr-offset [cptr any/c]) any]
The resulting cpointer keeps the base pointer and offset separate. The
two pieces are combined at the last minute before any operation on the
pointer, such as supplying the pointer to a foreign function. In
particular, the pointer and offset are not combined until after all
allocation leading up to a foreign-function call; if the called
function does not itself call anything that can trigger a garbage
collection, it can safey use pointers that are offset into the middle
of a GCable object.}
Returns the offset of a pointer that has an offset. (The resulting offset is
always in bytes.)
@defproc[(set-ptr-offset! [cptr any/c][offset-k nonnegative-exact-integer?][ctype any/c #f]) any]
@defproc[(offset-ptr? [cptr cpointer?]) boolean?]{
Sets the offset component of an offset pointer. The arguments are used in the
same way as @scheme[ptr-add]. Raises an error if it is given a pointer that
has no offset.
A predicate for cpointers that have an offset, such as pointers that
were created using @scheme[ptr-add]. Returns @scheme[#t] even if such
an offset happens to be 0. Returns @scheme[#f] for other cpointers
and non-cpointers.}
@defproc[(ptr-add! [cptr any/c][offset-k nonnegative-exact-integer?][ctype any/c #f]) any]
Like @scheme[ptr-add], but destructively modifies the offset contained in a
pointer. (This can also be done using @scheme[ptr-offset] and
@scheme[set-ptr-offset!].)
@defproc[(ptr-offset [cptr cpointer?]) exact-integer?]{
@defproc[(cpointer-tag [cptr any/c]) any]
Returns the offset of a pointer that has an offset. The resulting
offset is always in bytes.}
Returns the Scheme object that is the tag of the given @var{cptr} pointer.
@defproc[(set-cpointer-tag! [cptr any/c][tag any/c]) any]
@defproc[(set-ptr-offset! [cptr cpointer?][offset exact-integer?][ctype ctype? _byte])
void?]{
Sets the tag of the given @var{cptr}. The @var{tag} argument can be
any arbitrary value; other pointer operations ignore it. When a
Sets the offset component of an offset pointer. The arguments are
used in the same way as @scheme[ptr-add]. If @scheme[cptr] has no
offset, the @scheme[exn:fail:contract] exception is raised.}
@defproc[(ptr-add! [cptr cpointer?][offset exact-integer?][ctype ctype? _byte])
void?]{
Like @scheme[ptr-add], but destructively modifies the offset contained
in a pointer. The same operation could be performed using
@scheme[ptr-offset] and @scheme[set-ptr-offset!].}
@defproc[(cpointer-tag [cptr cpointer?]) any]{
Returns the Scheme object that is the tag of the given @scheme[cptr]
pointer.}
@defproc[(set-cpointer-tag! [cptr cpointer?][tag any/c]) void?]{
Sets the tag of the given @scheme[cptr]. The @scheme[tag] argument can
be any arbitrary value; other pointer operations ignore it. When a
cpointer value is printed, its tag is shown if it is a symbol, a byte
string, a string. In addition, if the tag is a pair holding one of
these in its @scheme[car], the @scheme[car] is shown (so that the tag
can contain other information).
can contain other information).}
\scmutilsection{memmove}{cptr \Optional{offset-k}
src-cptr \Optional{src-offset-k}
count-k \Optional{ctype}}
Copies to @var{cptr} from @var{src-cptr}. The destination pointer can be
offset by an optional @var{offset-k}, which is in bytes if @var{ctype}
is not supplied, or in @var{ctype} instances when supplied.
The source pointer can be similarly offset by @var{src-offset-k}.
The number of bytes copied from source to destination is determined by @var{count-k},
which is also in bytes if @var{ctype} is not supplied, or in @var{ctype}
instances when supplied.
@defproc*[([(memmove [cptr cpointer?]
[src-cptr cpointer?]
[count nonnegative-exact-integer?]
[type ctype? _byte])
void?]
[(memmove [cptr cpointer?]
[offset exact-integer?]
[src-cptr cpointer?]
[count nonnegative-exact-integer?]
[type ctype? _byte])
void?]
[(memmove [cptr cpointer?]
[offset exact-integer?]
[src-cptr cpointer?]
[src-offset exact-integer?]
[count nonnegative-exact-integer?]
[type ctype? _byte])
void?])]{
\scmutilsection{memcpy}{cptr \Optional{offset-k}
src-cptr \Optional{src-offset-k}
count-k \Optional{count-ctype}}
Copies to @scheme[cptr] from @scheme[src-cptr]. The destination
pointer can be offset by an optional @scheme[offset], which is in
@scheme[type] instances. The source pointer can be similarly offset
by @scheme[src-offset]. The number of bytes copied from source to
destination is determined by @scheme[count], which is in @scheme[type]
instances when supplied.}
Like @scheme[memmove], but the result is
undefined if the destination and source overlap.
@defproc*[([(memcpy [cptr cpointer?]
[src-cptr cpointer?]
[count nonnegative-exact-integer?]
[type ctype? _byte])
void?]
[(memcpy [cptr cpointer?]
[offset exact-integer?]
[src-cptr cpointer?]
[count nonnegative-exact-integer?]
[type ctype? _byte])
void?]
[(memcpy [cptr cpointer?]
[offset exact-integer?]
[src-cptr cpointer?]
[src-offset exact-integer?]
[count nonnegative-exact-integer?]
[type ctype? _byte])
void?])]{
\scmutilsection{memset}{cptr \Optional{offset-k}
byte
count-k \Optional{count-ctype}}
Like @scheme[memmove], but the result is undefined if the destination
and source overlap.}
Similar to @scheme[memmove], but the destination is uniformly filled with
@var{byte} (i.e., an exact integer between 0 and 255 includive).
@defproc*[([(memset [cptr cpointer?]
[byte byte?]
[count nonnegative-exact-integer?]
[type ctype? _byte])
void?]
[(memset [cptr cpointer?]
[offset exact-integer?]
[byte byte?]
[count nonnegative-exact-integer?]
[type ctype? _byte])
void?])]{
%------------------------------------------------------------
Similar to @scheme[memmove], but the destination is uniformly filled
with @scheme[byte] (i.e., an exact integer between 0 and 255
inclusive).}
@; ------------------------------------------------------------
@section{Memory Management}
For general information on C-level memory management with MzScheme, see
{\InsideMzSchemeManual}.
For general information on C-level memory management with PLT Scheme,
see @|InsideMzScheme|.
\scmutilsection{malloc}{bytes-or-type
\Optional{type-or-bytes}
\Optional{cptr}
\Optional{mode} \Optional{'fail-ok}}{procedure}
@defproc[(malloc [bytes-or-type (or/c exact-nonnegative-integer? ctype?)]
[type-or-bytes (or/c exact-nonnegative-integer? ctype?) #, @elem{absent}]
[cptr cpointer? #, @elem{absent}]
[mode (one-of/c 'nonatomic 'stubborn 'uncollectable
'eternal 'interior 'atomic-interior
'raw)
#, @elem{absent}]
[fail-mode (one-of/c 'failok) #, @elem{absent}])
cpointer?]{
Allocates a memory block of a specified size using a specified
allocation. The result is a @scheme[cpointer] to the allocated
memory. Alhough not reflected above, the four arguments can appear in
any order since they are all different types of Scheme objects; a size
specification is required at minimum:
Allocates a memory block of a specified size using a specified allocation. The result is a
@scheme[cpointer] to the allocated memory. The four arguments can appear in
any order since they are all different types of Scheme objects; a
size specification is required at minimum:
@itemize{
@item{If a C type @var{bytes-or-type} is given, its size is used to the block
allocation size.}
@item{If an integer @var{bytes-or-type} is given, it specifies the required
size in bytes.}
@item{If both @var{bytes-or-type} and @var{type-or-bytes} are given, then the
allocated size is for a vector of values (the multiplication of the size of
the C type and the integer).}
@item{If a @var{cptr} pointer is given, its contents is copied to the new
block, it is expected to be able to do so.}
@item{A symbol @var{mode} argument can be given, which specifies what
allocation function to use. It should be one of @indexed-scheme['nonatomic] (uses
@cpp{scheme_malloc} from MzScheme's C API), @indexed-scheme['atomic] (@cpp{scheme_malloc_atomic}),
@indexed-scheme['stubborn] (@cpp{scheme_malloc_stubborn}), @indexed-scheme['uncollectable]
(@cpp{scheme_malloc_uncollectable}), @indexed-scheme['eternal] ({\tt
scheme_malloc_eternal}), @indexed-scheme['interior]
(@cpp{scheme_malloc_allow_interior}), @indexed-scheme['atomic-interior]
(@cpp{scheme_malloc_atomic_allow_interior}), or @indexed-scheme['raw] (uses the
operating system's @cpp{malloc}, creating a GC-invisible block).}
@item{If an additional @indexed-scheme['failok] flag is given, then {\tt
scheme_malloc_fail_ok} is used to wrap the call.}
@item{If a C type @scheme[bytes-or-type] is given, its size is used
to the block allocation size.}
@item{If an integer @scheme[bytes-or-type] is given, it specifies the
required size in bytes.}
@item{If both @scheme[bytes-or-type] and @scheme[type-or-bytes] are given, then the
allocated size is for a vector of values (the multiplication of the size of
the C type and the integer).}
@item{If a @scheme[cptr] pointer is given, its content is copied to
the new block.}
@item{A symbol @scheme[mode] argument can be given, which specifies
what allocation function to use. It should be one of
@indexed-scheme['nonatomic] (uses @cpp{scheme_malloc} from PLT
Scheme's C API), @indexed-scheme['atomic]
(@cpp{scheme_malloc_atomic}), @indexed-scheme['stubborn]
(@cpp{scheme_malloc_stubborn}), @indexed-scheme['uncollectable]
(@cpp{scheme_malloc_uncollectable}), @indexed-scheme['eternal]
(@cpp{scheme_malloc_eternal}), @indexed-scheme['interior]
(@cpp{scheme_malloc_allow_interior}),
@indexed-scheme['atomic-interior]
(@cpp{scheme_malloc_atomic_allow_interior}), or
@indexed-scheme['raw] (uses the operating system's @cpp{malloc},
creating a GC-invisible block).} @item{If an additional
@indexed-scheme['failok] flag is given, then
@cpp{scheme_malloc_fail_ok} is used to wrap the call.}
}
If no mode is specified, then @scheme['nonatomic] allocation is used
when the type is any pointer-based type, and @scheme['atomic]
allocation is used otherwise.
allocation is used otherwise.}
@defproc[(free [cpointer any/c]) any]
@defproc[(free [cptr cpointer?]) void]{
Uses the operating system's @cpp{free} function for
@scheme['raw]-allocated pointers, and for pointers that a foreign
library allocated and we should free. Note that this is useful as
part of a finalizer (see below) procedure hook (e.g., on the Scheme
pointer object, freeing the memory when the pointer object is
collected, but beware of aliasing).
collected, but beware of aliasing).}
@defproc[(end-stubborn-change [cpointer any/c]) any]
@defproc[(end-stubborn-change [cptr cpointer?]) void?]{
Uses @cpp{scheme_end_stubborn_change} on the given stubborn-allocated
pointer (see {\InsideMzSchemeManual}).
pointer.}
@defproc[(malloc-immobile-cell [v any/c]) any]
@defproc[(malloc-immobile-cell [v any/c]) cpointer?]{
Allocates memory large enough to hold one arbitrary (collectable)
Scheme value, but that is not itself collectable or moved by the
memory manager. The cell is initialized with @var{v}; use the type
memory manager. The cell is initialized with @scheme[v]; use the type
@scheme[_scheme] with @scheme[ptr-ref] and @scheme[ptr-set!] to get
or set the cell's value. The cell must be explicitly freed with
@scheme[free-immobile-cell].
@scheme[free-immobile-cell].}
@defproc[(free-immobile-cell [cpointer any/c]) any]
Frees an immobile cell created by @scheme[malloc-immobile-cell].
@defproc[(free-immobile-cell [cptr cpointer?]) void?]{
%% *** Documentation for the disabled C code. A Scheme impl. is used now.
%% @defproc[(register-finalizer [cptr any/c][finalizer any/c]['pointer any/c #f]) any]
%%
%% Registers a finalizer procedure @var{finalizer-proc} with the given @var{cptr}
%% object. The finalizer is called by the primitive GC finalizer mechanism, make
%% sure no references to the object are recreated. Using @scheme[#f] for
%% @var{finalizer-proc} means erase the existing finalizer, if any. The finalizer
%% is registered for the Scheme pointer object --- be careful with aliasing.
%%
%% If an optional @var{'pointer} symbol argument is used, the finalizer is
%% registered with the actual pointer rather than the Scheme object. The
%% procedure gets a new C pointer object that points to the collected pointer.
%% This should be used only with pointers that the GC can access.
Frees an immobile cell created by @scheme[malloc-immobile-cell].}
@defproc[(register-finalizer [obj any/c][finalizer any/c]) any]
Registers a finalizer procedure @var{finalizer-proc} with the given @var{obj}
which can be any Scheme (GC-able) object. The finalizer is registered with a
will executor (see \MzSecRef{willexecutor}); it is invoked when @var{obj} is
about to be collected. (This is done by a thread that is in charge of
triggering these will executors.)
@defproc[(register-finalizer [obj any/c][finalizer (any/c . -> . any)]) void?]{
This is mostly intended to be used with cpointer objects (for freeing
unused memory that is not under GC control), but it can be used with
any Scheme object --- even ones that have nothing to do with foreign
code. Note, however, that the finalizer is registered for the
@italic{Scheme} object. If you intend to free a pointer object, then you
must be careful to not register finalizers for two cpointers that
Registers a finalizer procedure @scheme[finalizer-proc] with the given
@scheme[obj], which can be any Scheme (GC-able) object. The finalizer
is registered with a will executor; see
@scheme[make-will-executor]. The finalizer is invoked when
@scheme[obj] is about to be collected. (This is done by a thread that
is in charge of triggering these will executors.)
Finalizers are mostly intended to be used with cpointer objects (for
freeing unused memory that is not under GC control), but it can be
used with any Scheme object---even ones that have nothing to do with
foreign code. Note, however, that the finalizer is registered for the
@italic{Scheme} object. If you intend to free a pointer object, then
you must be careful to not register finalizers for two cpointers that
point to the same address. Also, be careful to not make the finalizer
a closure that holds on to the object.
For example, suppose that you're dealing with a foreign function that returns a C
string that you should free. Here is an attempt at creating a suitable type:
%
@schemeblock[
(define _bytes/free
(make-ctype _pointer
#f ; a Scheme bytes can be used as a pointer
(lambda (x)
(let ([b (make-byte-string x)])
(register-finalizer x free)
b))))
]
%
This is wrong: the finalizer is registered for @scheme[x], which is no longer
needed once the byte string is created. Changing this to register the
finalizer for @scheme[b] correct this problem, but then @scheme[free] will be
invoked on it instead of on @scheme[x]. In an attempt to fix this, we will be
careful and print out a message for debugging:
%
@schemeblock[
(define _bytes/free
(make-ctype _pointer
#f ; a Scheme bytes can be used as a pointer
(lambda (x)
(let ([b (make-byte-string x)])
(register-finalizer b
(lambda (_)
(printf "Releasing ~s\n" b)
(free x)))
b))))
]
%
but we never see any printout --- the problem is that the finalizer is a
closure that keeps a reference to @scheme[b]. To fix this, you should use the
input argument to the finalizer. Simply changing the @scheme[_] to @scheme[b]
will solve this problem. (Removing the debugging message also avoids the problem,
since the finalization procedure would then not close over @scheme[b].)
@defproc[(make-sized-byte-string [cptr any/c][length any/c]) any]
@schemeblock[
(define bytes/free
(make-ctype _pointer
#f (code:comment #, @t{a Scheme bytes can be used as a pointer})
(lambda (x)
(let ([b (make-byte-string x)])
(register-finalizer x free)
b))))
]
Returns a byte string made of the given pointer and the given
length. No copying is done. This can be used as an alternative to make
The above code is wrong: the finalizer is registered for @scheme[x],
which is no longer needed once the byte string is created. Changing
this to register the finalizer for @scheme[b] correct this problem,
but then @scheme[free] will be invoked on it instead of on @scheme[x].
In an attempt to fix this, we will be careful and print out a message
for debugging:
@schemeblock[
(define bytes/free
(make-ctype _pointer
#f (code:comment #, @t{a Scheme bytes can be used as a pointer})
(lambda (x)
(let ([b (make-byte-string x)])
(register-finalizer b
(lambda (ignored)
(printf "Releasing ~s\n" b)
(free x)))
b))))
]
but we never see any printout. The problem is that the finalizer is a
closure that keeps a reference to @scheme[b]. To fix this, you should
use the input argument to the finalizer. Simply changing
@scheme[ignored] to @scheme[b] will solve this problem. (Removing the
debugging message also avoids the problem, since the finalization
procedure would then not close over @scheme[b].)}
@defproc[(make-sized-byte-string [cptr cpointer?][length exact-nonnegative-integer?])
bytes?]{
Returns a byte string made of the given pointer and the given length.
No copying is done. This can be used as an alternative to make
pointer values accessible in Scheme when the size is known.
If @var{cptr} is an offset pointer created by @scheme[ptr-add], the
If @scheme[cptr] is an offset pointer created by @scheme[ptr-add], the
offset is immediately added to the pointer. Thus, this function cannot
be used with @scheme[ptr-add] to create a substring of a Scheme byte
string, because the offset pointer would be to the middle of a
collectable object (which is not allowed).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
}
collectable object (which is not allowed).}

View File

@ -237,8 +237,8 @@ value of @cpp{NULL} is translated to a Scheme @scheme[eof] value.}
Corresponds to Scheme ``C pointer'' objects. These pointers can have
an arbitrary Scheme object attached as a type tag. The tag is ignored
by built-in functionality; it is intended to be used by interfaces.
See @secref["tagged-pointers"] for creating pointer types that use
these tags for safety.}
See @secref["foreign:tagged-pointers"] for creating pointer types that
use these tags for safety.}
@defthing[_scheme ctype?]{

View File

@ -0,0 +1,29 @@
#lang scribble/doc
@(require "utils.ss")
@title{Macros for Unsafety}
@defform[(unsafe!)]{
Makes most of the bindings documented in this module available. See
@secref["intro"] for information on why this declaration is required.}
@defform/subs[#:literals (unsafe rename-out)
(provide* provide-star-spec ...)
([provide-star-spec (unsafe id)
(unsafe (rename-out [id external-id]))
provide-spec])]{
Like @scheme[provide], but @scheme[id]s under @scheme[unsafe] are not
actually provided. Instead, they are collected for introduction into
an importing module via a macro created by @scheme[define-unsafer].
Providing users with unsafe operations without using this facility
should be considered a bug in your code.}
@defform[(define-unsafer id)]{
Cooperates with @scheme[provide*] to define @scheme[id] as a
@scheme[unsafe!]-like form that introduces definitions for each
binding provided as @scheme[unsafe].}

View File

@ -3,6 +3,7 @@
(require scribble/manual
scribble/struct
scribble/decode
(only-in "../inside/utils.ss" cpp)
(for-syntax scheme/base)
(for-label scheme/base
scheme/contract
@ -15,7 +16,6 @@
scheme/contract
"unsafe-foreign.ss")))
(define cpp tt)
(define InsideMzScheme
(italic (secref #:doc '(lib "scribblings/inside/inside.scrbl")

View File

@ -26,13 +26,13 @@ embedded:
messages that possibly contain non-terminating nuls. The default is
@var{NULL}.}
@item{@cppdef{scheme_check_for_break} --- \index{user breaks} This
points to a function of no arguments that returns an integer. It is
used as the default user-break polling procedure in the main
thread. A non-zero return value indicates a user break, and each time
the function returns a non-zero value, it counts as a new break
signal (though the break signal may be ignored if a previous signal
is still pending). The default is @cpp{NULL}.}
@item{@cppdef{scheme_check_for_break} --- This points to a function
of no arguments that returns an integer. It is used as the default
user-break polling procedure in the main thread. A non-zero return
value indicates a user break, and each time the function returns a
non-zero value, it counts as a new break signal (though the break
signal may be ignored if a previous signal is still pending). The
default is @cpp{NULL}.}
@item{@cppdef{scheme_case_sensitive} --- If this flag is set to a
non-zero value before @cppi{scheme_basic_env} is called, then
@ -41,7 +41,7 @@ embedded:
set. The default is zero.}
@item{@cppdef{scheme_allow_set_undefined} --- This flag determines
the initial value of \scmi{compile-allow-set!-undefined}. The default
the initial value of @scheme[compile-allow-set!-undefined]. The default
is zero.}
@item{@cppdef{scheme_console_printf} --- This function pointer was

View File

@ -9,9 +9,11 @@ interpreter to be extended by a dynamically-loaded library, or
embedded within an arbitrary C/C++ program. The manual assumes
familiarity with PLT Scheme as described in @|MzScheme|.
For an alternative way of dealing with foreign code, see ..., which
describes the @schememodname[scheme/foreign] module for manipulating
low-level libraries and structures purely through Scheme code.
For an alternative way of dealing with foreign code, see
@italic{@secref[#:doc '(lib "scribblings/foreign/foreign.scrbl")
"top"]}, which describes the @schememodname[scheme/foreign] module for
manipulating low-level libraries and structures purely through Scheme
code.
@table-of-contents[]

View File

@ -23,52 +23,52 @@ Parameter values for built-in parameters are obtained and modified
through the following indices:
@itemize{
@item{@cppi{MZCONFIG_ENV} --- @scheme[current-namespace] (use @cppi{scheme_get_env})}
@item{@cppi{MZCONFIG_INPUT_PORT} --- @scheme[current-input-port]}
@item{@cppi{MZCONFIG_OUTPUT_PORT} --- @scheme[current-output-port]}
@item{@cppi{MZCONFIG_ERROR_PORT} --- @scheme[current-error-port]}
@item{@cppdef{MZCONFIG_ENV} --- @scheme[current-namespace] (use @cpp{scheme_get_env})}
@item{@cppdef{MZCONFIG_INPUT_PORT} --- @scheme[current-input-port]}
@item{@cppdef{MZCONFIG_OUTPUT_PORT} --- @scheme[current-output-port]}
@item{@cppdef{MZCONFIG_ERROR_PORT} --- @scheme[current-error-port]}
@item{@cppi{MZCONFIG_ERROR_DISPLAY_HANDLER} --- @scheme[error-display-handler]}
@item{@cppi{MZCONFIG_ERROR_PRINT_VALUE_HANDLER} --- @scheme[error-value->string-handler]}
@item{@cppdef{MZCONFIG_ERROR_DISPLAY_HANDLER} --- @scheme[error-display-handler]}
@item{@cppdef{MZCONFIG_ERROR_PRINT_VALUE_HANDLER} --- @scheme[error-value->string-handler]}
@item{@cppi{MZCONFIG_EXIT_HANDLER} --- @scheme[exit-handler]}
@item{@cppdef{MZCONFIG_EXIT_HANDLER} --- @scheme[exit-handler]}
@item{@cppi{MZCONFIG_INIT_EXN_HANDLER} --- @scheme[uncaught-exception-handler]}
@item{@cppdef{MZCONFIG_INIT_EXN_HANDLER} --- @scheme[uncaught-exception-handler]}
@item{@cppi{MZCONFIG_EVAL_HANDLER} --- @scheme[current-eval]}
@item{@cppi{MZCONFIG_LOAD_HANDLER} --- @scheme[current-load]}
@item{@cppdef{MZCONFIG_EVAL_HANDLER} --- @scheme[current-eval]}
@item{@cppdef{MZCONFIG_LOAD_HANDLER} --- @scheme[current-load]}
@item{@cppi{MZCONFIG_PRINT_HANDLER} --- @scheme[current-print]}
@item{@cppi{MZCONFIG_PROMPT_READ_HANDLER} --- @scheme[current-prompt-read]}
@item{@cppdef{MZCONFIG_PRINT_HANDLER} --- @scheme[current-print]}
@item{@cppdef{MZCONFIG_PROMPT_READ_HANDLER} --- @scheme[current-prompt-read]}
@item{@cppi{MZCONFIG_CAN_READ_GRAPH} --- @scheme[read-accept-graph]}
@item{@cppi{MZCONFIG_CAN_READ_COMPILED} --- @scheme[read-accept-compiled]}
@item{@cppi{MZCONFIG_CAN_READ_BOX} --- @scheme[read-accept-box]}
@item{@cppi{MZCONFIG_CAN_READ_PIPE_QUOTE} --- @scheme[read-accept-bar-quote]}
@item{@cppdef{MZCONFIG_CAN_READ_GRAPH} --- @scheme[read-accept-graph]}
@item{@cppdef{MZCONFIG_CAN_READ_COMPILED} --- @scheme[read-accept-compiled]}
@item{@cppdef{MZCONFIG_CAN_READ_BOX} --- @scheme[read-accept-box]}
@item{@cppdef{MZCONFIG_CAN_READ_PIPE_QUOTE} --- @scheme[read-accept-bar-quote]}
@item{@cppi{MZCONFIG_PRINT_GRAPH} --- @scheme[print-graph]}
@item{@cppi{MZCONFIG_PRINT_STRUCT} --- @scheme[print-struct]}
@item{@cppi{MZCONFIG_PRINT_BOX} --- @scheme[print-box]}
@item{@cppdef{MZCONFIG_PRINT_GRAPH} --- @scheme[print-graph]}
@item{@cppdef{MZCONFIG_PRINT_STRUCT} --- @scheme[print-struct]}
@item{@cppdef{MZCONFIG_PRINT_BOX} --- @scheme[print-box]}
@item{@cppi{MZCONFIG_CASE_SENS} --- @scheme[read-case-sensitive]}
@item{@cppi{MZCONFIG_SQUARE_BRACKETS_ARE_PARENS} --- @scheme[read-square-brackets-as-parens]}
@item{@cppi{MZCONFIG_CURLY_BRACES_ARE_PARENS} --- @scheme[read-curly-braces-as-parens]}
@item{@cppdef{MZCONFIG_CASE_SENS} --- @scheme[read-case-sensitive]}
@item{@cppdef{MZCONFIG_SQUARE_BRACKETS_ARE_PARENS} --- @scheme[read-square-brackets-as-parens]}
@item{@cppdef{MZCONFIG_CURLY_BRACES_ARE_PARENS} --- @scheme[read-curly-braces-as-parens]}
@item{@cppi{MZCONFIG_ERROR_PRINT_WIDTH} --- @scheme[error-print-width]}
@item{@cppdef{MZCONFIG_ERROR_PRINT_WIDTH} --- @scheme[error-print-width]}
@item{@cppi{MZCONFIG_ALLOW_SET_UNDEFINED} --- @scheme[allow-compile-set!-undefined]}
@item{@cppdef{MZCONFIG_ALLOW_SET_UNDEFINED} --- @scheme[allow-compile-set!-undefined]}
@item{@cppi{MZCONFIG_CUSTODIAN} --- @scheme[current-custodian]}
@item{@cppdef{MZCONFIG_CUSTODIAN} --- @scheme[current-custodian]}
@item{@cppi{MZCONFIG_USE_COMPILED_KIND} --- @scheme[use-compiled-file-paths]}
@item{@cppdef{MZCONFIG_USE_COMPILED_KIND} --- @scheme[use-compiled-file-paths]}
@item{@cppi{MZCONFIG_LOAD_DIRECTORY} --- @scheme[current-load-relative-directory]}
@item{@cppdef{MZCONFIG_LOAD_DIRECTORY} --- @scheme[current-load-relative-directory]}
@item{@cppi{MZCONFIG_COLLECTION_PATHS} --- @scheme[current-library-collection-paths]}
@item{@cppdef{MZCONFIG_COLLECTION_PATHS} --- @scheme[current-library-collection-paths]}
@item{@cppi{MZCONFIG_PORT_PRINT_HANDLER} --- @scheme[global-port-print-handler]}
@item{@cppdef{MZCONFIG_PORT_PRINT_HANDLER} --- @scheme[global-port-print-handler]}
@item{@cppi{MZCONFIG_LOAD_EXTENSION_HANDLER} --- @scheme[current-load-extension]}
@item{@cppdef{MZCONFIG_LOAD_EXTENSION_HANDLER} --- @scheme[current-load-extension]}
}

View File

@ -13,11 +13,16 @@
(except-out (all-from-out scribble/manual) var)
(for-label (all-from-out scheme/base)))
(define (as-cpp-defn name s)
(make-target-element #f
(list (as-index s))
`(cpp ,(format "~a" name))))
(define-syntax (function stx)
(syntax-case stx ()
[(_ (ret name [type arg] ...) . body)
#'(*function (cpp/sym 'ret)
(as-index (cpp/sym 'name))
(as-cpp-defn 'name (cpp/sym 'name))
(list (type/sym 'type) ...)
(list (var/sym 'arg) ...)
(lambda ()
@ -125,9 +130,25 @@
(define (var/sym s)
(*var (symbol->string s)))
(define cpp tt)
(define cppi tt)
(define cppdef (lambda (x) (as-index (tt x))))
(define cpp
(case-lambda
[(x)
(if (string? x)
(let ([e (tt x)])
(make-delayed-element
(lambda (r part ri)
(let ([d (resolve-get/tentative part ri `(cpp ,x))])
(list
(if d
(make-link-element "schemesyntaxlink" (list e) `(cpp ,x))
e))))
(lambda () e)
(lambda () e)))
(tt x))]
[more (apply tt more)]))
(define cppi cpp)
(define cppdef (lambda (x) (as-cpp-defn x (as-index (cpp x)))))
(define *var italic)
(define mzc (exec "mzc"))

View File

@ -240,16 +240,16 @@ The following are additional number predicates:
@itemize{
@item{@cppi{SCHEME_NUMBERP} --- all numerical types}
@item{@cppdef{SCHEME_NUMBERP} --- all numerical types}
@item{@cppi{SCHEME_REALP} --- all non-complex numerical types, plus
@item{@cppdef{SCHEME_REALP} --- all non-complex numerical types, plus
@cpp{scheme_complex_izi_type}}
@item{@cppi{SCHEME_EXACT_INTEGERP} --- fixnums and bignums}
@item{@cppdef{SCHEME_EXACT_INTEGERP} --- fixnums and bignums}
@item{@cppi{SCHEME_EXACT_REALP} --- fixnums, bignums, and rationals}
@item{@cppdef{SCHEME_EXACT_REALP} --- fixnums, bignums, and rationals}
@item{@cppi{SCHEME_FLOATP} --- both single-precision (when enabled)
@item{@cppdef{SCHEME_FLOATP} --- both single-precision (when enabled)
and double-precision flonums}
}