Binding Nested Structs in Chicken Scheme
I have a project right now in Chicken Scheme that has led me to need to create some bindings to a C library for the first time. It is every bit as challenging as I ever expected, but I'm making do, and making progress. One quandary that came up was how to write a binding for nested structs. I worked at the problem myself, searched the web to no avail, enlisted the help of friends, and finally asked in the Chicken IRC channel, and this is what I learned.
Here is a bit of C code similar to what I wanted to create a Chicken binding for. Notice that the first struct is a member of the second by value, not by pointer.
typedef struct {
unsigned short red;
unsigned short green;
unsigned short blue;
unsigned short alpha;
} RGBAColor;
typedef struct {
char *name;
RGBAColor color;
} RGBANameColor;
I'm using Chicken 4.6.0, with the help of the foreigners
egg to write my
bindings.
(import chicken scheme foreign foreigners)
Here are the two define-foreign-record-type
forms to bind the nested
structs. The part that had evaded me for so long was, in the form for
RGBANameColor, the type of the nested struct. It is given as (struct
RGBAColor)
. I had tried as many other combinations as I could think of,
and almost resorted to writing scheme code to build up the structure as a
blob, but this is the right way to nest a struct by value.
(define-foreign-record-type (rgbacolor RGBAColor)
(constructor: make-rgbacolor)
(destructor: free-rgbacolor)
(unsigned-short red rgbacolor-red rgbacolor-red-set!)
(unsigned-short green rgbacolor-green rgbacolor-green-set!)
(unsigned-short blue rgbacolor-blue rgbacolor-blue-set!)
(unsigned-short alpha rgbacolor-alpha rgbacolor-alpha-set!))
(define-foreign-record-type (rgbanamecolor RGBANameColor)
(constructor: make-rgbanamecolor)
(destructor: free-rgbanamecolor)
(c-string name rgbanamecolor-name rgbanamecolor-name-set!)
((struct RGBAColor) color rgbanamecolor-color))
The next catch is that Chicken 4.6.0 does not support automatically creating a setter for the nested struct. If you need a setter for it, the workaround is to simply define it yourself:
(define rgbanamecolor-color-set!
(foreign-lambda* void ((rgbanamecolor x) (rgbacolor c))
"x->color = *c;"))
Now let's test it. Put all the code into a file, the C code at the top
between #>
and <#
marks, and here is a snippet to do some basic
manipulations of these objects:
(let ((nc (make-rgbanamecolor))
(r (make-rgbacolor)))
(rgbacolor-red-set! r 5)
(rgbacolor-green-set! r 7)
(rgbacolor-blue-set! r 11)
(rgbacolor-alpha-set! r 100)
(rgbanamecolor-name-set! nc "foo")
(rgbanamecolor-color-set! nc r)
(printf "~A: ~S ~S ~S ~S~%"
(rgbanamecolor-name nc)
(rgbacolor-red (rgbanamecolor-color nc))
(rgbacolor-green (rgbanamecolor-color nc))
(rgbacolor-blue (rgbanamecolor-color nc))
(rgbacolor-alpha (rgbanamecolor-color nc))))
Here is the expected output of the program:
foo: 5 7 11 100
Big thanks to zbigniew on the Chicken IRC channel for helping me with this problem.