
Similar to `struct-predicate-procedure?`. Allows Typed Racket (and the contract system generally) to avoid chaperone wrapping for cpointer predicates.
109 lines
5.1 KiB
Racket
109 lines
5.1 KiB
Racket
#lang scribble/doc
|
|
@(require "utils.rkt")
|
|
|
|
@title[#:tag "foreign:tagged-pointers"]{Tagged C Pointer Types}
|
|
|
|
The unsafe @racket[cpointer-has-tag?] and @racket[cpointer-push-tag!]
|
|
operations manage tags to distinguish pointer types.
|
|
|
|
@defproc*[([(_cpointer [tag any/c]
|
|
[ptr-type (or/c ctype? #f) _pointer]
|
|
[racket-to-c (or/c (any/c . -> . any/c) #f) values]
|
|
[c-to-racket (or/c (any/c . -> . any/c) #f) values])
|
|
ctype]
|
|
[(_cpointer/null [tag any/c]
|
|
[ptr-type (or/c ctype? #f) _pointer]
|
|
[racket-to-c (or/c (any/c . -> . any/c) #f) values]
|
|
[c-to-racket (or/c (any/c . -> . any/c) #f) values])
|
|
ctype])]{
|
|
|
|
Constructs a C pointer type, @racket[__tag], that gets a specific tag
|
|
when converted to Racket, and accept only such tagged pointers when
|
|
going to C. For any optional argument, @racket[#f] is treated
|
|
the same as the default value of the argument.
|
|
|
|
The @racket[ptr-type] is used as the base pointer type for
|
|
@racket[__tag]. Values of @racket[ptr-type] must be represented as
|
|
pointers.
|
|
|
|
Although any value can be used as @racket[tag], by convention it is
|
|
the symbol form of a type name---without a leading underscore. For
|
|
example, a pointer type @racketidfont{_animal} would normally use
|
|
@racket['animal] as the tag.
|
|
|
|
Pointer tags are checked with @racket[cpointer-has-tag?] and changed
|
|
with @racket[cpointer-push-tag!], which means that other tags are
|
|
preserved on an existing pointer value. Specifically, if a base
|
|
@racket[ptr-type] is given and is itself produced by @racket[_cpointer], then
|
|
the new type will handle pointers that have the new tag in addition to
|
|
@racket[ptr-type]'s tag(s). When the tag is a pair, its first value
|
|
is used for printing, so the most recently pushed tag (which
|
|
corresponds to the inheriting type) is displayed.
|
|
|
|
A Racket value to be used as a @racket[__tag] value is first passed to
|
|
@racket[racket-to-c], and the result must be a pointer that is tagged
|
|
with @racket[tag]. Similarly, a C value to be returned as a
|
|
@racket[__tag] value is initially represented as pointer tagged with
|
|
@racket[tag], but then it is passed to @racket[c-to-racket] to obtain
|
|
the Racket representation. Thus, a @racket[__tag] value is represented
|
|
by a pointer at the C level, but (unlike the given @racket[ptr-type])
|
|
it can have any representation at the Racket level as implemented by
|
|
@racket[racket-to-c] and @racket[c-to-racket].
|
|
|
|
The @racket[_cpointer/null] function is similar to @racket[_cpointer], except that
|
|
it tolerates @cpp{NULL} pointers both going to C and back. Note that
|
|
@cpp{NULL} pointers are represented as @racket[#f] in Racket, so they
|
|
are not tagged.}
|
|
|
|
|
|
@defform*[[(define-cpointer-type _id)
|
|
(define-cpointer-type _id ptr-type-expr)
|
|
(define-cpointer-type _id ptr-type-expr
|
|
racket-to-c-expr c-to-racket-expr)]]{
|
|
|
|
A macro version of @racket[_cpointer] and @racket[_cpointer/null],
|
|
using the defined name for a tag symbol, and defining a predicate
|
|
too. The @racket[_id] must start with @litchar{_}.
|
|
|
|
The optional expressions produce optional arguments to @racket[_cpointer].
|
|
|
|
In addition to defining @racket[_id] to a type generated by
|
|
@racket[_cpointer], @racket[_id]@racketidfont{/null} is bound to a
|
|
type produced by @racket[_cpointer/null] type. Finally,
|
|
@racketvarfont{id}@racketidfont{?} is defined as a predicate, and
|
|
@racketvarfont{id}@racketidfont{-tag} is defined as an accessor to
|
|
obtain a tag. The tag is the symbol form of @racketvarfont{id}.}
|
|
|
|
@defproc[(cpointer-predicate-procedure? [v any/c]) boolean?]{Returns
|
|
@racket[#t] if @racket[v] is a predicate procedure generated by
|
|
@racket[define-cpointer-type] or @racket[define-cstruct], @racket[#f]
|
|
otherwise.
|
|
|
|
@history[#:added "6.6.0.1"]{}
|
|
}
|
|
|
|
@defproc*[([(cpointer-has-tag? [cptr cpointer?] [tag any/c]) boolean?]
|
|
[(cpointer-push-tag! [cptr cpointer?] [tag any/c]) void])]{
|
|
|
|
These two functions treat pointer tags as lists of tags. As described
|
|
in @secref["foreign:pointer-funcs"], a pointer tag does not have any
|
|
role, except for Racket code that uses it to distinguish pointers;
|
|
these functions treat the tag value as a list of tags, which makes it
|
|
possible to construct pointer types that can be treated as other
|
|
pointer types, mainly for implementing inheritance via upcasts (when a
|
|
struct contains a super struct as its first element).
|
|
|
|
The @racket[cpointer-has-tag?] function checks whether if the given
|
|
@racket[cptr] has the @racket[tag]. A pointer has a tag @racket[tag]
|
|
when its tag is either @racket[eq?] to @racket[tag] or a list that
|
|
contains (in the sense of @racket[memq]) @racket[tag].
|
|
|
|
The @racket[cpointer-push-tag!] function pushes the given @racket[tag]
|
|
value on @racket[cptr]'s tags. The main properties of this operation
|
|
are: (a) pushing any tag will make later calls to
|
|
@racket[cpointer-has-tag?] succeed with this tag, and (b) the pushed tag
|
|
will be used when printing the pointer (until a new value is pushed).
|
|
Technically, pushing a tag will simply set it if there is no tag set,
|
|
otherwise push it on an existing list or an existing value (treated as
|
|
a single-element list).}
|