Cookbook(3tcl)

NAME
Cookbook - Commands in namespace Cookbook

COOKBOOK

While using CFFI to wrap a few ad-hoc functions is fairly straightforward, wrapping a large API into a complete package can be more involved as decisions how C types are translated to script level annotations. This page provides some recipes for mapping C declarations to CFFI declarations depending on the declaration context (parameter, struct field etc.).

DEFINING A FUNCTION

A function definition consists of three components.

    +------------------+ +-----------+ +--------------+
    |Calling convention+ +Return type+ +Parameter list|
    +------------------+ +-----------+ +--------------+
    Function definition syntax

Sections below detail definition of each.

  CALLING CONVENTION

Check the header file or documentation of the function for the expected calling convention. Generally, this is only an issue if the application supports the 32-bit Windows platform. Note the function calling convention may be defined through a macro such as WINAPI etc. Most Windows OS API's are defined using the stdcall convention. The default C language calling convention is either unspecified or _cdecl in C header files. Most third party API's are defined using this convention. CFFI does not support any calling convention other than these two.

    LIB function RETURNTYPE PARAMLIST
    LIB stdcall RETURNTYPE PARAMLIST
    Calling convention syntax

•   Define using stdcall if the function is defined on 32-bit Windows using __stdcall or its equivalent. Note on other platforms, including 64-bit Windows, this is treated as equivalent to function

•   Define using function otherwise after ensuring it does not use some other esoteric platform-specific convention (rare).

Examples

C

    WINAPI DWORD GetCurrentProcessId(void);
    int getpid(void);

CFFI

    KERNEL32 stdcall GetCurrentProcessId ulong {}
    LIBC function getpid int {}

Note WINAPI in the Windows SDK headers maps to __stdcall.

  RETURN TYPE DECLARATION

Void returns

    void
    Syntax for void return type

The void return type does not permit any annotations.

Examples

C

    void tzset(void)

CFFI

    LIBC function tzset void {}

Integer returns

               +-         +-      -+ -+ +-           +-                 -+ -+
    schar      | {enum E} | bitmask|  | | zero       | lasterror         |  |
    uchar      +-         +-      -+ -+ | nonzero    | winerror          |  |
    short                               | positive   | errno             |  |
    ushort                              | nonnegative| {onerror HANDLER} |  |
    int                                 +-           +-                 -+ -+
    uint
    long
    ulong
    longlong
    ulonglong
    Syntax for integer return types

•   If the returned values are constrained to a set and a symbolic representation is preferred, optionally add the enum attribute with the enumeration defined as a literal or separately through the ::cffi::enum command.

•   If a symbolic representation is preferred as above and the value is actually a bit mask, optionally add the bitmask attribute to have the value returned as a list of symbols from the enumeration.

•   If return values may indicate error conditions, add an error checking annotation like zero, nonzero etc.

•   If a error checking annotation is added and additional detail is available from the system, add lasterror, winerror or errno as appropriate. If error information is available through some other API, add the onerror handler. This may also be used to filter errors or convert error conditions to success.

Examples

C

    int open(const char *pathname, int flags);

CFFI

    XXX        LIBC open

Floating point returns

    float
    double
    Syntax for floating point return types

Floating point types float and double do not permit any annotations on return type declarations.

Examples

C

    double sin(double x);

CFFI

    LIBM function sin double {x double}

Pointer returns

Pointer return values may be dealt with at the script level either as raw pointers or as implicitly dereferenced values.

•   Declare the function as returning a raw pointer if the returned pointers point to resources that have to be freeing at a later point. If typed as implicitly dereferenced values, the raw pointer will not be available for freeing.

•   Declare the function as returning an implicitly dereferenced value if the returned pointers point to some internal or static storage that does not need to be freed. An example is the strerror C runtime library function. Note that implicitly dereferenced types are a convenience; they may be typed as raw pointers instead while the reverse will likely produce memory leaks.

The type declarations for raw and implicitly dereferenced pointers are separately described below.

Raw pointers

                  +-      -+ +-                 -+
    pointer       | counted| | nullok            |
    pointer.TAG   | unsafe | | lasterror         |
                  +-      -+ | errno             |
                             | {onerror HANDLER} |
                             +-                 -+
    Syntax for raw pointer return type

•   The TAG is optional but recommended wherever possible for additional tag-based type checks. If the pointer's target type is a struct, it is is recommended, but not mandatory, that the tag used be the name of the struct instance command. This makes it convenient to access the referenced memory using the struct methods.

