((section 2 "Outdated egg!" (p "This is an egg for CHICKEN 4, the unsupported old release.  You're almost certainly looking for " (int-link "/eggref/5/foreigners" "the CHICKEN 5 version of this egg") ", if it exists.") (p "If it does not exist, there may be equivalent functionality provided by another egg; have a look at the " (link "https://wiki.call-cc.org/chicken-projects/egg-index-5.html" "egg index") ". Otherwise, please consider porting this egg to the current version of CHICKEN.")) (section 2 "foreigners" (toc) (section 3 "Synopsis" (p (b "foreigners") " is a collection of useful FFI macros.") (p "To use this extension, import both the core FFI syntax and the syntax we provide:") (pre "(import foreign)     ; core FFI syntax\n(import foreigners)  ; this egg")) (section 3 "Interface" (section 4 "define-foreign-record-type" (def (sig (syntax "(define-foreign-record-type name [decl ...] slot ...)" (id define-foreign-record-type))) (p "This macro defines accessor procedures for a C structure definition.  It is the counterpart to Chicken 3's " (tt "define-foreign-record") ", but modeled after SRFI 9's " (tt "define-record-type") " for better hygiene.") (p (tt "NAME") " should either be a symbol or a list of the form " (tt "(TYPENAME FOREIGNNAME)") ". If " (tt "NAME") " is a symbol, then a C declaration will be generated that defines a C struct named " (tt "struct NAME") ". If " (tt "NAME") " is a list, then no struct declaration will be generated and " (tt "FOREIGNNAME") " should name an existing C record type.  A foreign-type specifier named " (tt "NAME") " (or " (tt "TYPENAME") ") will be defined as a pointer to the given C structure.")) (section 5 "Slot definitions" (p "A " (tt "SLOT") " definition should be a list of one of the following forms:") (pre "(type slotname getter [setter])\n(type (slotname size) getter [setter])") (p "where " (tt "TYPE") ", " (tt "SLOTNAME") ", " (tt "GETTER") " and " (tt "SETTER") " are all symbols.  The latter form defines an array of " (tt "SIZE") " elements of the type " (tt "TYPE") " embedded in the structure.  For every slot, a getter procedure and optionally a setter procedure will be generated.")) (section 5 "The getter" (pre "(getter foreign-record-pointer [index])") (p "A procedure of one argument (a pointer to a C structure), that returns the slot value of the slot " (tt "SLOTNAME") ". If a " (tt "SIZE") " has been given in the slot definition, then an additional argument " (tt "INDEX") " is required that specifies the index of an array-element.")) (section 5 "The setter" (pre "(setter foreign-record-pointer [index] value)") (p "A procedure of two arguments (a pointer to a C structure) and a value, that sets the slot value of the slot " (tt "SLOTNAME") " in the structure. If a " (tt "SIZE") " has been given in the slot definition, then an additional argument " (tt "INDEX") " is required for the array index.") (p "If the setter is omitted from the slot form, no setter procedure will be generated.  This is the equivalent of the " (tt "(const ...)") " modifier in Chicken 3.  Slots of the types " (tt "(struct ...)") " or " (tt "(union ...)") " are accessed as pointers to the embedded struct (or union) and no setter will be generated regardless.")) (section 5 "record declarations" (p "Additionally, special record declarations (" (tt "DECL ...") ") may be given, where each declaration consists of a list of the form " (tt "(KEYWORD ARGUMENT ...)") ".  Declarations " (i "must") " occur before slots.  The available declarations are:") (section 6 "constructor" (pre "(constructor: NAME)") (p "Generate a constructor-procedure with no arguments that has the name " (tt "NAME") " (a symbol) that returns a pointer to a structure of this type. The storage will be allocated with " (tt "malloc(3)") ".") (p "Memory allocated by a constructor is " (i "not") " automatically freed. You will probably need to generate a destructor (see below) and call it manually, or register a finalizer yourself.")) (section 6 "destructor" (pre "(destructor: NAME)") (p "Generate a destructor function with the name " (tt "NAME") " that takes a pointer to a structure of this type as its single argument and releases the storage with " (tt "free(3)") ". If the argument is " (tt "#f") ", the destructor procedure does nothing.")))) (section 4 "define-foreign-enum-type" (def (sig (syntax "(define-foreign-enum-type (type-name native-type [default-value]) (scheme->number number->scheme) enumspec ...)" (id define-foreign-enum-type))) (p "Defines a foreign-type that maps the elements of a C/C++ enum (or a enum-like list of constants) to and from a set of symbols.  Also defines a pair of conversion procedures which can be used to do this mapping manually, along with a foreign-variable for each enumeration value.") (ul (li (tt "type-name") " is the foreign type name;") (li (tt "native-type") " is the underlying type, e.g. " (tt "int") " or " (tt "(enum \"foo\")") ";") (li (tt "default-value") " is the result of mapping from the native type when no such mapping exists.  When supplied the form is used unquoted, otherwise the result is '();") (li (tt "scheme->number") " is the name of a procedure which will convert a symbol (or list of symbols) into the corresponding numeric enum value;") (li (tt "number->scheme") " is a procedure converting an enum into a symbol;") (li (tt "enumspec") " is a specification for each enum value, described in detail below.")) (p "The two procedures are defined by the macro in lexical scope for your use, and are also used in the foreign-type conversion:") (pre "(define-foreign-type type-name native-type scheme->number number->scheme)") (p "If passed a list of symbols, " (tt "scheme->number") " will combine their numeric values with " (tt "bitwise-ior") ".  However, " (tt "number->scheme") " returns a single symbol and will not decompose a value into its component symbols; rather, you will probably get " (tt "default-value") ".")) (section 5 "enumspec" (p "The most general form of " (tt "enumspec") " is " (tt "((symbol var-name) native-name destination-value)") ".  Some of these items can be omitted; see below.  For each of these forms, a foreign-variable is generated:") (pre "(define-foreign-variable var-name native-type native-name)") (p "Furthermore, forward and reverse mappings are added:") (pre "  {{scheme->number}}: from {{symbol}} to the foreign value {{native-name}}; also accepts {{symbol}}'s keyword equivalent\n  {{number->scheme}}: from {{native-name}} to {{destination-value}}") (p "Normally, " (tt "destination-value") " is omitted from the spec, and the " (tt "symbol") " is used as the destination value, making the mapping reversible.  But occasionally, overriding the value can be useful, for example in changing a zero return value to #f.") (p "If " (tt "native-name") " is omitted, " (tt "symbol") " is used --- useful if you want your Scheme symbols to reflect the underlying constant names.  If " (tt "var-name") " is omitted, a temporary generated symbol is used, so the foreign-variable will not be visible to your compilation unit.") (p "Stating the " (tt "enumspec") " rules explicitly:") (pre "((s v) n d)                  ; general form\n((s v) n)   -> ((s v) n s)\n((s) n d)   -> ((s t) n d)   ; t is a temporary name (gensym)\n((s) n)     -> ((s t) n s)\n(s n d)     -> ((s s) n d)\n(s n)       -> ((s s) n s)\ns           -> ((s s) s s)")))) (section 3 "Examples" (section 4 "define-foreign-enum-type" (p "Example of a module which uses " (tt "define-foreign-enum-type") ":") (pre "#>\n#define STATUS_OK 0\n#define STATUS_BUSY 1\n#define STATUS_FAIL 2\n\nstatic int _status = STATUS_OK;\nint get_status() { return _status; }\nvoid set_status(int s) { _status = s; }\n<#\n\n(module enum-1\n  (status->int int->status\n   get-status set-status busy?)\n\n  (import scheme chicken foreigners)\n\n  (define-foreign-enum-type (status int)\n    (status->int int->status)\n    ((ok status/ok) STATUS_OK)\n    ((busy status/busy) STATUS_BUSY)\n    ((fail status/fail) STATUS_FAIL))\n\n  (define get-status\n    (foreign-lambda status \"get_status\"))\n  (define set-status\n    (foreign-lambda void \"set_status\" status))\n  (define (busy? s)\n    (= s status/busy))\n  )") (p "This may be tested with:") (pre "(require-extension test)\n(require-extension enum-1)\n\n(test \"status->int\" 1 (status->int 'busy))\n(test \"int->status\" 'fail (int->status 2))\n(set-status 'busy)\n(test \"get-status\" 'busy (get-status))\n(test \"status/busy via busy?\" #t (busy? (status->int (get-status))))")) (section 4 "define-foreign-record-type" (p "Example that wraps the " (tt "servent") " structure --- see " (tt "getservent(3)") ".") (pre " #>\n struct servent {\n   char  *s_name;\n   char **s_aliases;\n   int    s_port;\n   char  *s_proto;\n };                 <#\n\n(define-foreign-type port-number int      ; used by servent type\n  (foreign-lambda int \"htons\" int)\n  (foreign-lambda int \"ntohs\" int) )\n\n(define-foreign-record-type (servent \"struct servent\")\n  (constructor: make-servent)\n  (destructor: free-servent)\n  (c-string s_name servent-name servent-name-set!)\n  (c-pointer s_aliases servent-s_aliases)              ; const\n  (port-number s_port servent-port servent-port-set!)\n  (c-string s_proto servent-proto servent-proto-set!))"))) (section 3 "Author" (p "Jim Ursetto")) (section 3 "Version history" (ul (li "1.1 define-foreign-enum-type bugfix") (li "1.0 Initial release"))) (section 3 "License" (p "BSD."))))