Как я могу вызвать команды Tcl из разных пространств имен без явного указания? - PullRequest
3 голосов
/ 27 марта 2012

По сути, проблема состоит в том, чтобы выполнить код Tcl, определенный в одном пространстве имен, используя вызовы функций в этом пространстве имен, в eval внутри другого пространства имен.

Следующий код работает нормально:

namespace eval ::eggs {
    namespace export e1 eeval
    proc e1 {}  { puts pe1 }
    proc eeval body  { puts "in cb enter"; eval $body; puts "in cb exit" }
}
namespace import ::eggs::* 

namespace eval ::spam {
    namespace export s1 scall
    proc scb   {}  { puts pscb }
    proc scall {}  { puts "in call enter"; eeval {::spam::scb}; puts "in call exit" }
    e1
    scall
}
namespace import ::spam::*

и отпечатки:

% % % % % % pe1
in call enter
in cb enter
pscb
in cb exit
in call exit

Теперь я хочу заменить ::spam::scb на scb. Я бы обменял упаковку на eeval.

Мой вариант использования: Пространство имен eggs - это очень простая библиотека для регрессионного тестирования. Пространство имен spam - это небольшая библиотека, в которой реализована хорошая функция. Они должны быть проверены после перезагрузки. Для этого вызывается scall и используется специальная тестовая функция в eggs с именем eeval.

eeval - это модульный тест. Из соображений удобства и «не повторяй себя» я бы не хотел использовать полное имя пространства имен для любой функции, определенной в spam.

В идеале scall будет выглядеть так:

proc scall {}  { puts "in call enter"; eeval {scb}; puts "in call exit" }

Однако во время выполнения eval в ::eggs::eeval не знает, где найти scb. Поскольку eggs - это просто библиотека тестов, я не могу импортировать пространство имен spam.

Есть ли способ, например, можно разработать обертку, например eeval чтобы запустить его в другом пространстве имен?

Ответы [ 2 ]

2 голосов
/ 28 марта 2012

Если вы собираетесь передать код в eval (возможно, добавляя аргументы), тогда самый простой метод для генерации скрипта обратного вызова - это namespace code:

eeval [namespace code {scb}]

Это генерирует весь упаковочный код, чтобы гарантировать, что все работает правильно, включая обработку всевозможных случаев, с которыми вы не имели дело:

% namespace eval boo {
    puts [namespace code {foo bar}]
}
::namespace inscope ::boo {foo bar}

Однако, если вы выполняете обратный вызов немедленно, то наиболее распространенным подходом является не описанный выше, а использование uplevel 1 для выполнения обратного вызова:

proc eeval body {
    puts "in cb enter"
    uplevel 1 $body
    puts "in cb exit"
}

Это имеет то преимущество, что вызываемый код действительно находится в контексте стека вызывающей стороны, что позволяет ему делать полезные вещи, такие как доступ к локальным переменным. Поскольку код выполняется в том же контексте, в котором вы его определили [*] , его можно использовать для разрешения имен команд.


[*] Это большая жирная ложь - семантика Tcl довольно сложна - но все работает так, как я сказал, правда.

2 голосов
/ 27 марта 2012

namespace current - это СУХОЙ, которую вы, вероятно, ищете.

namespace eval ::spam {
    proc scall {}  {
        puts {in call 2 enter}
        eeval [namespace current]::scb
        puts {in call 2 exit}
    }
}

Или передать текущее пространство имен в eeval proc

proc ::eggs::eeval {body {namespace ""}} {
    puts "in cb enter"
    if {$namespace == ""} {
        eval $body
    } else {
        namespace eval $namespace $body
    }
    puts "in cb exit"
}

proc ::spam::scall {} {
    puts "in call 3 enter"
    eeval scb [namespace current]
    puts "in call 3 exit"
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...