critcl-cproc-types(3tcl) | C Runtime In Tcl (CriTcl) | critcl-cproc-types(3tcl) |
critcl-cproc-types - Critcl - cproc Type Reference
package require Tcl 8.4
package require critcl ?3.1.18?
::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
C Runtime In Tcl, or CriTcl , is a system for compiling C code embedded in Tcl on the fly and either loading the resulting objects into Tcl for immediate use or packaging them for distribution. Use CriTcl to improve performance by rewriting in C those routines that are performance bottlenecks.
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.
Before going into the details first a quick overview:
Critcl type | C type | Tcl type | Notes ----------- | -------------- | --------- | ------------------------------ Tcl_Interp* | Tcl_Interp* | n/a | Special, only first ----------- | -------------- | --------- | ------------------------------ Tcl_Obj* | Tcl_Obj* | Any | Read-only object | | | Alias of Tcl_Obj* above list | critcl_list | List | Read-only ----------- | -------------- | --------- | ------------------------------ char* | const char* | Any | Read-only, string rep pstring | critcl_pstring | Any | Read-only bytes | critcl_bytes | ByteArray | Read-only ----------- | -------------- | --------- | ------------------------------ int | int | Int | long | long | Long | wideint | Tcl_WideInt | WideInt | double | double | Double | float | float | Double | ----------- | -------------- | --------- | ------------------------------ X > 0 | | | For X in int ... float above. X >= 0 | | | C types as per the base type X. X < 0 | | | Allowed argument values are X <= 0 | | | restricted as per the shown X > 1 | | | relation X >= 1 | | | X < 1 | | | This is not a general mechanism X <= 1 | | | open to other values. Only 0/1. ----------- | -------------- | --------- | ------------------------------ boolean | int | Boolean | bool | | | Alias of boolean above ----------- | -------------- | --------- | ------------------------------ bytearray | | | DEPRECATED rawchar | | | DEPRECATED rawchar* | | | DEPRECATED double* | | | DEPRECATED float* | | | DEPRECATED int* | | | DEPRECATED void* | | | DEPRECATED
When used, the argument will contain a reference to the current interpreter that the function body may use. Furthermore the argument will not be an argument of the Tcl command for the function.
This is useful when the function has to do more than simply returning a value. Examples would be setting up error messages on failure, or querying the interpreter for variables and other data.
typedef struct critcl_pstring {
Tcl_Obj* o;
const char* s;
int len; } critcl_pstring;
typedef struct critcl_list {
Tcl_Obj* o;
Tcl_Obj* const* v;
int c; } critcl_list;
Note the const. The list is read-only. Any modification can have arbitrary effects, from pulling out the rug under the script because of string value and internal representation not matching anymore, up to crashes anytime later.
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.
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;
const unsigned char* s;
int len; } critcl_list;
Note the const. The bytes are read-only. Any modification can have arbitrary effects, from pulling out the rug under the script because of string value and internal representation not matching anymore, up to crashes anytime later.
Note the const. The string is read-only. Any modification can have arbitrary effects, from pulling out the rug under the script because of string value and internal representation not matching anymore, up to crashes anytime later.
Beyond that the channel must not be shared by multiple interpreters, an error is thrown otherwise.
Beyond that the code removes the channel from the current interpreter without closing it, and disables all pre-existing event handling for it.
With this the function takes full ownership of the channel in question, taking it away from the interpreter invoking it. It is then responsible for the lifecycle of the channel, up to and including closing it.
Should the system the function is a part of wish to return control of the channel back to the interpeter it then has to use the result type return-channel. This will undo the registration changes made by this argument type. Note however that the removal of pre-existing event handling done here cannot be undone.
Attention Removal from the interpreter without closing the channel is effected by incrementing the channel's reference count without providing an interpreter, before decrementing the same for the current interpreter. This leaves the overall reference count intact without causing Tcl to close it when it is removed from the interpreter structures. At this point the channel is effectively a globally-owned part of the system not associated with any interpreter.
The complementary result type then runs this sequence in reverse. And if the channel is never returned to Tcl either the function or the system it is a part of have to unregister the global reference when they are done with it.
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.
Before going into the details first a quick overview:
Critcl type | C type | Tcl type | Notes ------------- | -------------- | --------- | ------------------------------ void | n/a | n/a | Always OK. Body sets result ok | int | n/a | Result code. Body sets result ------------- | -------------- | --------- | ------------------------------ int | int | Int | boolean | | | Alias of int above bool | | | Alias of int above long | long | Long | wideint | Tcl_WideInt | WideInt | double | double | Double | float | float | Double | ------------- | -------------- | --------- | ------------------------------ char* | char* | String | Makes a copy vstring | | | Alias of char* above const char* | const char* | | Behavior of char* above ------------- | -------------- | --------- | ------------------------------ string | | String | Freeable string set directly
| | | No copy is made dstring | | | Alias of string above ------------- | -------------- | --------- | ------------------------------
| | | For all below: Null is ERROR
| | | Body has to set any message Tcl_Obj* | Tcl_Obj* | Any | refcount -- object | | | Alias of Tcl_Obj* above Tcl_Obj*0 | | Any | refcount unchanged object0 | | | Alias of Tcl_Obj*0 above ------------- | -------------- | --------- | ------------------------------ known-channel | Tcl_Channel | String | Assumes to already be registered new-channel | Tcl_Channel | String | New channel, will be registered
Note that setting an error message requires the function body to have access to the interpreter the function is running in. See the argument type Tcl_Interp* for the details on how to make that happen.
Note further that the returned Tcl_Obj* should have a reference count greater than 0. This is because the converter decrements the reference count to release possession after setting the interpreter result. It assumes that the function incremented the reference count of the returned Tcl_Obj*. If a Tcl_Obj* with a reference count of 0 were returned, the reference count would become 1 when set as the interpreter result, and immediately thereafter be decremented to 0 again, causing the memory to be freed. The system is then likely to crash at some point after the return due to reuse of the freed memory.
Note that setting an error message requires the function body to have access to the interpreter the function is running in. See the argument type Tcl_Interp* for the details on how to make that happen.
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.
If name is already declared an error is 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:
resulttype int { Tcl_SetObjResult(interp, Tcl_NewIntObj(rv)); return TCL_OK;
}
resulttype ok { /* interp result must be set by cproc body */ return rv;
} int
If name is already declared an error is thrown.
body is a C code fragment that converts a Tcl_Obj* into a C value which is stored in a helper variable in the underlying function.
body is called inside its own code block to isolate local variables, and the following items are in scope:
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;
}
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.
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)}]
}
critcl::cproc math {double x double y double z} double {
double up = rand () * sin (x);
double down = pow(y, log (z));
return up/down;
}
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);
}
...
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> */
...
}
...
critcl::emap::def yaml_sequence_style_t {
any 0
block 1
flow 2
}
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*
...
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;
const 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;
const char* s;
int len;
} critcl_pstring;
}
critcl::argtyperelease pstring {
ckfree ((char*)) @A);
}
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*
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;
}
Jean Claude Wippler, Steve Landers, Andreas Kupries
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.
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
Glueing/Embedded C code
Copyright (c) Jean-Claude Wippler Copyright (c) Steve Landers Copyright (c) 2011-2018 Andreas Kupries
3.1.18 | doc |