TCLCOMMANDWRITING(3tcl)

๐Ÿ“ฆ version 8.6.3, in ubuntu stonking

๐Ÿ’– NAME ๐Ÿ’–

TclCommandWriting โ€” writing C language extensions to Tcl. This is the 3tcl manual page, bestie, and it's the one you read before you go poking at Tcl's internals with a C compiler. ๐Ÿฆ‹


๐ŸŒธ OVERVIEW ๐ŸŒธ

Okay so this doc? It's for you, the programmer who wants to extend Tcl with C language routines. Write <pink>her</pink> some new commands, give her new powers. Periodt.

It's also genuinely useful if you're trying to bolt Tcl into something you already have โ€” an existing editor, a communications program, a window manager, whatever. Any of it. ๐Ÿ’…

And if you want even more C programming tea, go look at the *.3 manual pages in the doc directory of the Berkeley distribution, plus the *.3 manpages in the man directory of Extended Tcl. Two whole treasure chests. Go off. ๐Ÿ“š


โœจ WRITING TCL EXTENSIONS IN C โœจ

So here's the vibe: every single C-based Tcl command gets called with exactly four arguments:

  • a client data pointer
  • an interpreter pointer
  • an argument count
  • a pointer to an array of pointers to character strings โ€” those strings being the Tcl arguments to the command

Four. That's the whole squad. No more, no less.

Lowkey the cutest little Tcl extension in C looks like this, and we're gonna break it down right after:

#include "tcl.h"

int App_EchoCmd(clientData, interp, argc, argv)
    void       *clientData;
    Tcl_Interp *interp;
    int         argc;
    char      **argv;
{
        int i;

        for (i = 1; i < argc; i++) {
                printf("%s",argv[i]);
          if (i < argc - 1) printf(" ");
        }
        printf("\n");
        return TCL_OK;
}

The client data pointer? We'll get to her later, she needs her own moment. ๐Ÿซถ

the interpreter pointer, a whole icon ๐Ÿ‘‘

The interpreter pointer is the ``key'' to an interpreter. It's handed to you by Tcl_CreateInterp, it gets used extensively inside Tcl, and bestie, it's gonna get used extensively by your C extensions too.

The data structure that this pointer points at โ€” plus all the subordinate structures branching off of it โ€” that is a Tcl interpreter. All of it. Which means: all the currently defined procedures, commands, variables, arrays, and the execution state of that interpreter. Yeah. Everything. ๐Ÿคฏ

Want more on creating and deleting interpreters? Go read the CrtInterp(3) manpage in the Berkeley Tcl distribution.

Want to create interpreters that come pre-loaded with the commands Extended Tcl provides? Run, don't walk, to the TclX_Init(3) manpage of Extended Tcl.

And if you want a manual page that breaks down the user-visible fields of a Tcl interpreter? That's Interp(3) in Berkeley Tcl. โœจ

argc and argv โ€” it's giving C main energy

The argument count and the pointer to an array of pointers to textual arguments? You handle those in your C code exactly the way you'd handle them writing a C main function. Same deal. The argument count and the array of pointers work the same as in a C main call, and pointers to the arguments to the function live in the argv array.

