Foreign Structs (Guile Reference Manual) (original) (raw)


6.19.7 Foreign Structs

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))

Scheme Procedure: sizeof type

C Function: scm_sizeof (type)

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 astruct with ABI-conventional packing is returned.

Scheme Procedure: alignof type

C Function: scm_alignof (type)

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 astruct with ABI-conventional packing is returned.

Guile also provides some convenience syntax to efficiently read and write C structs to and from bytevectors.

Scheme Syntax: read-c-struct bv offset
((field type) …) k

Read a C struct with fields of type type... from the bytevectorbv, at offset offset. Bind the fields to the identifiersfield..., 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.

Scheme Syntax: write-c-struct bv offset
((field type) …)

Write a C struct with fields field... of type type... to the bytevectorbv, 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 astruct { 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 toread-c-struct and write-c-struct, but which uses run-time dispatch, and operates on foreign pointers instead of bytevectors.

Scheme Procedure: parse-c-struct foreign types

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->pointerand pointer->bytevector routines, one can create and parse tightly packed structs and unions by hand. See the code for(system foreign) for details.