•   By default, pointers returned from functions are marked as safe pointers. This is appropriate for unique pointers that are returned for allocated resources and are only valid until they are deallocated through a corresponding free function. A variation is when the pointer returned is not unique as is the case for reference counted resources. In this case the pointer should be annotated as counted. For cases where neither of the above apply, for example there is no clear transfer of ownership or freeing, or pointers are internal to the API, there are two choices. First, it can be defaulted as a safe pointer. In this case, since there is no free function, the ::cffi::pointer dispose command must be called at the appropriate time by the application to unregister the pointer. Alternatively, the unsafe annotation can be added to the declaration. In this case, there is no need to unregister the pointer but it can only be used in places (arguments and struct fields) that also have the unsafe annotation. For more details on pointer management and safety, see Pointer safety.

•   Irrespective of the pointer safety annotations, NULL pointers returned from functions will by default raise a generic Pointer is NULL exception. As for integer return values, if additional information is available from the system or API, the type can be annoted with lasterror, errno or onerror. On the other hand, if a NULL pointer is a valid return value, the nullok annotation will suppress the exception and return a NULL pointer at the script level.

Examples

C

    void *malloc(size_t sz);
    FILE *fopen(const char *pathname, const char *mode);
    git_tree_entry *git_tree_entry_byname(git_tree *, const char *filename);

CFFI

    LIBC malloc {pointer errno} {sz size_t}
    LIBC fopen {pointer.FILE errno} {pathname string mode string}
    LIBGIT git_tree_entry_byname {pointer.git_tree_entry unsafe} {
        pTree {pointer.git_tree counted}
        filename string
        }

The unsafe and counted annotations on git_tree_entry_byname arise from the libgit2 API. The returned pointer is internal to libgit2 and hence marked as unsafe while git_tree structs are reference counted.

Implicitly dereferenced pointers

Raw pointers at script level, while as versatile as their C counterparts, are not as convenient to use as plain old values. Additional steps are needed to access the memory as script level values. Declaring pointer return types as implicitly dereferenced obviates the need for this additional step. As stated earlier, implicit dereferencing should only be done when there is no need for the pointer value itself for freeing later.

For types other than pointers to character (or unicode) strings, the CFFI declaration has the form

              +-      -+
    TYPE byref| nullok |
              +-      -+
    Syntax for implicitly dereferenced pointers

where TYPE may be one of the numeric types or a STRUCT. The byref annotation on the return type declaration denotes that the function return value is nominally a pointer that references the true function result.

Examples

C

    struct git_oid *git_tree_oid(git_tree *pTree);

CFFI

    LIBGIT function git_tree_oid {struct.git_oid byref} {pTree pointer.git_tree}

A call to the git_tree_oid function will then return the git_oid struct (assumed defined previously with ::cffi::Struct) in its script level dictionary form. The actual C level pointer is not accessible but not needed as it is internal to the git_tree and not to be freed or manipulated.

As for raw pointers, a null pointer return will raise an exception by default. The nullok annotation may suppress the exception in the case that TYPE is a struct with defaults defined for all fields (possibly with the -clear option). In that the dictionary corresponding to a struct value with all fields defaulted is returned. For all other types and structs with at least one field without a default, an exception is raised irrespective of nullok.

For C pointers typed as char * which return pointers to character strings, the implicit dereference syntax is

          +-      -+
    STRING| nullok |
          +-      -+
    Syntax for character string return types

The byref is not present because string and unistring are already implicit pointers, (string byref would correspond to char **, not char *). An optional nullok annotation will cause a NULL pointer return to be mapped to the empty string instead of generating an exception.

Note that if STRING is string, an encoding identifier may be optionally attached, e.g. string.utf-8.

Examples

C

    const char *strerror(int);
    char *strstr(const char *haystack, const char *needle);

CFFI

    LIBC function strerror string {error_code int}
    LIBC function strstr {string nullok} {haystack string needle string}

Note the definition of strstr to return an empty string if the needle is not found instead of raising an exception.

Struct returns

    struct.STRUCT
    Syntax for struct return type

A struct function return type does not permit any annotations. STRUCT must be a previously defined ::Struct. Returning a struct by value is rarely seen in C API's.

Examples

C

    typedef struct {
        int quot;
        int rem;
    } div_t;
    div_t div (int numer, int denom);

CFFI

    Struct create div_t {quot int rem int}
    LIBC function struct.div_t {numer int denom int}

  PARAMETER DECLARATION

Parameters to a C function may be used to pass values to the function (input parameters), retrieve values from the function (output parameters), or (input-output parameters).

Scalar values and structs that are passed by value are always input parameters. Input parameters may also be passed by reference via a pointer, generally with a const attribute to indicate the function does not modify the referenced value.

Output and input-output parameters are always passed to C function as pointers to a location where the value is stored. At the script level the argument is the name of the variable where the value is stored and not the value itself.

Integer parameters

