2
votes

As you know TCL has some mathematical functions such as sin, cos, and hypot that are called in expr command with () braces as follows:

puts [expr sin(1.57)]

Now how can I add a function using TCL library functions so that it was called exactly the same way, and was doing something that a certain proc defines.

I would like to clarify my question. Say there is a proc (string) as follows:

proc add { a b } { return [expr $a+$b] } ;# string of a proc

Also I have a TCL interpreter in my C++ code. Now I want get the string of a proc and runtime register a function called add into the tcl::mathfunc namespace (I guess I should use Tcl_CreateObjCommand) so that I could call the following:

puts [expr add(1.57, 1.43)]

How this can be done. Could you please write a simple example. I could not find any example in TCL documentation and in books as well which describe the usage of this command.

1
Technically, it's not a command. It's a function in Tcl's C API… - Donal Fellows

1 Answers

3
votes

Creating a function from C isn't too hard. To do it, you've got to write an implementation of a command that will perform the operation, and register that implementation as a command in the correct namespace. (In 8.4 and before, functions were done with a separate interface that was quite a bit nastier to use; the mechanism was wholly overhauled in 8.5.)

Command Implementation

Note that the signature is defined, and the ignored parameter is not used here. (It's really a void *great when you're wanting to do things like binding a command to an object — but it simply isn't needed for doing an addition.)

static int AddCmd(ClientData ignored, Tcl_Interp *interp, int objc,
        Tcl_Obj *const objv[]) {
    double x, y, sum;

    /* First, check number of arguments: command name is objv[0] always */
    if (objc != 3) {
        Tcl_WrongNumArgs(interp, 1, objv, "x y");
        return TCL_ERROR;
    }

    /* Get our arguments as doubles */
    if (    Tcl_GetDoubleFromObj(interp, objv[1], &x) != TCL_OK ||
            Tcl_GetDoubleFromObj(interp, objv[2], &y) != TCL_OK) {
        return TCL_ERROR;
    }

    /* Do the real operation */
    sum = x + y;

    /* Pass the result out */
    Tcl_SetObjResult(interp, Tcl_NewDoubleObj(sum));
    return TCL_OK;
}

Don't worry about the fact that it's allocating a value here; Tcl's got a very high performance custom memory manager that makes that a cheap operation.

Command Registration

This is done usually inside an initialization function that is registered as part of a Tcl package definition or which is called as part of initialization of the overall application. You can also do it directly if you are calling Tcl_CreateInterp manually. Which you do depends on how exactly how you are integrating with Tcl, and that's quite a large topic of its own. So I'll show how to create an initialization function; that's usually a good start in all scenarios.

int Add_Init(Tcl_Interp *interp) {
    /* Use the fully-qualified name */
    Tcl_CreateObjCommand(interp, "::tcl::mathfunc::add", AddCmd, NULL, NULL);
    return TCL_OK;
}

The first NULL is the value that gets passed through as the first (ClientData) parameter to the implementation. The second is a callback to dispose of the ClientData (or NULL if it needs no action, as here).

Doing all this from C++ is also quite practical, but remember that Tcl is a C library, so they have to be functions (not methods, not without an adapter) and they need C linkage.


To get the body of a procedure from C (or C++), by far the easiest mechanism is to use Tcl_Eval to run a simple script to run info body theCmdName. Procedure implementations are very complex indeed, so the interface to them is purely at the script level (unless you actually entangle yourself far more with Tcl than is really wise).