Как работает return -code $ value из процедуры в TCL - PullRequest
1 голос
/ 08 марта 2019

Я должен использовать proc Z, который возвращает что-то вроде «return -code 2» $ что-то, но я не могу понять, как это работает.

proc X {} {
    puts "X - in"
    Y
    puts "X - out"
}
proc Y {} {
    puts "Y - in"
    Z
    puts "Y - out"
}
proc Z {} {
    puts "Z - in"
    return -code 2 "some value"
    puts "Z - out"
}
X

Вывод:

X - in
Y - in
Z - in
X - out

Я не могу изменить возврат в proc Z, но я также хочу, чтобы он продолжил свою работу в proc Y и поставил "Y-out". Могу я это сделать?

1 Ответ

2 голосов
/ 08 марта 2019

Команда return концептуально вызывает исключение (типа return, которое соответствует коду результата TCL_RETURN в Tcl C API), которое перехватывается процедурой (строго, оболочкой процедуры, которая делает вещи как управление callframe и сортировка аргументов), в котором он работает, чтобы заставить что-то делать. В случае обычного return он просто заставляет процедуру возвращать это значение, но когда предоставляется опция -code, он может направить процедуру на выполнение чего-то другого; это делается после извлечение callframe из стека вызовов Tcl.

Поскольку 2 является фактическим значением кода результата TCL_RETURN, это означает, что он говорит оболочке процедуры (т. Е. Общей процедуре Z) вести себя так, как будто это был простой вызов return именно поэтому puts "Y - out" не выполняется; есть исключение возврата, обрабатываемое в результате выполнения Z. (FWIW, return -code 2 встречается довольно редко. return -code 1 / return -code error встречается гораздо чаще, так как это способ получения исключения error , которое полезно, потому что трассировки стека только построено на исключениях ошибок.)

Вы можете переопределить эту обработку, используя catch или try. Немного проще сделать это правильно с try, так как catch является довольно широким примитивом. Попробуйте это:

proc Y {} {
    puts "Y - in"
    try {
        Z
    } on return {msg} {
        puts "Z told us to return '$msg'"
    }
    puts "Y - out"
}

Если вы просто хотите, чтобы короткий фрагмент кода работал после вызова, но в остальном не мешал, tryfinally - правильный путь:

proc Y {} {
    puts "Y - in"
    try {
        Z
    } finally {
        puts "Y - out"
    }
}

Если вы используете Tcl 8.5 (или раньше), у вас не будет try. Вот версия с catch:

proc Y {} {
    puts "Y - in"
    set code [catch {
        Z
    } msg]
    if {$code == 2} {
        puts "Z told us to return '$msg'"
    }
    puts "Y - out"
}

Проблема с catch заключается в том, что очень легко потерять ошибки случайно.

...