And just like a C main, the first argument โ€” argv[0] โ€” is the name the routine was called as. (In a main, it's the name the program was invoked as. Same flavor, different context. ๐Ÿ’ซ)

In that example up there, every argument gets printed out with a space between each one, by looping through argv from one up to the argument count, argc. Then a newline gets printed to terminate the line. So basically โ€” an ``echo'' command. She's simple, she's effective, we love her. ๐Ÿ’–

everything arrives as a string, no cap

All arguments from a Tcl call to a Tcl C extension are passed as strings. That's the law. So if your C routine is expecting numeric arguments, you have to convert them first. Your options:

  • the Tcl_GetInt function
  • the Tcl_GetDouble function
  • Extended Tcl's Tcl_GetLong
  • Extended Tcl's Tcl_GetUnsigned
  • or honestly some other method of your own devising, you creative genius

And for converting boolean values? Use Tcl_GetBoolean. Obviously. ๐Ÿ™ˆ

Here's the slay part: these routines automatically leave an appropriate error message in the Tcl interpreter's result buffer, and they return TCL_ERROR if a conversion error happens. They handle the drama for you. (More on these routines in the GetInt(3) manpage in the Berkeley Tcl distribution.)

returning numbers back to Tcl

Flip side, bestie: if your program produces a numeric result, it should return a string equivalent to that numeric value. Common way to do it is something likeโ€ฆ

sprintf(interp->result, "%ld", result);

writing straight into the result buffer โ€” read this part ๐Ÿ“ข

Writing results directly into the interpreter's result buffer is only good for relatively short results. That's the catch. That's the whole catch.

Tcl has a function for the big-girl cases: Tcl_SetResult. It gives your C extensions the ability to return very large strings to Tcl. And while it's at it, it lets you tell the interpreter what's up with the string:

  • whether it ``owns'' the string โ€” meaning Tcl should delete the string when it's done with it
  • that the string is likely to be changed or overwritten soon โ€” meaning Tcl should make a copy of the string right away
  • or that the string won't change โ€” so Tcl can just use the string as-is and not worry about it at all
Understanding how results are passed back to Tcl is essential to the C extension writer. Study the SetResult(3) manual page in the Tcl distribution. For real. ๐Ÿ“–

validate your args, it's not optional

Sophisticated commands should verify their arguments whenever possible. All of it:

  • by examining the argument count
  • by verifying that numeric fields are really numeric
  • that values are in range (when their ranges are known)
  • and so forth, you get the idea

Tcl is designed to be as bullet-proof as possible โ€” in the sense that no Tcl program should be able to cause Tcl to dump core. Please carry that notion forward with your C extensions by validating arguments like we just said. Don't be the one who breaks the streak. ๐Ÿ’€


๐Ÿ”ฅ ANOTHER C EXTENSION โ€” THE MAX COMMAND ๐Ÿ”ฅ

Okay, new command. In the one below, two or more arguments get compared, and the one with the maximum value is returned โ€” if all goes well. ๐ŸŒŸ

It is an error if there are fewer than three arguments. We're counting: the pointer to the ``max'' command text itself (argv[0]), and pointers to at least two arguments to compare the values of.

This routine also shows off the programmer-labor-saving Tcl_AppendResult routine. See the Tcl manual page SetResult(3) for the details on that one. And while you're at it, go examine the calls Tcl_AddErrorInfo, Tcl_SetErrorCode and Tcl_PosixError, documented in the Tcl manual page AddErrInfo(3). ๐Ÿ‘‘

int
Tcl_MaxCmd (clientData, interp, argc, argv)
    char       *clientData;
    Tcl_Interp *interp;
    int         argc;
    char      **argv;
{
    int maxVal = MININT;
    int maxIdx = 1;
    int value, idx;

    if (argc < 3) {
        Tcl_AppendResult (interp, "bad # arg: ", argv[0],
                          " num1 num2 [..numN]", (char *)NULL);
        return TCL_ERROR;
    }

    for (idx = 1; idx < argc; idx++) {
        if (Tcl_GetInt (argv[idx], 10, &Value) != TCL_OK)
            return TCL_ERROR;

        if (value > maxVal) {
            maxVal = value;
            maxIdx = idx;
        }
    }
    Tcl_SetResult (interp, argv [maxIdx], TCL_VOLATILE);
    return TCL_OK;
}

TCL_OK vs TCL_ERROR, the classic duo ๐Ÿ’ซ

When Tcl-callable functions complete, they should normally return TCL_OK or TCL_ERROR.

  • TCL_OK is returned when the command succeeded.
  • 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. Every time. No exceptions.

What's less clear in some cases is whether Tcl errors should be returned, or whether a function should just return a status value. Example that lives in my head rent-free: end-of-file during a gets returns a status, but open returns an error if the open fails. Different vibes, same library. ๐Ÿคทโ™€๏ธ

Errors can be caught from Tcl programs using the catch command. (Go see Tcl's catch(n) and error(n) manual pages.)

the rare return values ๐Ÿ‘€

Less common return values are TCL_RETURN, TCL_BREAK and TCL_CONTINUE. These are used if you're adding new control and/or looping structures to Tcl. Which is a whole power move, honestly.

To see these values in action, go examine the source code to Tcl's while, for and if, and Extended Tcl's loop commands. Read the source, it's good for you. ๐Ÿ“š

the TCL_VOLATILE moment

Note the call to Tcl_SetResult in the command above, setting the return value to Tcl. TCL_VOLATILE is used because the memory containing the result will be freed upon the function's return. So Tcl needs to know: copy this now before it evaporates. ๐Ÿ’จ


๐Ÿฆ‹ ANOTHER C EXTENSION โ€” THE LREVERSE COMMAND ๐Ÿฆ‹

Last one for this part, and she's a cutie. In the command below, one list is passed as an argument, and a list containing all of the elements of that 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 (argv[0]), and a pointer to the list to reverse.

Here's the little flow, step by step:

  1. Once lreverse has determined that it has received the correct number of arguments, Tcl_SplitList is called to break the list into an argc and argv array of pointers.
  2. lreverse then operates on the array of pointers, swapping them from lowest to highest, second-lowest to second-highest, and so forth. Classic.
  3. Finally Tcl_Merge is called to create a single new string containing the reversed list, and it's set as the result via Tcl_SetResult.

Note that TCL_DYNAMIC is used to tell Tcl_SetResult that it now owns the string, and it is up to Tcl to free the string when it is done with it. Hand it over, Tcl's got it now. ๐Ÿซถ

Also note: it is safe to play around with the argv list like this, and a single call to ckfree can be made to free all the data returned by Tcl_SplitList in this manner. One call. So satisfying. ๐Ÿ˜ญ

int
Tcl_LreverseCmd(notUsed, interp, argc, argv)
    ClientData notUsed;           /* Not used. */
    Tcl_Interp *interp;           /* Current interpreter. */
    int argc;                /* Number of arguments. */
    char **argv;             /* Argument strings. */
{
    int listArgc, lowListIndex, hiListIndex;
    char **listArgv;
    char *temp, *resultList;

    if (argc != 2) {
     Tcl_AppendResult(interp, "wrong # args: should be
          " list
     return TCL_ERROR;
    }

    if (Tcl_SplitList(interp, argv[1], &listArgc, &listArgv) != TCL_OK) {
     return TCL_ERROR;
    }
    for (lowListIndex = 0, hiListIndex = listArgc;
      --hiListIndex > lowListIndex; lowListIndex++) {
     temp = listArgv[lowListIndex];
     listArgv[lowListIndex] = listArgv[hiListIndex];
     listArgv[hiListIndex] = temp;
    }
    resultList = Tcl_Merge (listArgc, listArgv);
    ckfree (listArgv);
    Tcl_SetResult (interp, resultList, TCL_DYNAMIC);
    return TCL_OK;
}

โœจ INSTALLING YOUR COMMAND โœจ

okay so bestie, you wrote the C function, but Tcl doesn't know it exists yet ๐Ÿ’€ to actually get it into an interpreter you call Tcl_CreateCommand, and you gotta hand it these five things:

  • the pointer to the interpreter you want the command installed into
  • the name of the command
  • a pointer to the C function that implements the command
  • a client data pointer
  • a pointer to an optional callback routine

the client data pointer and the callback routine get their own lil explanation later, don't worry ๐Ÿซถ

so like, for the max function we were just talking about (which, fun fact, comes from TclX's tclXmath.c in the TclX7.4/src directory):

Tcl_CreateCommand (interp, "max", Tcl_MaxCmd, (ClientData)NULL,
                  (void (*)())NULL);

in that example the max function gets added to the interpreter you specified, and both the client data pointer and the callback function pointer are NULL. (if you want the FULL tea on Tcl_CreateCommand and its bestie Tcl_CommandInfo, go read the CrtCommand(3) command page in the Berkeley Tcl distribution, no cap.)


๐ŸŒˆ DYNAMIC STRINGS ๐ŸŒˆ

dynamic strings are this super important abstraction that first showed up with Tcl 7.0, and they're kinda iconic ngl. dynamic strings, aka DStrings, let you build up arbitrarily long strings by repeatedly appending stuff to them โ€” over and over, forever, whatever length you need. the slay part? DStrings cut down on how much allocating and copying you gotta do to add info to a string, and they also just make the whole process simpler. for the complete rundown on dynamic strings, go examine the DString(3) manual page in the Berkeley Tcl distribution ๐Ÿ’–


๐ŸŽ€ CLIENT DATA ๐ŸŽ€

the client data pointer is how Tcl commands get to have data associated with them that ISN'T global to the C program and ISN'T baked into the Tcl core. this is essential in a multi-interpreter environment (aka when one single program has created and is using multiple Tcl interpreters) so your C routines can keep whatever permanent data they need on a per-interpreter basis. without it? reentrancy problems. the way Tcl dodges that whole mess is literally the client data mechanism, periodt.

so when you're about to call Tcl_CreateCommand to add a new command to an interpreter, and that command needs to keep some read/write data across invocations, you allocate the space โ€” preferably using ckalloc โ€” and then pass the address of that space as the ClientData pointer to Tcl_CreateCommand.

when your command gets called from Tcl, the ClientData pointer you gave to Tcl_CreateCommand back when you added the command to that interpreter gets handed right to your C routine through the ClientData pointer calling argument. chef's kiss ๐Ÿซถ

and if commands need to share this data with each other? easy โ€” they just use the same ClientData pointer when the commands are added.

โญ important to note: the Tcl extensions in the tclX7.4/src directory have had ALL of their data set up in this exact way. since release 6.2, Extended Tcl has supported multiple interpreters within one invocation of Tcl. slay.

๐Ÿ‘‘ THEORY OF HANDLES ๐Ÿ‘‘

sometimes you need a data element that just... isn't readily representable as a string within Tcl. like a pointer to a complex C data structure, for example. and bestie, do NOT try to pass pointers around inside Tcl as strings by converting them to and from hex or integer representations. it's way too easy to mess one up, and the likely outcome of doing that is a core dump ๐Ÿ˜ญ๐Ÿ’€

instead, we developed and made use of the concept of handles. handles are identifiers a C extension can pass to, and accept from, Tcl โ€” they make the transition between what your C code knows something as and what name Tcl knows it by as safe and painless as humanly possible. for example, the stdio package included in Tcl uses file handles. when you open a file from Tcl, a handle comes back of the form filen where n is a file number. when you pass that file handle back to puts, gets, seek, flush and so forth, they validate the file handle by checking that 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. the whole pipeline, iconic.

handles have proven SO useful that, as of release 6.1a, general support has been added for them โœจ if you need a similar capability, it would be best to use the handle routines, documented in Handles(3) in Extended Tcl. our recommendation: 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. because ngl, tracking down a bug with an implicated handle named something like file4 or bitmap6 is so much easier than tracking down one named just 6. like, imagine debugging just 6. nightmare fuel ๐Ÿ™ˆ


๐Ÿงš TRACKING MEMORY CORRUPTION PROBLEMS ๐Ÿงš

occasionally you may write code that scribbles past the end of an allocated piece of memory (we've all been there, no judgement ๐Ÿ’…). the memory debugging routines included in Tcl can help find these problems. see Memory(TCL) for details.


๐ŸŒธ INSTALLING YOUR EXTENSIONS INTO EXTENDED TCL ๐ŸŒธ

to add your extensions to Extended Tcl, you gotta compile them AND cause them to be linked with TclX. for the routines to be linked into the tcl and wishx executables, they must be referenced (directly or indirectly) from TclX. and for these extensions to be visible as Tcl commands, they must be installed into Tcl with Tcl_CreateCommand.

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 that you create. this function should take the address of the interpreter it should install its commands into, and it should install those commands with Tcl_CreateCommand and do any other application-specific startup that's 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;
}

and as you can probably guess from that 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. simple as ๐Ÿซถ

when you examine Tcl_AppInit, note that there's already one call sitting there to install an application โ€” the call to TclX_Init installs Extended Tcl into the Tcl core. cute, right?


๐Ÿ’œ 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 get 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 creates external declarations for the variables. then set the variables in your init routine, 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. order matters bestie!

๐Ÿฆ‹ 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's called so that any memory that was allocated without ever being freed can be detected. this greatly reduces the amount of work needed to detect and track down memory leaks โ€” which is the situation where some piece of your code allocates memory repeatedly without ever freeing it, or without always freeing it. the classic ๐Ÿ˜ญ

it's also often necessary for an application to perform special cleanup functions upon the deletion of an interpreter. 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 Berkeley Tcl.


โญ EXECUTING TCL CODE FROM YOUR C EXTENSION โญ

suppose you're in the middle of coding a C extension and you realize you need some operation performed โ€” one that would be simple from Tcl but possibly excruciating to do directly in C. been there bestie. Tcl provides the Tcl_Eval, Tcl_VarEval, Tcl_EvalFile and Tcl_GlobalEval functions for exactly this purpose: executing Tcl code from within a C extension. the results of the call will be in interp->result. for more information please consult the Eval(3) manual page within the Tcl distribution ๐Ÿ’…


๐Ÿ“ ACCESSING TCL VARIABLES AND ARRAYS FROM YOUR C EXTENSIONS ๐Ÿ“

Tcl variables and arrays can be read from a C extension through the Tcl_GetVar and Tcl_GetVar2 functions, and set from C extensions through the Tcl_SetVar and Tcl_SetVar2 functions. they can also be unset via the Tcl_UnsetVar and Tcl_UnsetVar2 functions. for complete information on these functions, please refer to the SetVar(3) manual page in the doc directory of the Berkeley 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 will be returned, and when the Tcl variable is written, the C variable will be updated with the new value. automatic. magical. iconic. โœจ

Tcl_LinkVar uses variable traces to keep the Tcl variable named by varName in sync with the C variable at the address given by addr.

whenever the Tcl variable is read the value of the C variable will be returned, and whenever the Tcl variable is written the C variable will be updated to have the same value.

int, double, boolean and char * variables are supported. for more information, please examine the LinkVar(3) manual page in the Berkeley Tcl distribution ๐Ÿซถ

โœจ ADDING NEW MATH FUNCTIONS TO TCL โœจ

okay so back in the day, like Tcl version 7.0 era, math functions like sin, cos and friends got built directly into Tcl expressions. iconic behavior honestly. and that means all those old Extended Tcl commands that used to make you call these functions explicitly? yeah they're obsolete now, been obsolete for many releases. RIP to them ๐Ÿ’€

but bestie, if you wanna add brand-new math functions to Tcl, or replace the ones that already exist? you do that by calling Tcl_CreateMathFunc. that's the move.

for more info on adding math functions, go peek at the CrtMathFnc(3) manual page in the Berkeley Tcl distribution. ๐Ÿ“–


๐ŸŽ€ PERFORMING TILDE SUBSTITUTIONS ON FILENAMES ๐ŸŽ€

so there's this function called Tcl_TildeSubst and it's there specifically for C extension writers who wanna do tilde substitutions on filenames. here's the tea: if the name starts with a ~ character, the function hands you back a new string where that name gets replaced with the home directory of whatever user you specified. so cute, so convenient.

want the deeper lore? consult the TildeSubst(3) manual page in the Berkeley Tcl distribution. ๐Ÿ 


๐ŸŒธ SETTING THE RECURSION LIMIT ๐ŸŒธ

Tcl has a preset recursion limit, and what it does is cap the maximum allowable nesting depth of calls inside an interpreter. this is genuinely so useful because it catches infinite recursions before other limits get blown through โ€” like your process memory limit, or worse, the available swap space on the system. ๐Ÿ˜ญ nobody wants that.

the default limit is literally just a guess though, lowkey. so if your app is doing heavy recursion, you may need to call Tcl_SetRecursionLimit to bump that limit up. do what you gotta do.

for more info, please consult the SetRecLmt(3) manual page in the Berkeley Tcl distribution. ๐Ÿ“š


๐Ÿฆ‹ HANDLING SIGNALS FROM TCL EXTENSIONS ๐Ÿฆ‹

okay bestie this one's important, pay attention. if an event like a signal happens while a Tcl script is executing, it is not safe to do much of anything inside the signal handling routine. like, at all. the Tcl environment can't be safely manipulated at that point because it could be right in the middle of some operation โ€” updating pointers, for example โ€” which leaves the interpreter in an unreliable state. ๐Ÿšจ

the only safe approach is to set a flag indicating the event occurred, then handle the event later when the interpreter has returned to a safe state, like after the current Tcl command completes. that's the whole strategy. no shortcuts.

the functions Tcl_AsyncCreate, Tcl_AsyncMark, Tcl_AsyncInvoke, and Tcl_AsyncDelete provide a safe mechanism for dealing with signals and other asynchronous events. for more info on how to use this capability, please refer to the Async(3) manual page in the Berkeley Tcl distribution. โœจ


๐Ÿ’… PARSING BACKSLASH SEQUENCES ๐Ÿ’…

Tcl_Backslash is the function you call to parse Tcl backslash sequences. and these are the usual sort you already know and love from C โ€” like \n for newline, \r for return, and so on and so forth. Tcl_Backslash parses a single backslash sequence and returns a single character corresponding to that backslash sequence. one at a time, baby. ๐ŸŽฏ

for more info on this call, look at the Backslash(3) manual page in the Berkeley Tcl distribution. and for info on the valid backslash sequences themselves, consult the summary of Tcl language syntax, Tcl(n), in the same distribution. ๐Ÿ”ค


๐ŸŒˆ HASH TABLES ๐ŸŒˆ

hash tables give Tcl a high-performance facility for looking up and managing key-value pairs that live and get maintained in memory. Tcl uses hash tables internally to locate procedure definitions, Tcl variables, array elements, file handles and so forth โ€” and then it makes those same hash table functions accessible to C extension writers too. sharing is caring. ๐Ÿ’–

here's the slay part: hash tables grow automatically to maintain efficiency, instead of exposing the table size to the programmer at allocation time. and that design choice matters, because the alternative would needlessly add complexity to Tcl and would be prone to inefficiency โ€” you'd have to guess how many items are gonna go into the table, and there'd be this seemingly inevitable growth in the amount of data processed per run over the life of the program. no thank you. ๐Ÿ™…โ€โ™€๏ธ

for more info on hash tables, please consult the Hash(3) manual page in the Berkeley 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. that's the power of variable traces. ๐Ÿซถ

and fun fact: variable traces are literally the mechanism that lets Tk toolkit widgets โ€” radio and checkbuttons, messages and so forth โ€” update themselves without any Tcl programmer intervention when their data variables change. it's also what Tcl_LinkVar, the routine that links Tcl and C variables (described above ๐Ÿ‘€), uses.

you call Tcl_TraceVar to establish a variable trace. and yes, entire arrays and individual array elements can be traced too, not just plain variables. if the programmer already has an array name in one string and a variable name in another, you can call Tcl_TraceVar2 instead. calls are also available to request information about traces and to delete them. full service. โœจ

for more info on variable traces, consult the TraceVar(3) manual page in the Berkeley Tcl distribution. ๐Ÿ‘‘


๐ŸŽฌ TRACING EXECUTION ๐ŸŽฌ

Tcl can call C routines for every command it executes, up to a specified depth of nesting levels. that's the vibe. the command Tcl_CreateTrace creates an execution trace, and Tcl_DeleteTrace deletes it. simple as.

command tracing is what Extended Tcl uses to implement the cmdtrace Tcl command โ€” which is a genuinely useful command for debugging Tcl applications. iykyk. ๐Ÿ›

for complete info on execution tracing, please look at the CrtTrace(3) manual pages in the Berkeley Tcl distribution. ๐Ÿ“ฝ๏ธ


๐Ÿงฎ EVALUATING TCL EXPRESSIONS FROM C ๐Ÿงฎ

Tcl_ExprLong, Tcl_ExprDouble, Tcl_ExprBool, and Tcl_ExprString can all be called to evaluate Tcl expressions from inside a C routine. which one did you call? that decides what you get back:

  • Tcl_ExprLong hands you a C long
  • Tcl_ExprDouble hands you a double
  • Tcl_ExprBool hands you a boolean (an int with a value of 0 or 1)
  • Tcl_ExprString hands you a char * โ€” pointed to by interp->result

for complete info on evaluating Tcl expressions from C, you're invited to examine the ExprLong(3) manpage in the Berkeley Tcl distribution. ๐Ÿ’œ


๐ŸŽฏ PATTERN MATCHING ๐ŸŽฏ

Tcl_StringMatch is the function you call to see if a string matches a specified pattern. and here's why this matters: Tcl_StringMatch is what the Tcl string match command calls, so the format for patterns is identical. the pattern format is similar to the one the C-shell uses โ€” string(n) describes that format in detail.

more info about Tcl_StringMatch is available in the StrMatch(3) manpage in the Berkeley Tcl distribution. โœจ


๐Ÿงš REGULAR EXPRESSION PATTERN MATCHING ๐Ÿงš

Tcl_RegExpMatch can be called to figure out whether a string matches a regular expression. and yeah, Tcl_RegExpMatch is used internally by the regexp Tcl command, so you already know it's legit. ๐ŸŽ€

for more info on this function, please consult the RegExp(3) manpage in the Berkeley Tcl distribution.


๐Ÿ“‹ MANIPULATING TCL LISTS FROM C EXTENSIONS ๐Ÿ“‹

bestie, the C extension writer often needs to create, manipulate and decompose Tcl lists. that's just the job. so:

  • Tcl_SplitList parses a list into an argv and argc, sorta like the way command-line arguments get passed to a Tcl extension.
  • Tcl_Merge, likewise, creates a single string (pointer to a char *) from an argv and argc.

and there are two more routines โ€” Tcl_ScanElement and Tcl_ConvertElement โ€” that do most of the actual work of Tcl_Merge, so they may also be of use to the C programmer. ๐Ÿ™Œ

for more info on these commands, please consult the SplitList(3) manual page in the Berkeley Tcl distribution. ๐Ÿ—’๏ธ


๐Ÿงต CONCATENATING STRINGS ๐Ÿงต

Tcl_Concat concatenates zero or more strings into a single string, and the strings come out space-separated. Tcl_Concat works like Tcl_Merge, except โ€” and this is the key difference โ€” Tcl_Concat does not attempt to make the resulting string into a valid Tcl list. so don't expect list behavior from it, okay?

Tcl_Concat is documented in the Concat(3) manpage in the Berkeley 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 already have a complete command, or whether they need more data. (programs that read typed-in Tcl input, like Tcl shells, need this capability. it's their whole thing.) Tcl_CommandComplete can be used to tell whether or not you have a complete command. that's literally its job and it slays at it. ๐Ÿ’…

for more info, examine CmdCmplt(3) in the Berkeley Tcl distribution. ๐Ÿ“–


๐Ÿ“œ RECORDING COMMANDS FOR COMMAND HISTORY ๐Ÿ“œ

Tcl has a history mechanism that you access from Tcl through the history command. and to propagate commands into that command history, your extension should call Tcl_RecordAndEval. this command works just like Tcl_Eval, except that it records the command as well as executing it. two birds, one call. ๐Ÿ•Š๏ธ

but listen โ€” Tcl_RecordAndEval should only be called with user-entered top-level commands, because the history mechanism exists to let the user easily access, edit and reissue previously issued commands. so don't just yeet any old thing in there.

for complete info on this function, please examine the RecordEval.3 manual page in the Berkeley Tcl distribution. ๐Ÿ”ฅ


๐Ÿ”ข CONVERTING FLOATING POINT VALUES TO STRINGS ๐Ÿ”ข

Tcl_PrintDouble converts a C double into an ASCII string. and it's got this clever trick: it ensures the string output will continue to be interpreted as a floating point number rather than an integer, by always putting a '.' or 'e' into the string representing the number. subtle, smart, obsessed. ๐Ÿ‘‘

the precision of the output string is controlled by the Tcl tcl_precision variable. so if your decimals are looking weird, that's where to look. ๐Ÿ‘€

for complete info on Tcl_PrintDouble, examine PrintDbl(3) in the Berkeley Tcl distribution. ๐ŸŽ€


๐Ÿš€ CREATING CHILD PROCESSES AND PIPELINES FROM C ๐Ÿš€

Tcl_CreatePipeline is a genuinely useful procedure for spawning child processes. the child (or pipeline of children, if you're fancy) can have its standard input, output and error redirected from files, variables or pipes. all the redirection energy. ๐ŸŒ€

and to actually understand the meaning of the redirection symbols this function understands, go look at the exec(n) Tcl command. that's your reference. for complete info on Tcl_CreatePipeline, please examine CrtPipelin(3). โš™๏ธ


๐Ÿ“‚ ACCESSING TCL FILEHANDLES FROM C ๐Ÿ“‚

files you open from your C code can be made visible to Tcl code via the Tcl_EnterFile function. and going the other direction: Tcl filehandles passed to your C extension can be translated to a Posix FILE * structure using the Tcl_GetOpenFile function. it's giving bidirectional, it's giving teamwork. ๐Ÿซถ

for complete explanations of these commands, please look at EnterFile(3) in the Berkeley Tcl distribution. ๐Ÿ’–

โœจ MANAGING BACKGROUND PROCESS TERMINATION AND CLEANUP โœจ

Okay so when a Posix system does a fork to create a new process, the process ID of the child gets returned to the caller. But after the child process exits, its process table entry (and some other data associated with the process) cannot be reclaimed by the operating system until a call to waitpid, or one of a couple of other, similar system calls, has been made by the parent process. It's giving unresolved fr fr. ๐Ÿ˜ญ

The C extension writer who has created a subprocess, by whatever mechanism, can turn over responsibility for detecting the process's termination and calling waitpid to obtain its exit status by calling Tcl_DetachPids. Lowkey handing off the babysitting duties ๐Ÿ’…โœจ

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's the cleanup crew, the icon, the moment. ๐Ÿงน๐Ÿ’–

For complete information on these routines, please look at DetachPids(3) in the Berkeley Tcl distribution. No cap, that's the reference right there. ๐Ÿ“š


๐ŸŒˆ 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. Like, the source is right there, bestie โ€” it's giving open-book exam ๐Ÿ“–โœจ

The comp.lang.tcl Usenet newsgroup is read by tens of thousands of Tcl people, and is a good place to ask questions. Tens of thousands! That's a whole vibe, a whole community, you're never alone in the Tcl-verse ๐Ÿซถ๐Ÿ’œ

Finally, if you have interactive Internet access, you can ftp to ftp.aud.alcatel.com, the site for contributed Tcl sources. This site contains quite a few extensions, applications, and so forth, including several object-oriented extension packages. It's a whole treasure chest, no cap ๐ŸŽ๐Ÿ‘‘


๐Ÿ’– AUTHORS ๐Ÿ’–

Extended Tcl was created by Karl Lehenbauer (karl@neosoft.com) and Mark Diekhans (markd@grizzly.com). Legends, honestly. Kings. ๐Ÿ‘‘โœจ


That's a wrap on this whole Command Writing(3tcl) situation, brought to you by Tcl ๐Ÿ’–โœจ The source/version is Tcl, the date energy is Command Writing(3tcl), and we are obsessed. Go write something iconic, bestie. Periodt. ๐ŸŽ€๐Ÿฆ‹

๐Ÿ“ฆ where this page comes from