Finally, one last note on foreign values before moving on to actually
calling foreign functions. Sometimes you need to deal with C structs,
which requires interpreting each element of the struct according to the
its type, offset, and alignment. The (system foreign)
module has
some primitives to support this.
(use-modules (system foreign))
Return the size of type, in bytes.
type should be a valid C type, like int
.
Alternately type may be the symbol *
, in which
case the size of a pointer is returned. type may
also be a list of types, in which case the size of a
struct
with ABI-conventional packing is returned.
Return the alignment of type, in bytes.
type should be a valid C type, like int
.
Alternately type may be the symbol *
, in which
case the alignment of a pointer is returned. type may
also be a list of types, in which case the alignment of a
struct
with ABI-conventional packing is returned.
Guile also provides some convenience syntax to efficiently read and write C structs to and from bytevectors.
Read a C struct with fields of type type... from the bytevector
bv, at offset offset. Bind the fields to the identifiers
field..., and return (k field ...)
.
Unless cross-compiling, the field types are evaluated at macro-expansion time. This allows the resulting bytevector accessors and size/alignment computations to be completely inlined.
Write a C struct with fields field... of type type... to the bytevector bv, at offset offset. Return zero values.
Like write-c-struct
above, unless cross-compiling, the field
types are evaluated at macro-expansion time.
For example, to define a parser and serializer for the equivalent of a
struct { int64_t a; uint8_t b; }
, one might do this:
(use-modules (system foreign) (rnrs bytevectors)) (define-syntax-rule (define-serialization (reader writer) (field type) ...) (begin (define (reader bv offset) (read-c-struct bv offset ((field type) ...) values)) (define (writer bv offset field ...) (write-c-struct bv offset ((field type) ...))))) (define-serialization (read-struct write-struct) (a int64) (b uint8)) (define bv (make-bytevector (sizeof (list int64 uint8)))) (write-struct bv 0 300 43) (call-with-values (lambda () (read-struct bv 0)) list) ⇒ (300 43)
There is also an older interface that is mostly equivalent to
read-c-struct
and write-c-struct
, but which uses run-time
dispatch, and operates on foreign pointers instead of bytevectors.
Parse a foreign pointer to a C struct, returning a list of values.
types
should be a list of C types.
Our parser and serializer example for struct { int64_t a; uint8_t
b; }
looks more like this:
(parse-c-struct (make-c-struct (list int64 uint8) (list 300 43)) (list int64 uint8)) ⇒ (300 43)
As yet, Guile only has convenience routines to support
conventionally-packed structs. But given the bytevector->pointer
and pointer->bytevector
routines, one can create and parse
tightly packed structs and unions by hand. See the code for
(system foreign)
for details.