c++ duplicates tcl interpreter trace

c++ - Tcl: el intérprete crea una copia del objeto rastreado si cambia



duplicates interpreter (1)

#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; }

Esto es parte de mi programa c ++ / tcl. De hecho, no muestra el problema, pero intentaré explicarlo.

La database variable tiene un tipo personalizado. Se registra utilizando Tc_RegisterObjType proc. El problema es que cuando realizo un cambio con un objeto rastreado en el proceso myTraceProc , el intérprete duplica el objeto (se llama a Tcl_DupInternalRepProc ). Este no es el comportamiento deseado del programa. Sería genial si no se crea el clon y todas las cargas se realizan con el objeto exacto. Miré la documentación de Tcl_TraceVar pero no encontré la forma de desactivarla.


En primer lugar, el sistema de tipos de Tcl es muy diferente al utilizado en C ++ (y en muchos otros idiomas además) en que:

  1. Los tipos se asocian explícitamente a los valores, no a las variables.
  2. Los valores pueden ser mutados entre tipos. (Esto se puede hacer por serialización a una cadena y luego analizar esa cadena, o puede hacerse a través de un mecanismo más eficiente, los detalles son muy específicos para el ejemplo exacto).

En segundo lugar, Tcl_RegisterObjType() no tiene una relación especial con ninguna otra API, excepto Tcl_GetObjType() , que realiza la búsqueda en la tabla en la que T_RegisterObjType realiza una entrada. Tcl no llama a Tcl_GetObjType ningún lugar; no obtiene ninguna ventaja registrando el tipo que no sea para permitir que otro paquete de extensión obtenga el tipo si lo desea. Tampoco documentamos qué tipos hay. No todos los tipos internos de Tcl están registrados, el conjunto de tipos ni siquiera está garantizado entre versiones de parches, y no hay garantía pública de cuál es el efecto de las operaciones en los tipos de argumentos (aunque algunos son actualmente bastante fáciles de adivinar, como con operaciones de lista y diccionario).

Debido a estos puntos, debe cambiar el enfoque que está utilizando. En lugar de poner un identificador de base de datos directamente en el valor, en lugar de poner una cadena legible por humanos que puede usar para buscar el identificador real en una tabla hash. Es bastante fácil hacerlo bien y requiere una codificación significativamente menos complicada. El único inconveniente es que terminas teniendo que usar la eliminación manual del mango; normalmente harías esto teniendo una operación closeDatabase $handle , o estableciendo un rastreo no establecido en una variable de tal manera que simplemente puedas unset handle (o simplemente de un procedimiento, en el caso de una variable local) para tener el eliminación suceda. Este es un enfoque clásico sobre el que se ha escrito mucho , así que no entraré en detalles aquí. (También puede encontrar este código interesante que escribí hace mucho tiempo).

Un enfoque más sofisticado es unir el mango a un objeto TclOO. La API de TclOO C tiene un mecanismo que le permite registrar el identificador como un valor interno oculto en el objeto de instancia que puede recuperar fácilmente de sus métodos TclOO (siempre que estén usando la API de TclOO C, en lugar de tener secuencias de comandos) y usted luego aproveche el código de administración de por vida utilizado en TclOO (constructores y destructores bien definidos, devoluciones de llamada cuando se elimina la entidad que lo contiene, etc.). Esta es la cantidad de controladores de base de datos de TDBC que funcionan (por ejemplo, tdbc::odbc hace exactamente esto bajo el capó ).

Finalmente, si esta es una base de datos real con la que está hablando, use una extensión de base de datos existente (se recomienda TDBC-compatible). ¿Por qué? Porque entonces no tienes que mantener un gran cuerpo de código para hacer la gestión de la conexión; puede delegar todo eso en scripts (fáciles de escribir) y extensiones mantenidas por otras personas. Sus llamadas para acceder a la base de datos desde C ++ pueden convertirse en invocaciones de comandos Tcl (provistos por ellos mismos), probablemente a través de Tcl_EvalObjv ya que es la función de invocación de mandatos pública más eficiente.