Copyright (C) 2000-2012 |
GNU Info (g-wrap.info)Defining New Wrapped TypesDefining 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 |