Integer input parameters

              +- -+ +-   -+ +-      -+ +-     -+ +-         -+ +-               -+
    schar     |in | |byref| |{enum E}| |bitmask| |{default N}| |lasterror        |
    uchar     +- -+ +-   -+ +-      -+ +-     -+ +-         -+ |winerror         |
    short                                                      |errno            |
    ushort                                                     |{onerror HANDLER}|
    int                                                        +-               -+
    uint
    long
    ulong
    longlong
    ulonglong
    Syntax for integer input parameters

•   The in annotation is optional because it is the default if none of out, retval or inout annotations is present.

•   The byref annotation should be specified iff the value is being passed by reference, i.e. the function parameter is a pointer to the value (e.g. int * at the C level). This is very rare for integer values that are input only. In either case, whether the byref parameter is present or not, the value at script level is still specified directly and not through a variable.

•   Add the enum annotation to pass enumeration symbolic values for readability purposes. Note integer values will still be accepted.

•   If the parameter holds a bit mask, add the bitmask annotation which allows the argument to be a list of integers which will be bitwise OR-ed to construct the value passed to the function. May be combined with enum to pass a list of symbols.

•   The default annotation may be used to supply a default value to be passed if no argument is supplied for the parameter when the function is invoked. All succeeding parameter in the parameter list must also have default annotations.

•   Error handling annotations like errno may be present but are ignored. This is to permit the same aliases to be used in both return and parameter declarations.

Examples

C

    int SetHandleInformation(void * hObject, unsigned long dwMask, unsigned long dwFlags);

CFFI

    cffi::enum define HANDLE_FLAGS {INHERIT 1 PROTECT_FROM_CLOSE 2}
    KERNEL32 stdcall SetHandleInformation {int nonzero lasterror} {
        hObject pointer
        dwMask {ulong {enum HANDLE_FLAGS} bitmask}
        dwFlags {ulong {enum HANDLE_FLAGS} bitmask}
    }

Output and input-output integer parameters

                    +-      -+ +-     -+ +-          -+ +-               -+
    schar     out   |{enum E}| |bitmask| |storeonerror| |lasterror        |
    uchar     inout +-      -+ +-     -+ |storealways | |winerror         |
    short     retval                     +-          -+ |errno            |
    ushort                                              |{onerror HANDLER}|
    int                                                 +-               -+
    uint
    long
    ulong
    longlong
    ulonglong
    Syntax for integer output and input-output parameters

•   Output and in-out parameters in C are always passed as pointers to the location where output value is to be stored. Thus out and inout always imply byref which is redundant and need not be specified.

•   The script level argument corresponding to out and inout parameters are the name of the variable to which the output value is to be assigned. For inout parameters this must exist at the time of function call and hold a valid value for the type.

•   Use the retval annotation for output parameters where you want the output parameter value from the function to be returned as the function result in lieu of the actual function return value. See Output parameters as function result.

•   Add the enum annotation if output values are to be mapped to symbolic names for readability. Further, if the values returned are actually bitmasks, add bitmask to have the returned value transformed to a list of symbolic names.

•   If the function return value is subject to error checks (for example null pointer checks) and fails the checks, output values are not stored in variables as they may hold garbage values. The storealways annotation indicates the output value is valid regardless of function failure. The storeonerror annotation indicates the output value is valid only on function failure (for example an error code). See Errors and output parameters.

•   Error handling annotations like errno may be present but are ignored. This is to permit the same aliases to be used in both return and parameter declarations.

Examples

C

    int GetHandleInformation(void*  hObject, unsigned long *lpdwFlags);

CFFI

    cffi::enum define HANDLE_FLAGS {INHERIT 1 PROTECT_FROM_CLOSE 2}
    KERNEL32 stdcall GetHandleInformation {int nonzero lasterror} {
        hObject pointer
        flags {ulong retval {enum HANDLE_FLAGS} bitmask}
    }

TODO

double output parameter double modf (double x, double* intpart);

Floating point parameters

Floating point input parameters

           +- -+ +-   -+ +-         -+
    float  |in | |byref| |{default N}|
    double +- -+ +-   -+ +-         -+
    Syntax for floating point input parameters

•   The in annotation is optional because it is the default if none of out, retval or inout annotations is present.

•   The byref annotation should be specified iff the value is being passed by reference, i.e. the function parameter is a pointer to the value (e.g. double * at the C level). This is very rare for integer values that are input only. In either case, whether the byref parameter is present or not, the value at script level is still specified directly and not through a variable.

•   The default annotation may be used to supply a default value to be passed if no argument is supplied for the parameter when the function is invoked. All succeeding parameter in the parameter list must also have default annotations.

Examples

C

    double sin(double x);

CFFI

    LIBM sin double {x double}

Out and in-out floating point parameters

                    +-          -+
    float    out    |storeonerror|
    double   inout  |storealways |
             retval +-          -+
    Syntax for floating point output and input-output parameters

