'''Extension Stubs Tables''' is about building an [Extending Tcl%|%extension] that provides a [Stubs%|%stubs] table of [C] functions that other extensions can use. ** Description ** The `[https://core.tcl-lang.org/sampleextension/artifact?filename=Makefile.in&ci=trunk%|%Makefile.in]` in [SampleExtension] includes a recipe to compile a stubs libary, and the `TEA_MAKE_LIB` in [tclconfig%|%tclconfig's] [http://core.tcl.tk/tclconfig/artifact?filename=tcl.m4&ci=trunk%|%tcl.m4] configures the Makefile to build it if appropriate. Add the needed source files to `TCL_ADD_STUB_SOURCES` in [http://core.tcl.tk/sampleextension/artifact?filename=configure.ac&ci=trunk%|%configure.ac] It is also possible to create an extension that provides stubs using [critcl]. See the [http://andreas-kupries.github.io/critcl/doc/files/critcl_pkg.html#subsection2%|%api] command. ** Extensions that Provide Stubs ** In addition to [Tcl], which exports a stubs table, extensions that provide stubs tables include: [nsf]: Provides stubs tables for Tcl 8.5, 8.6, and 8.7. [Trf]: [Snack]: Also includes a simple example of using the snack API from another Tcl extension [Tk]: [TclOO]: [TclXml], [TclDOM], [TclXSLT]: TclDOM/libxml2 and [TDOM] both import stubs tables and export stubs tables of their own. ** How to Provide a Stubs Table ** Add the following files: * extension.h ** includes all typedef's and macros that are part API of your extension ** at the end of the file, make sure to ======c #include "extensionDecls.h" ====== * extensionDecls.h ** can be generated by genStubs.tcl * extensionStubInit.c ** can be generated by genStubs.tcl * extensionStubLib.c [http://core.tcl.tk/tcl/artifact?filename=tools/genStubs.tcl&ci=trunk%|%genStubs.tcl] is included in the "tools" directory of the Tcl project. ** genStubs.tcl Definition File ** `genStubs.tcl` requires a definition file as input. The format of the definition file is fairly self-explanatory: ======c library extensionName interface extensionName declare 0 generic { int ExtensionName_Function1( Tcl_Interp* interp ) } declare 1 generic { int ExtensionName_Function2( Tcl_Interp* interp, ClientData foobar ) } ... etc ... ====== Each definition looks mostly like a standard C prototype, but without the trailing semilcolon (`;`). Examples are [http://core.tcl.tk/tcl/artifact?filename=generic/tcl.decls&ci=trunk%|%tcl.decls], [http://core.tcl.tk/tcl/artifact?filename=generic/tclInt.decls&ci=trunk%|%tclInt.decls], [http://core.tcl.tk/tcl/artifact?filename=generic/tclOO.decls&ci=trunk%|%tclOO.decls], and [http://core.tcl.tk/tcl/artifact?filename=generic/tclTomMath.decls&ci=trunk%|%tclTomMath.decls]. To run `genStubs.tcl`: ====== tclsh path/to/genStubs.tcl extensionName.decls ====== `genStubs.tcl` ''updates'' ``extensionDecls.h`` and `extensionStubInit.c`, which must already exist in the output directory. Everything in those files after the first line that matches `*!BEGIN!*`, or the end of the file, whichever comes first, is replaced by generated content. `ExtensionStubInit.c` is mainly a global structure: The stubs table of your extension. A Tcl extension typically declares itself in the [load%|%initialization function] of the extension with something like: ======c Tcl_PkgProvide (interp, "extension", "1.3") ====== A tcl extension, that exports a stubs table must also propagate its stubs table, so instead the above snippet is modified to: ======c Tcl_PkgProvideEx (interp, "extension", "1.3", &extensionStubs) ====== where `&extensionStubs` is a pointer to the stubs table in the in `extensionStubInit.c`. To pacify the compiler add a declaration like: ======c extern extensionStubs extensionStubs; ====== to the compilation unit containing the initialization function for the extension. Add `extensionStubInit.c` to the files of your extension and recompile. Finally, compile and link `extensionStubLib.c` into a static archive against which other extensions may link to obtain the stubs table and use the extension. The contents of `extensionStubLib.c` may be cribbed from [http://core.tcl.tk/tk/artifact?filename=generic/tkStubLib.c&ci=trunk%|%tkStubLib.c] or [http://core.tcl.tk/tcl/artifact?filename=generic/tclOOStubLib.c&ci=trunk%|%tclOOStubLib.c]. It should look something like: ======c #ifndef USE_TCL_STUBS #define USE_TCL_STUBS #endif #undef USE_TCL_STUB_PROCS #include "tcl.h" #include "extension.h" /* ** Ensure that Tdom_InitStubs is built as an exported symbol. The other stub ** functions should be built as non-exported symbols. */ #undef TCL_STORAGE_CLASS #define TCL_STORAGE_CLASS DLLEXPORT ExtensionStubs *extensionStubsPtr; /* **---------------------------------------------------------------------- ** ** Extension_InitStubs -- ** ** Checks that the correct version of Extension is loaded and that it ** supports stubs. It then initialises the stub table pointers. ** ** Results: ** The actual version of Extension that satisfies the request, or ** NULL to indicate that an error occurred. ** ** Side effects: ** Sets the stub table pointers. ** **---------------------------------------------------------------------- */ char * Extension_InitStubs (Tcl_Interp *interp, char *version, int exact) { char *actualVersion; actualVersion = Tcl_PkgRequireEx(interp, "extension", version, exact, (ClientData *) &extensionStubsPtr); if (!actualVersion) { return NULL; } if (!extensionStubsPtr) { Tcl_SetResult(interp, "This implementation of Extension does not support stubs", TCL_STATIC); return NULL; } return actualVersion; } ====== Compile this and build a static library out of it. Under unix, this means, do something like this (makefile style): ====== > ar cr libextensionstub$(VERSION).a $(STUBOBJ) ====== ** Example: TclOO ** To provide a stubs table, [TclOO] does the following: 1. Provides [http://core.tcl.tk/tcloo/artifact?filename=generic/tclOO.h&ci=trunk%|%tclOO.h], which defines `Tcl__OOInitStubs` when `USE_TCLOO_STUBS` is true. 1. Uses `genStubs.tcl` to generates [http://core.tcl.tk/tcl/artifact?filename=generic/tclOODecls.h&ci=trunk%|%tclOODecls.h] and [http://core.tcl.tk/tcl/artifact?filename=generic/tclOOStubInit.c&ci=trunk%|%tclOOStubInit.c] from [http://core.tcl.tk/tcl/artifact?filename=generic/tclOO.decls&ci=trunk%|%tclOO.decls]. 1. Implements `TclOOInitializeStubs` in [http://core.tcl.tk/tcl/artifact?filename=generic/tclOOStubLib.c&ci=trunk%|%tclOOStubLib.c]. 1. In the he self-standing version of TclOO `tclOOStubLib.c` [http://core.tcl.tk/tcloo/artifact?filename=generic/tclOOStubLib.c&ci=trunk%|%also includes its own implementation of isDigit and RequireExactVersion], copied from [http://core.tcl.tk/tk/artifact?filename=generic/tkStubLib.c&ci=trunk%|%Tk/generic/tkStubLib.c], to work around variance in the [C] library implementation of `isDigit` on [Microsoft Windows%|%Windows]. At some point this issue should be fixed in Tcl, after which these local implementations can be removed. ** How to Use a Stubs Table ** To use the API of extension1 in extension2, include the `extension1.h` file. Next, add `-DUSE_EXTENSION1_STUBS` (or whatever macro extension1 prescribes) to the compiler flags for extension2. In the makefile, add something like ======none -L/path/to/your/extension1/libfile -lextension1stub ====== Finally, initialize the stubs table for extension1. As a good tcl citizen you've already included the following in the initialization function for extension2: ======c #ifdef USE_TCL_STUBS if (Tcl_InitStubs(interp, "8", 0) == NULL) { return TCL_ERROR; } #endif ====== Now add ======c #ifdef USE_EXTENSION1_STUBS if (Extension1_InitStubs(interp, version, exact) == NULL) { return TCL_ERROR; } #endif ====== That's all. ** Discussion ** Ah, you find this all a bit complicated. Well, I also do. To say the truth, this all wasn't that satisfying. I was able, to make this work on linux, for a given example of extension1 and extension2. But this was not that bit of progress, since under linux, this hole 'using extension1 from extension2' stuff worked (at least under linux and for me) already without this hole stubs hussle, just by exporting the API functions and do the usual. My problem was, that this 'usual' way of doing things - of course, 'usual' depends on the viewpoint - haven't worked at MS plattforms. It still does not. OK, I take this back, and argue the converse. After some struggling (in fact, a lot of, but, to be fair, most may be due to my limited experiences with the MS build tools) I was able to make this work also under windows. Now that I have a basic understanding of what to do, to make an extenstion stubs table and how it works I'm still curious about the 'why'. Sure, with an extenstion stubs table, you get some version independence: you can update extension1, and extension2 still works, without recompilation (the same, as your stubs enabled extension still work after you have updated your tcl installation). My experience is, that at least under linux you don't strictly 'must' use an extension stubs table, even if you have an extension2, that uses functions of an extension1 (just export the API functions of extension1, as you would do for every ordinary library, and you're done). But it seems to me, that under windows you 'must' use a extension stubs table, because it doesn't work also the simple way, as with linux. I'm far away to be an expert in dynamic loading stuff (especially under windows). Someone out there, that could confirm or correct my presumption? And are there other OS'es, for which one must use the extension stubs table mechanism, for this extension2 uses extension1 scenario? [Lars H] 2004-12-18: Another take on the "why" might be to consider the case of a third extension. What if extension2 and extension3 both need the API of extension1? Would this even be possible with "usual" linking as in the three paragraphs above, if extension2 and extension3 are to be separate? ---- On a related subject, [jcw] posted this comment: "Stubs are not limited to tcl.h - this seems to be a common misconception. Did you know that there are over 130 stub definitions for tclInt.h? There's even a (small) third set of stubs for platform-specific calls." ** Page Authors ** [kbk]: [de]: [PYK]: <>Porting