Posted to tcl by de at Tue Feb 27 22:03:32 GMT 2018view pretty
testcb.c: #include <tcl.h> #include <string.h> typedef struct { Tcl_Obj *callback; Tcl_ObjCmdProc *callbackObjProc; ClientData callbackClientData; } testcbCmdData; static int testcbInstanceCmd ( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[] ) { testcbCmdData *cmdData = clientData; int methodIndex, calls, call, rc, result; Tcl_Obj *vector[2], *cmdPtr; Tcl_CmdInfo cmdInfo; static const char *methods[] = { "configure", "run", NULL }; enum method { m_configure, m_run }; if (objc != 3) { Tcl_SetResult (interp, "wrong # args: should be \"testcbcmd configure <callback>\"" " or \"testcbcmd run <nrOfCallbackCalls>\"", NULL); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[1], methods, "method", 0, &methodIndex) != TCL_OK) { return TCL_ERROR; } switch ((enum method) methodIndex) { case m_configure: if (cmdData->callback) { Tcl_DecrRefCount (cmdData->callback); } cmdData->callback = objv[2]; Tcl_IncrRefCount (cmdData->callback); rc = Tcl_GetCommandInfo(interp, Tcl_GetString(objv[2]), &cmdInfo); if (rc && cmdInfo.isNativeObjectProc) { cmdData->callbackObjProc = cmdInfo.objProc; cmdData->callbackClientData = cmdInfo.objClientData; } else { cmdData->callbackObjProc = NULL; cmdData->callbackClientData = NULL; } break; case m_run: if (Tcl_GetIntFromObj(interp, objv[2], &calls) != TCL_OK) { Tcl_SetResult(interp, "Second arg must be an integer", NULL); return TCL_ERROR; } for (call = 1; call <= calls; call++) { if (cmdData->callbackObjProc != NULL) { vector[0] = cmdData->callback; vector[1] = Tcl_NewIntObj(call); Tcl_IncrRefCount(vector[1]); result = cmdData->callbackObjProc( cmdData->callbackClientData, interp, 2, vector ); Tcl_DecrRefCount(vector[1]); } else { if (cmdData->callback != NULL) { cmdPtr = Tcl_DuplicateObj(cmdData->callback); Tcl_IncrRefCount(cmdPtr); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewIntObj(call)); result = Tcl_EvalObjEx(interp, cmdPtr, TCL_EVAL_GLOBAL | TCL_EVAL_DIRECT); Tcl_DecrRefCount(cmdPtr); } } if (result != TCL_OK) { Tcl_SetResult(interp, "Callback didn't returned TCL_OK", NULL); return TCL_ERROR; } } Tcl_ResetResult(interp); return TCL_OK; break; } return TCL_OK; } static void testcbDeleteCmd ( ClientData clientData ) { testcbCmdData *cmdData = clientData; } static int testcbObjCmd ( ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[] ) { testcbCmdData *cmdData; if (objc != 2) { Tcl_SetResult(interp, "wrong # of args, call it: testcb <cmdName>", NULL); return TCL_ERROR; } cmdData = (testcbCmdData *) Tcl_Alloc (sizeof (testcbCmdData)); memset (cmdData, 0, sizeof (testcbCmdData)); Tcl_CreateObjCommand(interp, Tcl_GetString(objv[1]), testcbInstanceCmd, (ClientData) cmdData, testcbDeleteCmd); return TCL_OK; } int Testcb_Init (interp) Tcl_Interp *interp; /* Interpreter to initialize. */ { Tcl_InitStubs(interp, "8", 0); Tcl_CreateObjCommand(interp, "testcb", testcbObjCmd, NULL, NULL ); Tcl_PkgProvide(interp, "testcb", "1.0"); return TCL_OK; } build.sh #!/bin/bash rm -f testcb.o testcb.so gcc -DUSE_TCL_STUBS=1 -fPIC -o testcb.o -c testcb.c #gcc -shared -o testcb.so testcb.o -ltclstub8.6 gcc -shared -o testcb.so testcb.o -ltclstub8.5 test.tcl: load ./testcb.so testcb cmd1 cmd1 configure callback proc callback {nr} { global count incr count $nr } puts [info patchlevel] testcb cmd2 cmd2 configure callback set count 0 puts "Canonical:" puts [time {cmd1 run 1000000}] puts $count puts "" puts "Tricky:" set count 0 puts [time {cmd2 run 1000000}] puts $count Results: 8.6.8 Canonical: 1262410 microseconds per iteration 500000500000 Tricky: 385482 microseconds per iteration 500000500000 8.5.19 Canonical: 1084528 microseconds per iteration 500000500000 Tricky: 337282 microseconds per iteration 500000500000