•   Output and in-out parameters in C are always passed as pointers to the location where output value is to be stored. Thus out and inout always imply byref which is redundant and need not be specified.

•   The script level argument corresponding to out and inout parameters are the name of the variable to which the output value is to be assigned. For inout parameters this must exist at the time of function call and hold a valid value for the type.

•   Use the retval annotation for output parameters where you want the output parameter value from the function to be returned as the function result in lieu of the actual function return value. See Output parameters as function result.

•   If the function return value is subject to error checks (for example null pointer checks) and fails the checks, output values are not stored in variables as they may hold garbage values. The storealways annotation indicates the output value is valid regardless of function failure. The storeonerror annotation indicates the output value is valid only on function failure (for example an error code). See Errors and output parameters.

Pointer parameters

As for return values, pointer parameters to C functions can be dealt with either as raw pointers or as implicitly dereferenced values.

In general, pointers that are only used in the C API to pass arguments by reference, for example to pass large or non-scalar input values (as for structs and arrays) or receive output values, can be declared as implicitly dereferenced. The CFFI declarations for these is described in other sections with use of the byref and out annotations.

This section deals only with raw pointers where the pointer values are directly visible at the script level. This is necessary when the pointers reference resources that need to freed, or in more complex structures in memory with multiple indirections etc.

Raw pointer input parameters

                +- -+ +-   -+ +-     -+ +-              -+ +-    -+ +-              -+
    pointer     |in | |byref| |counted| |dispose         | |nullok| |{default PTRVAL}|
    pointer.TAG +- -+ +-   -+ |unsafe | |disposeonsuccess| +-    -+ +-              -+
                              +-     -+ +-              -+
    Syntax for input raw pointer parameters

•   The TAG is optional but recommended wherever possible for additional tag-based type checks. If the pointer's target type is a struct, it is is recommended, but not mandatory, that the tag used be the name of the struct instance command. This makes it convenient to access the referenced memory using the struct methods.

•   The in annotation is optional because it is the default if none of out, retval or inout annotations is present.

•   The byref annotation should be specified iff the pointer is being passed by reference, i.e. the function parameter is a pointer to a pointer. Generally, input parameters passed by reference will have const in the C parameter declaration.

•   By default, pointers passed into functions are expected to be registered as safe pointers. They may be alternatively be annotated with counted or unsafe if they are reference counted or unregistered. For more details on pointer management and safety, see Pointer safety.

•   When a C function releases the resource associated with a registered pointer (safe or counted), the pointer should be unregistered. The dispose annotation should be placed on the parameter annotation to convey to CFFI that the pointer should be unregistered. The disposeonsuccess annotation is similar except that the pointer is unregistered only in the case the function returns successfully. For more details on pointer management and safety, see Pointer safety.

•   CFFI assumes by default that functions do not accept NULL pointers and will raise an exception if an attempt is made to pass one. The nullok annotation indicates that the function allows for the pointer to be NULL.

•   The default annotation may be used to supply a default value to be passed if no argument is supplied for the parameter when the function is invoked. All succeeding parameter in the parameter list must also have default annotations.

Examples

C

    void free(void *ptr);
    void *realloc(void *ptr, size_t size);

CFFI

    LIBC function free void {ptr {pointer dispose}}
    LIBC function realloc {pointer errno} {
        ptr {pointer disposeonsuccess}
        size size_t
    }

Raw pointer out and in-out parameters

                 +-    -+ +-     -+ +-              -+ +-    -+ +-              -+ +-          -+
    pointer      |inout | |counted| |dispose         | |nullok| |{default PTRVAL}| |storealways |
    pointer.TAG  |out   | |unsafe | |disposeonsuccess| +-    -+ +-              -+ |storeonerror|
                 |retval| +-     -+ +-              -+                             +-          -+
                 +-    -+
    Syntax for output and input-output raw pointer parameters

•   Output parameters in C are always passed as pointers to the location where output value is to be stored. Thus out always implies byref which is redundant and need not be specified.

•   The script level argument corresponding to an out parameter is the name of the variable to which the output value is to be assigned.

•   Use the retval annotation for output parameters where you want the output parameter pointer value from the function to be returned as the function result in lieu of the actual function return value. See Output parameters as function result.

•   By default, pointers returned in out and in-out parameters are registered as safe pointers. They may be alternatively be annotated with counted or unsafe if they are to be registered as reference counted or not to be registered at all. For more details on pointer management and safety, see Pointer safety.

•   When a C function releases the resource associated with a registered pointer (safe or counted), the pointer should be unregistered. The dispose annotation should be placed on the parameter annotation to convey to CFFI that the pointer should be unregistered. The disposeonsuccess annotation is similar except that the pointer is unregistered only in the case the function returns successfully. The dispose and disposeonsuccess annotations is not valid with the out or retval annotations. For more details on pointer management and safety, see Pointer safety.

