Ever wanted to make a TclOO method, yet found doing it in Tcl just a bit awkward (perhaps because it requires access to some low level API that would suck to do in Tcl directly)? This page is for you. **Writing a Method** TclOO methods consist of several parts. Firstly, they have at least an implementation function. They may also have a function used to delete a `clientData` (just as in normal commands) and they could have a function used to copy the method (since there's a `copymethod` command inside an [oo::define] context). These pieces are assembled into a method type descriptor record, in part to allow for future flexibility, and in part because passing all those functions into any use of the type of method would suck. Finally, when the method is ''actually'' used, it's installed with `[Tcl_NewMethod]` or `[Tcl_NewInstanceMethod]`; that's also when the `clientData` would be passed in (those functions are very much like `[Tcl_CreateObjCommand]`). ***The Implementation Function*** First, we need a method function. ======c 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; } ====== ***The Method Type Descriptor*** Next, we need a method definition record. ======c static Tcl_MethodType exampleMethodType = { TCL_OO_METHOD_VERSION_CURRENT, /* Allow future versioning. */ "exampleMethod", /* The name of the method TYPE; useful when debugging. */ ExampleMethod, /* The implementation function. */ NULL, /* 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". */ }; ====== ***Installing the Method*** 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). The only tricky wrinkle here that is different to creating a normal command is that the name of the method is taken as a [Tcl_Obj], and the command doesn't guarantee to retain a reference (or not) to the name object; don't pass in a refcount-zero object. ======c 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); ====== [AMG]: Where in the documentation is there any warning against passing a nameObj with zero refcount? Where else is this unsafe or forbidden? If it's generally unsafe, where is it permissible? **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`). **Classes** [AMG]: How does one go about defining an entire TclOO class in C? I imagine Tcl_NewObjectInstance() is used to create a new instance of [oo::class], then Tcl_NewMethod() is used (as above) to actually define the methods that will be available in instances of the class. Do I have this right? Not a lot of examples to go around... A bit more detail. Tcl_NewObjectInstance() needs a Tcl_Class to instantiate, so how does one get the Tcl_Class for oo::class? I'm thinking call Tcl_GetObjectFromObj() with its second argument being Tcl_NewStringObj("oo::class", -1), then pass the returned Tcl_Object to Tcl_GetObjectAsClass() to get the corresponding Tcl_Class. That so? I'll try actually coding up the above guesses and will report back with success or further questions. Update: Actually an example was written just yesterday: [oo::widget] <>TclOO