Version 233 of How to embed Tcl in C applications

Updated 2015-12-29 22:22:34 by RLE

The intent of this page is to address C++ codings also, not just C. There appears to be a problem inserting "++" in a page title, though.

Extending Tcl with compiled code

This page should be called Extending TCL with C. Are you sure? See Embedding vs. Extending. They're generally considered to be two different approaches.

WJP 2008-03-02: The second paragraph below "The approach is essentially to write a wrapper function which calls your code...." is about extending Tcl, not about embedding Tcl in C.

Lars H: Not necessarily \u2014 the classical case of embedding Tcl is to use it as a Command Language for some application, and then there typically are some commands in the embedded Tcl that does things with the application, as described. Exactly where the boundary goes between embedding and extending is probably open to debate, though. Some might argue that everything that isn't done via load is embedding. Others might want to draw the line according to where the main function comes from.

WJP: I don't see your point. The second paragraph talks about writing C code and adding it as a command to Tcl. That is extending Tcl. It is true that one may extend an embedded Tcl interpreter with commands that deal with the application, but that is another level. Putting a paragraph about extending Tcl so early and describing the extension process as "the approach" is terribly misleading. Really, what needs to be said is that to embed a Tcl interpreter in a C program one needs to call certain functions from Tcl's C API. Then one can point out that it may be necessary to give the embedded interpreter access to the C program and that one can accomplish this by extending the embedded interpreter.