•   The nullok annotation indicates that pointers passed in and out of the function can be NULL. Without the annotation, an exception is raised if the pointer stored on output is NULL or in the case of inout the pointer passed in is NULL.

•   The default annotation may be used to supply a default pointer value to be passed if no argument is supplied for the parameter when the function is invoked. All succeeding parameter in the parameter list must also have default annotations. This annotation cannot be used with out and retval annotations.

•   If the function return value is subject to error checks (for example null pointer checks) and fails the checks, output values are not stored in variables as they may hold garbage values. The storealways annotation indicates the output value is valid regardless of function failure. The storeonerror annotation indicates the output value is valid only on function failure (for example an error code). See Errors and output parameters.

Examples

C

    int git_repository_open_bare(git_repository **out, const char *bare_path);
    int ConvertSidToStringSidA(SID *pSid, char **strSid);

CFFI

    LIBGIT2 function git_repository_open_bare {int zero} {
        out {pointer.git_repository counted retval}
        string bare_path
    }
    ADVAPI32 stdcall ConvertSidToStringSidA {int nonzero lasterror} {
        pSid   pointer.SID
        strSid pointer
    }
    KERNEL32 stdcall LocalFree {pointer nullok} {p {pointer dispose}}

In the libgit2 example, out is annotated as counted because git_repository is a reference counted opaque structure. The retval annotation is added to have CFFI return the output parameter value as the result of the function.

In the Win32 example, the returned pointer from ConvertSidToStringSidW needs to be freed with LocalFree and hence needs to be declared as a raw pointer. The string can be retrieved from the returned pointer with ::cffi::memory tostring before freeing it.

Struct parameters

Struct values are represented as the script level as dictionaries. This requires their definition as described in Structs and Defining structs.

Struct input parameters

                  +- -+ +-   -+ +-            -+ +-         -+
    struct.STRUCT |in | |byref| |{default DICT}| |nullifempty|
                  +- -+ +-   -+ +-            -+ +-         -+
    Syntax for struct input parameters

•   The in annotation is optional because it is the default if none of out, retval or inout annotations is present.

•   The byref annotation should be specified iff the value is being passed by reference, i.e. the function parameter is a pointer to a struct value that is to be passed. Generally, input parameters passed by reference will have const in the C parameter declaration. Irrespective of this annotation, the argument is always a dictionary, not a pointer.

•   The default annotation may be used to supply a default value to be passed if no argument is supplied for the parameter when the function is invoked. All succeeding parameter in the parameter list must also have default annotations.

•   Some C functions accept a NULL pointer for optional parameters. The nullifempty annotation identifies such parameters. In this case, passing an empty dictionary as the script level argument will result in a NULL pointer being passed to the function. This annotation only makes sense in the presence of the byref annotation.

Examples

C

    int git_checkout_head(git_repository *repo, const git_checkout_options *opts);

CFFI

    cffi::Struct create git_checkout_options {...}
    LIBGIT function git_checkout_head {int zero} {
        pRepo pointer.git_repository
        opts  {struct.git_checkout_options byref nullifempty}
    }

Output and input-output struct parameters

                         +-          -+
    struct.STRUCT out    |storeonerror|
                  inout  |storealways |
                  retval +-          -+
    Syntax for struct output and input-output parameters

•   Output and in-out parameters in C are always passed as pointers to the location where output value is to be stored. Thus out and inout always imply byref which is redundant and need not be specified.

•   The script level argument corresponding to out and inout parameters are the name of the variable to which the output value is to be assigned. For inout parameters this must exist at the time of function call and hold a valid value for the type.

•   Use the retval annotation for output parameters where you want the output parameter value from the function to be returned as the function result in lieu of the actual function return value. See Output parameters as function result.

•   If the function return value is subject to error checks (for example null pointer checks) and fails the checks, output values are not stored in variables as they may hold garbage values. The storealways annotation indicates the output value is valid regardless of function failure. The storeonerror annotation indicates the output value is valid only on function failure (for example an error code). See Errors and output parameters. These annotations are rarely applicable to struct output parameters.

Examples

C

    int GetWindowRect(void *hWnd, RECT *lpRect);

CFFI

    cffi::Struct create RECT {left int top int right int bottom int}
    LIBUSER32 stdcall GetWindowRect int {hwnd pointer rect {struct.RECT out}}

String parameters

Input string parameters

                    +- -+ +-   -+ +-         -+
    string          |in | |byref| |nullifempty|
    string.ENCODING +- -+ +-   -+ +-         -+
    unistring
    Syntax for input character string parameters

