TIP 596: Stubs support for Embedding Tcl in other applications

Login
Author:         Jan Nijtmans <[email protected]>
State:          Final
Type:           Project
Vote:           Done
Tcl-Version:    9.0
Tcl-Branch:     rfe-854941
Vote-Summary:	Accepted 5/0/0
Votes-For:	BG, JN, KBK, KW, MC
Votes-Against:	none
Votes-Present:	none

Abstract

The Stubs mechanism was originally written to be able to extend Tcl with binary extensions, without the extensions depending on a specific Tcl version. But what if Tcl is being embedded inside another application? The Stub mechanism was never designed for that.

David Gravereaux wrote a RFE long ago (december 2003), touching on the problems arising. And Shannon Noe wrote TIP #531 which also was inspired on the same problem.

This TIP enhances Tcl 9.0 with no new API, but internal modifications, such that embedding Tcl in other applications using the Stub mechanism is finally possible without the described problems. It follows David Gravereaux's outline, providing new wrapper functions in the stub library (libtclstub9.0.a / tclstub90.lib) which are able to locate, load and initialized the Tcl 9.0 shared library (libtcl9.0.so / tcl90.dll), just assuming it is somewhere on the PATH or in <prefix>/lib. No new functions are added to the API, only existing functions which previously only worked for extensions are now made suitable to be used for Embedding applications as well.

Tcl 8.7 is enhanced with a new API: the function Tcl_SetPreInitScript(), which is nothing more than the renamed internal function TclSetPreInitScript(). It will not be exposed through the stub table.

Specification

The following 4 functions are added to the Tcl Stub library. Their actual name in the stub library is different, in order to prevent symbol conflicts, but they function under the following names:

Any application which wants to embed Tcl only needs to call one (or more) of those 4 functions first. If the application is compiled with -DUSE_TCL_STUBS, a wrapper function is used in stead which loads the Tcl 9.0 core, initializes it, initializes a stub table in the embedder application, and then runs the corresponding function in the Tcl core. After that, the embedding application can call any other functions from the Tcl API through the stub table, everything further is fully transparent.

An example Embedding "Hello World" application:

#define USE_TCL_STUBS
#include <tcl.h>
#include <stdio.h>
int main(int argc, char **argv) {
    const char *version = Tcl_InitSubsystems(); /* Load/Initialize the Tcl core */
    if (version == NULL) {printf("Cannot find the Tcl core\n"); return 1;}
    Tcl_Interp *interp = Tcl_CreateInterp();
    Tcl_Eval(interp, "puts stdout {Hello World}");
    Tcl_DeleteInterp(interp);
    return 0;
}

Compile this example application, and link it to the Tcl stub library, that's all.

Which of the 4 functions the embedder application calls first, depends on what details are needed for the initialization. Tcl_InitSubsystems() just initializes the minimum possible. Tcl_SetPanicProc() can be used if the embedder application provides its own panicproc. Tcl_FindExecutable() is needed when the interpreter needs access to info nameofexecutable. TclZipfs_AppHook() is needed if the embedder application has a zip file attached which needs to be mounted to be made available to the Tcl interpreter. Multiple such calls can be done, if desired: the first call will load the Tcl core, succeeding calls don't do that again.

The signatures of the 4 initialization functions change: All functions return a const char * now, which will return NULL if locating the Tcl core fails. The functions return the full Tcl version number if the initialization succeeds. This can be used to check if the Tcl version is actually the expected one.

There are 5 more functions exposed through the stub library this way, but which are not capable of initializing the stub table. They can be called from the embedder application after the Tcl core has already been loaded and initialized. These are:

They need special treatment because those functions are not exposed through the stub table. The only way to handle those is to search for the correct symbol in the Tcl shared library, and call it directly.

Since Tcl 9.0 is the only version in development after 8.7, there is no mechanism yet to prefer Tcl 9.1 over 9.0, or make another selection on exactly which Tcl version is desired. That exercise is left for the future, when the development of Tcl 9.1 starts.

Implementation

Implementation is in Tcl branch rfe-854941.

Compatibility

This is 100% upwards compatible with Tcl 8.6.

Copyright

This document has been placed in the public domain.