Previous: , Up: Introduction   [Contents][Index]


1.3 A Simple Example

Defining and exporting your wrapset class

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.

Further customizations for Guile

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.

Let’s generate the C code, compile and use it

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)

G-wrap is very flexible!

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 it if needed.

Predefined wrapper types

The wrapper types currently available by default include:

basic

wrapper types to handle C types which can be represented most appropriately on the Scheme side by a conversion directly to a native Scheme type, e.g. double and char.

non-native

a wrapper type to handle "non-native" objects, that is, C types which can not, or should not be represented on the Scheme side by a conversion to a native Scheme representation, or types for which preserving the C-side pointer equivalency is important. Instances of this wrapper type are represented at runtime by a Guile SMOB containing the actual C pointer.

enumeration

a wrapper type to handle C enumerations which automatically grabs the right C-side values at runtime.

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 can wrap functions for multiple modules on the fly from any invocation of Guile.


Previous: , Up: Introduction   [Contents][Index]