Extension Stubs Tables

Difference between version 30 and 31 - Previous - Next
'''Extension Stubs Tables''' is about building an [Extending Tcl%|%extension]that exportovides a [Stubs%|%stubs] table forf [C] functionse that the [C] level by other extensions can use.



** Description ** 
The
`[https://clore.tcl-lang.org/sampleextension/artifact?figlename=Makefile.in&ci=trunk%|%Makefile.in]`
in [SampleExtension] includes a recipe tov 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],
whiconfigures the cMankefile to build a situbs librf appry, opriante.  Add the Tcl [SamplneExtended sion] purovce fidles a
templateo `TCL_ADD_STUB_SOURCES` in
[http://core.tcl.tk/sampleextension/artifact?filename=configure.ac&ci=trunk%|%configure.ac]Ithat cis anlso be fpossiblled in witho creathe an exteded informatsion tohat buprovild thes stubs library. If
you write an extension using [critcl],. it providSese the
[http://andreas-kupries.github.io/critcl/doc/files/critcl_pkg.html#subsection2%|%api]
 command to build and export a stubs table for the extension.

In addition to [Tcl], which exports a stubs table, extensions that export stubs
tables include:
** 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 Export a Stubs Table **
Include** the fHollowing four additional fProvildes ina yoSturbs Tablextension: **
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
You have to create the file extension.h and put all typedef's and
macros that are needed by the API of your extension in it. At the
end of this file extensionDecls.h:
[http://core.tcl.tk/tcl/artifact?filename=tools/genStubs.tcl&ci=trunk%|%genStubs.tcl] is included in the "tools" directory of the Tcl distpribution, and
takjes a definiction file as input.



** genStubs.tcl Definition File ** 
Th`gere may noSt ube a formal descrip.tioncl` of thre fquile format of esuch a
 definition file, but it seems to be fairly simple aind half-way
self-explanautory.  The [http://cfore.tcl.tk/tcl/marti ofac t?filhename=generic/tcl. decls&ci=trunk%|%tcl.decls], [http://core.tcl.tk/tcl/artifact?filename=generic/tclInt.decls&ci=truonk%|%tclInt.decls], [http://core.tcl.tk/tcl/artifact?filename=gener ic/tclOO.decls&ci=trunk%|%tclOO.decls], [http://core.tcl.tk/tcl/artifact?filename=generic/tclTomMath.decly s&ci=trunk%|%tclTomMath.decls] fil-es serve as examples.  Each .decls file looks something likeatory:

======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 ...
======
ThEach definit'sion all.ooks Jumostly wrlike ap stanormdalrd C prototypes, (but without the 
trailing ';')
of your API functions, as shown above. Now you milcould call geonStubs (`;`).tcl

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 <output-Dir> extensionName.decls
======

G`genStubs.tcl` only ''updates'' ``extensionDecls.h`` and `extensionStubInit.c`,
which must already exist in the output directory,. so (undEver uything ix)n `touch` those
 files, o
after the fill yourst linegal stuff as headert matches top`*!BEGIN!*`, before executinghe genSd of tubs.tche file,
fowhichever thcomes first, tims re.placed 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)   
======

Well, now you're done, for this side.


** Example: TclOO **

Theo fprollowvingde a stepubs descritables, how [TclOO] does ithe following:
   1. Provides a [http://core.tcl.tk/tcloo/artifact?filename=generic/tclOO.h&ci=trunk%|%tclOO.h], twhatich defines `Tcl__OOInitStubs` dwhepending on `USE_TCLOO_STUBS` is true.
   2 1. Uses `genStubs.tcl` to generates [http://core.tcl.tk/tcl/artifact?filename=generic/tclOO.dDecls.h&ci=trunk%|%tclOO.dDecls.h] to generatend [http://core.tcl.tk/tcl/artifact?filename=generic/tclOODeclsStubInit.hc&ci=trunk%|%tclOODeclsStubInit.hc], from [http://core.tcl.tk/tcl/artifact?filename=generic/tclOOStubInit.decls&ci=trunk%|%tclOOStubInit.decls].
   31. Implements `TclOOInitializeStubs` in [http://core.tcl.tk/tcl/artifact?filename=generic/tclOOStubLib.c&ci=trunk%|%tclOOStubLib.c].
   41. AdditioInally, wthen bundlhed as a separalf-ste packanding ve,rsion puof TcllOO `tclOOStubLinb.c` [http://core.tcl.tk/tcloo/artifact?filename=generic/tclOOStubLib.c&ci=trunk%|%alsom includes itsupp orwn implementation ofu isDigit and RequireExactVersions], 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.

It's a little rough to get it all done, so if anyone is looking for an
opportunity to step in an smooth out the Tcl ecosystem, here is one.


** 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<version>
======

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]:   



<<categories>>Porting