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