This chapter assumes that you know some C or C++. You do not have to be an expert programmer to use the Tcl APIs. Indeed, one of Tcl's strengths is the ease with which you can extend it by writing C code. This chapter provides a few working examples that explain how to initialize your application and create Tcl commands. It describes how to organize your code into packages. It concludes with notes about compiling Tcl under UNIX, Windows, and Macintosh.
There are two ways to get started writing C code for Tcl applications. The easiest way is to write an extension that just adds some new commands to a standard Tcl shell like tclsh or wish. With this approach the Tcl shell creates a basic framework for you, and your C code just extends this framework with new commands. Tcl supports dynamic loading, so you can compile your extension as a shared library (i.e., DLL) and load it into a running Tcl shell. This is the easiest approach because the Tcl shell handles the details of startup and shutdown, and it provides an interactive console to enter Tcl commands. In the case of wish, it also provides the framework for a graphical user interface. Finally, a loadable extension can be shared easily with other Tcl users.
The second way to use the Tcl library is to add it to an existing application. If your application is very simple, it may make sense to turn it into an extension for a standard Tcl shell, which brings you back to the first, simpler approach. However, if your application already has a complex framework (e.g., it is a long-running server process), then you can just add Tcl to it and export the functionality of your application as one or more Tcl commands. Once you do this, you will find that you can extend your application with all the features provided by Tcl.
C Command Procedures and Data Objects
The C or C++ code that implements a Tcl command is called a command procedure. The interface to a command procedure is much like the interface to a main program. The inputs are an array of values that correspond exactly to the arguments in the Tcl script command. The result of the command procedure becomes the result of the Tcl command.
There are two kinds of command procedures: string-based and "object-based." I've quoted "object" here because we are really talking about the data representation of the arguments and results. We are not talking about methods and inheritance and other things associated with object oriented programming. However, the Tcl C APIs use a structure called a Tcl_Obj, which is called a dual ported object in the reference material. I prefer the term "Tcl_Obj value".
The string interface is quite simple. A command procedure gets an array of strings as arguments, and it computes a string as the result. Tcl 8.0 generalized strings into the Tcl_Obj type, which can have two representations: both a string and another native representation like an integer, floating point number, list, or bytecodes. An object-based command takes an array of Tcl_Obj pointers as arguments, and it computes a Tcl_Obj as its result. The goal of the Tcl_Obj type is to reduce the number of conversions between strings and native representations. Object-based commands will be more efficient than the equivalent string-based commands, but the APIs are a little more complex. For simple tasks, and for learning, you can use just the simpler string-based command interface.
David Beasley created a nice tool called SWIG (Simple Wrapper Interface Generator) that generates the C code that implements command procedures that expose a C or C++ API as Tcl commands. This can be a great time saver if you need to export many calls to Tcl. The only drawback is that a C interface may not feel that comfortable to the script writer. Handcrafted Tcl interfaces can be much nicer, but automatically-generated interfaces are just fine for rapid prototyping and for software testing environments. You can learn more about SWIG at its web site:
Before you can use your command procedures from Tcl scripts, you need to register them with Tcl. In some cases, you may also need to create the Tcl interpreter, although this is done for you by the standard Tcl shells.
If you are writing an extension, then you must provide an initialization procedure. The job of this procedure is to register Tcl commands with Tcl_CreateCommand or Tcl_CreateObjCommand. This is shown in Example 44-1 on page 608. The name of this procedure must end with _Init, as in Expect_Init, Blt_Init, or Foo_Init, if you plan to create your extension as a shared library. This procedure is called automatically when the Tcl script loads your library with the load command, which is described on page 607.
If you need to create the Tcl interpreter yourself, then there are two levels of APIs. At the most basic level there is Tcl_CreateInterp. This creates an interpreter that includes the standard commands listed in Table 1-4 on page 22. You still have to initialize all your custom commands (e.g., by calling Foo_Init) and arrange to run a script using Tcl_Eval or Tcl_EvalFile. However, there are a lot of details to get right, and Tcl provides a higher level interface in Tcl_Main and Tcl_AppInit. Tcl_Main creates the interpreter for you, processes command line arguments to get an initial script to run, and even provides an interactive command loop. It calls out to Tcl_AppInit, which you provide, to complete the initialization of the interpreter. The use of Tcl_Main is shown in Example 44-13 on page 629. There are even more details to get right with a Tk application because of the window system and the event loop. These details are hidden behind Tk_Main, which makes a similar call out to Tk_AppInit that you provide to complete initialization.
Calling Out to Tcl Scripts
An application can call out to the script layer at any point, even inside command procedures. Tcl_Eval is the basic API for this, and there are several variations depending on how you pass arguments to the script. When you look up Tcl_Eval in the reference material, you will get a description of the whole family of Tcl_Eval procedures.
You can also set and query Tcl variables from C using the Tcl_SetVar and Tcl_GetVar procedures. Again, there are several variations on these procedures that account for different types, like strings or Tcl_Obj values, and scalar or array variables. The Tcl_LinkVar procedure causes a Tcl variable to mirror a C variable. Modifications to the Tcl variable are reflected in the C variable, and reading the Tcl variable always returns the C variable's value. Tcl_LinkVar is built on a more general variable tracing facility, which is exposed to Tcl as the trace command, and available as the Tcl_TraceVar C API.
I think a well-behaved extension should provide both a C and Tcl API, but most of the core Tcl and Tk commands do not provide an exported C API. This forces you to eval Tcl scripts to get at their functionality. Example 44-15 on page 635 shows the Tcl_Invoke procedure that can help you work around this limitation. Tcl_Invoke is used to invoke a Tcl command without the parsing and substitution overhead of Tcl_Eval.
Using the Tcl C Library
Over the years the Tcl C Library has grown from a simple language interpreter into a full featured library. An important property of the Tcl API is that it is cross platform: its works equally well on UNIX, Windows, and Macintosh. One can argue that it is easier to write cross-platform applications in Tcl than in Java! Some of the useful features that you might not expect from a language interpreter include:
A general hash table package that automatically adjusts itself as the hash table grows. It allows various types of keys, including strings and integers.
A dynamic string (i.e., DString) package that provides an efficient way to construct strings.
An I/O channel package that replaces the old "standard I/O library" found on UNIX with something that is cross-platform, does buffering, allows nonblocking I/O, and does character set translations. You can create new I/O channel types.
Network sockets for TCP/IP communication.
Character set translations between Unicode, UTF-8, and other encodings.
An event loop manager that interfaces with network connections and window system events. You can create new "event sources" that work with the event loop manager.
Multithreading support in the form of mutexes, condition variables, and thread-local storage.
A registration system for exit handlers that are called when Tcl is shutting down.
This Chapter focuses just on the Tcl C API related to the Tcl interpreter. Chapter 47 gives a high-level overview of all the procedures in the Tcl and Tk C library, but this book does not provide a complete reference. Refer to the on-line manual pages for the specific details about each procedure; they are an excellent source of information. The manual pages should be part of every Tcl distribution. They are on the book's CD, and they can be found web at:
The Tcl source code is worth reading.
Finally, it is worth emphasizing that the source code of the Tcl C library is a great source of information. The code is well written and well commented. If you want to see how something really works, reading the code is worthwhile.