GNU Info

Info Node: (g-wrap.info)Defining New Wrapped Types

(g-wrap.info)Defining New Wrapped Types


Next: Wrapping C Functions Prev: Creating a Wrapper Module Up: Usage
Enter node , (file) or (file)node

Defining New Wrapped Types
==========================

Though g-wrap already provides a number of wrapped types in the
gw-runtime library, 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 Timespec64 {
         long long seconds;
         long int nanoseconds;
       } timespec_64;
     
       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 C 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 "ccodegens") 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 `<miscutils:timespec-64>'.  Note that whatever name
you pick here will actually be bound to a value at runtime.  The reason
for this will become clear later.

To begin, you have to add your new type to your module.  Presume that
the following code is inside the body of the "let" defined in our
previous examples.  i.e. `mod' is bound to the miscutils g-wrap module
you're creating.


       (let ((wt (gw:wrap-type mod
                               '<miscutils:timespec-64>
                               "Timespec64"
                               "const Timespec64")))
         #t)

The above bit of code does nothing more than create a new (empty)
wrapped type.  In order to make this type useful, you have to define
some relevant ccodegens.  Let's start with one that will tell g-wrap how
to check instances of this type that are passed in as arguments from the
Scheme side.  Presume this code is placed where the `#t' is in the
"let" above.


         (gw:type-set-scm-arg-type-test-ccodegen!
          wt
          (lambda (param)
            (list
             "msu_scm_timespec_64_p(" (gw:param-get-scm-name param) ")")))

A ccodegen is just a lambda that generates 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 scm-arg-type-test-ccodegen is always called with just one argument,
a representation of the function parameter whose type should be checked.
`gw:param-get-scm-name' is an accessor for these parameter
representations that will return the name of the C variable that will be
holding the Scheme value passed as an argument to any function with a
parameter of this wrapped type.  The C code returned by your
scm-arg-type-test-ccodegen should check this Scheme value, and return a
non-zero value if the argument is of the correct type.  The above
ccodegen presumes that the C function `msu_scm_timesepec_64_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 check the type of incoming SCM
arguments, but it doesn't know how to convert between the C and Scheme
representations of your Timespec64 values.  To teach it, you need to
define a `pre-call-arg-ccodegen'.  This ccodegen is used to generate C
code in the wrapper functions that will be executed after all of the
Scheme values passed as arguments to the wrapped function have been
checked by their respective test code (generated by their respective
`scm-arg-type-test-ccodegen' functions), but before the call to the C
function being wrapped.

When you specify a `pre-call-arg-ccodegen', it will be called with one
argument.  As with your `scm-arg-type-test-ccodegen', this argument
will be a representation of the function parameter that's being
processed, and the code you generate with this ccodegen will, in most
cases, just be responsible for converting from the Scheme representation
for the argument into a C representation that can be passed to the C
function that's being wrapped.  In addition to the function used above,
`gw:param-get-scm-name', you can also ask the param for the name of the
C variable that will be used in the call to the C function that's being
wrapped by calling `gw:param-get-c-name'.

So in our Timespec64 example, we might defined our pre-call ccodegen
like this:


         (gw:type-set-pre-call-arg-ccodegen!
          wt
          (lambda (param)
            (let* ((scm-name (gw:param-get-scm-name param))
                   (c-name (gw:param-get-c-name param)))
              (list
               c-name
               " = msu_scm_timespec64_to_c_timespec64(" scm-name ");\n"))))

As you can see this ccodegen grabs the Scheme name and the C names of
the param and then assignes the C value to be the result of converting
the scheme value with the hypothetical
`msu_scm_timespec64_to_c_timespec64' function.  You can do other things
in your pre-call ccodegen, but making sure that a C representation of
the Scheme value ends up in the variable named "c-name" is usually the
critical thing.

Note too that if you don't like the "tree of strings" approach to
generating the C code, you can always rewrite the code like this:(2)


         (gw:type-set-pre-call-arg-ccodegen!
          wt
          (lambda (param)
            (simple-format #f
                           "~A = ~A;\n"
                           (gw:param-get-scm-name param)
                           (gw:param-get-c-name param))))

STOPPED HERE BELOW UNFINISHED...


         (gw:type-set-call-ccodegen!
          wt
          (lambda (standard-c-call-gen result func-call-code)
            (list (gw:result-get-c-name result) " = " func-call-code ";\n")))
     
       (define (add-standard-result-handlers! type c->scm-converter)
         (define (standard-pre-handler result)
           (let* ((ret-type-name (gw:result-get-proper-c-type-name result))
                  (ret-var-name (gw:result-get-c-name result)))
             (list "{\n"
                   "    " ret-type-name " " ret-var-name ";\n")))
     
         (gw:type-set-pre-call-result-ccodegen! type standard-pre-handler)
     
         (gw:type-set-post-call-result-ccodegen!
          type
          (lambda (result)
            (let* ((scm-name (gw:result-get-scm-name result))
                   (c-name (gw:result-get-c-name result)))
              (list
               (c->scm-converter scm-name c-name)
               "  }\n")))))
     
         (add-standard-result-handlers!
          wt
          (lambda (scm-name c-name)
            (let ((old-func
                   (lambda (x)
                     (list "gnc_timespec2timepair(" x ")"))))
              (list scm-name
                    " = "
                    (old-func c-name)
                    ";\n")))))

Also, note that although there are a bunch of ccodegens you can define
for a type, many are optional.

Note that though the code here is C-code, you can also in-line scheme
code in many cases with a special construct...

---------- Footnotes ----------

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

(2) This could result in a loss in portability across Scheme
implementations, though right now g-wrap only works with Guile, and may
never be ported anywhere else.


automatically generated by info2www version 1.2.2.9