Scroll to navigation

critcl-cproc-types(3tcl) C Runtime In Tcl (CriTcl) critcl-cproc-types(3tcl)


NAME

critcl-cproc-types - Critcl - cproc Type Reference

SYNOPSIS

package require Tcl 8.4

package require critcl ?3.1.17?

::critcl::has-resulttype name

::critcl::resulttype name body ?ctype?

::critcl::resulttype name = origname

::critcl::has-argtype name

::critcl::argtype name body ?ctype? ?ctypefun?

::critcl::argtype name = origname

::critcl::argtypesupport name code ?guard?

::critcl::argtyperelease name code


DESCRIPTION

Welcome to the C Runtime In Tcl, CriTcl for short, a system to build C extension packages for Tcl on the fly, from C code embedded within Tcl scripts, for all who wish to make their code go faster.

This document is a breakout of the descriptions for the predefined argument- and result-types usable with the critcl::cproc command, as detailed in the reference manpage for the critcl package, plus the information on how to extend the predefined set with custom types. The breakout was made to make this information easier to find (toplevel document vs. having to search the large main reference).

Its intended audience are developers wishing to write Tcl packages with embedded C code.

STANDARD ARGUMENT TYPES

Tcl_Obj*
object
The function takes an argument of type Tcl_Obj*. No argument checking is done. The Tcl level word is passed to the argument as-is.
pstring
The function takes an argument of type critcl_pstring containing the original Tcl_Obj* reference of the Tcl argument, plus the length of the string and a pointer to the character array.
typedef struct critcl_pstring {
    Tcl_Obj* o;
    char*    s;
    int      len;
} critcl_pstring;
list
The function takes an argument of type critcl_list containing the original Tcl_Obj* reference of the Tcl argument, plus the length of the Tcl list and a pointer to the array of the list elements.
typedef struct critcl_list {
    Tcl_Obj*  o;
    Tcl_Obj** v;
    int       c;
} critcl_list;
The Tcl argument must be convertible to List, an error is thrown otherwise.
bytearray
rawchar*
rawchar
The function takes an argument of type char*. The Tcl argument must be convertible to ByteArray, an error is thrown otherwise. Note that the length of the ByteArray is not passed to the function, making this type not very usable.
bytes
This is the new and usable ByteArray type.

The function takes an argument of type critcl_bytes containing the original Tcl_Obj* reference of the Tcl argument, plus the length of the byte array and a pointer to the byte data.

typedef struct critcl_bytes {
    Tcl_Obj* o;
    char*    s;
    int      len;
} critcl_list;
The Tcl argument must be convertible to ByteArray, an error is thrown otherwise.
char*
The function takes an argument of type char*. The string representation of the Tcl argument is passed in.
double
The function takes an argument of type double. The Tcl argument must be convertible to Double, an error is thrown otherwise.
float
The function takes an argument of type float. The Tcl argument must be convertible to Double, an error is thrown otherwise.
boolean
bool
The function takes an argument of type int. The Tcl argument must be convertible to Boolean, an error is thrown otherwise.
int
The function takes an argument of type int. The Tcl argument must be convertible to Int, an error is thrown otherwise.
long
The function takes an argument of type long int. The Tcl argument must be convertible to Long, an error is thrown otherwise.
wideint
The function takes an argument of type Tcl_WideInt. The Tcl argument must be convertible to WideInt, an error is thrown otherwise.
void*
double*
float*
int*
The function takes an argument of the same-named C type. The Tcl argument must be convertible to ByteArray, an error is thrown otherwise. The bytes in the ByteArray are then re-interpreted as the raw representation of a single C pointer of the given type which is then passed as argument to the function. In other words, this is for Tcl values somehow holding raw C pointers, i.e. memory addresses.

Attention: These types are considered DEPRECATED. It is planned to remove their documentation in release 3.2, and their implementation in release 3.3. Their deprecation can be undone if good use cases are shown.

STANDARD RESULT TYPES

Tcl_Obj*
object
The function returns a value of type Tcl_Obj*. This value becomes the interpreter result, if not 0. The Tcl status is TCL_ERROR when a 0 is returned, and TCL_OK otherwise.

