Dynamically links a code library (typically written in C, C++, or some comparable language) with the program containing the Tcl interpreter. Most extensions use this mechanism.
http://www.purl.org/tcl/home/man/tcl8.5/TclCmd/load.htm
If fileName does not contain any directory separators, the library will be searched for in OS-specific ways. If packageName is not provided, it is derived from the fileName by trimming certain prefixes and suffixes and applying capitalization rules (so /usr/local/lib/libfoobar.so would become Foobar); it is used to locate the name of the initialization function inside the library. If interp is not supplied, the library is initialized for the current interpreter.
Clif Flynt has made available a useful C extension to load shared libraries that don't know anything about tcl's _Init function.
The extension allows one to invoke dlopen to load libraries that do not have Tcl entry points (for use by other things that do have legitimate foo_Init entry points).
The dlopen extension is available at:
http://noucorp.com/cgi-bin/noucorp/generic.tcl?dir=/var/www/html/tcl/utilities
It's a minimal, single C file extension, but it has been known to work on both Windows and Posix (just retested on Linux)
You have a design for some software that will be implemented in a dll written in C (or C++ code with extern "C" {} around it). You must write the active code in a function, and add some initialisation code to register the dll's functions with tcl (Tcl_CreateCommand or Tcl_CreateObjCommand is faster).
The initialisation required in the code that compiles to make tclext.dll (the Tclext_Init name is composed from the dll name by Tcl):
// this is your useful procedure. It can be a C++ or C linked procedure. int tclextfunction (ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]) { // dosomething - clientdata is a pointer to something the programmer may or may not define return TCL_OK; } #ifdef __cplusplus extern "C" { #endif int PROBE_EXPORT Tclext_Init(Tcl_Interp *interp) { // tcl stubs and tk stubs are needed for dynamic loading, you must have this set as a compiler option #ifdef USE_TCL_STUBS if (Tcl_InitStubs(interp, TCL_VERSION, 1) == NULL) { Tcl_SetResult(interp, "Tcl_InitStubs failed",TCL_STATIC); return TCL_ERROR; } #endif #ifdef USE_TK_STUBS if (Tk_InitStubs(interp, TCL_VERSION, 1) == NULL) { Tcl_SetResult(interp, "Tk_InitStubs failed",TCL_STATIC); return TCL_ERROR; } #endif Tcl_CreateCommand( "tclextfunction", tclextfunction ); // register your functions with Tcl return TCL_OK; } #ifdef __cplusplus } #endif
Then in Tcl script use
load tclext.dll tclextfunction a b c
to pass the strings a,b,c to tclextfunction in the array argv - argc will be 3 of course. The ClientData is a pointer to anything (perhaps a global data structure or a class defined by a ClientData statement often dynamically allocated in the Tclext_Init function).
The reader should be able to find all the required .h files from the net without any more help from me.
So what do you do when load, or, even more mysteriously, package, fails? There are many, many ways for this to happen, and typical built-in diagnostics from supplied loaders are inscrutable.
One handy tool for such situations is a dependency utility. Under Windows, DependencyWalker [L1 ] walks dependencies. When you let it load a specific dll, it assists you in determining what other DLLs it needs.
AMG: I just had a very strange problem with [load]. When I screw up and try to call my command without loading the the package first, of course I get an error for the command, but I also get an error when trying to load the package. When I do things in the right order, everything works. Yeah I know, I shouldn't be trying to do things backwards, but I still wonder why the load fails.
[andy@toaster|~/e6b]$ tclsh % atc::get_dnlink invalid command name "atc::get_dnlink" % load atc.so couldn't load file "atc.so": atc.so: cannot open shared object file: No such file or directory [andy@toaster|~/e6b]$ tclsh % load atc.so % atc::get_dnlink ABCD conversion failed % info patchlevel 8.6b2 % exec uname -a Linux toaster 3.4.0-andy #2 Fri Jun 1 23:52:05 CDT 2012 i686 Pentium II (Deschutes) GenuineIntel GNU/Linux
Just so you know, that "conversion failed" message is the right result.
This library is not stubs-enabled. It also does not link against -ltcl8.6. Update: linking against -ltcl8.6 doesn't help; this error still happens.
I'd file a bug report, but the above is all the information I have at the moment. It seems like more is needed, yet I don't currently have any time to research further.
Update: here's another rather interesting failure mode:
[andy@toaster|~/e6b]$ tclsh % load per.so % per::get_integer ABCD 0 4 10 % load atc.so couldn't load file "atc.so": atc.so: cannot open shared object file: No such file or directory [andy@toaster|~/e6b]$ tclsh % load atc.so % load per.so couldn't load file "per.so": per.so: cannot open shared object file: No such file or directory
Even when the first command doesn't result in an error, if the second or subsequent command is a [load], it fails. This makes it impossible to load more than one library.
The next step for me is to build the very latest Tcl and see if the problem persists.
Update 2: I just built the most recent trunk (1581f9beac5715890d105a41090756a89b0f3630 2012-06-15 09:39:34 UTC), but the problem persists, exactly the same as before. The system is running Slackware 13.1, if that helps. I might be able to fiddle around in gdb if anyone can point me toward something I ought to be looking at.
Update 3: This is only a problem for interactive use. Everything works fine when [load] is called from a script.