Version 1 of Writing a TclOO Method in C

Updated 2016-06-28 09:18:27 by dkf

First, we need a method function.

static int
ExampleMethod(
    ClientData clientData,        /* Same as usual for commands. */
    Tcl_Interp *interp,                /* Interpreter context. */
    Tcl_ObjectContext context,        /* The object/call context. */
    int objc,                        /* Number of arguments. */
    Tcl_Obj *const *objv)        /* The actual arguments. */
{
    /*
     * Get the object instance we're applied to.
     */

    Tcl_Object object = Tcl_ObjectContextObject(context);

    /*
     * Get the number of arguments to "skip" when doing argument parsing.
     * If we did this for ordinary Tcl commands, the # skipped args would be 1 for the
     * command name, but we need to subtract a *VARIABLE* number of arguments since
     * we can be called as [$obj exampleName …] and also as [next …] from a subclass.
     *
     * The context knows what's really going on, so we ask it.
     */

    const int skip = Tcl_ObjectContextSkippedArgs(context);

    if (objc - skip != 2) {
        Tcl_WrongNumArgs(interp, skip, objv, "foo bar");
        return TCL_ERROR;
    }

    /*
     * Usual argument parsing. Note the offset from 'skip'.
     */

    int foo, bar;
    if (Tcl_GetIntFromObj(interp, objv[skip+0], &foo) != TCL_OK ||
            Tcl_GetIntFromObj(interp, objv[skip+1], &bar) != TCL_OK) {
        return TCL_ERROR;
    }

    /*
     * I'll skip the rest; it's usual API usage now…
     */

    return TCL_OK;
}

Next, we need a method definition record.

static Tcl_MethodType exampleMethodType = {
    /* Allow future versioning. */
    TCL_OO_METHOD_VERSION_CURRENT,
    /* The name of the method TYPE; useful when debugging. */
    "exampleMethod",
    /* The implementation function. */
    ExampleMethod,
    /* The function for deleting the clientData, or NULL for "don't bother". */
    NULL,
    /* The function for copying the clientData, or NULL for "mustn't copy". */
    NULL
};

Finally, we can register the method on a class and at the same time set up a clientData if necessary (not this time, but very useful for standard methods which are all the same type).

    Tcl_Class cls = …;
    Tcl_Obj *nameObj = Tcl_NewStringObj("example", -1);

    Tcl_IncrRefCount(nameObj);
    Tcl_NewMethod(interp, cls, nameObj, 1, &exampleMethodType, (ClientData) NULL);
    Tcl_DecrRefCount(nameObj);

Notes

The API is a bit more complicated than for Tcl commands, though it's as simple as possible. You can also register a method on an individual instance object using Tcl_NewInstanceMethod, and the 1 in the call above is whether the method is public (1) or private (0).