问题描述
我是 Tcl C API 的初学者,我正在尝试了解如何使用它。我有这段代码从 get_my_list
proc 获取一个 tcl 列表,然后我遍历它以发送一些信息,在这种情况下,与属性 A
、B
和 {{1 }} 我用 C
proc 得到的。当我在一个基本上只有这个代码的非常简单的例子中运行它时,一切都完美无缺,但是当我在一个大的 tcl 项目中添加这个库时,它最终崩溃了。我怀疑这是由于 tcl 对象的引用计数使用不当,我的意思是,它们的生命周期。在下面的示例中,我可能做错了什么?我使用的是 Tcl 8.6。
get_attr_info
解决方法
棘手的一点是您在 Tcl_Eval()
中调用 Get_Info()
,它可以通过引用计数管理您没有自己引用的值来执行各种操作,包括闪烁您在 my_list
中持有引用的值的列表表示,它可以从其元素下方拉出地毯。特别是,current_info_obj
中引用的对象必须的引用计数从Tcl_ListObjIndex()
之后到循环体的结尾递增。
// ...
for (int i = 0; i < my_list_size; ++i) {
Tcl_Obj* current_info_obj;
Tcl_ListObjIndex(interp,my_list,i,¤t_info_obj);
// HOLD THE REFERENCE; IT OWNS THE current_info_ctr STRING FOR US
Tcl_IncrRefCount(current_info_obj);
const char* current_info_ctr = Tcl_GetStringFromObj(current_info_obj,NULL);
/* getting info A */
Tcl_Obj* info_a_obj = Get_Info(current_info_ctr,"A");
const char* info_a = Tcl_GetStringFromObj(info_a_obj,NULL);
Copy_Info_A(info_a);
/* getting info B */
Tcl_Obj* info_b_obj = Get_Info(current_info_ctr,"B");
const char* info_b = Tcl_GetStringFromObj(info_b_obj,NULL);
Copy_Info_B(info_b);
/* getting info C */
Tcl_Obj* info_c_obj = Get_Info(current_info_ctr,"C");
const char* info_c = Tcl_GetStringFromObj(info_c_obj,NULL);
Copy_Info_C(info_c);
// RELEASE THE REFERENCE
Tcl_DecrRefCount(current_info_obj);
}
// ...
改进你的代码
你也可以走捷径,用Tcl_GetString(objPtr)
代替Tcl_GetStringFromObj(objPtr,NULL)
。你可以把它放在 Get_Info
里面。您可能还应该考虑改用 Tcl_EvalObjv()
,因为这是一种更快的 API(它避免了解析级别),但使用起来更复杂。
const char *Get_Info(Tcl_Interp *interp,Tcl_Obj* info,const char* attr) {
Tcl_Obj *argv[3],*result;
argv[0] = Tcl_NewStringObj("get_attr_info",-1); // cacheable
argv[1] = info;
argv[2] = Tcl_NewStringObj(attr,-1);
Tcl_IncrRefCount(argv[0]);
Tcl_IncrRefCount(argv[1]);
Tcl_IncrRefCount(argv[2]);
// It's very bad form to omit error handling
if (Tcl_EvalObjv(interp,3,argv,0) != TCL_OK) {
Tcl_Panic("problem: %s",Tcl_GetString(Tcl_GetObjResult(interp)));
}
result = Tcl_GetObjResult(interp);
Tcl_DecrRefCount(argv[0]);
Tcl_DecrRefCount(argv[1]);
Tcl_DecrRefCount(argv[2]);
return Tcl_GetString(result);
}
在某些情况下,可以减少引用计数处理的数量。开始时不要担心这样做!最好绝对避免崩溃!
Tcl_EvalObjv
是(有效地)Tcl_Eval
调用以在交互模式下解析后实际调度命令,字节码引擎调用它来评估任何非内联命令。它比将字符串解析为单词要高效得多。