Jogusto 2008-08-30: Setting aside the philosophical debates/rants, and the confusion/debate over the embedding vs. extending terminology, this page, based on its title, really needs a clear and useable exposition on Embedding Tcl in a C or C++ application. It seems to me that there are at least two easy-to-identify scenarios (and I don't doubt, several more but I am not that smart to list them here...): (1) making an app that is essentially an interpreter which uses Tcl as glue or scripting scaffolding over a bunch of C code, to make the app scriptable and powerfully configurable, flexible architecturally, and testable exposition on the virtues provided elsewhere very nicely on this wiki; and (2) using Tcl as a callout from what is predominantly C/C++ (or other native code) to either a subsystem implemented in Tcl, or to configuration scripting which sets up or customizes the app in question.

The Welsh/Jones book 4th edition [L1 ] has good information and I think what is needed is for a few cogent well-written examples to be posted here. Absent these examples, there are some subtle points alluded to in the book about "many details being attended to" which I think would be missed on a first go, and implementers would struggle to get the details right (such as supporting important underlying mechanisms such as the unknown command and the package mechanism).

There is absolutely no doubt in my mind that there is tremendous usefulness in embedding an interpreter inside a C application, initializing it by sourcing or loading some Tcl code (perhaps a very extensive system of some kind, already existing), and then calling into that interpreter with Tcl_Eval() to use that subsystem towards some purpose. In my case, I want to make use of a chip debugger written in Tcl inside a C++ application that extends that functionality (and I don't have a choice to do it another way... the C++ app already exists). I would also imagine it very useful to use Tcl scripts as simple configuration files for applications, and one would similarly want to create an interpreter, have that interpreter source a config file, then have the C/C++ app get the settings by examining the Tcl state (variable values), then destroy the interpreter and move on with the business at hand.

I strongly urge those who have created successful implementations with recent Tcl versions (at least 8.4 or beyond) to contribute their knowledge to this page. Thanks!

TODO This page is horrific. This is the first result on google for "embedding Tcl in C". Move the holy wars to a talk page. Giving example code then writing "Don't do it this way any more" and the phrase "backward-compatibility fanatics" don't belong here. Compare this page with the first result for "embedding Lua in C"! I intend this criticism to be as constructive as possible but any user searching for "embedding Tcl in C" for the first time is going to be immediately turned off and head straight for Lua. Someone who knows Tcl needs to sort this page out, because as it stands its worse than nothing.


"How do I embed a Tcl interpreter in my existing C (or C++) application?" is a very frequently-asked question. It's straightforward, certainly far easier than doing the same with Perl or, in general, Python; moreover, this sort of "embeddability" was one of the original goals for Tcl, and many, many projects do it. There are no complete discussions of the topic available, but we can give an overview here. (RWT 14-Oct-2002)

The approach is essentially to write a wrapper function which calls your C code. This wrapper function is in a standard format that Tcl understands. You then call a Tcl function to "register" the wrapper function with the Tcl interpreter, creating a new command. After that, every time that command is called in the interpreter, it calls the wrapper function, which calls your C code.

See the complementary page How to use C functions from Tcl for some pointers to some tools to help write the interface code for you. SWIG is often used, and is highly recommended. In fact, the example code in this section was generated by SWIG.

Here is a very simple C function which we will integrate into the Tcl interpreter. We will create a new command called square to call this function. The C code looks like this:

    int square (int i) {
        return i*i;
    }

Back in the "good old days" (before Tcl 8.0) everything really was a string inside of the Tcl interpreter. That made the interface functions pretty simple. They are a little like a main() function, with argc and argv. Our interface code is pretty simple, copying the argument from argv, and returning a result string. .

    #include <tcl.h>
    static int _wrap_square(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[]) {
        int  _result;
        int  _arg0;
        _arg0 = (int) atol(argv[1]);
        _result = (int )square(_arg0);
        sprintf(interp->result,"%ld", (long) _result);
        return TCL_OK;
    }

Note: This is just an example of a simple interface function. Don't do it this way any more, even though the backward-compatibility fanatics in the Tcl community continue to support all of the syntax. In particular, copying the result string into the interp->result field is not recommended. Bad things might happen.

At some point in our application, we have to start up a Tcl interpreter, and register our wrapper function with the interpreter. This is usually done with a function called Tcl_AppInit(). Once this initialization is complete, your application can execute scripts from strings or files by calling Tcl_Eval() or Tcl_EvalFile().

    int Tcl_AppInit(Tcl_Interp *interp){
      if (Tcl_Init(interp) == TCL_ERROR)
        return TCL_ERROR;
      /* Now initialize our functions */
        Tcl_CreateCommand(interp, "square", _wrap_square, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
      return TCL_OK;
    }

Call this initialization function somewhere in your main line. First create an interpreter, then call Tcl_AppInit(). You could do this all in one fell swoop, but the Tcl_AppInit() format is standard, and supports creating extended interpreter shells - where your application actually becomes a Tcl interpreter, which is Extremely Cool!(tm)

    Tcl_Interp *interp;
    interp = Tcl_CreateInterp();
    Tcl_AppInit(interp);

The more modern approach to integrating Tcl applications depends on the object interface supported in Tcl versions 8.0 and higher. The object model supports the on-the-fly bytecode compiler, and is very efficient for integrating C code. It is not necessary to shimmer values back and forth to strings. Unfortunately, the object interface is a little more complex, with special functions to convert from Tcl_Objs to basic types. You should also do a little more careful parsing of arguments, and even generate error messages if the Tcl command is malformed. Again, SWIG is a great tool that can generate all this for you. But you can still do it yourself. The new object interface function might look like this:

    static int _wrap_square(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
        int  _result;
        int  _arg0;
        Tcl_Obj * tcl_result;
        int tempint;

        clientData = clientData; objv = objv;
        tcl_result = Tcl_GetObjResult(interp);
        if ((objc < 2) || (objc > 2)) {
            Tcl_SetStringObj(tcl_result,"Wrong # args. square i ",-1);
            return TCL_ERROR;
        }
        if (Tcl_GetIntFromObj(interp,objv[1],&tempint) == TCL_ERROR) return TCL_ERROR;
        _arg0 = (int ) tempint;
        _result = (int )square(_arg0);
        tcl_result = Tcl_GetObjResult(interp);
        Tcl_SetIntObj(tcl_result,(long) _result);
        return TCL_OK;
    }

And, you would, of course, register this new command as an object command. The rest of the initialization remains the same

    Tcl_CreateObjCommand(interp, SWIG_prefix "square", _wrap_square, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);

Embedding Tcl into a Compiled application

A consequence of the lack of tutorial material is that the subject seems harder than it truly is. (Too?) many programmers seem to learn what they know on this subject by imitation of an existing project that embeds a Tcl interpreter.

One reason for the lack of documentation is deep ambivalence about this whole topic. See "Adding Tcl/Tk to a C application" for a spirited description of the deep difficulties.


[References will appear on 5 September 2001.]

[Refer to BOOK Practical Programming in Tcl and Tk, Third edition as acknowledged best treatment in a book of subject of embedding interpreters.]

[CL's page on "How to combine C and Tcl codings" [L2 ] points to several tutorials.]


Users are often correctly pointed to the Tcl and Tk source distributions for simple examples of embedding Tcl in a C application - the tclsh and wish applications are simple examples of this technique.


D. Richard Hipp has the most experience at embedding Tk in C-coded applications. His mktclapp project enormously simplifies the task for mortals.


David Gravereaux is justly proud of his TES work as a model for several of the more esoteric aspects of interpreter embedding.


See also TIP 66: http://purl.org/tcl/tip/66.html (strongly recommended).


[There's much more to say on this topic. No one's made time yet, though.]


Davy aptly observes that there are "essentially two models:

1) Macro style. Tcl_CreateInterp/Tcl_EvalFile/TclDeleteInterp. Simple to implement, but doesn't maintain a persistency and is unfriendly to the parent application by blocking it until the macro finishes. WinCVS is an example of this.

2) Event style. Source the scripts at the beginning to setup their procs and entry points and maintain the interp(s) for the application lifetime. Entry is made to Tcl through its event loop by 'tossing' jobs. It is possible to run Tcl's execution in a separate thread or 'meld' the applications event with Tcl's event loop resulting in a friendlier GUI to the user. This allows the use of Tk, too."


How to dynamically link against the tcltk .dll/.so libs at runtime in an embedded interpreter:

  1. first pull in the required tcl library-version+path-name from the environment or some such.
  2. dlopen/loadlibrary the required tcl library
  3. pull Tcl_CreateInterp from the dll dlsym/GetProcAddress
  4. create the interp
  5. run Tcl_InitStubs(interp,"8.2",0)(In the hardlinked stubs library)
  6. Tcl_FindExecutable(NULL); (set tcl paths etc?)
  7. dlopen the matching tk library
  8. dlsym(tkhandle,"Tk_Init") / GetProcAddress(tklibhandle,"Tk_Init")
  9. (*tk_init)(interp); activate Tk_Init
  10. run Tk_InitStubs(interp,"8.2",0); (in the hardlinked libtkstub8.x)
  11. carry on as per usual.

Compile your "tcl-embedded" software with the -DUSE_TCL_STUBS and -DUSE_TK_STUBS and link against the static libtclstub8.2.a and libtkstub8.2.a libraries.

David Gravereaux suggests that the steps (7),(8) and (9) and (10) could be simply replaced with the calls

  1. Tcl_PkgRequireEx(interp,"Tk","8.2"....)
  2. Tk_InitStubs
  3. Tk_Init

That would probably be better because there is the off chance that you may be trying to pull in a tk library incompatible with the tcl library. Using PackageRequire should pick up the correct one automagically I would guess.

If you do this you can distribute binaries that can be compatible with any version of tcl/tk that matches your minimum requirement and should make fewer demands on your end users, to install ancient library version x.y. In principle.

PT 07-Jul-2003: This is very interesting - linking applications rather than just extensions using the Tcl stubs mechanism. Interesting enough that I gave a try - some example Windows source is attached below.


"Writing Tcl-Based Applications in C" is yet another variation on this same theme. It's the closest this Wiki apparently has, as of early July 2003, to a minimal C application which invokes Tcl scripts.

It would be useful, however, if the above page, and related pages, were updated to reflect the latest Tcl APIs, best practices, etc.


While one might guess that "Invoking Tcl commands from Cplusplus" does the same, it quickly jumps past the basics to what CL regards as rather esoteric performance considerations.


/* tclembed.c - Copyright (C) 2003 Pat Thoyts <[email protected]>
 *
 * Sample of an embedded Tcl application linked using the Tcl stubs mechanism
 * Method taken from http://wiki.tcl.tk/2074
 *
 * ----------------------------------------------------------------------
 * This source code is public domain.
 * ----------------------------------------------------------------------
 *
 * $Id: 2074,v 1.34 2006-08-15 18:00:05 jcw Exp $
 */
 
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <tchar.h>
 
#define USE_TCL_STUBS
#include <tcl.h>
 
typedef Tcl_Interp *(*LPFNTCLCREATEINTERP)();
 
static Tcl_Interp *InitializeTcl(int argc, char *args[]);
 
int
main(int argc, char *argv[])
{
     Tcl_Interp *interp;
     int r = TCL_OK;
 
     interp = InitializeTcl(argc, argv);
     if (interp == NULL) {
         fprintf(stderr, "error: failed to initialize Tcl runtime\n");
     } else {
         if (argc > 1) {
             r = Tcl_EvalFile(interp, argv[1]);
             printf(Tcl_GetStringResult(interp));
         }
         Tcl_DeleteInterp(interp);
     }
 
     return r;
}
 
static Tcl_Interp *
InitializeTcl(int argc, char *argv[])
{
     Tcl_Interp *interp = NULL;
     Tcl_DString dString;
     TCHAR szLibrary[16];
     char *args;
     int nMinor;
     LPFNTCLCREATEINTERP lpfnTcl_CreateInterp;
     HINSTANCE hTcl = NULL;
     
     for (nMinor = 5; hTcl == NULL && nMinor > 2; nMinor--) {
         wsprintf(szLibrary, _T("tcl8%d.dll"), nMinor);
         hTcl = LoadLibrary(szLibrary);
     }
 
     if (hTcl != NULL) {
         lpfnTcl_CreateInterp = (LPFNTCLCREATEINTERP)
             GetProcAddress(hTcl, "Tcl_CreateInterp");
         if (lpfnTcl_CreateInterp != NULL) {
             interp = lpfnTcl_CreateInterp();
             if (interp != NULL) {
                 Tcl_InitStubs(interp, "8.2", 0);
                 Tcl_FindExecutable(argv[0]);
                 Tcl_InitMemory(interp);
                 Tcl_Init(interp);
             }
         }
     }
     return interp;
}

[Mention that books by JO, Brent Welch, and Clif Flynt have minimal examples.]


GPS provided this example of invoking Tcl commands from a C application during August, 2003 on comp.lang.tcl:

#include <stdlib.h>
#include <tcl.h>

int main (int argc, char *argv[]) {
  Tcl_Interp *interp;

  Tcl_FindExecutable (argv[0]);

  interp = Tcl_CreateInterp ();

  if (TCL_OK != Tcl_Init (interp)) {
   fprintf (stderr, "Tcl_Init error: %s\n", Tcl_GetStringResult (interp));
   exit (EXIT_FAILURE);
  }

 {
  char script[] = "proc p1 a { puts $a }";

  Tcl_Eval (interp, script);
  fprintf (stderr, "res 1: %s\n", Tcl_GetStringResult (interp));
 }



  Tcl_Eval (interp, "puts [info commands p*]");
  fprintf (stderr, "res 2: %s\n", Tcl_GetStringResult (interp));
 
  Tcl_Eval (interp, "p1 abc");
  fprintf (stderr, "res 3: %s\n", Tcl_GetStringResult (interp));

  return EXIT_SUCCESS;
}

TV Maybe the title could be seen the other way around, where one embeds a computing core, written in C for instance, in a tcl/tk program which controls it and gives it UI elements.

One way to do this which at least doesn't require compiles of the tcl/tk version you need is to go where X servers have gone before and use sockets to pass information between the C program and the tcl/tk program part, which is a good incentive to do a decent design of the interactions, and for many user interfaces, a not so hard, and automatable (scriptable..) protocol can be used, assuming the socket communication is mastered appropriately. The latter is a matter of making good use of seperable blocks and flush commands, and using the same idea as in the event loop (which is the same in the (underlyingm too) C 'select' function. And it requires the socket interfaces to work properly, for instance guaranteeing delivery of all data at least after some time (which is fine for a UI), which, unfortunately, is a problem under windows (MS), at least when I used the cygwin compiler, which can compile quite hefty unix stuff and make it work fine, I got errors related to stream data getting temporarily stuck in buffers, until new data was pushed in. Recently I found that that could be because I was using openGL simulataneously, so I hope it can be solved, and lets not hope winsock is the real reason. I checked some packages which 'test' sockets, and on an XP system they were absolutely not reliable, but depending on the kind. Also, I don't know if under windows, and on the Mac, there is such efficiency saving thing as a pipe (UNIX sockets) when all for starters takes place on the same machine, which lets you not take the whole tcp/ip stack and deamons along in your communication time and memory and process switching time.

An example, from years ago, actually, and the first things I did like this were even older and done on HP UX before tcl even had sockets of its own (but open |probe & worked fine) can be seen here:

http://members.tripod.com/~theover/mesa.html

When well done, one could use any scripting language (tcl alone, tcl/tk, tcl/tk with a web server, perl, another C program, too, visual basic, even smalltalk, and what else), or special program to control a certain piece of program. When the protocol design is done even better, one can even write generally useable mergers and switchers and protocol analysers, and while doing that get away from the nitty gritty of a lot of OO hassle (I did objective-C for years, but still haven't found all I'm looking for, lets say)

TV (8 Oct '03) I just tried compiling it on a recent cygwin on XP, and it seems the AUX library from the older mesa is dropped, at least the include file isn't there. I'll look into that, and try linux. A rewrite based on recent example code I may do, expecially I want to see the socket / opengl select() combination, which used to make a tcl control program over a socket link have to limit its output bandwidth/lines per second.

jcw - "aux" is a black hole in Windows (so is "nul", "lpt", "com1", and a few more - all are device names, it's best to avoid these, especially in tars from Unix)

AM The "aux" library Theo is talking about is one distributed with OpenGL, it has naught to do with pseudo-files like "AUX" under the former MSDOS ...

TV I had to squeeze in a new harddisk in a quite reasonable machine I can luckily use, and when transfering from the old one, I was stupid enough to let XP do its own installation stuff after I did a raw partition copy, with the old disk as second disk. Neither XP nor cygwin have completely come around to my views on how everything from the partition numbering, registry info, OS dirs and of course PATH like environment vars should perfectly come together (putting it mildly) ...

It works allright (even unattended for weeks running a tclhttpd webserver), and the cygnus gnu gcc compiler isn't unuseable, but maybe it should be simply reinstalled (as sort of a matter of principle I don't like to be forced to that) to get flawless paths and everything. Anyhow, I'm working on getting the thing back together. On the abovementioned page, ALL mesa libraries are available, I think also my (adapted) sources from that time, but recompiling that was fine 5 years ago, currently I think it is preferable to use the cygwin supplied libs and stubs to OS level openGL support.

I can use a fairly fast most recent RedHat linux capable machine, which I intend to use also for video graphics things, where the compiler, Xwindows with NVidea accelerator, and openGL are spinning now, which should also run the above example, I'll try it out, and maybe make a new version.


RHS 15Nov2004

I couldn't find another place to put a link, so here is Useful C Commands For Tcl