Next: , Previous: G-Wrap's High-level API, Up: G-Wrap's High-level API


4.2.1 Wrapping a C Function

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.

— method: wrap-function! (wrapset <gw-wrapset>) . args

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 type specifier may in turn be one of the following:

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.

The aggregated Type Qualifier

The 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-functions (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.