سؤال

#include <tcl.h>
#include <iostream>
using namespace std;

char* myTraceProc(ClientData clientData, Tcl_Interp* interp, const char* name1, const char* name2, int flags) {
    cout << "myTraceProc" << endl;
    //changing the object
    return NULL;
}

int main(int argc, char* argv[]) {
    Tcl_FindExecutable(argv[0]);
    Tcl_Interp *interp = Tcl_CreateInterp(); 

    Tcl_TraceVar(interp, "database", TCL_TRACE_WRITES, myTraceProc, 0);

    return 0;
}

This is a part of my c++/tcl program. In fact it doesn't show the problem but I'll try to explain it.

The variable database has custom type. It is registrated using Tcl_RegisterObjType proc. The problem is that when I do a change with traced object in the myTraceProc proc, interpreter duplicates the object (Tcl_DupInternalRepProc is called). This is not desired behavior of the program. It would be great if clone doesn't be created and all charges be done with the exact object. I have looked Tcl_TraceVar documentation but didn't find an way to disable it.

هل كانت مفيدة؟

المحلول

First off, Tcl's type system is very different to that used in C++ (and many other languages besides) in that:

  1. Types attach explicitly to values, not to variables.
  2. Values can be mutated between types. (This can be done by serialisation to a string and then parsing that string, or it can be done via a more efficient mechanism; the details are very specific to the exact example.)

Secondly, Tcl_RegisterObjType() has no special relationship to any other API except for Tcl_GetObjType(), which does the lookup in the table that T_RegisterObjType makes an entry in. Tcl itself does not call Tcl_GetObjType anywhere; you gain no advantage from registering the type other than to allow another extension package to get hold of the type if it wishes. We also don't document what types there are. Not all of Tcl's internal types are registered — the set of types is not even guaranteed between patch versions — and there's no public guarantee of what the effect of operations are on the types of arguments (though some are currently pretty easy to guess, such as with list and dictionary operations).

Because of these points, you need to change the approach you are using. Instead of putting a database handle directly in the value, instead put a human-readable string that you can use to look up the real handle in a hash table. This is pretty easy to get right, and requires significantly less tricky coding. The only downside is that you end up having to use manual disposal of the handle; typically you'd do this by having a closeDatabase $handle operation, or by setting an unset trace on a variable such that you can just do unset handle (or just to from a procedure, in the case of a local variable) to have the deletion happen. This is a classic approach that has been written about a lot, so I won't go into all the details here. (You might also find this code interesting which I wrote rather a long time ago.)

A more sophisticated approach is to bind the handle into a TclOO object. The TclOO C API has a mechanism to allow you to register the handle as a hidden internal value on the instance object which you can retrieve easily from your TclOO methods (provided they're using the TclOO C API, instead of being scripted) and you then get to benefit from the lifetime management code used in TclOO (well-defined constructors and destructors, callbacks when the containing entity is deleted, etc.). This is how many of TDBC's database drivers work (e.g., tdbc::odbc does exactly this under the hood).

Finally, if this is a real database you're talking to, use an existing database extension (TDBC-compliant is recommended). Why? Because then you're not having to maintain a large body of code to do the connection management; you can delegate that all to scripts (easy to write) and extensions maintained by other people. Your calls to access the database from C++ can then just become invocations of (self-provided) Tcl commands, probably via Tcl_EvalObjv as that is the most efficient public command invocation function.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top