Note how it is the responsibility of the function to set the interpreter result to an appropriate error message when returning 0.

Attention: The conversion assumes that the value belonged to the function, with an associated reference count, and decrements the reference count to indicate the loss of ownership by the function. This means that it is an error to return a value whose reference count is zero. The system will crash at some point after the return due to reuse of the freed memory.

Tcl_Obj*0
object0
The function returns a value of type Tcl_Obj*. This value becomes the interpreter result, if not 0. The Tcl status is TCL_ERROR when a 0 is returned, and TCL_OK otherwise.

Note how it is the responsibility of the function to set the interpreter result to an appropriate error message when returning 0.

Attention: Contrary to Tcl_Obj* above this conversion assumes that the returned value has a reference count of 0 and performs no decrement. Returning a value whose reference count is greater than zero will likely cause a memory leak.

char*
vstring
The function returns a value of type char*. This value becomes the interpreter result, wrapped in a String. It is assumed that the string is volatile in some way, with the wrapping in a String duplicating it before making it the result, ensuring that we will not access a dangling pointer in the future. The Tcl status is always TCL_OK.
const char*
Like type char* above, except that the returned string is const-qualified.
string
dstring
The function returns a value of type char*. Contrary to the previous string types here it is assumed that the value is dynamically allocated, via Tcl_Alloc. This value becomes the interpreter result, as usual, but is not copied. The Tcl status is always TCL_OK.
double
The function returns a value of type double. This value becomes the interpreter result, properly wrapped as a Double. The Tcl status is always TCL_OK.
float
The function returns a value of type float. This value becomes the interpreter result, properly wrapped as a Double. The Tcl status is always TCL_OK.
boolean
bool
The function returns a value of type int, interpreted as boolean. This value becomes the interpreter result, properly wrapped as a Int. The Tcl status is always TCL_OK.
int
The function returns a value of type int. This value becomes the interpreter result, properly wrapped as a Int. The Tcl status is always TCL_OK.
long
The function returns a value of type long int. This value becomes the interpreter result, properly wrapped as a Long. The Tcl status is always TCL_OK.
wideint
The function returns a value of type Tcl_WideInt. This value becomes the interpreter result, properly wrapped as a WideInt. The Tcl status is always TCL_OK.
ok
The function returns a value of type int. It is interpreted as the Tcl status code. The interpreter result is left untouched (empty). For a different outcome the function has to set the interpreter result by itself.
void
The function does not return a value. The interpreter result is left untouched (empty). For a different outcome the function has to set the interpreter result by itself. The Tcl status is always TCL_OK.

ADVANCED: ADDING TYPES

While the critcl::cproc command understands the most common C types (as per the previous 2 sections), sometimes this is not enough.

To get around this limitation the commands in this section enable users of critcl to extend the set of argument and result types understood by critcl::cproc. In other words, they allow them to define their own, custom, types.

::critcl::has-resulttype name
This command tests if the named result-type is known or not. It returns a boolean value, true if the type is known and false otherwise.
::critcl::resulttype name body ?ctype?
This command defines the result type name, and associates it with the C code doing the conversion (body) from C to Tcl. The C return type of the associated function, also the C type of the result variable, is ctype. This type defaults to name if it is not specified.

If name is declared already an error will be thrown. Attention! The standard result type void is special as it has no accompanying result variable. This cannot be expressed by this extension command.

The body's responsibility is the conversion of the functions result into a Tcl result and a Tcl status. The first has to be set into the interpreter we are in, and the second has to be returned.

The C code of body is guaranteed to be called last in the wrapper around the actual implementation of the cproc in question and has access to the following environment:

interp
A Tcl_Interp* typed C variable referencing the interpreter the result has to be stored into.
rv
The C variable holding the result to convert, of type ctype.
As examples here are the definitions of two standard result types:
    resulttype int {
	Tcl_SetObjResult(interp, Tcl_NewIntObj(rv));
	return TCL_OK;
    }
    resulttype ok {
	/* interp result must be set by cproc body */
	return rv;
    } int
