Next: , Previous: , Up: A More Detailed Example   [Contents][Index]


2.2 Defining New Wrapped Types

Though G-Wrap already provides a number of wrapped types in the standard wrapset, there will be many cases where you will need to define your own wrapped types.

As an example, let’s presume someone has added a fine-grained, wide ranging, time type to miscutils along with a function that uses that type like this:

FIXME: Add an overview of how G-Wrap thinks about types – i.e. using a template-like process with ccodegens inserting content in important places.

  typedef struct {
    long long seconds;
    long int nanoseconds;
  } Timespec64;

  Timespec64 elapsed_time(Timespec64 start, Timespec64 finish);

and let’s further presume that we’ve decided that you want to represent Timespec64 values on the scheme side using a cons pair where the car will be the seconds and the cdr will be the nanoseconds.1

Since you’ve decided to use a native Scheme representation for the type, you’ll want to define it as an instance of the G-Wrap "normal" or "native" wrapper type. This primarily involves telling G-Wrap how to translate from the C representation of the type to the Scheme representation and vice-versa.

The way you do this, is by declaring your new wrapped type and specifying a set of C-code generator functions (or "codegens") that will handle producing the C code inside your function wrappers that will handle the translations.

The first thing you need for your new type is a G-Wrap name. For this example, we’ll use timespec64.

To begin, you have to define a new class for your type. We derive it from <gw-type>2.

  (define-class <timespec64-type> (<gw-type>))

Then you can add your new type to your wrapset. Presume that the following code is inside the body of the initialize method of our previous examples; i.e. ws is bound to the miscutils wrapset you’re creating.

    (add-type! ws (make <timespec64-type> #:name 'timespec64))

We also specialize c-type-name for our type, so G-Wrap knows about the C name of the type:

(define-method (c-type-name (type <timespec64-type>) (typespec <gw-typespec>))
  "Timespec64")

In order to make this type useful, you have to define some relevant codegen methods. Let’s start with one that will tell G-Wrap how to “unwrap” instances of this type that are passed in as arguments from the Scheme side.

  (define-method (unwrap-value-cg (type <timespec64-type>)
                                  (value <gw-value>) error-var)
    (list
      "if (SCM_FALSEP(msu_scm_timespec64_p(" (scm-var value) ")))"
      `(gw:error ,error-var type ,(wrapped-var value))
      "else\n"
      "  " (var value) " = msu_timespec64_to_c(" (scm-var value) ");\n"))

A codegen returns a list where each element in the list may either be a sub-list or a string, basically a tree of strings. In the end, this list will be flattened into one long string and inserted into the C code being generated for the wrapper module.

The unwrap-value-cg is called with the type that should be “unwrapped”, the actual value, which consists of a Scheme and a C variable. As you can see this codegen grabs the Scheme name and the C names of the value and then assignes the C value to be the result of converting the scheme value with the hypothetical msu_timespec64_to_scm function.

The list produced by unwrap-value-cg contains a special sublist, starting with gw:error. This indicates a failure condition, which is checked in the generated code and acted upon properly (i.e. raising an “argument type” error).

The above codegen presumes that the C function msu_scm_timespec64_p has been defined and returns non-zero if the given SCM is a pair and both elements are integers.

At this point G-Wrap knows how to convert and incoming Scheme arguments, but it doesn’t know how to convert from C values to Scheme, which is needed if you return a Timespec64. To teach it, you need to define the wrap-value-cg for <timespec64-type>, which has the duty to “wrap” the C value for Scheme.

So in our Timespec64 example, we might define our unwrap codegen like this:

  (define-method (wrap-value-cg (type <timespec64-type>)
                                (value <gw-value>) error-var)
    (list
      (scm-var value) " = msu_timespec64_to_scm(" (var value) ");\n"))

There are a bunch of codegens you can define for a type but if you do nothing overly fancy, you can get away with just wrap-value-cg, unwrap-value-cg. For C values that need deconstruction, there is also destruct-value-cg.


Footnotes

(1)

Though you should probably consider using the time type from SRFI-19 http://srfi.schemers.org/srfi-19/ instead.

(2)

If the wrapped type is not an aggregate value (e.g. a pointer type), <gw-rti-type> can (and should) be used instead of the more generic <gw-type>. When <gw-rti-type> is used, G-Wrap is able to use shared conversion functions and not emit code for each wrapper function, in most cases, which can considerably reduce the emitted code size


Next: , Previous: , Up: A More Detailed Example   [Contents][Index]