chickadee » jiffi » define-struct-accessors

(define-struct-accessors ...)syntax

Defines getters and/or setters for one or more fields of a C struct/union. Also generates type declarations for the getters and setters.

Usage:

(define-struct-accessors
  (ARMOR-NAME "STRUCT_NAME" PRED UNWRAP)
  ("FIELD_NAME"
   type:   (SCHEME-TYPE FOREIGN-TYPE)   ; or `type: FOREIGN-TYPE`
   getter: GETTER                       ; optional
   g-conv: G-CONV                       ; optional
   setter: SETTER                       ; optional
   s-conv: S-CONV)                      ; optional
  ...)

ARMOR-NAME is the name of the record type that is unwrapped by UNWRAP, such as the record name that was passed to define-armor-type.

"STRUCT_NAME" is a string containing the name of the C struct/union, exactly as it appears in C.

PRED is an existing type predicate for the armor type, such as a procedure defined with define-armor-type.

UNWRAP is an existing armor unwrapper procedure, such as a procedure defined with define-armor-type.

"FIELD_NAME" is a string containing the name of the field, exactly as it appears in C. You can access nested data by using "." or "->" in the field name. For example, "foo.bar" would access the foo field of the struct/union, then the bar field of foo. If the foo field holds a pointer, you would write "foo->bar" instead.

You can define multiple accessors with the same "FIELD_NAME", as long as the GETTER and SETTER names are different. For example, you might want to define a getter that returns the field as an integer, and another getter that uses an enum converter as G-CONV to convert the field into a symbol.

SCHEME-TYPE is a Scheme type specifier, which is used as the return type of the getter (after conversion) and as the argument type of the setter (before conversion). FOREIGN-TYPE is a foreign type specifier for the struct field. If SCHEME-TYPE and FOREIGN-TYPE are the same, you can write type: FOREIGN-TYPE.

GETTER and SETTER are procedure names to define as the getter and setter procedures. Alternatively, if SETTER is the form (setter GETTER), the field will be settable using (set! (GETTER struct) value). The procedures are created using struct-getter and struct-setter. You can omit either keyword clause if you don't need a getter or setter.

G-CONV is an existing procedure used by GETTER to convert the field value before returning it. For example, this could be a int->symbol enum converter defined with define-enum-group, an enum unpacker defined with define-enum-unpacker, or an armor wrapper defined with define-armor-type. The procedure must accept one argument, the value to convert. If G-CONV is #f or the keyword clause is omitted, the getter will not perform any conversion.

S-CONV is an existing procedure used by SETTER to validate and/or convert the argument before setting the field value. For example, this could be a symbol->int enum converter defined with define-enum-group, an enum packer defined with define-enum-packer, or an armor unwrapper defined with define-armor-type. You can perform validation by signaling an exception if the argument is invalid. The procedure must accept one argument, the value to convert. If S-CONV is #f or the keyword clause is omitted, the setter will not perform any conversion.

Example:

;; C struct and union definitions
(foreign-declare "
typedef struct {
  int type;
  FOO_KeyCode code;
  FOO_KeyMod mods;
} FOO_KeyEvent;

/* Union of two structs. */
typedef union {
  int type;
  FOO_KeyEvent key;
  FOO_SensorEvent sensor;
} FOO_Event;
")

(define-struct-accessors
  (key-event "FOO_Event" key-event? unwrap-key-event)

  ;; Access the "key" union member, then the "code" struct field
  ("key.code"
   type:   (fixnum int)
   getter: key-event-code-int
   setter: key-event-code-int-set!)

  ;; With enum converters
  ("key.code"
   type:   (symbol int)
   getter: key-event-code
   g-conv: int->keycode
   setter: key-event-code-set!
   s-conv: keycode->int))

;; Also make code settable with (set! (key-event-code struct) value)
(set! (setter key-event-code) key-event-code-set!)