Предварительный ответ (на актуальный вопрос)
Как указывалось ранее, нет способа надежно обнаружить вложенную оценку заданной команды (proc) путем интроспекции команд или фреймов вызова. Кроме того, для этого требуется доступ к внутренним компонентам Tcl (частным заголовкам и т. Д.), И он доступен только для Tcl 8.6+ (если это важно для вас). Случай if
-едных и не if
вызовов на вашу команду (myCommand
) может быть обнаружен, по крайней мере, из того, что вы нам сообщили, с помощью команды sth:
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"};
Итак, этот подход не обобщает, как показано на примере.
Предложение по улучшению
"Я не должен печатать результат команды, если он внутри [] скобок и т. Д."
Я хотел бы вернуться к одному из предложений Донала и продемонстрировать, как можно легко распутать различные контексты использования вашей команды (myCommand
), предоставив две отдельные команды, по одной для каждого контекста, сохраняя при этом внешний вид ' не чувствую наличия одного. Вы можете предоставить основное, которое вычисляет предполагаемое возвращаемое значение, и предоставить вспомогательную оболочку, которая «перенаправляет» возвращаемое значение в стандартный вывод (или любой другой).
(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
, для получения текущего стекового кадра и затем поднимаясь вниз по стеку. Однако, как ясно говорит Донал, это имеет очень сильный запах и может вызвать много ложных срабатываний (например, [myCommand]
в любом месте структуры if
, if
не обязательно означает так называемую структуру потока управления, в случае, если кто-то решил добавить к этому имени команды и т. д.)
Лучше, во-первых, пересмотреть, почему вы чувствуете необходимость в этом.
Дело в том, что я не знаю, что делать позже, потому что не могу напечатать команду
результат, если он внутри [] скобок и т. д.
Например, мне остается неясным, в чем разница для клиентов вашей команды, вызывается ли она в if
-контексте или нет. Это не должно иметь значения, как пишет Донал.
Если вы хотите распечатать возвращаемое значение myCommand
в условии if
, не изменяя поведения, напишите sth. как в вашем скрипте (вместо манипулирования самой командой):
if {[set tmp [myCommand]; puts $tmp; set tmp]} { puts "then, here!"};
Обновление
Если вы не хотите манипулировать сценарием (на сайте вызовов myCommand
), тогда используйте трассировку выполнения , чтобы наблюдать за вашими myCommand
выполнениями:
proc logMyCommand {call code result op} {
# puts ...
}
trace add execution myCommand leave logMyCommand