TclCommandWriting
Command Writing(TCL) Command Writing(TCL)
NAME
TclCommandWriting - Writing C language extensions to Tcl.
OVERVIEW
This document is intended to help the programmer who wishes to extend
Tcl with C language routines. It should also be useful to someone
wishing to add Tcl to an existing editor, communications program, win-
dow manager, etc. Experienced extension writers may find this manual
helpful in rewriting their applications to use the new Tcl object sys-
tem. We assume you are already fluent in the C programming language
and that you have built and installed Tcl on your machine.
Information on the available C interface routines to Tcl can be found
in the *.3 manual pages in the doc directory of the baseline Tcl dis-
tribution, and in the *.3 manpages in the doc directory of Extended
Tcl.
TCL OBJECT SYSTEM
With the release of Tcl version 8, Tcl has a new system for managing
Tcl values internally. To the Tcl programmer, the new objects look and
act like strings, as before. But at the C level, these objects can now
also hold cached internal representations of the strings in various
native datatypes. For example, an object containing a string consist-
ing of an integer, will now maintain a machine-code integer representa-
tion, if an integer representation has been needed. Using these
objects is much more efficient than using the older-style Tcl strings,
although the older style is still (currently) supported.
Although the object system has almost no effect at all on how the Tcl
programmer uses Tcl, the object system's C interfaces to strings, inte-
gers, lists, etc., have changed considerably. While converting a pack-
age to use the new system can be a lot of work, the combination of the
object system, which saves Tcl from having to constantly convert
strings to integers and back, etc., and the on-the-fly bytecode com-
piler (which keeps Tcl from having to continually reparse code it is to
execute) yield Tcl programs that routinely execute several times more
quickly than with previous versions (Tcl 7 and before), and in some
cases run as much as 2500 (!) times faster than before.
We have chosen, then, to rewrite the Command Writer's manpage, which
has been shipping with Extended Tcl for a number of years, to produce
this new version based on the new object system. The old manpage,
based on the older string-oriented routines, will still be included in
TclX releases for now, as it is still relevant to Tcl releases through
version 7, and may be of use to those modifying/upgrading packages
written for the old model. The old manual will be dropped from the
release once we deem it unneeded; the old interfaces should now be con-
sidered legacy interfaces, and all new development should be done using
the new object interfaces, unless backwards compatibility to pre-Tcl-8
releases is needed.
A SIMPLE C EXTENSION
All C-based Tcl commands are called with four arguments: a client data
pointer, an interpreter pointer, an argument count and a pointer to an
array of Tcl objects containing the arguments to the command.
A simple C extension to Tcl is now presented, and described below:
#include "tcl.h"
int App_DumpArgsObjCmd(clientData, interp, objc, objv)
void *clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj **objv;
{
int i;
int stringLen;
char *stringPtr;
for (i = 1; i < objc; i++) {
stringPtr = Tcl_GetStringFromObj (objv [i], &stringLen);
printf("%s", stringPtr);
if (i < objc - 1) printf(" ");
}
printf("\n");
return TCL_OK;
}
The client data pointer will be described later.
INTERPRETERS
The interpreter pointer is the ``key'' to an interpreter. It is
returned by Tcl_CreateInterp and is used extensively within Tcl, and
will be used by your C extensions. The data structure pointed to by
the interpreter pointer, and all of the subordinate structures that
branch off of it, make up a Tcl interpreter, which includes all of the
currently defined procedures, commands, variables, arrays and the exe-
cution state of that interpreter. (For more information on creating
and deleting interpreters, please examine the CrtInterp(3) manpage in
the core Tcl distribution. For information on creating interpreters
that include the commands provided by Extended Tcl, check out the
TclX_Init(3) manpage of Extended Tcl. For a manual page describing the
user-visible fields of a Tcl interpreter, please look at Interp(3) in
core Tcl.)
OBJECT COUNT AND ARGUMENTS
The argument count, or object count (objc), and pointer to an array of
pointers to Tcl objects of the command's arguments (objv) is handled by
your C code, in a manner similar to the one you would use in writing a
C main function -- an argument count and array of pointers works the
same as in a C main call; pointers to the arguments to the function are
contained in the objv array. Similar to a C main, the first argument
(objv[0]) is an object containing the name the routine was called as
(in a C main, the name the program was invoked as).
In Tcl, however, the array of pointers are not pointers to character
strings (although they were in all version of Tcl before 8.0).
In the above example, all of the arguments are output with a space
between each one by looping through elements of the objv array from one
to the argument count, objc, and a newline is output to terminate the
line -- a simple ``echo'' command. This example uses printf for sim-
plicity. Of course in production code you would want to use the Tcl
filesystem interfaces. See GetFile(3) and friends for more informa-
tion.
All arguments from a Tcl call to a Tcl C extension are passed as Tcl
Objects. If your C routine wants to look at one of those arguments as
an integer, you need to make a call to a routine to fetch the represen-
tation of the object that you need. In the earlier example, for
instance, Tcl_GetStringFromObj is called to obtain a textual represen-
tation of an object. Additional routines are available to fetch the
representation of a data element as other data types. Tcl_GetBoolean-
FromObj, Tcl_GetDoubleFromObj, Tcl_GetIntFromObj, Tcl_GetLongFromObj,
and Tcl_GetIndexFromObj, fetch object representations of Tcl strings as
booleans, double-precision floating point, integer, long integer, and
lists, among others.
These routines automatically leave an appropriate error message in the
Tcl interpreter's result object and return TCL_ERROR if a conversion
error occurs. (For more information on these routines, please look at
the Object(3) manpage in the core Tcl distribution.)
RETURNING RESULTS
As you might expect, the API for setting results from C extensions has
changed significantly under the object system. The old technique of
writing small results directory into the interpreter's result buffer is
no longer used, for example. The notion of having to tell Tcl whether
a result is static or dynamic is also a thing of the past. Under the
object system, results are objects that are set up by your code, and
objects are freed when their reference counts say they should be. More
on this later.
If you program produces a numeric result, it should set the result
object to contain that numeric value. A common way of doing this is
something like...
Tcl_Obj *obj;
obj = Tcl_GetObjResult (interp);
Tcl_SetIntObj (obj, value);
The above code obtains a pointer to the result object (an object made
available to your routine that you're supposed to store your results
into) and sets the integer value value into it.
Another way to do it would be to set up a new object and tell Tcl that
this object contains the result...
Tcl_Obj *resultObj;
/* create a new object for use as a result */
resultObj = Tcl_NewObj ();
Tcl_SetIntObj (obj, value);
Tcl_SetObjResult (interp, resultObj);
Understanding how results are passed back to Tcl is essential to the C
extension writer. Please study the SetObjResult(3) manual page in the
Tcl distribution for more information.
VALIDATING ARGUMENTS
It is a design goal of Tcl that no Tcl program be able to cause Tcl to
dump core. It is important that the extension writers, likewise, use
the avaiable methods and tools to make sure that their extensions do
not allow unchecked input, for example, to cause the code to get some
kind of runtime exception.
The object system has simplified, to some degree, the task of validat-
ing arguments, in that the object system automatically attempts type
conversions as needed, and will return an error when a type conversion
fails.
A simple, but important, check that every C extension should do is ver-
ify that it has the right number of arguments.
The act of trying to use, say, a string as an integer, implicitly per-
forms the type conversion of the string and, if it doesn't work as an
integer, returns TCL_ERROR. The developer should check for the
TCL_ERROR return from all of the GetXxxFromObj commands, and handle
them as appropriate. Usually this will mean propagating the error on
back to the user, or to an intevening catch, as the case may be.
You should also check that values are in range (when their ranges are
known), and so forth. When C data structures need to be handled in Tcl
in some form or another, yet the contents of the data must remain
opaque to Tcl, as is usually the case with binary data (although
futures releases of Tcl are expected to have native abilities to read,
write and manipulate binary data instrinsically), handles need to be
used. Handles will be described and examples presented, later in this
doc.
ANOTHER C EXTENSION - THE MAX COMMAND
In the command below, two or more arguments are compared, and the one
with the maximum value is returned, if all goes well. It is an error
if there are fewer than two arguments (the pointer to the ``max'' com-
mand text itself, objv[0], and a pointer to at least one object to com-
pare the values of).
int
Tcl_MaxCmd (clientData, interp, objc, objv)
char *clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj **objv;
{
int maxVal = MININT;
int value, idx;
if (objc < 3)
return TclX_WrongArgs (interp, objv[0],
" num1 num2 [..numN]");
for (idx = 1; idx < objc; idx++) {
if (Tcl_GetIntFromObj (interp, objv[idx], &value) != TCL_OK)
return TCL_ERROR;
if (value > maxVal) {
maxVal = value;
}
}
Tcl_SetIntObj (Tcl_GetObjResult (interp), value);
return TCL_OK;
}
Here we introduce the Extended Tcl helper function TclX_WrongArgs.
This routine makes it easy to create an error message and error return
in response to the common mistake of being called with a wrong number.
Tcl_GetIntFromObj is used to fetch the integer values of the remaining
arguments. If any fail to be converted, we return a Tcl error. If an
interpreter is specified in the call to Tcl_GetIntFromObj, an appropri-
ate error message about the conversion failure will be left in the
result, so we do that here.
After examining all of the arguments to find the largest value, we set
the result object to contain that value, and return TCL_OK.
RETURNING RESULTS
When Tcl-callable functions complete, they should normally return
TCL_OK or TCL_ERROR. TCL_OK is returned when the command succeeded,
and TCL_ERROR is returned when the command has failed in some abnormal
way. TCL_ERROR should be returned for all syntax errors, non-numeric
values when numeric ones were expected, and so forth. Less clear in
some cases is whether Tcl errors should be returned or whether a func-
tion should just return a status value. For example, end-of-file dur-
ing a gets returns a status, but open returns an error if it fails.
Errors can be caught from Tcl programs using the catch command. (See
Tcl's catch(n) and error(n) manual pages.)
Less common return values are TCL_RETURN, TCL_BREAK and TCL_CONTINUE.
These are used if you are adding new control and/or looping structures
to Tcl. To see these values in action, examine the source code to
Extended Tcl's loop commands. Tcl's while, for and if commands used to
work in the just same manner, but are now compiled into bytecode by the
bytecode for performance.
ANOTHER C EXTENSION - THE LREVERSE COMMAND
In the command below, a list is passed as an argument, and a list con-
taining all of the elements of the list in reverse order is returned.
It is an error if anything other than two arguments are passed (the
pointer to the ``lreverse'' command text itself, objv[0], and a pointer
to the list to reverse.
Once lreverse has determined that it has received the correct number of
arguments, Tcl_ListObjGetElements is called to split the list into its
own objc count of elements and objv array of pointers to the list's
elements.
lreverse then operates on the array of pointers, swapping them from
lowest to highest, second-lowest to second-highest, and so forth.
Tcl_ListObjAppendElement is called on successive list elements to build
up the new list, which is finally returned as result of the command.
int
Tcl_LreverseObjCmd(notUsed, interp, objc, objv)
ClientData notUsed; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
int objc; /* Number of arguments. */
Tcl_Obj **obj; /* Argument strings. */
{
int listObjc, lowListIndex, hiListIndex;
Tcl_Obj **listObjv;
char *temp, *resultList;
Tcl_Obj **newListObjv;
/* Verify argument count. Since we take only one argument, argument
* count must be 2 (command plus one argument).
*/
if (objc != 2)
return TclX_WrongArgs (interp, objv [0], "list");
/* Create an object to handle the new list we're creating */
newListObjv = Tcl_NewObj();
/* Crack the list at objv[1] into its own count and array of object
* pointers.
*/
if (Tcl_ListObjGetElements (interp, objv[1], &listObjc, &listObjv) != TCL_OK) {
return TCL_ERROR;
}
/* For each element in the source list from last to first, append an
* element to the new list.
*/
for (listIndex = listObjc - 1; listIndex >= 0; listIndex--) {
Tcl_ListObjAppendElement (interp, newListObjv, listObjv[listIndex]);
}
FIX: NEED TO RETURN THE LIST.
return TCL_OK;
}
INSTALLING YOUR COMMAND
To install your command into Tcl you must call Tcl_CreateObjCommand,
passing it the pointer to the interpreter you want to install the com-
mand into, the name of the command, a pointer to the C function that
implements the command, a client data pointer, and a pointer to an
optional callback routine.
The client data pointer and the callback routine will be described
later.
For example, for the max function above (which, incidentally, comes
from TclX's tclXmath.c in the TclX7.4/src directory):
Tcl_CreateCommand (interp, "max", Tcl_MaxCmd, (ClientData)NULL,
(void (*)())NULL);
In the above example, the max function is added to the specified inter-
preter. The client data pointer and callback function pointer are
NULL. (For complete information on Tcl_CreateCommand and its companion
routine, Tcl_CommandInfo, please examine the CrtCommand(3) command page
in the core Tcl distribution.)
DYNAMIC STRINGS
Dynamic strings are an important abstraction that first became avail-
able with Tcl 7.0. Dynamic strings, or DStrings, provide a way to
build up arbitrarily long strings through a repeated process of append-
ing information to them. DStrings reduce the amount of allocating and
copying required to add information to a string. Further, they sim-
plify the process of doing so.
At first glance, it may seem that the object system supersedes
DStrings. It does not, in that the performance improvements made pos-
sible by the lazy conversion of an object's representation from one
datatype to another does not come into play much while constructing
strings as the string representation is always available either without
any type conversion or where type conversion would be necessary in any
case as a string representation of the object is required when strings
are being constructed by concatenation, etc.
It should be noted, however, that the C level string manipulation capa-
bilites of objects, such as Tcl_AppendToObj and Tcl_AppendStringsToObj,
are often plenty enough for what you need to do. For complete informa-
tion on dynamic strings, please examine the DString(3) manual page in
the core Tcl distribution. For more on Tcl object's string-oriented
calls, seek Tcl_StringObj(3) in the same location.
CLIENT DATA
The client data pointer provides a means for Tcl commands to have data
associated with them that is not global to the C program nor included
in the Tcl core. Client data is essential in a multi-interpreter envi-
ronment (where a single program has created and is making use of multi-
ple Tcl interpreters) for the C routines to maintain any permanent data
they need on a per-interpreter basis. If needed static data was simply
declared static in C, you will probably have reentrancy problems when
you work with multiple interpreters.
Tcl solves this through the client data mechanism. When you are about
to call Tcl_CreateObjCommand to add a new command to an interpreter, if
your command needs to keep some read/write data across invocations, you
should allocate the space, preferably using Tcl_Alloc instead of
malloc, then pass the address of that space as the ClientData pointer
to Tcl_CreateObjCommand.
When your command is called from Tcl, the ClientData pointer you passed
to Tcl_CreateObjCommand will be passed to your C routine through the
ClientData pointer calling argument.
Commands that need to share this data with one another can do so by
using the same ClientData pointer when the commands are added.
It is important to note that the Tcl extensions in the tclX8.0.0 direc-
tory have had all of their data set up in this way. Since release 6.2,
Extended Tcl has supported multiple interpreters within one invocation
of Tcl.
THEORY OF HANDLES
Sometimes you need to have a data element that isn't readily repre-
sentable as a string within Tcl, for example a pointer to a complex C
data structure. It is not a good idea to try to pass pointers around
within Tcl as strings by converting them to and from hex or integer
representations, for example. It is too easy to mess one up, and the
likely outcome of doing that is a core dump.
Instead we have developed and made use of the concept of handles. Han-
dles are identifiers a C extension can pass to, and accept from, Tcl to
make the transition between what your C code knows something as and
what name Tcl knows it by to be as safe and painless as possible. For
example, the I/O system included in Tcl uses file handles. When you
open a file from Tcl, a handle is returned of the form filen where n is
a file number. When you pass the file handle back to puts, gets, seek,
flush and so forth, they validate the file handle by checking the the
file text is present, then converting the file number to an integer
that they use to look into a data structure of pointers to Tcl open
file structures, which contain a Unix file descriptor, flags indicating
whether or not the file is currently open, whether the file is a file
or a pipe and so forth.
Handles have proven so useful that, since TclX release 6.1a, general
support has been available to help create and manipulate them. Many of
these capabilities have migrated into baseline Tcl. If you have a sim-
ilar need, you might like to use the handle routines documented in Han-
dles(3) in Extended Tcl. We recommend that you use a unique-to-your-
package textual handle coupled with a specific identifier and let the
handle management routines validate it when it's passed back. It is
much easier to track down a bug with an implicated handle named some-
thing like file4 or bitmap6 than just 6.
Note that Tcl's object offers another way for complex data structures
to exist in parallel with and underneath Tcl strings. As of this writ-
ing (May 30, 1997) this is fairly new territory, but things are looking
good for the prospects of using the Tcl object system in this manner,
and for enhancements to the object system that allow even Tcl objects
to have methods in a very straightforward and simple way.
USING COMMANDS TO DO THE SAME THING, AND MORE
Another handle-like technique, first popularized in the Tk toolkit,
offers handle-like capabilities as well as some neat additional capa-
bilities. That is to create a new Tcl command, from C, that uses
ClientData to keep a "handle" on its complex underlying data structure.
Then by having that command look at its second argument for what it is
to do (its sub-functions), you get these nice methods, where you have
several additional sub-commands that don't pollute the global namespace
and only work on (and are available with) the objects (new commands)
they are relevant to. For example, in Tk, creating a checkbutton
(checkbutton .b) creates a new Tcl command (.b), that has methods to
configure the button, select, deselect, toggle and flash it.
A lot of people think this is really the way to go, and I am pretty
much leaning that way myself. If you use the incr tcl script-level
object system for Tcl, objects that you define in Tcl will be highly
compatible in terms of their command interfaces and configuration man-
agement with objects you create in C using the the command-and-Client-
Data technique described here. I believe Tk has some nice facilities
for making this easy for the Tcl programmer. Itcl certainly does.
TRACKING MEMORY CORRUPTION PROBLEMS
Occasionally you may write code that scribbles past the end of an allo-
cated piece of memory. This will usually result in a core dump or mem-
ory allocation failure sometime later in the program, often implicating
code that is not actually responsible for the problem (as you start
looking from the point where the error is detected, which is usually
where the later routine has failed).
The memory debugging routines included in Tcl can help find these prob-
lems. Developed by Mark and Karl, the memory debugging routines are
now part of baseline Tcl, and is to our knowledge the largest piece of
TclX to drop into the core without being reengineered first. (You see,
summer back in '91, John was sitting in his office in the CS building
at UC Berkeley trying to find a memory leak somewhere in Tcl, when he
was paid a visit by two long-haired-yet-polite programmers bearing
gifts in the form of the technology grab-bag known as Extended Tcl. He
saw that, using TclX's malloc routines, Tcl could be prompted to print
the filename and line number of every single memory allocation that did
not have a corresponding free. It was just what the doctor ordered ;-)
See Memory(TCL) for details.
INSTALLING YOUR EXTENSIONS INTO TCL
To add your extensions to Tcl, you used to have to statically link
them, together with any other extensions, into a single binary exe-
cutable image. Today, although the statically linked executable is
still an option, most operating systems, even Microsoft Windows, sup-
port shared libraries, and in most cases, Tcl can now make use of those
shared libraries such that you extensions, and most others, can now be
built a shared libraries that can be loaded in (using package require)
by scripts that need them. Shared libraries can simplify a Tcl instal-
lation, because only one copy of Tcl is required, rather than a hode-
podge of combinations of applications that you might have found at a
big Tcl site in the previous era.
GNU AUTOCONF
While the build procedure for shared libraries varies from system to
system, most Unix and Unix workalike systems will figure out the
nuances of the compiler and linker arguments automatically when the
configure script is run. If you are building a package that you plan
to make generally available, we strongly recommend that you use GNU
autoconf (ftp://prep.ai.mit.edu/pub/gnu) to set up an automatic config-
ure script for it. Be forewarned that autoconf uses some pretty heavy
duty shell and sed script magic to get the job done, and the learning
curve can be pretty steep. Once done and shaken out, though, it's
rewarding to know that your package can build and run on everything
from a notebook to a Cray to a RISC SMP server.
Application-specific startup is accomplished by creating or editing the
Tcl_AppInit function. In Tcl_AppInit you should add a call to an
application-specific init function which you create. This function
should take the address of the interpreter it should install its com-
mands into, and it should install those commands with Tcl_CreateCommand
and do any other application-specific startup that is necessary.
The naming convention for application startup routines is App_Init,
where App is the name of your application. For example, to add an
application named cute one would create a Cute_Init routine that
expected a Tcl_Interp pointer as an argument, and add the following
code to Tcl_AppInit:
if (Cute_Init (interp) == TCL_ERROR) {
return TCL_ERROR;
}
As you can guess from the above example, if your init routine is unable
to initialize, it should use Tcl_AppendResult to provide some kind of
useful error message back to TclX, then return TCL_ERROR to indicate
that an error occurred. If the routine executed successfully, it
should return TCL_OK.
When you examine Tcl_AppInit, note that there is one call already there
to install an application -- the call to TclX_Init installs Extended
Tcl into the Tcl core.
MAKING APPLICATION INFORMATION VISIBLE FROM EXTENDED TCL
TclX's infox command can return several pieces of information relevant
to Extended Tcl, including the application's name, descriptive name,
patch level and version. Your application's startup can set these
variables to application-specific values. If it doesn't, they are
given default values for Extended Tcl.
To set these values, first be sure that you include either tclExtend.h
or tclExtdInt.h from the source file that defines your init routine.
This will create external declarations for the variables. Then, set
the variables in your init route, for example:
tclAppName = "cute";
tclAppLongName = "Call Unix/Tcl Environment";
tclAppVersion = "2.1";
Note that the default values are set by TclX_Init, so if you wish to
override them, you must call your init routine in Tcl_AppInit after its
call to TclX_Init.
EXTENDED TCL EXIT
When Extended Tcl exits, Tcl_DeleteInterp may be called to free memory
used by Tcl -- normally, this is only called if TCL_MEM_DEBUG was
defined, since Unix will return all of the allocated memory back to the
system, anyway. If TCL_MEM_DEBUG was defined, it is called so that any
memory that was allocated without ever being freed can be detected.
This greatly reduces the amount of work to detect and track down memory
leaks, a situation where some piece of your code allocates memory
repeatedly without ever freeing it, or at least without always freeing
it.
It is often necessary for an application to perform special cleanup
functions upon the deletion of an interpreter as well. To facilitate
this activity, Tcl provides the ability to perform a function callback
when an interpreter is deleted. To arrange for a C function to be
called when the interpreter is deleted, call Tcl_CallWhenDeleted from
your application initialization routine. For details on how to use
this function, read the CallDel(3) manual page that ships with core
Tcl.
EXECUTING TCL CODE FROM YOUR C EXTENSION
Suppose you are in the middle of coding a C extension and you realize
that you need some operation performed, one that would be simple from
Tcl, but possibly excruciating to do directly in C. Tcl provides a
number of C-level interfaces whereby you can cause Tcl code to be exe-
cuteed. The old-style calls are Tcl_Eval, Tcl_VarEval, Tcl_EvalFile
and Tcl_GlobalEval. The results of these calls can be dug out of the
interpreter using Tcl_GetStringResult, if you want a string representa-
tion of the result, or Tcl_GetObjResult if you want the object. (The
use of interp->result to access the result string has been deprecated.)
The Tcl object system adds Tcl_EvalObj and Tcl_GlobalEvalObj. The dif-
ference here is that we are evaluating an object, not just a string,
and using these routines in preference to the aforementioned ones can
result in a major performance improvement in your code, when the code
is executed repeatedly (even if it only executes once but loops several
times within itself), as these routines make it possible for the byte-
code compiler to compile the code being evaluated and save the compiled
code with the data structure, in an implementation-dependent manner.
For more information please consult the EvalObj(3) and Eval(3) manual
pages within the Tcl distribution.
ACCESSING TCL VARIABLES AND ARRAYS FROM YOUR C EXTENSIONS
In addition to the non-object-system ways of reading from and storing
to Tcl variables, using routines such as Tcl_SetVar2 and Tcl_GetVar2,
Tcl variables and arrays can be read from a C extension as Tcl objects
by using the Tcl_ObjGetVar2 function, and set from C extensions through
the Tcl_ObjSetVar2 function.
Please note that the object versions do not carry forward analogues to
the one-variable-name-argument Tcl_GetVar, Tcl_SetVar, and Tcl_Unset-
Var. If you know you have a scalar, call the object variable get and
set functions with a NULL second argument. If your variable name might
contain an array reference via a self-contained embedded array index
(i.e., I'm asking Tcl_ObjGetVar2 for "foo(5)" instead of "foo" "5"),
add the TCL_PARSE_PART1 to the flags in your call.
While the fact that Tcl_ObjGetVar2 retrieves Tcl objects, rather than
strings, is critical for the object system to be able to provide the
performance boosts from "lazy" type conversion and the binary data
capabilities, the arguments containing the variable name, or the array
name and element name if they've been split out, also must be specified
as Tcl objects rather than strings. While this is useful on occasion,
those writing C extensions for Tcl in the post-object-system era usu-
ally have the names available as plain old char * variables, requiring
conversion of the strings to objects before use and account for their
possible destruction afterwards.
To simplify the task in those cases, TclX adds the TclX_ObjGetVar2S
subroutine. It works just like Tcl_ObjGetVar2, except the one or two
variable name arguments are specified as strings, and the routine takes
care of making and disposing of object equivalents.
Tcl variables can be unset from C via the Tcl_UnsetVar and Tcl_Unset-
Var2 functions. There are currently (as of 8.0) no object-system
equivalents, so in the rare case where you have the name of the vari-
able you want unset as an object instead of a string, you can call
Tcl_GetStringFromObj to obtain the string representation first.
For complete information on these functions, please refer to the
ObjSetVar(3) and SetVar(3) manual pages in the doc directory of the
core Tcl distribution.
LINKING TCL VARIABLES TO C VARIABLES
Tcl_LinkVar and Tcl_UnlinkVar can be used to automatically keep Tcl
variables synchronized with corresponding C variables. Once a Tcl
variable has been linked to a C variable with Tcl_LinkVar, anytime the
Tcl variable is read, the value of the C variable is converted (if nec-
essary) and returned, and when the Tcl variable is written, the C vari-
able will be updated with the new value.
Tcl_LinkVar uses variable traces to keep the Tcl variable named by var-
Name in sync with the C variable at the address given by addr.
Int, double, boolean and char * variables are supported. You can make
your linked variables read only from the Tcl side, as well. Please
note that the C variables must continually exist while they are linked,
in other words, linking "automatic" C variables, those created on the
stack while a routine is being executed and destroyed afterwards, will
result in a malfunctioning program at best and a coredump or more at
worst.
For more information, please examine the LinkVar(3) manual page in the
core Tcl distribution.
ADDING NEW MATH FUNCTIONS TO TCL
As of Tcl version 7.0, math functions such as sin, cos, etc, are
directly supported within Tcl expressions. These obsolete the Extended
Tcl commands that provided explicit commands for these functions for
many, many releases, although procs equivalencing the old TclX commands
to the new math functions are still provided for backwards compatibil-
ity.
New math functions can be added to Tcl, or existing math functions can
be replaced, by calling Tcl_CreateMathFunc.
ACCESSING AND MANIPULATING THE RANDOM NUMBER GENERATOR
Prior to Tcl version 8.0, the Tcl core did not provide access to a ran-
dom number generator, but TclX did, through its random command. As of
Tcl version 8.0, access to a random number generator is provided by
baseline Tcl through the new math functions, rand and srand.
The TclX random command is still available -- it has some useful capa-
bilities not directly provided by the new baseline functions.
For more information on adding your own math functions to Tcl, please
study the CrtMathFnc(3) manual page in the core Tcl distribution.
CONVERTING FILENAMES TO NATIVE FORM AND PERFORMING TILDE SUBSTITUTIONS
The Tcl_TranslateFileName function is available to C extension writers
to translate filenames to a form suitable for use by the local operat-
ing system. It converts network names to their native form, and if the
name starts with a ``~'' character, the function returns a new string
where the name is replaced with the home directory of the given user.
For more information please consult the Translate(3) manual page in the
core Tcl distribution.
SETTING THE RECURSION LIMIT
Tcl has a preset recursion limit that limits the maximum allowable
nesting depth of calls within an interpreter. This is useful for
detecting infinite recursions before other limits such as the process
memory limit or, worse, available swap space on the system, run out.
The default limit is just a guess, however, and applications that make
heavy use of recursion may need to call Tcl_SetRecursionLimit to raise
this limit. For more information, please consult the SetRecLmt(3) man-
ual page in the core Tcl distribution.
HANDLING SIGNALS FROM TCL EXTENSIONS
If an event such as a signal occurs while a Tcl script is being exe-
cuted, it isn't safe to do much in the signal handling routine -- the
Tcl environment cannot be safely manipulated at this point because it
could be in the middle of some operation, such as updating pointers,
leaving the interpreter in an unreliable state.
The only safe approach is to set a flag indicating that the event
occurred, then handle the event later when the interpreter has returned
to a safe state, such as after the current Tcl command completes.
The Tcl_AsyncCreate, Tcl_AsyncMark, Tcl_AsyncInvoke, and
Tcl_AsyncDelete functions provide a safe mechanism for dealing with
signals and other asynchronous events. For more information on how to
use this capability, please refer to the Async(3) manual page in the
core Tcl distribution.
Note that Extended Tcl provides built-in support for managing signals
in numerous ways, including generating them with alarm(2) and kill(2),
ignoring them, trapping them, getting, setting, blocking and unblocking
them. You can cause specific code to execute at a safe point after a
signal occurs, or cause a Tcl error backtrace on one's occurrence. For
more information, please examine the TclX documentation.
PARSING BACKSLASH SEQUENCES
The Tcl_Backslash function is called to parse Tcl backslash sequences.
These backslash sequences are the usual sort that you see in the C pro-
gramming language, such as \n for newline, \r for return, and so forth.
Tcl_Backslash parses a single backslash sequence and returns a single
character corresponding to the backslash sequence.
For more info on this call, look at the Backslash(3) manual page in the
core Tcl distribution. For information on the valid backslash
sequences, consult the summary of Tcl language syntax, Tcl(n) in the
same distribution.
HASH TABLES
Hash tables provide Tcl with a high-performance facility for looking up
and managing key-value pairs located and maintained in memory. Tcl
uses hash tables internally to locate procedure definitions, Tcl vari-
ables, array elements, file handles and so forth. Tcl makes the hash
table functions accessible to C extension writers as well.
Hash tables grow automatically to maintain efficiency, rather than
exposing the table size to the programmer at allocation time, which
would needlessly add complexity to Tcl and would be prone to ineffi-
ciency due to the need to guess the number of items that will go into
the table, and the seemingly inevitable growth in amount of data pro-
cessed per run over the useful life of the program.
For more information on hash tables, please consult the Hash(3) manual
page in the core Tcl distribution.
TRACING VARIABLE ACCESSES
The C extension writer can arrange to have a C routine called whenever
a Tcl variable is read, written, or unset. Variable traces are the
mechanism by which Tk toolkit widgets such as radio and checkbuttons,
messages and so forth update without Tcl programmer intervention when
their data variables are changed. They are also used by the routine
that links Tcl and C variables, Tcl_LinkVar, described above.
Tcl_TraceVar is called to establish a variable trace. Entire arrays
and individual array elements can be traced as well. If the programmer
already has an array name in one string and a variable name in another,
Tcl_TraceVar2 can be called. Calls are also available to request
information about traces and to delete them.
For more information on variable traces, consult the TraceVar(3) manual
page in the core Tcl distribution.
TRACING TCL EXECUTION
Tcl has the ability to call C routines each time it executes a Tcl com-
mand, up to a specified depth of nesting levels. The command Tcl_Cre-
ateTrace creates an execution trace; Tcl_DeleteTrace deletes it.
Command tracing is used in Extended Tcl to implement the cmdtrace Tcl
command, a useful command for debugging Tcl applications.
For complete information on execution tracing, please look at the Crt-
Trace(3) manual pages in the core Tcl distribution.
EVALUATING TCL EXPRESSIONS FROM C
Tcl_ExprLong, Tcl_ExprDouble, Tcl_ExprBool, and Tcl_ExprString all take
string arguments and, when called, evaluate those strings as Tcl
expressions. Depending on the routine called, the result is either a C
long, a double, a boolean (int with a value of 0 or 1), or a char *
(obtainable through Tcl_GetResult).
To take advantage of the performance gains available through the byte-
code compiler, Tcl_ExprLongObj, Tcl_ExprDoubleObj, Tcl_ExprBoolObj, and
Tcl_ExprObj all take an object containing an expression to be evaluated
(rather than a string.) The result is that bytecode-compiled version
of the expression will be kept in the object, alongside the string rep-
resentation. If the expression is evaluated again, without being
changed, it does not have to be recompiled... a major performance win.
For complete information on evaluating Tcl expressions from C, you are
invited to examine the ExprLong(3) and ExprLongObj(3) manpages in the
core Tcl distribution.
PATTERN MATCHING
The Tcl_StringMatch function can be called to see if a string matches a
specified pattern. Tcl_StringMatch is called by the Tcl string match
command, so the format for patterns is identical. The pattern format
is similar to the one used by the C-shell; string(n) describes this
format.
More information about Tcl_StringMatch is available in the StrMatch(3)
manpage in the core Tcl distribution.
REGULAR EXPRESSION PATTERN MATCHING
Tcl_RegExpMatch can be called to determine whether a string matches a
regular expression. Tcl_RegExpMatch is used internally by the regexp
Tcl command.
As regular expressions are typically "compiled" before use, a fairly
involved process, Tcl also supports routines that separate the compila-
tion of an expression from its use: Tcl_RegExpCompile, Tcl_RegExpExec,
and Tcl_RegExpRange. If an expression is going to be matched many
times, doing the compile once and caching the compiled regular expres-
sion result, then reusing the cached version by using Tcl_RegExpExec,
can be a significant performance win.
For more information on this function, please consult the RegExp(3)
manpage in the core Tcl distribution.
MANIPULATING TCL LISTS FROM C EXTENSIONS
The C extension writer often needs to create, manipulate and decompose
Tcl lists. Tcl_SplitList and Tcl_Merge used to be the only way to
parse strings into lists and vice versa. As of Tcl 8, lists can be
parsed and assembled, object-style, using Tcl_ListObjGetElements and
Tcl_SetListObj, and friends. Once again the "win" of using object-sys-
tem-based list manipulation, instead of the previous string based rou-
tines, is that the parsing of a string in an object to a list is cached
in the object structure, the same as with integers and floating point
numbers, compiled procedures, etc. The next time this string needs to
be looked at as a list, if the contents of the string have not changed,
the string does not have to be parsed.
In the author's experience, working with an admittedly degenerate test
whereby we iterated rather inefficiently across a 6,000-element list, a
speedup factor of more than 2500 was obtained over the previous non-
object-based version of Tcl.
For more information on these commands, please consult the ListObj(3)
manual page in the core Tcl distribution.
CONCATENATING STRINGS
Tcl_ConcatObj concatenates the string representation of zero or more
objects into a single new object. The elements of the new string are
space-separated. Tcl_Concat does the same thing for strings, as
Tcl_ConcatObj does for objects.
Concatenating strings is similar to constructing lists from them,
except that Tcl_ConcatObj and Tcl_Concat do not attempt to make the
resulting string into a valid Tcl list.
Tcl_Concat is documented in the Concat(3) manpage, and Tcl_ConcatObj in
the tringObj manpage, both in the core Tcl distribution.
DETECTING WHETHER OR NOT YOU HAVE A COMPLETE COMMAND
C routines that collect data to form a command to be passed to Tcl_Eval
often need a way to tell whether they have a complete command already
or whether they need more data. (Programs that read typed-in Tcl input
such as Tcl shells need this capability, for instance.) Tcl_Command-
Complete can be used to tell whether or not you have a complete com-
mand.
For more information examine CmdCmplt(3) in the core Tcl distribution.
RECORDING COMMANDS FOR COMMAND HISTORY
Tcl has a history mechanism that is accessed from Tcl through the his-
tory command. If you want your extension to propagate commands into
the command history, you should call Tcl_RecordAndEvalObj (object sys-
tem) or Tcl_RecordAndEval (old system),
These commands work like Tcl_EvalObj and Tcl_Eval, respectively, except
that these versions record the command as well as executing it.
Tcl_RecordAndEval and Tcl_RecordAndEvlObj should only be called with
user-entered top-level commands, since the history mechanism exists to
allow the user to easily access, edit and reissue previously issued
commands.
For complete information on these functions, please examine the
RecordEval.3 and RecEvalObj.3 manual pages in the core Tcl distribu-
tion.
CONVERTING FLOATING POINT VALUES TO STRINGS
The Tcl object system's Tcl_GetDoubleFromObj and Tcl_SetDoubleObj use
Tcl objects, rather than the strings used by Tcl_PrintDouble, and con-
vert, when necessary, an ASCII string to a double and back again.
These routines ensure that the string output will continue to be inter-
pretable as a floating point number, rather than an integer, by always
putting a ``.'' or ``e'' into the string representing the number.
The precision of the output string is controlled by the Tcl tcl_preci-
sion variable.
For complete information on these routines, please examine DoubleObj(3)
and PrintDbl(3) in the core Tcl distribution.
CREATING CHILD PROCESSES AND PIPELINES FROM C
Tcl_OpenCommandChannel provides a C-level interface to the exec and
open commands. The child (or pipeline of children) can have its stan-
dard input, output and error redirected from files, variables or pipes.
To understand the meaning of the redirection symbols understood by this
function, look at the exec(n) Tcl command. For complete information on
Tcl_OpenCommandChannel, please examine OpenFileChnl(3).
ACCESSING TCL FILEHANDLES FROM C
On Posix/Unix systems, Tcl filehandles passed to your C extension can
be translated to a Posix FILE * structure using the Tcl_GetOpenFile
function, documented in GetOpnFl.3.
MANAGING BACKGROUND PROCESS TERMINATION AND CLEANUP
When a Posix system does a fork to create a new process, the process ID
of the child is returned to the caller. After the child process exits,
its process table entry (and some other data associated with the pro-
cess) cannot be reclaimed by the operating system until a call to wait-
pid, or one of a couple of other, similar system calls, has been made
by the parent process.
The C extension writer who has created a subprocess, by whatever mecha-
nism, can turn over responsibility for detecting the processes' termi-
nation and calling waitpid to obtain its exit status, by calling
Tcl_DetachPids on it.
Tcl_ReapDetachedProcs is the C routine that will detect the termination
of any processes turned over to Tcl, permitting the processes to be
fully reclaimed by the operating system. It is usually not necessary
to call Tcl_ReapDetachedProcs, as it is called automatically every time
exec is performed.
For complete information on these routines, please look at Detach-
Pids(3) in the core Tcl distribution.
FOR MORE INFORMATION
In addition to the documentation referenced above, you can learn a lot
by studying the source code of the commands added by Tcl, Tk and
Extended Tcl, etc. The comp.lang.tcl Usenet newsgroup is read by hun-
dreds of thousands of Tcl people. A number of Frequently Asked Ques-
tions (FAQs) about Tcl are posted there periodically. The newsgroup is
a good place to ask questions (after you've made sure they're not
already answered in the FAQ ;-)
Finally, if you have interactive Internet access, you can ftp to
ftp://ftp.neosoft.com/pub/tcl, the site for contributed Tcl sources.
This site contains quite a few extensions, applications, and so forth,
including several object-oriented extension packages.
If you have access via the world-wide web, check out the Sun Microsys-
tems site (http://sunscript.sun.com), the contributed sources archive
website (http://www.neosoft.com/tcl), and the homepage for Extended Tcl
(http://www.neosoft.com/tclx).
AUTHORS
Extended Tcl was created by Karl Lehenbauer (karl@neosoft.com) and Mark
Diekhans (markd@grizzly.com).
Tcl Command Writing(TCL)