•   C does not have a string type and the string and unistring CFFI types map to pointers to nul terminated character strings: char* in the case of string and Tcl_UniChar * in the case of unistring. For input parameters, they have the const attribute.

•   The in annotation is optional because it is the default if none of out, retval or inout annotations is present.

•   For the string type an optional encoding may be specified and defaults to the system encoding. If a library API uses a specific encoding (commonly UTF-8), it may be convenient to define a type alias.

•   The byref annotation should be specified iff the value is being passed by reference, i.e. the function parameter is a pointer to the pointer to the character string (double indirection). This is rare for passing character string values that are input only. In either case, whether the byref parameter is present or not, the value at script level is still specified directly and not through a variable.

•   Some C functions accept a NULL pointer for optional parameters. The nullifempty annotation identifies such parameters. In this case, passing an empty string as the script level argument will result in a NULL pointer being passed to the function.

Examples

C

    int chdir(const char *path);

CFFI

    LIBC function chdir {int zero errno} {path string}

Output and input-output string parameters

Character strings are returned from C functions in one of two ways:

•   The C function expects the caller to supply a pointer to an array of characters. The size of this array may be a compile time constant or passed through another parameter to the function. The function then stores the string in this buffer. The C parameter declaration is of the form char *buf or char buf[].

•   The C function expects the caller to pass a pointer to a memory location where it will store the pointer to a string that is either dynamically allocated or an internal static string. The C declaration is of the form char **ptr_to_buf reflecting the double indirection.

The two are handled differently in CFFI as described below.

Output character arrays

                      +-    -+ +-          -+
    chars[N]          |out   | |storealways |
    chars.ENCODING[N] |inout | |storeonerror|
    unichars[N]       |retval| +-          -+
                      +-    -+
    Syntax for character array output parameters

This is the first case described above where the C function expects a pointer to a character (or Tcl_UniChar in the case of unichars) array.

•   The script level argument corresponding to out and inout parameters are the name of the variable to which the output value is to be assigned. For inout parameters this must exist at the time of function call and hold a valid value for the type.

•   Use the retval annotation for output parameters where you want the output parameter value from the function to be returned as the function result in lieu of the actual function return value. See Output parameters as function result.

•   The array size N in the declaration may either be an integer constant or the name of a integer type parameter in the same function declaration. See Dynamically sized arrays.

•   If ENCODING is not specified, it defaults to the system encoding.

•   If the function return value is subject to error checks (for example null pointer checks) and fails the checks, output values are not stored in variables as they may hold garbage values. The storealways annotation indicates the output value is valid regardless of function failure. The storeonerror annotation indicates the output value is valid only on function failure (for example an error code). See Errors and output parameters.

Examples

C

    char *getcwd(char *buf, size_t size);

CFFI

    LIBC function getcwd {string errno} {buf {chars[size] out} size size_t}

Output pointers to strings

The second case is when the C function parameter is a pointer to pointer to a character string (char **).

How this is best handled depends on whether the returned pointer needs to be available at the script level as a raw pointer, possibly for later freeing. See Pointer returns for a more detailed discussion about raw pointers versus implicit dereferencing. If this is the case, see Raw pointer out and in-out parameters for appropriate declarations.

If retention of the raw pointer is not required (for example, pointer to static storage), then its most convenient to implicitly declare it as an output parameter of

                    +-    -+ +-    -+ +-          -+
    string          |out   | |nullok| |storealways |
    string.ENCODING |retval| +-    -+ |storeonerror|
    unistring       +-    -+          +-          -+
    Syntax for output character string parameters

•   Output parameters in C are always passed as pointers to the location where output value is to be stored. Thus out always implies byref which is redundant and need not be specified.

•   The script level argument corresponding to an out parameter is the name of the variable to which the output value is to be assigned.

•   Use the retval annotation for output parameters where you want the output parameter value from the function to be returned as the function result in lieu of the actual function return value. See Output parameters as function result.

•   If the function return value is subject to error checks (for example null pointer checks) and fails the checks, output values are not stored in variables as they may hold garbage values. The storealways annotation indicates the output value is valid regardless of function failure. The storeonerror annotation indicates the output value is valid only on function failure (for example an error code). See Errors and output parameters. These annotations are rarely applicable to struct output parameters.

Binary string parameters

Binary strings are similar to strings except that instead of treating pointers at the C level as character strings in Tcl, they treat them as binary strings so there is no character encoding/decoding transformation applied and nul characters have no special treatment.

Input binary string parameters

           +- -+ +-   -+ +-         -+ +-               -+
    binary |in | |byref| |nullifempty| |{default DEFAULT}|
           +- -+ +-   -+ +-         -+ +-               -+
    Syntax for input binary string parameters

•   The usual C declarations that map to binary are char *, unsigned char * and void *. Further, input parameters have the const attribute.

