Могу ли я создать структуру в Allegro CL и ABCL, используя только имя типа?

В большинстве реализаций вы можете использовать (make-instance 'struct-type) создать структуру независимо от того, определили ли вы функцию конструктора для этого типа. Это не работает на Allegro или ABCL, но эти реализации могут создавать структуры во время чтения с #S(struct-type), что заставляет меня думать, что должен быть какой-то другой способ конструировать их во время выполнения, учитывая имя типа в виде символа.

3 ответа

Вы не должны смешивать структуру и создание объекта. Стандарт определен make-instance за standard-class, и для symbol такой, что предоставленный символ передается find-class, затем make-instance. рекурсивно

Таким образом, стандартная реализация, которая расширяет make-instance для именованных типов структуры не соответствует, и ваш код, если вы полагаетесь на него.

Затем, #S указывается для корректной работы, только если структура имеет стандартный конструктор, поэтому там не так много магии.

Учитывая это ограничение, вы могли бы реализовать #S себя интернируя символ с именем make- объединяется с именем структуры, за которым следует список аргументов ключевого слова.

Но опять же, зависит от реализации деталей. Вы спрашиваете, когда нет конструктора, что подразумевает :constructor nil в defstruct, Обратите внимание, что отсутствие указания аргумента конструктора означает, что он будет иметь конструктор по умолчанию. Реализация может иметь внутреннюю бухгалтерию, включая скрытый стандартный конструктор, который она создает независимо от ваших параметров (или конструктор без параметров и средства доступа к слотам), которые будут использоваться в make-load-form-using-slots, расширенный #S и, возможно, оптимизировать загрузку литеральной структуры (в отличие от форм, которые ее создают) при компиляции файлов через make-load-form Специализация для структур.

Может быть, не очень элегантно, но как насчет:

(defun make-struct-by-name (name &rest args)
  (apply (symbol-function
          (intern (concatenate 'string "MAKE-"
                               (symbol-name name))))
         args))

Использование:

CL-USER> (defstruct foo
           a b)
FOO

CL-USER> (make-struct-by-name 'foo :a 1 :b 2)
#S(FOO :A 1 :B 2)

Я думал об одном потенциальном решении, но это немного взломать. Так как #S макрос для чтения работает, но я не могу выяснить, как ABCL и Allegro реализуют его (он может быть полностью внутренним для реализации и не определен в Lisp), я могу по крайней мере сгенерировать строку и программно вызвать функцию макроса для чтения:

(defun make-struct-from-type (type-name)
  (with-input-from-string
      (s (format nil "(~A::~A)"
                 (package-name (symbol-package type-name))
                 (symbol-name type-name)))
    (funcall (get-dispatch-macro-character #\# #\S)
             s #\S nil)))

Это может не работать на других реализациях, хотя. SBCL жалуется на sb-impl::*read-buffer* будучи не связанным, поэтому может потребоваться немного больше ловкости, чтобы обмануть реализацию, заставляя думать, что макрос считывателя вызывается в типичном контексте.

Это должно вызвать конструктор по умолчанию, даже если ему было дано имя без значения по умолчанию make- префикс.

Другие вопросы по тегам