Ошибки обхода путем определения переменных с помощью SETF
Экипаж,
Я один из тех типов, которые настаивают на определении моих переменных с помощью SETF. Я обновился до новой машины (и новой версии SBCL), и это не позволяет мне сойти с рук (естественно, я получаю соответствующую ошибку "==> undefined variable...")
Моя проблема здесь в том, что я уже написал 20000 строк кода (неправильно), определяя мои переменные с помощью SETF, и мне не нравится перспектива переписать весь мой код, чтобы интерпретатор мог переварить все это.
Есть ли способ закрыть эту ошибку, чтобы интерпретация могла продолжаться?
Любая помощь приветствуется.
С уважением,
-Todd
2 ответа
Одним из вариантов является настройка среды вашего пакета так, чтобы символ setf
относится к my-gross-hack::setf
вместо cl:setf
, Например, вы можете настроить такие вещи, как:
(defpackage #:my-gross-hack
(:use #:cl)
(:shadow #:setf))
;;; define your own setf here
(defpackage #:my-project
(use #:cl)
(shadowing-import-from #:my-gross-hack #:setf))
;;; proceed to use setf willy-nilly
Вы можете обрабатывать предупреждения, так что компиляция прекращается; при этом вы можете собрать достаточно информации, чтобы исправить свой код.
Протестировано с SBCL 1.3.13.
Обрабатывать предупреждения
У меня есть предупреждения при использовании setf
с неопределенными переменными. Следующее вызывает отладчик, из которого я могу вызвать muffle-warning
:
(handler-bind ((warning #'invoke-debugger))
(compile nil '(lambda () (setf *shame* :on-you))))
Предупреждение имеет тип SIMPLE-WARNING
, который имеет следующие методы доступа: SIMPLE-CONDITION-FORMAT-CONTROL
а также SIMPLE-CONDITION-FORMAT-ARGUMENTS
,
(defparameter *setf-declarations* nil)
(defun handle-undefined-variables (condition)
(when (and (typep condition 'simple-warning)
(string= (simple-condition-format-control condition)
"undefined ~(~A~): ~S"))
(let* ((arguments (simple-condition-format-arguments condition))
(variable (and (eq (first arguments) :variable)
(second arguments))))
(when variable
(proclaim `(special ,variable))
(push variable *setf-declarations*)
(invoke-restart 'muffle-warning)))))
Используйте это как обработчик:
(handler-bind ((warning #'handle-undefined-variables))
;; compilation, quickload, asdf ...
)
Приведенный выше обработчик не является надежным: сообщение об ошибке может измениться в будущих версиях, код предполагает, что аргументы следуют заданному шаблону,... Но это нужно сработать только один раз, так как теперь вы собираетесь объявлять все свои переменные.
Исправьте ваш код
Теперь, когда ваш код компилируется, избавьтесь от уродливого. Или, по крайней мере, добавить правильные объявления.
(with-open-file (out #P"declarations.lisp" :direction :output)
(let ((*package* (find-package :cl-user)))
(format out
"(in-package :cl-user)~%~%~{(defvar ~(~S~))~%~}"
*setf-declarations*)))
Это перебирает все символы, которые вы собрали, и записывает объявления в одном файле. В моем примере это будет содержать:
(in-package :cl-user)
(defvar *shame*)
Попробуйте выполнить чистую перекомпиляцию без обработки ошибок, загрузив этот файл в начале процесса компиляции, но после определения пакетов. В конце концов, вы можете найти время, чтобы переместить эти декларации вместо setf
выражения, которые вызвали предупреждение.