/********************************************************************** * * A Minimal TCL Extension Example. * * Copyright 2004 Matti J. Kärki * * Created: 2004-05-24 * Last modified: 2005-10-19 (added "advanced tips") * 2006-05-23 (fixed some spelling mistakes) * **********************************************************************/ /********************************************************************** * * To make the library under Linux: * * 1) gcc -c test.c * 2) ld -s -shared -ltcl -otest.so test.o * * To make the library with MinGW under Windows: * * 1) gcc -IC:\Tcl\include -s -shared -o test.dll test.c C:\Tcl\bin\tcl84.dll * * To make the library with Borland C++ 5.5 under Windows: * * 1) Change the line "int Test_Init(..." to "int _export Test_Init(..." * 2) implib -a tcl.lib C:\Tcl\bin\tcl84.dll * 3) bcc32 -LC:\Tcl\include -tWD test.c tcl.lib * * To use the library in Tcl under Linux: * * % load "./test.so" * % hello "World" * * To use the library in Tcl under Windows: * * % load "./test.dll" * % hello "World" * **********************************************************************/ /********************************************************************** * * A short explanation, how things work: * * In the Tcl it is extremely easy to create extensions to the language with C. * Here I'll try to give a short description, what is required to create a * shared library for Tcl. I also try to explain what each thing in this file * means. * * In the C source file the only thing required is the Packagename_Init() * function. You have to define it in the file (think it as a replacement of * the main() function of the stand-alone program). * * The name of the Init() function consists of the actual package name with * the first letter capitalized and the "_Init" ending. So for the package * called "test" the Init() would be Test_Init(). * * The Package_Init() function receives a handle to the interpreter which * tries to load the package. * * The Package_Init() function introduces new commands to the interpreter * with Tcl_CreateObjCommand() function. For more specific information, * please see "man n load" and "man n Tcl_CreateObjCommand". * ********************************************************************** * * Tcl_CreateObjCommand will introduce a new command to the interpreter. * Parameters passed to the function are: * * 1) handle to the interpreter * 2) name of the command to be created * 3) pointer to the function, which will be associated to the command name * ClientData, an optional (one word) information to be passed to the * our "hello" command every time it is called. * 4) pointer to the function, which will be called when the command is * deleted from memory. Acts like a destructor in C++. If there is no * need for additional clean-up, use NULL. * ********************************************************************** * * Commands introduced with Tcl_CreateObjCommand() can have whatever name you * want to give to the function, but it is required that all your functions * can receive following parameters: * * ClientData clientData - This parameter has the same value, which was * passed to the Tcl_CreateObjCommand at the * creation time of the command. * Tcl_Interp *interp - This is a handle to the interpreter. * int objc - With how many arguments the command was called. * Objv array has objc elements. * Tcl_Obj objv[] - List of arguments. Note that the Tcl_Obj isn't * a string data type. You'll have to use * appropriate functions to extract information * from Tcl_Obj elements. The first element of * the list is always the name of the command * (in this case, it'll be always "hello"). * ********************************************************************** * * Your own function will pass a return value back to the interpreter by * calling Tcl_SetObjResult() function. See the code, how things work. * **********************************************************************/ /********************************************************************** * * Some advanced tips: * * To report wrong number of arguments at function call, check for * Tcl_WrongNumArgs function. It will do basically the same thing what * this example code does, when there isn't enough arguments for hello * function. However, the usage of the Tcl_WrongNumArgs function is easier * and it will produce an error message, which has the same format as other * Tcl extension functions. * * It's possible to compile a Tcl extension with "stubs". This means * that the extension will not be linked directly against specified * version of the shared Tcl library (.so or .dll file). With stubs * the extension will search for shared Tcl library from the machine * and check, what version of the library is available. To utilize * these stubs, please check http://www.tcl.tk/doc/howto/stubs.html * web page, which has an introduction for stubs and instructions, how * to use them with your own projects. To compile with MinGW compiler * with stubs, use the following command line: * * gcc -DUSE_TCL_STUBS -IC:\Tcl\include -s -shared -o test.dll test.c C:\Tcl\lib\libtclstub84.a * * Note that you will need a libtclstub84.a file. You can have one by * compiling Tcl/Tk from source or copying the lib file from the * ActiveState Tcl distribution and renaming the .lib extension to .a * extension. * **********************************************************************/ /********************************************************************** * The code. **********************************************************************/ #include /* strlen() */ #include /* This is required in all Tcl extensions. */ /* The "hello" function implementation. */ int cmd_hello(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *errormsg = "Error! Not enough arguments.\nSyntax: hello name"; char *hello = "Hello, "; char *exclamationmark = "!"; Tcl_Obj *obj_str; int retval; /* Check if there is enough arguments to proceed with the function. */ if (objc < 2) { /* If no arguments was given then set the error message. */ obj_str = Tcl_NewStringObj(errormsg, strlen(errormsg)); retval = TCL_ERROR; } else { /* Concatenates the result string using the first argument. */ obj_str = Tcl_NewStringObj(hello, strlen(hello)); Tcl_AppendObjToObj(obj_str, objv[1]); Tcl_AppendStringsToObj(obj_str, exclamationmark, NULL); retval = TCL_OK; } /* Set the return value to the interpreter. */ Tcl_SetObjResult(interp, obj_str); /* * Return the status of the command. Send TCL_OK if all went well and * TCL_ERROR if there was some internal error in your library. */ return retval; } /* Standard initialization procedure called by Tcl interpreter. */ int Test_Init(Tcl_Interp *interp) { /* This will introduce the "hello" command to the interpreter. */ Tcl_CreateObjCommand(interp, "hello", cmd_hello, NULL, NULL); /* Initialization OK. */ return TCL_OK; } /********************************************************************** * End of File. **********************************************************************/