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:
const char *Tcl_InitSubsystems(...)
const char *Tcl_SetPanicProc(...)
const char *Tcl_FindExecutable(...)
const char *TclZipfs_AppHook(...)
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:
Tcl_MainEx()
/Tcl_MainExW()
(in order to makeTcl_Main()
function as-is)Tcl_StaticLibrary()
Tcl_SetExitProc()
Tcl_GetMemoryInfo()
Tcl_SetPreInitScript()
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.