Extending Tcl

Difference between version 77 and 78 - Previous - Next
'''Extending Tcl''' is a [Tcl Extension: Developer Guide%|%guide%|%] to writing
[Tcl] [extension%|%extensions].



** See Also **

   [Critcl]:   Compiles C code embedded in a Tcl script on the fly. It can't be used for all extension work, but for many things it's good.  A variation on the methods employed by [Critcl] allows [Fortran] programmers to write extensions for Tcl - it is called Critclf (not very imaginative :), I admit) and is currently under development. Contact [AM] for more information.

   [Extending Tcl in C from Tcl], by [RS]:   Provides `cextend`, which takes some C code and generates a Tcl interpreter that exposes it as a Tcl command.

   [Extension Developer's Wishlist for Tcl]:   

   [Extension Stubs Tables]:   using your tcl extension1 (at [C] level).

   [Executable Modules]:   Commands that ship off computation to external programs.

   [How to debug memory faults in Tcl and extensions]:   

   [How to embed Tcl in C applications%|%How to embed Tcl in C applications]:   There are a few extra steps to embedding Tcl, after which extending an embedded can proceed as usual.

   [Mktclapp]:    Has an [API] for writing extensions.  It can be used to generate a stand-alone application by generating C code, or a [load%|%loadable] shared object library.  It can also be used to convert a Tcl script or scripts into C code for compilation with a C compiler, so its use is beyond just extensions.


   [Stubs]:   

   [Tcl extension prototype - proto.cpp]:      

   [Writing extensions for stubs and pre-stubs Tcl]:   

   [extension]:   

   [RPC]:   An alternative to extending Tcl is to offload functions to another process.

   [SWIG]:   Automates the task of generating an extension.  It can generate an interface for Tcl and other languages, which can then be compiled. [[How well does it work?  What is it good at doing?]

   [Tips for writing quality software]:   

   [Useful C Commands For Tcl]:   

   [Creating Extensions in C++]:   

   [Tcl C API]:   The [C] functions that [Tcl] makes available

   [http://www.cs.man.ac.uk/~fellowsd/tcl/scidx.html%|%Donal K. Fellows's Small C Extensions]:    Most, if not all of what is here that is useful has by now migrated into Tcl itself.

   [xWizard]:   Generate template C/C++ code for a Tcl extension. It is a GUI program written by pure Tcl/Tk so it works for cross-platform. More details in http://www.neatware.com/myrmecox/professional/wizard.html



** Tutorials **

   [TEA] documentation:   

   [http://www.equi4.com/jcw/extuse.html%|%How to use extensions in Tcl%|%]:   by [JCW%|%Jean-Claude Wippler], 1998-05.  A fine "how-to" for those who find themselves needing to build extensions that other people have written.

   [Book Practical Programming in Tcl and Tk%|%Practical Programming in Tcl and Tk]:   by Brent Welch.  The chapters "C Programming and Tcl" and "Compiling Tcl and Extensions." (available [http://www.beedub.com/book/3rd/bookTOC.html%|%online]) are relevant.



** Dated Tutorials **

   [Writing a Tcl extension : the Toocl example, Cédric BEUST 1995]:   predates `Tcl_Obj` API's

   HTML version of the ''TclCommandWriting'' man page that comes with [TclX]:   Explains the This page explains the C API to Tcl, providing an introduction/tutorial on writing Tcl extensions.  This paper, too, predates the ''Tcl_Obj'' API's.

   [http://phaseit.net/claird/comp.lang.tcl/HowToC.html%|%Cameron Laird's personal notes on how to use C with Tcl]:   Circa 2000. [APN] Most links are broken on this page. Remaining are outdated (reference the pre-8.0 API).

** Code Generation Tools **

   [C code generators], by [RS]:   A few tools for embedding [C] code into a Tcl script.

   [Critcl]:   [JCW]: Another way to get an extension off the ground in no time.  Described in [http://www.digital-smarties.com/Tcl2002/critcl.pdf]

   [C Code Generators]:   part of [RS]'s three-part 'Xmas2000' project, presents some Tcl scripts that automatically generate the repetitive C code that extensions use to get themselves connected with the Tcl interpreter. He didn't manage to build a [load]able DLL on his slightly ancient gcc though...

   [SWIG]:   Generates wrappers for [C], [C++], and [Objective-C] functions in various scripting languages, including Tcl.
   
   [tcltcc]:   A Tcl extension wrapping [tcc].  Partially emulates the Critcl API.

   [Mktclapp]:   A Tool For Mixing C/C++ with Tcl/Tk.

   [Tcl++]:   A package that makes development of extended Tcl/Tk interpreters easier and more pleasant for C++ programmers.

   [tclbind] (historical?):   Code to perform bindings of Tcl commands to C++ member functions.  Requires [TclX], but can also support TCL-DP.

   [tclobj]:   Access C++ classes and to operate C++ objects. 

   [cpptcl]:   several tools for working with [C++]

   [xWizard]:   A C/C++ program can be easily wrapped into a Tcl extension. 

   [Object Tcl] (historical?):   Provides tight object-oriented coupling to C++

   [jWrap]:   circa 1999 library that parses [C]/[C++] headers or source in order to automatically produce a C/C++ glue stub that can be linked with your component library to produce a loadable Tcl [extension].

   [TclObjectCommand] (historical?):    Define Tcl commands which manipulate C++ class and structure objects in manners similar to the way Tk manages widgets.

   [Itcl++] (historical?):   Parses C++ header files and generates a one-to-one mapping of C++ classes to itcl classes.

   [tcl_object] (historical?):   C++ code which allows you to develop a bi-directional interface to Tcl.

   [Hush] (historical?):   A [C++] API for Tcl 

   [Joy] (historical):   Integration between various langugages.



** System Libraries **

   [COM]:   [TWAPI COM support%|%TWAPI], [tcom], [optcl] are some available interfaces

   [Microsoft Windows API]:   [TWAPI] is the primary Tcl interface

   [POSIX]:   [TclX] provides access to various system facilities



** General Interfaces **

   [Ffidl%|%ffidl%|%]:   If your only purpose for developing an extension is to provide an interface to some third-party library, you may be able to avoid writing any C or C++ code altogether.  Ffidl provides a nearly universal means of invoking shared libraries from Tcl on a number of platforms.



** Example Extensions **
   [C++ object-style Tcl example extension], by [Jos Decoster]:   A clean, modern example of a C++ extension
   [Example of a Tcl extension in D]:   
   [Example of a Tcl extension in Fortran]:   
   [Example of a Tcl extension in Free Pascal]:   
   [Example of a Tcl extension in Go]:   
   [Example of a Tcl extension in Swift]:   
   [Example of a Tcl extension in Terra]:   
   [extension example]:   the most basic sample extension
   [Hello World as a C extension]:   provides a bare-bones sample extension in C with details of how to actually set up your environment and build an actual extension, and then how to load it into Tcl to try it out.
   [http://core.tcl.tk/tcl/dir?ci=388ef7d2c16b81da&name=unix/dltest%|%unix/dltest]  in the source distribution:   files for testing Tcl's dynamic loading/unloading capabilities
   [http://www.omanurkka.fi/files/test.c.txt%|%Minimal Tcl Extension Example%|%]:   Missing the [stubs] verbiage.
   [RPN C extension for Tcl]:   
   [rphoto], by [RS]:   I needed a fast [photo image rotation] quite badly at work, which would rotate fairly large images (say 2000 * 3000 pixels) by 90 degrees, in place. So after a long while, I did some C coding again, and some hours later, there it was: implementing a single command.
   [SampleExtension]:   the "official" example of a Tcl [C] extension
   [useless tcl extension]:   

** Design **

   [Concepts of Architectural Design for Tcl Applications]:   
   [Tcl C API Design Principles]:   

** Coding Techniques **

   [How do I use the Tcl C API?]:   

   [Blessed Tcl_Obj Values]:   [DKF]:  how to make that struct full of pointers be passable through Tcl while ''guaranteeing'' that no loose references can be generated

   [http://www.tcl.tk/doc/engManual.pdf%|%Tcl/Tk Engineering Manual%|%]:   the engineering guide for the Tcl core implementation and C-level extension

   [Managing the reference count of Tcl objects]:   A summary of when to use ''Tcl_IncrRefCount'' and ''Tcl_DuplicateObj''

   [Using Tcl_ListObj and his friends]:   

   [Pointer clean-up!]:   from [PSE]

   [Tcl Handles]:   Presents in detail two methods for representing pointers to the Tcl program.  The first represents program objects with Tcl command names; the second uses opaque "handles".  These are the two fundamental ways to pass around C/C++ structures in Tcl scripts.

   [HOWTO: Make a Tcl Extension Thread-Safe]:   

   [Thread-safe Tcl Extensions]:   

   [Invoking Tcl commands from Cplusplus]:   A C++ class that allows fast callbacks from C++ to tcl commands.
   [Cplusplus streams and Tcl channels]:   A C++ iostream class that connects to a Tcl I/O channel.  Includes initialization code to remap ''cin'', ''cout'' and ''cerr'' to the Tcl channels ''stdin'', ''stdout'', and ''stderr'' so that C++ I/O happens on the Tcl console.

   [Tclobj%|%Making C++ Loadable Modules Work%|%]:   Part of the [Tclobj] documentation, by Frank Pilhofer,   gives an overview of the various platform-dependent tricks needed to make C++ extensions work in the various Unix environments.



** Windows **

For information at building Tcl extension using Cygnus's Cygwin environment,
take a look at the following files:

   [http://www.flightlab.com/%7Ejoe/tcl/cygwin-howto.txt%|%HOWTO: Using CYGWIN to build a Tcl extension on Windows%|%]:   by [Joe English]

   [http://www.tcl.tk/doc/tea/windows.html%|%TEA on Windows%|%]:   but see also [Annotated 10 steps to success with TEA] for a contrarian view.

   [http://www.xraylith.wisc.edu/%7Ekhan/software/gnu-win32/%|%GNU Win32 related projects%|%]:   by Mumit Khan

   [http://sourceware.cygnus.com/cygwin/faq/faq_toc.html%|%The Cygwin FAQ]:   




** Basic Template **

An extension is normally compiled and linked against the Tcl [stubs] static
archive, '''not''' the Tcl shared object.  After writing the desired function
in [C], add an initialisation function, to be called by `[load]`,  that
typically initialises the Tcl stubs tables and creates new Tcl commands with
`Tcl_CreateCommand` or better, `Tcl_CreateObjCommand`.

The name of the initialisation function is derived from the name of the shared
object.  In this case the name of the shared object is `tclext[info
sharedlibextension]`, and therefore `[load]` looks for a function called
`Tclext_Init`.  Note the initial `T` has been capitalised.

======c
/* 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 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

    /* register your functions with Tcl */
    Tcl_CreateCommand( "tclextfunction", tclextfunction );
    return TCL_OK;
}
#ifdef __cplusplus
}
#endif
======

Then, in Tcl script use

======
load tclext[info sharedlibextension]
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).



** Description **

Reasons for extending Tcl include exposing an external [API], and  increasing
the speed of a CPU-intensive calculation.

Probably the easiest way to call C from Tcl is via [CriTcl]. 
CriTcl allows you to embed C functions within a Tcl script, 
compiling and cached the results the first time they are invoked. 
From then on the compiled C code will be dynamically loaded.
You can also use CritBind to pre-build libraries or executables. 

[stevel]: See [CriTcl builds C extensions on-the-fly] for more information.

----

'''Note that''' `interp->result` is now [Tcl_GetStringResult() forward
compatibility%|%forbidden%|%].

----

(putatively) [DKF]:

If you want to learn how to write a Tcl command in C, one of the easiest places
to look is the Tcl sources themselves. Particularly, try looking in
.../generic/tclCmd??.c.  For example, look at the definition of the `[source]`
in the function `Tcl_SourceObjCmd`, in `tclCmdMZ.c`.  If there's a simpler
command than that in the sources, I don't know of it.

An example:  

======c
#include <tcl.h>

int
Md5CryptCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
    Tcl_Obj *saltObj, *passwordObj;
    char tmpBuffer[TMP_BUF_SIZE]; /* Or whatever! */
    if (objc != 3) {
        Tcl_WrongNumArgs(interp, 1, objv, "salt password");
        return TCL_ERROR;
    }
    saltObj = objv[1];
    passwordObj = objv[2];
    yourFunctionToDoTheRealWork(Tcl_GetString(saltObj), Tcl_GetString(passwordObj), &tmpBuffer);
    Tcl_SetResult(interp, tmpBuffer, TCL_VOLATILE);
    return TCL_OK;
}
======

OTOH, using `Tcl_GetIntFromObj()` is a little more complex because that can
fail (e.g. when the argument is something like "foobar" )

======c
int
FoobarCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
    int foo, bar, resultValue;
    if (objc != 3) {
        Tcl_WrongNumArgs(interp, 1, objv, "foo bar");
        return TCL_ERROR;
    }
    if (Tcl_GetIntFromObj(interp, objv[1], &foo) != TCL_OK ||
        Tcl_GetIntFromObj(interp, objv[2], &bar) != TCL_OK) {

        /* There's already a nice error message */
        return TCL_ERROR;
    }
    resultValue = yourFunctionToDoTheRealWork(foo, bar);
    /* There's other ways to do this, but they're not much more efficient and are definitely less clear */
    Tcl_SetObjResult(interp, Tcl_NewIntObj(resultValue));
    return TCL_OK;
}
======



** Tcl C API **

The Tcl C [API] is powerful and for something like a new data type for a tree, threads, or other things that can not be easily done with executable modules it's a good solution. There are at least 2 approaches to extending Tcl via the C API.  A new tclsh-like shell can be created, or a loadable extension can be [load%|%loaded] into an interpreter.

Our first example will be a new interactive shell that performs simple addition.  This will teach you how to create a Tcl interpreter, initialize Tcl, and create a simple command for addition.  

'''Pro:'''

    1. The C API is powerful and some things can be done that are impossible to do with an EM.
    2. The C API can be used as a portable abstract layer for I/O, and other uses.
    3. Performance is generally excellent -- provided that you choose the right algorithms.

'''Con:'''

    1. [Tcl_Obj] management can be tricky for beginners. Bad Tcl_Obj reference counting practice can result in crashes in other parts of Tcl that can be difficult to diagnose.
    2. C is the only language binding for the Tcl API (although "C" in this case typically includes derivatives such as [C++]), unless you count some tools that use assembly language or things like [ffidl].
    3. The C API may change overtime, and tends to change more than the Tcl script-level interface.
    4. Depending on what part of the API you use or require (for an extension library), you may find that Tclkit doesn't work, because the tclInt.h functions used are not in the stubs table.

======c
#include <stdio.h>
#include <stdlib.h>
#include <tcl.h>

int AddObjCmd (ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
    long n1;
    long n2;
    Tcl_Obj *res;

    /*The command name is objv[0] so 3 arguments are expected.*/
    if (3 != objc) {
      Tcl_WrongNumArgs (interp, 1, objv, "n1 n2");
      return TCL_ERROR;
    }

    if (TCL_OK != Tcl_GetLongFromObj (interp, objv[1], &n1)) {
      /*The error result should be set by Tcl_GetLongFromObj.*/
      return TCL_ERROR;
    }

    if (TCL_OK != Tcl_GetLongFromObj (interp, objv[2], &n2)) {
      return TCL_ERROR;
    }

    res = Tcl_NewLongObj (n1 + n2);

    Tcl_SetObjResult (interp, res);

    return TCL_OK;
}

int main (int argc, char *argv[]) {
    Tcl_Interp *interp;

    /*
     * This finds Tcl's library files and performs some initialization.
     */
    Tcl_FindExecutable (argv[0]);

    interp = Tcl_CreateInterp ();

    if (TCL_OK != Tcl_Init (interp)) {
      fprintf (stderr, "Tcl_Init error: %s\n", Tcl_GetStringResult (interp));
      exit (EXIT_FAILURE);

    }

    Tcl_CreateObjCommand (interp, "+", AddObjCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);

    while (1) {
      char cmd[1024];

      fgets (cmd, sizeof (cmd), stdin);

      if (TCL_OK != Tcl_Eval (interp, cmd)) {
        fprintf (stderr, "error: %s\n", Tcl_GetStringResult (interp));
        continue;
      }

      printf ("result is: %s\n", Tcl_GetStringResult (interp));
    }

    return EXIT_SUCCESS;
}
======

----

[[explain how to build an export list for a DLL]
<<br>>
[[explain how to link using MSVC++]



** Wrapping Existing Functions **

The approach is essentially to write a wrapper function which calls your
C code.  This wrapper function is in a standard format that Tcl understands.
You then call a Tcl function to "register" the wrapper function with the Tcl
interpreter, creating a new command.  After that, every time that command
is called in the interpreter, it calls the wrapper function, which calls
your C code.

See [Extending Tcl] for some
pointers to some tools to help write the interface code for you.  [SWIG]
is often used, and is highly recommended.  In fact, the example code
in this section was generated by SWIG.

Here is a very simple C function which we will
integrate into the Tcl interpreter.  We will create a new command
called ''square'' to call this function.
The C code looks like this:

======c
int square (int i) {
    return i*i;
}
======

Back in the
"good old days" (before Tcl 8.0) everything really was a string inside of the
Tcl interpreter.  That made the interface functions pretty simple.  They are
a little like a main() function, with argc and argv.  Our interface code is
pretty simple, copying the argument from argv, and returning a result string.
.

======c
#include <tcl.h>
static int _wrap_square(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[]) {
    int  _result;
    int  _arg0;
    _arg0 = (int) atol(argv[1]);
    _result = (int )square(_arg0);
    sprintf(interp->result,"%ld", (long) _result);
    return TCL_OK;
}
======

'''Note:''' This is just an example of a simple interface function.  Don't do
it this way any more, even though the backward-compatibility fanatics in the
Tcl community continue to support all of the syntax.  In particular, copying the
result string into the ''interp->result'' field is not recommended.  Bad things
might happen.

At some point in our application, we have to start up a Tcl interpreter, and register
our wrapper function with the interpreter.  This is usually done with a function
called Tcl_AppInit().  Once this initialization is complete, your application can
execute scripts from strings or files by calling Tcl_Eval() or Tcl_EvalFile().

======c
int Tcl_AppInit(Tcl_Interp *interp){
    if (Tcl_Init(interp) == TCL_ERROR)
        return TCL_ERROR;
    /* Now initialize our functions */
    Tcl_CreateCommand(interp, "square", _wrap_square, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    return TCL_OK;
}
======

Call this initialization function somewhere in your main line.  First create an interpreter, then
call Tcl_AppInit().  You could do this all in one fell swoop, but the Tcl_AppInit() format is
standard, and supports creating extended interpreter shells - where your application actually
becomes a Tcl interpreter, which is ''Extremely Cool!(tm)''

======c
Tcl_Interp *interp;
interp = Tcl_CreateInterp();
Tcl_AppInit(interp);
======

The more modern approach to integrating Tcl applications depends on the object interface
supported in Tcl versions 8.0 and higher.  The object model supports the on-the-fly
bytecode compiler, and is very efficient for integrating C code.  It is not necessary
to shimmer values back and forth to strings.  Unfortunately, the object interface is a little
more complex, with special functions to convert from Tcl_Objs to basic types. 
You should also do a little more careful parsing of arguments, and even generate
error messages if the Tcl command is malformed.  Again, SWIG is a great tool that
can generate all this for you.  But you can still do it yourself.  The new object
interface function might look like this:

======c
static int _wrap_square(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
    int  _result;
    int  _arg0;
    Tcl_Obj * tcl_result;
    int tempint;

    clientData = clientData; objv = objv;
    tcl_result = Tcl_GetObjResult(interp);
    if ((objc < 2) || (objc > 2)) {
        Tcl_SetStringObj(tcl_result,"Wrong # args. square i ",-1);
        return TCL_ERROR;
    }
    if (Tcl_GetIntFromObj(interp,objv[1],&tempint) == TCL_ERROR) return TCL_ERROR;
    _arg0 = (int ) tempint;
    _result = (int )square(_arg0);
    tcl_result = Tcl_GetObjResult(interp);
    Tcl_SetIntObj(tcl_result,(long) _result);
    return TCL_OK;
}
======

And, you would, of course, register this new command as an object command.  The
rest of the initialization remains the same

======
Tcl_CreateObjCommand(interp, SWIG_prefix "square", _wrap_square, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
======






** Tcl C API using C++ **

Using C++ with Tcl is similar to using it with C.  There are a few tricks to using methods within a class.  Within the class we create a wrapper which calls a static function within the class.

======c
#include <stdio.h>
#include <stdlib.h>
  
/*This is specifically for C++.*/
extern "C" {
    #include <tcl.h>
}
  
/*Thanks to Kevin Kenny for his help with this.*/
  
class Math {
    public:
    static int AddObjCmd (ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
      return (reinterpret_cast<Math*>(clientData))->AddMethod (interp, objc, objv);
    }
    int AddMethod (Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
};
  
int Math::AddMethod (Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
    long n1;
    long n2;
    Tcl_Obj *res;
  
    /*The command name is objv[0] so 3 arguments are expected.*/
    if (3 != objc) {
      Tcl_WrongNumArgs (interp, 1, objv, "n1 n2");
      return TCL_ERROR;
    }
  
    if (TCL_OK != Tcl_GetLongFromObj (interp, objv[1], &n1)) {
      /*The error result should be set by Tcl_GetLongFromObj.*/
      return TCL_ERROR;
    }
  
    if (TCL_OK != Tcl_GetLongFromObj (interp, objv[2], &n2)) {
      return TCL_ERROR;
    }
    
    res = Tcl_NewLongObj (n1 + n2);
  
    Tcl_SetObjResult (interp, res);
  
    return TCL_OK;
}
  
int main (int argc, char *argv[]) {
    Tcl_Interp *interp;

    /*This finds Tcl's library files and performs some initialization.*/
    Tcl_FindExecutable (argv[0]);
  
    interp = Tcl_CreateInterp ();

    if (TCL_OK != Tcl_Init (interp)) {
      fprintf (stderr, "Tcl_Init error: %s\n", Tcl_GetStringResult (interp));
      exit (EXIT_FAILURE);
    }
  
    Math *inst = new Math;
  
    Tcl_CreateObjCommand (interp, "+", inst->AddObjCmd, (ClientData) inst, (Tcl_CmdDeleteProc *) NULL);
  
    while (1) {
      char cmd[1024];
    
      fgets (cmd, sizeof (cmd), stdin);
  
      if (TCL_OK != Tcl_Eval (interp, cmd)) {
        fprintf (stderr, "error: %s\n", Tcl_GetStringResult (interp));
        continue;
      }
  
      printf ("result is: %s\n", Tcl_GetStringResult (interp));
    }  
  
    return EXIT_SUCCESS;
}
======

[[perhaps someone more knowledgeable about C++ could improve this]]

[SLB] I'll just comment that the 'tricks' are dangerous. Compiling the above code with
Sun's compiler reports:

======none
  "tclmain.cpp", line 61: Warning (Anachronism): Formal argument proc of type extern "C" int(*)(void*,Tcl_Interp*,int,Tcl_Obj*const*)
  in call to Tcl_CreateObjCommand(Tcl_Interp*, const char*, extern "C" int(*)(void*,Tcl_Interp*,int,Tcl_Obj*const*), void*,
   extern "C" void(*)(void*)) is being passed int(*)(void*,Tcl_Interp*,int,Tcl_Obj*const*).
======
I don't have a reference to the relavent clause in the C++ standard to hand but warnings about portability problems from
treating C and C++ function pointers as interchangeable are widespread.
See for example [http://mail.gnome.org/archives/gtk-list/1999-December/msg00424.html]

Links:
   1. ftp://ftp.forwiss.uni-passau.de/pub/Os/Unix/languages/tcl/extensions/tkmin/  [[Does this URL work, just very slowly, or is it no longer valid?]]
   1. http://www.uni-frankfurt.de/%7Efp/Tcl/tcl-c++.txt [[This URL doesn't appear to be working]]
   1. http://prdownloads.sourceforge.net/incrtcl/sampleItclExtension1.1.zip?download



** Tcl C API using Objective-C **

To expose an Objective-C class to Tcl we can create a wrapper in much the same was as was done with C++ earlier.  

Compile this code using something like:

======none
gcc tcl_objc.m -lobjc -I/usr/pkg/include -L/usr/pkg/lib -ltcl84 -lm -lpthread -Wl,-rpath -Wl,/usr/pkg/lib
======

'''tcl_objc.h:'''

======c
/*
 * By George Peter Staplin
 * This is version 2, and much better than the previous code I put here...
 */
#include <stdio.h>
#include <stdlib.h>
#include <tcl.h>
#include <objc/Object.h>
 
 
@interface Adder : Object {
  /* This is just a pointless instance variable. */
  int lastresult; 
}
- (int) add:(Tcl_Interp *)interp x:(Tcl_Obj *)x  y:(Tcl_Obj *)y;
@end
int AdderWrapper (ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]);
====== 

'''tcl_objc.m:'''

======c
#include "tcl_objc.h"

@implementation Adder
 
- (int)add:(Tcl_Interp *)interp x:(Tcl_Obj *)x y:(Tcl_Obj *)y {
  int a, b;
 
  if (TCL_OK != Tcl_GetIntFromObj (interp, x, &a))
   return TCL_ERROR;
  
  if (TCL_OK != Tcl_GetIntFromObj (interp, y, &b))
   return TCL_ERROR;
 
  Tcl_SetObjResult (interp, Tcl_NewIntObj (lastresult = a + b));
 
  return TCL_OK;
}
@end
 
int AdderWrapper (
   ClientData cdata, 
   Tcl_Interp *interp,
   int objc, 
   Tcl_Obj *CONST objv[]) 
{
  id inst = (id) cdata;
 
  if (TCL_OK != [inst add: interp x: objv[1] y: objv[2]]) {
   return TCL_ERROR;
  }
 
  return TCL_OK;
}

int main (int argc, char *argv[]) {
  Tcl_Interp *interp;
  id inst;
 
  Tcl_FindExecutable (argv[0]);
 
  interp = Tcl_CreateInterp ();
 
  if (TCL_OK != Tcl_Init (interp)) {
   fprintf (stderr, "Tcl_Init error: '%s'\n", Tcl_GetStringResult (interp));
   return EXIT_FAILURE;
  }
 
  inst = [Adder new];
 
  Tcl_CreateObjCommand (interp, "add", AdderWrapper, (ClientData) inst,
    (Tcl_CmdDeleteProc *) NULL);
 
  if (TCL_OK != Tcl_Eval (interp, "add 200 123")) {
   fprintf (stderr, "Tcl_Eval error: '%s'\n", Tcl_GetStringResult (interp));
   return EXIT_FAILURE;
  }
 
  printf ("Success: the result of add 200 123 is: %s\n", Tcl_GetStringResult (interp));
 
  return EXIT_SUCCESS;
}
======
----

[jcw] 2003-01-15: Your "add" example above in Critcl:

======
package require critcl
critcl::cproc addup {long a long b} long {
    return a + b;
}
interp alias {} + {} addup
puts "123 + 864 = [+ 123 864]"
======

If you have tclkit and the critcl starkit, do:

======
$ critcl add.tcl 
123 + 864 = 987
======

If you want to turn this into a loadable package, use this code instead:

======
package provide add 1.0
package require critcl
critcl::cproc addup {long a long b} long {
    return a + b;
}
======

Then use the -pkg flag:

======
$ critcl -pkg add.tcl 
Source: add.tcl 
Library: add.so
Package: /home/jcw/lib/add
$
======

The result is a self-contained auto-generated package:

======none
$ ls -lR lib/add/
lib/add/:
total 9
drwxr-xr-x    2 jcw      users         104 Jan 15 18:52 Linux-x86
-rw-r--r--    1 jcw      users        1506 Jan 15 18:52 critcl.tcl
-rw-r--r--    1 jcw      users          64 Jan 15 18:52 pkgIndex.tcl

lib/add/Linux-x86:
total 12
-rwxr-xr-x    1 jcw      users        4660 Jan 15 18:50 add.so
-rw-r--r--    1 jcw      users         157 Jan 15 18:52 critcl.tcl
$
======




<<categories>> Tutorial