#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;
}
Это часть моей программы на c ++ / tcl. На самом деле это не показывает проблему, но я постараюсь объяснить это.
Переменная database
имеет пользовательский тип. Зарегистрировано с использованием Tcl_RegisterObjType Proc. Проблема в том, что когда я делаю изменение с отслеживаемым объектом в myTraceProc
proc, интерпретатор дублирует объект (Tcl_DupInternalRepProc
называется). Это не желаемое поведение программы. Было бы замечательно, если бы клон не был создан, и все обвинения были сделаны с точным объектом. я посмотрел Tcl_TraceVar документация, но не нашел способ ее отключить.
Во-первых, система типов Tcl очень отличается от того, что используется в C ++ (и многих других языках, кроме) тем, что:
Во-вторых, Tcl_RegisterObjType()
не имеет особых отношений с любым другим API, кроме Tcl_GetObjType()
, который делает поиск в таблице, T_RegisterObjType
делает запись в. Tcl сам не вызывает Tcl_GetObjType
в любом месте; Вы не получаете никаких преимуществ от регистрации типа, кроме как разрешить другой пакет расширения, чтобы получить тип, если он желает. Мы также не документируем, какие есть типы. Не все внутренние типы Tcl зарегистрированы — набор типов даже не гарантируется между версиями исправлений — и нет никакой публичной гарантии того, как операции влияют на типы аргументов (хотя некоторые в настоящее время довольно легко угадать, такие как со списком и словарными операциями).
Из-за этих моментов вам необходимо изменить подход, который вы используете. Вместо того, чтобы указывать дескриптор базы данных непосредственно в значении, вместо этого поместите удобочитаемую строку, которую можно использовать для поиска реального дескриптора в хеш-таблице. Это довольно легко сделать правильно и требует значительно менее сложного кодирования. Единственным недостатком является то, что вам придется использовать ручную утилизацию ручки; как правило, вы бы сделали это, имея closeDatabase $handle
операция, или установив неопределенную трассировку для переменной, так что вы можете просто сделать unset handle
(или просто из процедуры, в случае локальной переменной), чтобы произошло удаление. Это классический подход это было написано о многом, поэтому я не буду вдаваться во все детали здесь. (Вы также можете найти этот код Интересно, что я написал довольно давно.)
Более сложный подход — связать дескриптор в объект TclOO. TclOO C API имеет механизм, позволяющий вам зарегистрировать дескриптор как скрытое внутреннее значение объекта экземпляра, которое вы можете легко получить из ваших методов TclOO (при условии, что они используют TclOO C API вместо сценариев), и вы затем получите выгоду от кода управления временем жизни, используемого в TclOO (четко определенные конструкторы и деструкторы, обратные вызовы при удалении содержащего объекта и т. д.). Это то, сколько драйверов баз данных TDBC работает (например, tdbc::odbc
делает именно это под капотом).
Наконец, если вы говорите с реальной базой данных, используйте существующее расширение базы данных (рекомендуется TDBC-совместимое). Зачем? Потому что тогда вам не нужно поддерживать большой объем кода для управления соединением; Вы можете делегировать все это сценариям (легко писать) и расширениям, поддерживаемым другими людьми. Ваши вызовы для доступа к базе данных из C ++ могут затем стать вызовами (самостоятельно предоставленных) команд Tcl, возможно, через Tcl_EvalObjv
так как это самая эффективная функция вызова публичных команд.
Других решений пока нет …