•   The in annotation is optional because it is the default if none of out, retval or inout annotations is present.

•   The byref annotation should be specified iff the value is being passed by reference, i.e. the function parameter is a pointer to the pointer to the array of bytes string (double indirection). This is rare for passing binary values that are input only. In either case, whether the byref parameter is present or not, the value at script level is still specified directly and not through a variable.

•   Some C functions accept a NULL pointer for optional parameters. The nullifempty annotation identifies such parameters. In this case, passing an zero-length binary string as the script level argument will result in a NULL pointer being passed to the function.

•   The default annotation may be used to supply a default value to be passed if no argument is supplied for the parameter when the function is invoked. All succeeding parameter in the parameter list must also have default annotations.

Examples

C

    ssize_t write(int fd, const void *buf, size_t count);

CFFI

    LIBC function write {ssize_t nonnegative errno} {
        fd int
        buf binary
        count size_t
    }

Output and input-output binary string parameters

Binary strings are returned from C functions in one of two ways:

•   The C function expects the caller to supply a pointer to a memory block interpreted simply as an array of bytes. The size of this memory block may be a compile time constant or passed through another parameter to the function. The function then stores the data in this memory buffer. The C parameter declaration is generally one of void *, char *buf, char buf[] (or the unsigned variants).

•   The C function expects the caller to pass a pointer to a memory location where it will store the pointer to a block of memory that is either dynamically allocated or an internal buffer. The C declaration is of the form char **, unsigned char ** or void ** reflecting the double indirection.

The two are handled differently in CFFI as described below.

Output byte arrays

             +-    -+ +-          -+
    bytes[N] |out   | |storealways |
             |inout | |storeonerror|
             |retval| +-          -+
             +-    -+
    Syntax for byte array output parameters

This is the first case described above where the C function expects a pointer to a byte array.

•   The script level argument corresponding to out and inout parameters are the name of the variable to which the output value is to be assigned. For inout parameters this must exist at the time of function call and hold a Tcl binary string.

•   Use the retval annotation for output parameters where you want the output parameter value from the function to be returned as the function result in lieu of the actual function return value. See Output parameters as function result.

•   The array size N in the declaration may either be an integer constant or the name of a integer type parameter in the same function declaration. See Dynamically sized arrays.

•   If the function return value is subject to error checks (for example null pointer checks) and fails the checks, output values are not stored in variables as they may hold garbage values. The storealways annotation indicates the output value is valid regardless of function failure. The storeonerror annotation indicates the output value is valid only on function failure (for example an error code). See Errors and output parameters.

The difference between an array declared as bytes[N] versus schar[N] or uchars[N] is that while both might be arrays of char or unsigned char at the C level, the former declaration will result in a Tcl binary string at the script level while the latter declarations will result in lists of integers.

Examples

C

    ssize_t read(int fd, void *buf, size_t count);

CFFI

    LIBC function read {ssize_t nonnegative errno} {
        fd int
        buf bytes[count]
        count size_T
    }

Output pointers to binary strings

Another way of returning binary data is through a parameter that is a pointer to pointer to the data (char ** etc.). This case has to be handled as raw pointers since, unlike for character strings, there is no way for CFFI to know the size of the data being returned.

Arrays as parameters

    TYPE[N] type specific annotations ...

•   Arrays in C are always passed by reference, as a pointer to the first element of the array. CFFI array type declarations therefore are implicitly annotated as byref.

•   The array size in the type declaration may be an integer constant or specified as the name of another parameter. See Dynamically sized array.

•   The annotations permitted on an array type declaration are those permitted for the base type. For example, integer array declarations may have the enum annotation.

•   At the script level, arrays are represented as lists except for the special cases of as `chars` or `bytes` where they are character or binary strings.

DEFINING STRUCTS

Native C struct values may be dealt with at the script level either as Tcl dictionary values or kept in their native form and accessed through raw pointers. Both methods require the struct to be defined with ::cffi::Struct. In the former case, the fields are accessed using Tcl's dict command. In the latter case, the ::cffi::Struct accessor methods are used.

  FIELD TYPE DECLARATIONS

The field type declarations in a struct definition, while similar to function parameter type declarations, differ from them in some respects.

•   The void and binary base types cannot be used in struct fields.

•   The annotations for each type are differ from those applicable in a parameter context.

Integer fields

              +-      -+ +-     -+ +-         -+ +-        -+ +-               -+
    schar     |{enum E}| |bitmask| |{default N}| |structsize| |lasterror        |
    uchar     +-      -+ +-     -+ +-         -+ +-        -+ |winerror         |
    short                                                     |errno            |
    ushort                                                    |{onerror HANDLER}|
    int                                                       +-               -+
    uint
    long
    ulong
    longlong
    ulonglong
    Syntax for integer fields

