[Up: Using C++ objects with Tcl]
[Previous: The Tcl Domain] [Next: Examples]

Dynamic Loading

Object-oriented programming comes to its full power in a dynamic environment. If an existing class does not fit your purposes, you derive your own class, and attach the new module - at runtime! What C++ has been waiting for is now possible with the help of Tcl's generic mechanisms for dynamic loading.

Handling dynamic modules

``tclobj'' itself can be compiled into a shared library and loaded with the ``load'' command. Classes compiled into shared libraries can also be loaded this way by using the class name prefixed with c_ as package name. For example, you can load the ``fraction'' class from the examples with

load ./fraction.so c_fraction

However, because of the package naming policies of Tcl, this only works if the class name is all in lowercase. Another restriction imposed by Tcl is that it refuses to load more than one package from the same shared library. This can be quite annoying if you have several tiny interrelated classes and would be forced to put each of them into a separate shared library.

For this purpose, additional macros exist in order to ``package'' one or more class into a package, that upon loading, initializes all of the contained classes. The macros are best explained upon an example from our ``storage'' example, which defines one base class and three derived classes.

TCL_OBJECT_BEGIN_PACKAGE(storage);
TCL_OBJECT_PKG_CLASS(int_storage);
TCL_OBJECT_PKG_CLASS(double_storage);
TCL_OBJECT_PKG_CLASS(string_storage);
TCL_OBJECT_END_PACKAGE();

This sequence (which must be placed in a module - not a header file) defines the ``storage'' package, which is to initialize the classes ``int_storage'', ``double_storage'' and ``string_storage''. The package name must again be all in lowercase, but the class names can be mixed case. After building the module(s) into a shared library, it can be loaded using the package name prefixed with pkg_. If the shared library was named ``storage.so'', the load command would be

load ./storage.so pkg_storage

If you are familiar with loading Tcl packages from dynamic libraries, you know that each package must define a Package_Init function. This is exactly what the above macros do. In fact, you can write such a function yourself with a package name of your choice. By looking at the definition of the macros, you can easily mirror their behaviour.

Another macro, TCL_OBJECT_PKG_EXPECT(name), is allowed within a package declaration. If two classes depend on each other to be available, loading both classes individually would fail because the other would not be available at the time the first is processed. A similar chicken-egg problem exists with tclobj, which checks that all argument types are available. This macro declares that a certain type will be provided at a later time but that it is acceptable within an argument list.

However, dynamic loading of C++ code turns out to be system-dependent, not being as easy as producing dynamic modules from plain C code. The README file that comes with the distribution discusses this problem and possible solutions or workarounds.

Static linking

If dynamic loading of classes is not supported on your system, you can fall back to linking the class code statically with your application. You will have to build the tclobj package and all of your classes into a custom tclsh.

During setup, you must then call the appropriate initialization functions to make the packages and classes available to the interpreter. To set up tclobj itself, call

int Tclobj_Init (Tcl_Interp *);

For classes, you can then call the initialization function by the name of C_classname_Init. For collections of classes using the above ``packaging'' mechanism, the initialization function is named Pkg_pkgname_Init (note the case, the first letter and the `I' are capitalized). To load both the fraction and the storage class, you would do

if (Tclobj_Init     (interp) == TCL_ERROR) return TCL_ERROR;
if (C_fraction_Init (interp) == TCL_ERROR) return TCL_ERROR;
if (Pkg_Storage_Init(interp) == TCL_ERROR) return TCL_ERROR;

A slight pitfall is that you have to compile the ``main'' module (the one that contains the Tcl initialization) with a C compiler, but must perform linking with the C++ compiler (so that it adds the necessary C++ libraries to the linker command).

For more information on building your own custom tclsh, see the Tcl documentation.


[Previous: The Tcl Domain] [Next: Examples]
[Up: Using C++ objects with Tcl]

Frank Pilhofer
Wed Mar 12 14:37:08 MET 1997