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):
- the symbol which should be bound to the wrapped function in Scheme at runtime.
- the symbol naming the G-Wrap wrapped type of the C function result.
- a string giving the C function's name.
- a list of the C function's arguments where each element is of the form (more details below).
- a string describing the function; this string may contain Texinfo markup.
- 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:
- if the parameter in question is an input parameter;
- if the parameter in question is an output parameter;
- if the parameter in question is a C pointer that points to memory controlled by the caller;
- if the parameter in question is a C pointer that points to memory whose control is passed to the callee (FIXME: give an example);
- if the Scheme input parameter is to be converted to a C pointer, then let G-Wrap know that
#fis a valid value for this parameter;
#fwill translate to the
NULLpointer in C;
- 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
To illustrate this, here is an example of a valid argument description list (type
mcharswill 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
available in See Creating a Wrapper Module.
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
make-stuff-container; therefore, it may not be GC'd
unless the object returned by
make-stuff-container is GC'd
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-functions (see Wrapping a C Pointer Type) are called in the right order, i.e., referrer first, dependency
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
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.