•   If the enum annotation is present, the field is subject to mapping to symbols from the enumeration.

•   If the bitmask annotation is present, the dictionary element for the field is treated a list of integers that are bitwise OR-ed to form the native field value.

•   The default annotation specifies a default value to be used when constructing a native struct from the dictionary representation if the dictionary does not contain a key for the field.

•   The structsize annotation specifies that the field should be defaulted to the size of the native struct.

•   Error handling annotations like errno may be present but are ignored. This is to permit the same aliases to be used in both function struct definitions.

Floating point fields

           +-         -+
    float  |{default N}|
    double +-         -+
    Syntax for floating point fields

•   The default annotation specifies a default value to be used when constructing a native struct from the dictionary representation if the dictionary does not contain a key for the field.

Pointer fields

                +-     -+ +-    -+ +-              -+
    pointer     |counted| |nullok| |{default PTRVAL}|
    pointer.TAG |unsafe | +-    -+ +-              -+
                +-     -+
    Syntax for input raw pointer parameters

•   The TAG is optional but recommended wherever possible for additional tag-based type checks. If the pointer's target type is a struct, it is is recommended, but not mandatory, that the tag used be the name of the struct instance command. This makes it convenient to access the referenced memory using the struct methods.

•   The counted and unsafe annotations have their usual meaning as described in Pointer safety. By default, when a native C struct is constructed from its script level dictionary representation, the pointer value for a field is expected to have been registered as a safe pointer and an exception is raised otherwise. Conversely, when a dictionary is constructed from a native C struct, as output from a function for example, the extracted pointer is registered as a safe pointer. This is often not the right model for embedded pointers in structs unlike pointers directly returned from functions with a clear resource allocation and freeing semantics. Thus use of the unsafe annotation should be strongly considered for field type declarations.

•   The nullok annotation should be applied to any field that can validly hold a NULL pointer. In the absence of an annotation, an exception is raised if an attempt is made to assign the field in a native struct a NULL pointer value.

•   Pointer fields that hold pointers to character strings may be alternatively declared as string pointer fields.

String pointer fields

                    +-         -+
    string          |nullifempty|
    string.ENCODING +-         -+
    unistring
    Syntax for string pointer fields

•   The nullifempty annotation may be used in cases where an empty string at the script level should be stored as a NULL pointer in the struct field.

As for function parameters, fields that hold pointers to character strings can be more conveniently declared as one of the CFFI string types if the raw pointer is not required for freeing of resources or other purposes. There are is a restriction imposed when a struct field is declared as a string type. The struct definition may be used to pass and return parameters from functions but cannot be used with the ::cffi::Struct.new or ::cffi::Struct.allocate methods to allocate a native struct in memory.

Character array fields

    chars[N]          +-               -+
    chars.ENCODING[N] |{default DEFAULT}|
    unichars[N]       +-               -+
    Syntax for character array fields

Like string and unistring, the chars and unichars CFFI types are character strings at the script level. However, while the former maps to pointers at the C level, chars and unichars map to char[] and Tcl_UniChar[] arrays.

•   The default annotation may be used to supply a default value to be stored in the native C struct if the dictionary representation does not have a key matching the field.

•   The array size N must be an positive integer constant and not dynamically sized as is permitted for array parameters.

Byte array fields

             +-               -+
    bytes[N] |{default DEFAULT}|
             +-               -+
    Syntax for byte array fields

The difference between an array declared as bytes[N] versus schar[N] or uchars[N] is that while both might be arrays of char or unsigned char at the C level, the former declaration will result in a Tcl binary string at the script level while the latter declarations will result in lists of integers.

•   The default annotation may be used to supply a default value to be stored in the native C struct if the dictionary representation does not have a key matching the field.

•   The array size N must be an positive integer constant and not dynamically sized as is permitted for array parameters.

Nested struct fields

A struct definition may include a field that is declared as another struct.

                  +-         -+
    struct.STRUCT |{default DICT}|
                  +-         -+
    Syntax for nested struct fields

•   A nested STRUCT struct may have a default value specified as a dictionary mapping field names of STRUCT to field values.

Array fields

    TYPE[N] type specific annotations ...

•   The array size N must be an positive integer constant and not dynamically sized as is permitted for array parameters.

•   The annotations permitted on an array type declaration are those permitted for the base type. For example, integer array declarations may have the enum annotation.

•   At the script level, C arrays are represented as lists except for the special cases of as `chars` or `bytes` where they are character or byte arrays.

TIPS AND TRICKS

This section to be written

•   duplicating structure or function definition

•   pointer casting - implicit and explicit

•   use raw pointers for large data to avoid copies

•   use memory get command to decode native memory in type-specific manner

•   use defaults, -clear, structsize for structs