Version 34 of Hello World as a C extension

Updated 2006-09-21 07:13:05 by TR

NEM A question on the chat was how to do a simple "Hello, World!" type command or extension, using Tcl's C API. This is dead easy, and is probably a good place to start for newbies. So here is the code.

 /*
  * hello.c --
  *
  *    A dirt simple Tcl C extension.
  */
 #include <tcl.h>

 static int 
 Hello_Cmd(
     ClientData cdata, 
     Tcl_Interp *interp, 
     int objc,
     Tcl_Obj * CONST objv[])
 {
     Tcl_SetObjResult(interp, Tcl_NewStringObj("Hello, World!", -1));
     return TCL_OK;
 }

  /*
  * Hello_Init --
  *
  *    Called when Tcl [load]'s your extension.
  */
 int 
 Hello_Init(Tcl_Interp *interp)
 {
     if (Tcl_InitStubs(interp, TCL_VERSION, 0) == 0L) {
         return TCL_ERROR;
     }
     Tcl_CreateObjCommand(interp, "hello", Hello_Cmd, NULL, NULL);
     Tcl_PkgProvide(interp, "Hello", "1.0");
     return TCL_OK;
 }

RS: Wouldn't it be simpler to put the implementation of Hello_Cmd at top (to avoid a forward declaration)? And also, so as not to clutter the symbol table, make it static so it's only visible inside this file?

NEM: You could do. I personally prefer having the Init function as the first function in the file, but I could be persuaded otherwise. Added the static declarations, thanks.

NEM: I've buckled to pressure and removed the prototype -- looks clearer, certainly for this example.

SO: Added call to Tcl_PkgProvide - 8/8/05


Compile with:

 gcc -shared -DUSE_TCL_STUBS hello.c -o libhello.so -ltclstub8.4

and you should end up with a shared library that you can [ load ] into tclsh, and then call the "hello" command. Lots of details left out - consult the man pages, books and more extensive docs elsewhere. Also see the sampleextension. Oh, and adapt the compiler line to your OS/compiler combination.

For those who come from a mostly scripting background, such adaption can be quite some work. The following should do the trick in Mac OS X:

 gcc -dynamiclib -DUSE_TCL_STUBS hello.c /Library/Frameworks/Tcl.framework/libtclstub8.4.a -o libhello.dylib

There is probably a better way than giving the full path to the Tcl stubs library, though.

PR This is the Mac OS X way:

 gcc -dynamiclib -DUSE_TCL_STUBS hello.c -framework Tcl -o libhello.dylib

Note, if you do it this way - using the -framework - you are linking to the Tcl dylib, not the Tcl stub library. For an extension, it is better to link to the stub library if you can. The only way to do that is as above, though a more formally correct version is:

 gcc -dynamiclib -DUSE_TCL_STUBS hello.c -L/Library/Frameworks/Tcl.framework -ltclstub8.4 -o libhello.dylib

To load, remember to do:

 tclsh8.4
 % load ./libhello[info sharedlibextension]

so that Tcl finds it ([load] doesn't look in the current dir unless told to).


Using namespaces

TR - Many extensions nowadays create their commands in a namespace. To do the above example using namespaces, you only need two more lines in the code:

 Hello_Init(Tcl_Interp *interp)
 {
        Tcl_Namespace *nsPtr; /* pointer to hold our own new namespace */

      /* Note that I use '== NULL' here instead of '== 0L', since that seems more correct */
        if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) {
                return TCL_ERROR;
        }

      /* create the namespace named 'hello' */
        nsPtr = Tcl_CreateNamespace(interp, "hello", NULL, NULL);
        if (nsPtr == NULL) {
            return TCL_ERROR;
        }

      /* just prepend the namespace to the name of the command.
         Tcl will now create the 'hello' command in the 'hello'
         namespace so it can be called as 'hello::hello' */
        Tcl_CreateObjCommand(interp, "hello::hello", Hello_Cmd, NULL, NULL);
        Tcl_PkgProvide(interp, "Hello", "1.0");
        return TCL_OK;
 }

Note that compiling the example with the Tcl_CreateNamespace() function will give you are warning using Tcl 8.4 (at least until 8.4.12) and no warning using Tcl 8.5. This is because Tcl_CreasteNamespace is an internal function in 8.4 and public in 8.5. So compiling with Tcl 8.4 you should add the line '#include <tclInt.h>' to the C source if you want to avoid the warning. But the code code works regardless of this directive.

CLN - In my extension, I put the namespace in a #define so I have:

  #define NS "hello"
  ...
  nsPtr = Tcl_CreateNamespace(interp, NS, NULL, NULL);
  ...
  Tcl_CreateObjCommand(interp, NS "::hello", Hello_Cmd, NULL, NULL);

(Note that there's no operator between the NS and the "::hello" for Tcl_CreateObjCommand(), C very nicely catenates adjacent literal strings.)


There is also a sample with Windows build instructions at Building Tcl DLL's for Windows


Category Tutorial - Category Foreign Interfaces