我们在C / C ++应用程序中内置了 Tcl ,我们的代码中有一个地方需要检查命令是否为嵌套,这意味着命令结果稍后进行设置,例如:“ :: Tcl_SetResult(...)”。如果没有,则将其打印到控制台或使用“ printMessage(...)”将其重定向到文件。
不幸的是,类似 :: Tcl_GetCommandInfo 的东西并没有提供太多信息。 (我必须承认我对Tcl一无所知)。
示例:(函数的外观,Tcl调用它,然后我们处理数据,然后返回到Tcl,Tcl决定它是否是我们的命令并继续执行):
void traceProc(ClientData clientData,Tcl_Interp pInterp,int nLevel,char * pszCommand,Tcl_CmdProc * pCmdProc,ClientData cmdCliendData,int argc,char * argv [])*
执行我们的命令时可以看到该问题:
our_command; # -> nLevel == 1
if {[our_command] eq "sth"} {do_sth}; # -> also nLevel == 1
现在,我希望nLevel为2,因为它在if语句或第一个为0的内部,或者有关当前执行命令的某种附加信息。难道我做错了什么?问题是我不知道以后要做什么,因为如果打印命令结果在[]
括号内等,则“我不应该”打印该结果。
答案 0 :(得分:0)
Tcl_SetResult
函数用于将解释器的结果字段设置为给定的字符串值。 (该字段在Tcl的所有最新版本中完全是内部字段,实际上是多个实数字段,用于处理内存管理等。您可以忽略这些详细信息。)如果命令未设置结果,则其结果从Tcl角度来看为空字符串。尽管结果字段的解释会有所不同,但是您无需出于任何其他原因设置结果:如果命令产生错误(通过其实现函数返回TCL_ERROR
而不是TCL_OK
),则结果字段包含错误 message 。调用命令时,结果字段的值(在观察上等效)为空字符串。
没有通用的方法可以从API知道命令产生什么:它只是产生一个值(在逻辑上是一个字符串,因为这是Tcl可以理解的所有其他类型值的逻辑超类型)。命令的文档(如果存在)可能更具体。 Tcl的所有内置命令都应清楚其产生的内容,并且如果未记录在案以产生成功的结果,则应可预测地产生错误消息。 Tcl本身未提供的命令不受此限制;我们不能强迫您正确使用API!
根据命令的调用上下文是什么,改变命令的行为被认为是 极其糟糕的样式 ,例如,总的来说(例如,因为您处于不同的名称空间或过程中)。特别是,您无法确定在if
内是在表达式部分内还是在主体脚本之一内发生了调用。当需要区分时,可以通过显式参数告诉命令,也可以将命令分为两个名称。这样做的附带好处是它使您的代码更容易测试。
答案 1 :(得分:0)
如前所述,无法通过内省命令或调用帧来可靠地检测给定命令(proc)的嵌套评估。另外,这需要访问Tcl内部(专用标头等),并且仅适用于Tcl 8.6+(如果这对您很重要)。至少可以通过以下操作检测到if
版和非if
调用命令(myCommand
)的情况,至少对于您向我们显示的内容而言,如此:>
CmdFrame *framePtr;
Interp *iPtr = ((Interp *)interp);
Tcl_Obj* resObj = Tcl_NewIntObj(1);
framePtr = iPtr->cmdFramePtr;
Tcl_ResetResult(interp);
if (iPtr->cmdFramePtr->nextPtr &&
iPtr->cmdFramePtr->nextPtr->framePtr ==
iPtr->cmdFramePtr->framePtr &&
iPtr->cmdFramePtr->framePtr == iPtr->varFramePtr) {
Tcl_SetObjResult(interp, resObj);
} else {
fprintf(stderr, "The result is %s\n", Tcl_GetString(resObj));
}
return TCL_OK;
然后脚本执行如下:
myCommand; # w/ print-out
set y [myCommand]; # w/ print-out
if {[myCommand]} { puts "then, here!"} else {puts "notok"}; # w/o print-out
但是,您会注意到一个无法区分以下两种if
内部使用:
if {[myCommand]} { puts "then, here! [myCommand]" } else {puts "notok"}
第二个也不打印。在命令堆栈上只有一个(人工创建的)额外框架会做到这一点:
if {[myCommand]} { puts "then, here! [apply {{} {myCommand}}]"} else {puts "notok"};
因此,如示例所示,这种方法并不通用。
“如果命令结果在[]方括号内,则不应该打印命令结果。”
我想回到Donal的建议之一,并演示如何通过提供两个单独的命令(对于每个上下文一个),同时保留命令的使用性,轻松地解开命令(myCommand
)的不同用法上下文。看起来没有一个。您可以提供一个主要的工具来计算预期的返回值,并提供一个辅助包装器,该包装器将返回值“重定向”到stdout(或其他)。
(1)主命令:将现有的C实现的命令转换为不打印结果值,但仅使用Tcl_SetObjResult
返回结果的命令。将命令(或别名)放入::tcl::mathfunc::*
名称空间。使用Critcl,看起来可能像这样:
critcl::ccommand ::tcl::mathfunc::myCommand {cd interp objc objv} {
Tcl_Obj* resObj = Tcl_NewIntObj(1);
Tcl_SetObjResult(interp, resObj);
return TCL_OK;
}
(2)在具有相同(非限定)名称::
的顶级(myCommand
)或特定于项目的名称空间中创建脚本包装器:
proc ::myCommand {} {
puts stdout [uplevel 1 ::tcl::mathfunc::myCommand]
return
}
包装器调出主命令,并在需要的地方puts
调用结果。 return
将重置口译员的结果。另外,您可能还返回了它,因此包装器与主命令成为合同等效的。
您现在可以在myCommand
条件和非[expr]
条件这样的[if]
环境中以不同方式使用expr
:
myCommand; # main w/ print-out
if {myCommand()} { puts "then, here" }; # wrapper w/o print-out
set x [myCommand]; # main w/ print-out, x is set to ""
set y [expr {myCommand()}]; # wrapper w/o print-out, y is set to result
所有这些都依赖于专用名称空间::tcl::mathfunc
以及[expr]
如何处理其中的命令/过程。给我带来的好处是:
CmdFrame
/ CallFrame
上下文进行内省,这将受到限制,并且需要访问Tcl内部。你是某事吗?沿以下(脚本)行:
proc myCommand {} {
for {set i 1} {$i<=[info frame]} {incr i} {
set frameInfo [info frame $i]
set frameType [dict get $frameInfo type]
set cmd [dict get $frameInfo cmd]
if {$frameType eq "source" && [lindex $cmd 0] eq "if"} {
puts stderr {Called from (anywhere) within [if]}
break;
}
}
return 1
}
myCommand;
# some ancestor stackframe might reveal some [if] context
if {[myCommand]} { puts "then, here!"};
您可以达成目标。在C端使用TclGetFrame
类似,获得当前的堆栈帧,然后向下爬下堆栈。但是,正如多纳(Donal)明确指出的那样,它具有强烈的气味,并可能引起许多误报(例如,[myCommand]
结构中的任何地方的if
,if
不一定表示所谓的控制权-流结构,以防万一有人决定使用此命令名称等。
首先,最好重新评估一下为什么需要这样做。
问题是我不知道以后要做什么,因为我无法打印命令 如果在[]方括号等内,则返回结果。
例如,对于我来说,尚不清楚是否在if
上下文中调用命令,这对您的命令客户端有什么不同。正如Donal所写,这应该不会有所作为。
如果要在myCommand
条件下打印出if
的返回值,而又不改变其行为,请编写sth。就像在脚本中一样(而不是操纵命令本身):
if {[set tmp [myCommand]; puts $tmp; set tmp]} { puts "then, here!"};
如果您不想操作脚本(在myCommand
的调用站点),请使用execution trace来监视myCommand
的执行情况:
proc logMyCommand {call code result op} {
# puts ...
}
trace add execution myCommand leave logMyCommand