Next: Why Create a Wrapper Generator?, Previous: Introduction, Up: Introduction
Given a definition of the types and prototypes for a given C interface, G-Wrap will automatically generate the C code that provides access to that interface and its types from the Scheme level.
To use G-Wrap, you must make sure it knows how to handle all of the C types that the functions you will be calling take as arguments and return as values, and you must also tell G-Wrap about the C prototypes of these functions. Since G-Wrap already knows about quite a few of the common C types, for many simple C APIs, you may not need to add any custom type specifications.
G-Wrap is implemented as a Guile module, and so its interface is a
programmatic one. You tell G-Wrap about your functions and types, and
ask it to generate wrappers for you by calling the functions exported
from the (g-wrap)
module.
As a simple example, if you wanted to wrap a C API that contained only one function with a prototype like this
int frob(int x, double y);
a complete G-Wrap specification would look like this:
(define-module (my-wrapset) #:use-module (oop goops) #:use-module (g-wrap) ;; the core of G-Wrap #:use-module (g-wrap guile) ;; defines `<gw-guile-wrapset>' #:use-module (g-wrap guile ws standard) ;; the `standard' wrapset #:export (<my-wrapset>)) ;; Define a wrapset for Guile, and specify the wrapsets ;; it depends on. (define-class <my-wrapset> (<gw-guile-wrapset>) #:id 'my-wrapset #:dependencies '(standard)) (define-method (initialize (ws <my-wrapset>) initargs) (next-method) ;; Populate the wrapset. (wrap-function! ws #:name 'frob #:returns 'int #:arguments '((int x) (double y)) #:c-name "frob" #:description "Return the result of frobbing x and y."))
You can see that the specification is encapsulated in a Guile module
that exports a class - <my-wrapset>
derived from
<gw-wrapset>
. A wrapset is the G-Wrap counterpart to some API;
it describes how the API looks in C and provides additional
information for the target language. The wrapset above doesn't yet
provide any such information; it is abstract and can be refined
for every target language.
In the define-class
statement, the wrapset defines its ID, a
globally unique identifier, and declares a dependency on the
standard
wrapset, which provides the basic C types. In the
initialize
method, the frob
function is wrapped,
providing all relevant data such as return and argument types.
To refine the above wrapset for Guile, you derive from it and add the necessary information:
(define-module (my-guile-wrapset) #:use-module (oop goops) #:use-module (g-wrap) #:use-module (g-wrap guile) #:use-module (g-wrap guile ws standard) #:export (<my-guile-wrapset>)) (define-class <my-guile-wrapset> (<my-wrapset> <gw-guile-wrapset>)) (define-method (initialize (ws <my-guile-wrapset>) initargs) ;; Tell parent methods the name the generated Guile module ;; should have. (next-method ws (append '(#:module (my-module)) initargs)))
We derive our Guile-specific wrapset from the generic wrapset
<my-wrapset>
and the base class for all Guile wrapsets
<gw-guile-wrapset>
. In initialize
, we tell G-Wrap that
the wrapset should eventually reside in the Guile module
my-module
.
Once G-Wrap has seen this specification, the code for the wrappers can be generated with this code:
(use-modules (g-wrap) (g-wrap guile) (my-guile-wrapset)) ;; Generate wrapper code. (generate-wrapset 'guile 'my-wrapset "my-wrapset")
This will produce the C file my-wrapset.c, that can be compiled
into a shared library which will, when loaded by Guile, define a
Scheme function named frob
which you can call as expected:
(frob 4 2.3)
When it comes to defining how C types should be handled, G-Wrap is very flexible. G-Wrap provides a fairly generic underlying infrastructure which can then be customized for particular purposes by teaching it how to generate code for a given type or wrapset. You can take explicit control over what code is generated in the wrapper module to handle arguments and return values of a given type (both for their initialization and cleanup), what code is generated to handle the wrapper module's global initialization, and what code is generated to provide global declarations.
At the lowest level, there is a "wrapping protocol" for types, which you can plug into to describe how a type is converted from and to the target language as well as other aspects of the type. G-Wrap comes with a few of these more types pre-defined. This set should cover most of the common cases, but you can extend this set if needed. The wrapper types currently available by default include:
double
and char
.
Furthermore, G-Wrap allows you to define types in one wrapper module
that can then be used by other wrapper modules. So as an example, you
should be able to define a GLib wrapper module that provides wrapper
specifications for GList*
's that other wrapper modules can then
import and use in their own wrapper function prototypes for argument
and result types. The goal is for this to allow different wrapper
modules to be able to safely exchange data among their wrapped
functions when they share common wrapped types.
As mentioned, G-Wrap itself is implemented as purely Scheme-code Guile modules. This means that you you can wrap functions for multiple modules on the fly from any invocation of Guile.