Next: Wrapping a C Constant, Previous: G-Wrap's High-level API, Up: G-Wrap's High-level API
As seen in See Overview, wrapping a C function using G-Wrap's
high-level functions is relatively simple. Essentially, it boils down
to a call to the wrap-function!
GOOPS method.
Add the C function described by args to the list of functions to be wrapped by wrapset. The arguments in args must contain the following named parameters (see
(ice-9 optargs)
, for more information):
#:name
- the symbol which should be bound to the wrapped function in Scheme at runtime.
#:returns
- the symbol naming the G-Wrap wrapped type of the C function result.
#:c-name
- a string giving the C function's name.
#:arguments
- a list of the C function's arguments where each element is of the form (more details below).
#:description
- a string describing the function; this string may contain Texinfo markup.
The argument list which is passed as the
#:arguments
named parameter consists of a list of argument specifiers. Each argument specifier is itself a two-element list where:
- the first element is a type specifier;
- the second element is a symbol that names the parameter.
The type specifier may in turn be one of the following:
- a symbol naming a wrapped type;
- a list containing a symbol naming a wrapped type and type qualifier (in G-Wrap's code, this is usually referred to as a typespec, although this name is quite unfortunate); the type qualifier may be one of:
in
- if the parameter in question is an input parameter;
out
- if the parameter in question is an output parameter;
caller-owned
- if the parameter in question is a C pointer that points to memory controlled by the caller;
callee-owned
- if the parameter in question is a C pointer that points to memory whose control is passed to the callee (FIXME: give an example);
null-ok
- if the Scheme input parameter is to be converted to a C pointer, then let G-Wrap know that
#f
is a valid value for this parameter;#f
will translate to theNULL
pointer in C;aggregated
- if the parameter in question points to a C object that is aggregated by the object returned by the function being wrapped (more on this below). By object returned, we mean a wrapped C pointer or WCP (see Wrapping a C Pointer Type) that is either the return value or an
out
argument.To illustrate this, here is an example of a valid argument description list (type
mchars
will be detailed later on, see C Types Provided in the Standard Wrapset):'(((mchars null-ok caller-owned) input-string) (long input-integer) ((double out) output-floating-point))
Examples of valid usage patterns of wrap-function!
are
available in See Creating a Wrapper Module.
aggregated
Type QualifierThe aggregated
type qualifier is mostly useful when wrapping C
functions (constructors) that return a new object which aggregates
objects passed as its input parameters. In order to illustrate the
need for this typespec, let's imagine the following C API:
/* Return a new stuff. */ stuff_t *make_stuff (void); /* Return a stuff container that contains a pointer to CONTAINED. Note that the container returned is _not_ responsible for deallocating the resources attached to CONTAINED. */ stuff_container_t *make_stuff_container (stuff_t *contained);
And now, imagine the following Scheme code that uses bindings of the above functions:
(define c (make-stuff-container (make-stuff)))
Suppose the two C types are wrapped as WCTs (see Wrapping a C Pointer Type). The call to make-stuff
will create a new
Scheme object (a WCP, or a “SMOB” in Guile terms, see SMOBs, for details) for the underlying C
object. However, as soon as make-stuff-container
has returned,
the Scheme code no longer holds any SMOB representing the value that
was returned by make-stuff
. Consequently, the SMOB returned by
make-stuff
may soon be garbage-collected by Guile, and its
underlying C object (originally returned by make_stuff ()
) may
soon get freed as well.
But, here is the problem: the C stuff_container_t
object still
contains a pointer to that stuff_t
object that has just been
deleted! The goal of the aggregated
typespec is to solve
situations like this one. In the example above, the wrapped function
and the container type should be specified as follows:
(wrap-as-wct! ws #:name '<stuff> #:c-type-name "stuff_t *" #:c-const-type-name "const stuff_t *" #:allowed-options '(aggregated)) ... (wrap-function! ws #:name 'make-stuff-container #:c-name "make_stuff_container" #:returns '<stuff-container> #:arguments '(((<stuff> aggregated) stuff)))
Literally, this means: “the argument stuff of
make-stuff-container
is aggregated by the object
returned by make-stuff-container
; therefore, it may not be GC'd
unless the object returned by make-stuff-container
is GC'd
too.”
Additionally, G-Wrap, in this case, enforces the finalization
order of WCPs: even if both the referrer (the
<stuff-container>
object) and its dependency (the stuff
argument) become unreachable during the same GC phase, G-Wrap makes
sure that their wcp-free-function
s (see Wrapping a C Pointer Type) are called in the right order, i.e., referrer first, dependency
second.
Note that some libraries, such as GTK+, solve this problem by relying
on reference counting: aggregating objects must increment the
reference counter of the objects they refer to. The aggregated
type qualifier facility can be seen as a solution for those C
libraries that do not use reference counting but have memory
ownership semantics similar to the ones described above. An example
of such a library is Berkeley DB.