Version 2 of Tcl extension prototype - proto.cpp

Updated 2003-10-17 08:49:35

Weird, "C++" in the title seems to confuse this wiki

This is a minimal example on how to set up a Tcl extension which ties into a C++ command class. It appears to clean up properly when commands are renamed/deleted and when the interpreter exits -- JC

DKF: Hmm. Just wondering if a better extension of this would be to recast this class in terms of core class you can inherit from that sets up a command and manages its safe deletion for you. Then, the constructor for the class would have to be the place where the exit handler was installed, and you could leave the implementation of the command itself to a (naturally virtual) function. I suspect that there are a few typos below too... :^)


 #include <tcl.h>

 class xProtoCmd
 {
    Tcl_Interp* _interp;
    Tcl_Command _token;

 public:
    xProtoCmd (Tcl_Interp* interp_);
    ~xProtoCmd ();

    static int CommandProc(ClientData, Tcl_Interp*, int, struct Tcl_Obj* const []);
    static void DeleteProc(ClientData);
    static void ExitProc(ClientData);
 };

 int xProtoCmd::CommandProc(ClientData self_,
    Tcl_Interp* interp_, int objc_, struct Tcl_Obj* const objv_[])
 {  
    xProtoCmd* self = (xProtoCmd*) self_;

        // test code
    Tcl_SetIntObj(Tcl_GetObjResult(interp_), (int) self_);

    return TCL_OK;
 }

 void xProtoCmd::DeleteProc(ClientData self_)
 {
    xProtoCmd* self = (xProtoCmd*) self_;
    delete self;
 }

 void xProtoCmd::ExitProc(ClientData self_)
 {
     xProtoCmd* self = (xProtoCmd*) self_;
    Tcl_DeleteCommandFromToken(self->_interp, self->_token);
 }

 xProtoCmd::xProtoCmd (Tcl_Interp* interp_) 
    : _interp (interp_), _token (0)
 {
    _token = Tcl_CreateObjCommand (_interp, "proto", CommandProc, this, DeleteProc);
 }

 xProtoCmd::~xProtoCmd () 
 {
    Tcl_DeleteExitHandler(xProtoCmd::ExitProc, this);
    Tcl_DeleteCommandFromToken(_interp, _token);
 }

 extern "C" DLLEXPORT int Proto_Init(Tcl_Interp* interp_)
 {
    #if USE_TCL_STUBS
        if (Tcl_InitStubs(interp, "8.0", 0) == NULL) {
            return TCL_ERROR;
        }
    #endif

    Tcl_CreateExitHandler(xProtoCmd::ExitProc, new xProtoCmd (interp_));
    return Tcl_PkgProvide (interp_, "Proto", "0.1");
 }