::critcl::resulttype name = origname
This form of the resulttype command declares name as an alias of result type origname, which has to be defined already. If this is not the case an error is thrown.
::critcl::has-argtype name
This command tests if the named argument-type is known or not. It returns a boolean value, true if the type is known and false otherwise.
::critcl::argtype name body ?ctype? ?ctypefun?
This command defines the argument type name, and associates it with the C code doing the conversion (body) from Tcl to C The C type of the variable to hold the conversion result is ctype and the type of the function argument itself is ctypefun. Both types default to name if they are not specified (or the empty string).

If name is declared already an error will be thrown.

The body's responsibility is the conversion of a command's Tcl_Obj* argument into a C value for the underlying function and its storage in a helper variable.

The C code of body is guaranteed to be called inside of a separate C code block (thus allowing the use of local variables) which has access to the following environment:

interp
A Tcl_Interp* typed C variable referencing the interpreter the code is running in.
@@
A placeholder for the Tcl_Obj*-valued C expression providing the value of the argument to convert.
@A
A placeholder for the name of the C variable to store the converted argument into.
As examples here are the definitions of two standard argument types:
    argtype int {
	if (Tcl_GetIntFromObj(interp, @@, &@A) != TCL_OK) return TCL_ERROR;
    }
    argtype float {
	double t;
	if (Tcl_GetDoubleFromObj(interp, @@, &t) != TCL_OK) return TCL_ERROR;
	@A = (float) t;
    }
::critcl::argtype name = origname
This form of the argtype command declares name as an alias of argument type origname, which has to be defined already. If this is not the case an error is thrown.
::critcl::argtypesupport name code ?guard?
This command defines a C code fragment for the already defined argument type name which will be inserted before all functions using that type. Its purpose is the definition of any supporting C types needed by the argument type. If the type is used by many functions the system ensures that only the first of the multiple insertions of the code fragment is active, and the others disabled. The guard identifier is normally derived from name, but can also be set explicitly, via guard. This latter allows different custom types to share a common support structure without having to perform their own guarding.
::critcl::argtyperelease name code
This command defines a C code fragment for the already defined argument type name which will be inserted whenever the worker function of a critcl::cproc returns to the shim. It is the responsibility of this fragment to unconditionally release any resources the critcl::argtype conversion code allocated. An example of this are the variadic types for the support of the special, variadic args argument to critcl::cproc's. They allocate a C array for the collected arguments which has to be released when the worker returns. This command defines the C code for doing that.

EXAMPLES

The examples shown here have been drawn from section "Embedding C" in the document about Using CriTcl. Please see that document for many more examples.

A SIMPLE PROCEDURE

Starting simple, let us assume that the Tcl code in question is something like
    proc math {x y z} {
        return [expr {(sin($x)*rand())/$y**log($z)}]
    }
with the expression pretending to be something very complex and slow. Converting this to C we get:
    critcl::cproc math {double x double y double z} double {
        double up   = rand () * sin (x);
        double down = pow(y, log (z));
        return up/down;
    }
Notable about this translation:
[1]
All the arguments got type information added to them, here "double". Like in C the type precedes the argument name. Other than that it is pretty much a Tcl dictionary, with keys and values swapped.
[2]
We now also have to declare the type of the result, here "double", again.
[3]
The reference manpage lists all the legal C types supported as arguments and results.

CUSTOM TYPES, INTRODUCTION

When writing bindings to external libraries critcl::cproc is usually the most convenient way of writing the lower layers. This is however hampered by the fact that critcl on its own only supports a few standard (arguably the most import) standard types, whereas the functions we wish to bind most certainly will use much more, specific to the library's function.

The critcl commands argtype, resulttype and their adjuncts are provided to help here, by allowing a developer to extend critcl's type system with custom conversions.

This and the three following sections will demonstrate this, from trivial to complex.

The most trivial use is to create types which are aliases of existing types, standard or other. As an alias it simply copies and uses the conversion code from the referenced types.

