Вызов ошибки при сбросе отслеживаемой переменной - PullRequest
0 голосов
/ 11 июля 2020

Я пытаюсь создать некоторые переменные только для чтения, чтобы использовать их с кодом, оцениваемым в безопасной интерполяции. Используя trace, я могу генерировать ошибку при попытках их установить, но не при использовании unset:

% set foo bar
bar
% trace add variable foo {unset write} {apply {{var _ op} { error "$var $op trace triggered" }}}
% set foo bar
can't set "foo": foo write trace triggered
% unset foo
% 

Действительно, в конце концов я заметил, что документация даже мимоходом говорит :

Любые ошибки в неустановленных трассировках игнорируются.

Играя с разными return кодами, включая пользовательские числа, они все кажутся игнорировать. Он также не запускает обработчик interp bgerror. Есть ли другой способ вызвать ошибку при попытке сбросить определенную переменную?

Ответы [ 2 ]

1 голос
/ 11 июля 2020

На самом деле нет. Ключевая проблема заключается в том, что бывают случаи, когда Tcl собирается отключить переменную, когда эта переменная на самом деле будет удалена, потому что ее содержащая структура (пространство имен, фрейм стека или объект и, в конечном итоге, интерпретатор) также удаляется. В этот момент переменная обречена, и пользовательский код не может ее предотвратить (за исключением, конечно, ужасного подхода - никогда не возвращаться со следа, который бесконечно откладывает смерть и переводит все в странное состояние; не делайте этого). Воскресить переменную просто некуда. Следы удаления команд имеют ту же проблему; они тоже могут стрелять, потому что их хранилище исчезает. (Деструкторы TclOO немного более защищены от этого; они стараются не терять ошибки - их даже в крайнем случае помещают в interp bgerror - но все же это возможно в некоторых крайних случаях.)

Более того, есть в настоящее время в API нет ничего, что позволяло бы сообщению об ошибке выйти из процесса удаления пространства имен или кадра вызова. Я думаю, что это можно исправить (это потребует изменения некоторых API-интерфейсов publi c), но по уважительным причинам я думаю, что удаление все равно должно произойти, особенно для кадров стека. Кроме того, я не уверен, что должно произойти, если вы удалите пространство имен, содержащее две неустановленные отслеживаемые переменные, обе трассировки которых сообщают об ошибках. В чем должна быть ошибка? Я правда не знаю. (Я знаю, что конечным результатом должно быть то, что пространство имен все еще исчезло, FWIW, но детали имеют значение, и я понятия не имею, какими они должны быть.)

0 голосов
/ 28 июля 2020

Я пытаюсь создать некоторые переменные только для чтения, чтобы использовать их с оцениваемым кодом

Шелте и Донал уже предоставили своевременную и подробную обратную связь. Так что то, что приходит, подразумевается как скромное дополнение. Теперь, когда известно, что следы переменных выполняются после факта , ниже показано, как я имитирую режим только для чтения (или, скорее, keep-re_setting-to-a-one-time-value ) переменных, использующих трассировки (примечание: как объясняет Донал, это не распространяется на переменные pro c -local).

Приведенная ниже реализация допускает следующее:

namespace eval ::ns2 {}

namespace eval ::ns1 {
    readOnly foo 1
    readOnly ::ns2::bar 2
    readOnly ::faz 3
}

На основе variable, но только для одной пары переменная-значение.

proc ::readOnly {var val} {
  uplevel [list variable $var $val]
  if {![string match "::*" $var]} {
    set var [uplevel [list namespace which -variable $var]]
  }

  # only proceed iff namespace is not under deletion!
  if {[namespace exists [namespace qualifiers $var]]} {
    set readOnlyHandler {{var val _ _ op} {
      if {[namespace exists [namespace qualifiers $var]]} {
        if {$op eq "unset"} {
          ::readOnly $var $val
        } else {
          set $var $val
        }
        # optional: use stderr as err-signalling channel?
        puts stderr [list $var is read-only]
      }
  }}

  set handlerScript [list apply $readOnlyHandler $var $val]
  set traces [trace info variable $var]
  set varTrace [list {write unset} $handlerScript]
  if {![llength $traces] || $varTrace ni $traces} {
    trace add variable $var {*}$varTrace
  }
}

}

Некоторые примечания:

  • Это предназначено для работы только для глобальных или других переменных с пространством имен, а не для pro c -local;

  • Он оборачивается вокруг variable;

  • [namespace exists ...]: эти средства защиты защищают от операций, когда заданное родительское пространство имен в настоящее время удаляется (namespace delete ::ns1 или дочернее удаление интерполяции);

  • В случае unset сценарий обработчика повторно добавляет трассировку для хорошо воссозданной переменной (в противном случае любая последующая запись больше не будет перехвачена.);

  • [trace info variable ...]: помогает избежать добавления избыточных трассировок;

  • [namespace which -variable]: обеспечивает работу с полным именем переменной;

Несколько заключительных замечаний:

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

Конечно один вариант, но он не дает вам возможности охватить различные (косвенные) пути сброса переменной.

[...] в безопасном интерполяции.

Вы можете захотеть interp alias между variable в вашем безопасном интерпре и выше readOnly в родительском интерпре?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...