tcl: обернуть процесс с тем же именем

Я хочу заменить определение "proc N" процедурой с тем же именем и соглашениями о вызовах, но с небольшим дополнительным кодом обнаружения ошибок.

В python я мог бы делать то, что хочу, как показано ниже, но я не понимаю, как пространства имен и дескрипторы функций работают в tcl.

__orig_N = N
def N(arg1, arg2):
    if arg1 != 'GOOD VALUE':
        exit('arg1 is bad')
    return __orig_N(arg1, arg2)

person bukzor    schedule 24.05.2011    source источник


Ответы (2)


У Tcl неплохой самоанализ процедур. Это позволяет вам переписать процедуру, добавив еще немного кода:

# Assume there are no defaults; defaults make this more complicated...
proc N [info args N] [concat {
    # Use 'ne' for string comparison, '!=' for numeric comparison
    if {$arg1 ne "GOOD VALUE"} {
        error "arg1 is bad"
        # The semicolon is _important_ because of the odd semantics of [concat]
    };
} [info body N]]

Хорошо, это не единственный способ сделать это - ответ Эрика ближе к тому, как я обычно оборачиваю команду, и у него есть преимущество в том, что он работает и с непроцедурными командами, - но это решение имеет то преимущество, что связывает код в красивом и плотном так что там очень мало, чтобы пойти не так, как надо позже. Он также не вводит дополнительные кадры стека в какие-либо трассировки ошибок, что упрощает отладку.

person Donal Fellows    schedule 24.05.2011
comment
Примечание о error в Tcl по сравнению с exit в Python из комментариев к ответу Эрика применимо и здесь, но следует отметить, что использование error (или return -code error) является идиоматическим в Tcl, тогда как предоставление коду сдуть процесс не на том основании, что он не очень соседский. - person Donal Fellows; 24.05.2011
comment
Во втором у него есть то преимущество, что неясно, говорите ли вы об ответе Эрика или о своем собственном. - person bukzor; 25.05.2011
comment
Одним из недостатков вашего подхода является то, что он подвергает вас загрязнению исходного процесса из-за нового внедренного кода. Например, если материал в «оболочке» создает новые переменные, они будут видны исходному коду, что может привести к неожиданному поведению и очень хитрым ошибкам. Мой подход защищает вас от этого риска. - person Eric Melski; 25.05.2011
comment
@Eric: Для прямой проверки аргумента - то, о чем просил спрашивающий - загрязнение вряд ли будет проблемой. Для более существенной обработки все будет по-другому. Есть несколько способов справиться с этим (например, команда apply может ограничить объем временных файлов), каждый со своими специфическими компромиссами. Конечно, такие проблемы возникают и в других языках, например, AspectJ может сделать Java весьма невероятно неясной. - person Donal Fellows; 25.05.2011
comment
@bukzor: Уточнил, плюс объяснил еще одно преимущество этой версии. - person Donal Fellows; 25.05.2011

Вы можете использовать команду rename для переименования существующего процесса:

rename N __orig_N
proc N {arg1 arg2} {
    if { $arg1 != "GOOD_VALUE" } {
        puts stderr "arg1 is bad"
        exit 1
    }
    return [uplevel 1 __orig_N $arg1 $arg2]
}

На самом деле это немного сложнее, чем оригинал Python, поскольку использование uplevel эффективно полностью исключает оболочку из стека вызовов - что, по общему признанию, может не понадобиться в вашем случае, но приятно иметь возможность это сделать .

person Eric Melski    schedule 24.05.2011
comment
+1: error "arg1 is bad" может быть более прямым переводом вместо отдельных команд puts и exit. - person glenn jackman; 24.05.2011
comment
@glenn: Возможно, хотя [ошибка] может быть обнаружена, тогда как команда выхода Python, afaik, безоговорочно выходит из интерпретатора, поэтому семантика немного отличается. - person Eric Melski; 24.05.2011
comment
+1: Несколько замечаний: в 8.6 есть tailcall, что позволяет вам переписать последнюю строку как tailcall __orig_N $arg1 $arg2, чтобы получить еще более полное исключение из стека вызовов, и очень важно не переименовывать процедуры через границы пространства имен или изменения области разрешения. (Боже, неужели я ненавижу эту особенность!) - person Donal Fellows; 24.05.2011
comment
@Eric: Python exit() вызывает исключение SystemExit, которое можно перехватить. - person bukzor; 25.05.2011
comment
@Donal: я не могу представить, что значит переименовывать процедуры через границы пространства имен. Можете ли вы расширить это? - person bukzor; 25.05.2011
comment
@bukzor: спасибо за информацию. Я почти не использовал Python, поэтому я не знал. - person Eric Melski; 25.05.2011
comment
@букзор: rename ::ns1::foo ::ns2::bar. Но не делайте этого. - person Donal Fellows; 25.05.2011
comment
@bukzor: я бы не сказал, что python exit в целом эквивалентен tcl error. В этом конкретном случае error может лучше соответствовать семантике вашего примера Python. - person Eric Melski; 27.05.2011