Our example is pulled from an incomplete project of mine, a binding to Jeffrey Kegler's libmarpa library managing Earley parsers. Several custom types simply reflect the typedef's done by the library, to make the critcl::cprocs as self-documenting as the underlying library functions themselves.

    critcl::argtype Marpa_Symbol_ID     = int
    critcl::argtype Marpa_Rule_ID       = int
    critcl::argtype Marpa_Rule_Int      = int
    critcl::argtype Marpa_Rank          = int
    critcl::argtype Marpa_Earleme       = int
    critcl::argtype Marpa_Earley_Set_ID = int
    ...
    method sym-rank: proc {
        Marpa_Symbol_ID sym
        Marpa_Rank      rank
    } Marpa_Rank {
        return marpa_g_symbol_rank_set (instance->grammar, sym, rank);
    }
    ...

CUSTOM TYPES, SEMI-TRIVIAL

A more involved custom argument type would be to map from Tcl strings to some internal representation, like an integer code.

The first example is taken from the tclyaml package, a binding to the libyaml library. In a few places we have to map readable names for block styles, scalar styles, etc. to the internal enumeration.

    critcl::argtype yaml_sequence_style_t {
        if (!encode_sequence_style (interp, @@, &@A)) return TCL_ERROR;
    }
    ...
    critcl::ccode {
        static const char* ty_block_style_names [] = {
            "any", "block", "flow", NULL
        };
        static int
        encode_sequence_style (Tcl_Interp* interp, Tcl_Obj* style,
                               yaml_sequence_style_t* estyle)
        {
            int value;
            if (Tcl_GetIndexFromObj (interp, style, ty_block_style_names,
                                     "sequence style", 0, &value) != TCL_OK) {
                return 0;
            }
            *estyle = value;
            return 1;
        }
    }
    ...
    method sequence_start proc {
        pstring anchor
        pstring tag
        int implicit
        yaml_sequence_style_t style
    } ok {
        /* Syntax: <instance> seq_start <anchor> <tag> <implicit> <style> */
        ...
    }
    ...
It should be noted that this code precedes the advent of the supporting generator package critcl::emap. using the generator the definition of the mapping becomes much simpler:
    critcl::emap::def yaml_sequence_style_t {
        any   0
        block 1
        flow  2
    }
Note that the generator will not only provide the conversions, but also define the argument and result types needed for their use by critcl::cproc. Another example of such a semi-trivial argument type can be found in the CRIMP package, which defines a Tcl_ObjType for image values. This not only provides a basic argument type for any image, but also derived types which check that the image has a specific format. Here we see for the first time non-integer arguments, and the need to define the C types used for variables holding the C level value, and the type of function parameters (Due to C promotion rules we may need different types).
    critcl::argtype image {
        if (crimp_get_image_from_obj (interp, @@, &@A) != TCL_OK) {
            return TCL_ERROR;
        }
    } crimp_image* crimp_image*
    ...
        set map [list <<type>> $type]
        critcl::argtype image_$type [string map $map {
            if (crimp_get_image_from_obj (interp, @@, &@A) != TCL_OK) {
                return TCL_ERROR;
            }
            if (@A->itype != crimp_imagetype_find ("crimp::image::<<type>>")) {
                Tcl_SetObjResult (interp,
                                  Tcl_NewStringObj ("expected image type <<type>>",
                                                    -1));
                return TCL_ERROR;
            }
        }] crimp_image* crimp_image*
    ...

CUSTOM TYPES, SUPPORT STRUCTURES

The adjunct command critcl::argtypesupport is for when the conversion needs additional definitions, for example a helper structure.

An example of this can be found among the standard types of critcl itself, the pstring type. This type provides the C function with not only the string pointer, but also the string length, and the Tcl_Obj* this data came from. As critcl::cproc's calling conventions allow us only one argument for the data of the parameter a structure is needed to convey these three pieces of information.

Thus the argument type is defined as

    critcl::argtype pstring {
        @A.s = Tcl_GetStringFromObj(@@, &(@A.len));
        @A.o = @@;
    } critcl_pstring critcl_pstring
    critcl::argtypesupport pstring {
        typedef struct critcl_pstring {
            Tcl_Obj* o;
            char*    s;
            int      len;
        } critcl_pstring;
    }

In the case of such a structure being large we may wish to allocate it on the heap instead of having it taking space on the stack. If we do that we need another adjunct command, critcl::argtyperelease. This command specifies the code required to release dynamically allocated resources when the worker function returns, before the shim returns to the caller in Tcl. To keep things simple our example is synthetic, a modification of pstring above, to demonstrate the technique. An actual, but more complex example is the code to support the variadic args argument of critcl::cproc.

    critcl::argtype pstring {
        @A = (critcl_pstring*) ckalloc(sizeof(critcl_pstring));
        @A->s = Tcl_GetStringFromObj(@@, &(@A->len));
        @A->o = @@;
    } critcl_pstring* critcl_pstring*
    critcl::argtypesupport pstring {
        typedef struct critcl_pstring {
            Tcl_Obj* o;
            char*    s;
            int      len;
        } critcl_pstring;
    }
    critcl::argtyperelease pstring {
        ckfree ((char*)) @A);
    }
Note, the above example shows only the most simple case of an allocated argument, with a conversion that cannot fail (namely, string retrieval). If the conversion can fail then either allocation has to be defered to happen only on successful conversion, or the conversion code has to release the allocated memory itself in the failure path, because it will never reach the code defined via critcl::argtyperelease in that case.

CUSTOM TYPES, RESULTS

All of the previous sections dealt with argument conversions, i.e. going from Tcl into C. Custom result types are for the reverse direction, from C to Tcl. This is usually easier, as most of the time errors should not be possible. Supporting structures, or allocating them on the heap are not really required and therefore not supported.

The example of a result type shown below was pulled from KineTcl. It is a variant of the builtin result type Tcl_Obj*, aka object. The builtin conversion assumes that the object returned by the function has a refcount of 1 (or higher), with the function having held the reference, and releases that reference after placing the value into the interp result. The conversion below on the other hand assumes that the value has a refcount of 0 and thus that decrementing it is forbidden, lest it be released much to early, and crashing the system.

    critcl::resulttype KTcl_Obj* {
        if (rv == NULL) { return TCL_ERROR; }
        Tcl_SetObjResult(interp, rv);
        /* No refcount adjustment */
        return TCL_OK;
    } Tcl_Obj*
This type of definition is also found in Marpa and recent hacking hacking on CRIMP introduced it there as well. Which is why this definition became a builtin type starting with version 3.1.16, under the names Tcl_Obj*0 and object0.

Going back to errors and their handling, of course, if a function we are wrapping signals them in-band, then the conversion of such results has to deal with that. This happens for example in KineTcl, where we find

    critcl::resulttype XnStatus {
        if (rv != XN_STATUS_OK) {
            Tcl_AppendResult (interp, xnGetStatusString (rv), NULL);
            return TCL_ERROR;
        }
        return TCL_OK;
    }
    critcl::resulttype XnDepthPixel {
        if (rv == ((XnDepthPixel) -1)) {
            Tcl_AppendResult (interp,
                              "Inheritance error: Not a depth generator",
                              NULL);
            return TCL_ERROR;
        }
        Tcl_SetObjResult (interp, Tcl_NewIntObj (rv));
        return TCL_OK;
    }

AUTHORS

Jean Claude Wippler, Steve Landers, Andreas Kupries

BUGS, IDEAS, FEEDBACK

This document, and the package it describes, will undoubtedly contain bugs and other problems. Please report them at https://github.com/andreas-kupries/critcl/issues. Ideas for enhancements you may have for either package, application, and/or the documentation are also very welcome and should be reported at https://github.com/andreas-kupries/critcl/issues as well.

KEYWORDS

C code, Embedded C Code, code generator, compile & run, compiler, dynamic code generation, dynamic compilation, generate package, linker, on demand compilation, on-the-fly compilation

CATEGORY

Glueing/Embedded C code

COPYRIGHT

Copyright (c) Jean-Claude Wippler
Copyright (c) Steve Landers
Copyright (c) 2011-2015 Andreas Kupries
3.1.17 doc