Финализация класса: как избежать создания фиктивных экземпляров?

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

Пример:

(make-instance 'expression :op '+ :left 'nan :right 'nan)
(defmethod normalize-expression ((this expression))
  (optima:match this
    ((optima::or (expression :left 'nan) (expression :right 'nan)) 'nan)
    ((expression :op op :left x :right y) (funcall op x y))))

Если я не добавлю первую строку, функция не скомпилируется, что даст мне эту ошибку:

; caught ERROR:
;   (during macroexpansion of (SB-PCL::%DEFMETHOD-EXPANDER NORMALIZE-EXPRESSION ...))
;   SB-MOP:CLASS-SLOTS called on #<STANDARD-CLASS EXPRESSION>, which is not yet finalized.
;   See also:
;     AMOP, Generic Function SB-MOP:CLASS-SLOTS

optima — это библиотека сопоставления с образцом, (expression :op op ...) — сопоставление экземпляров класса expression с заданным образцом. Я не знаю подробностей, но похоже, что ему нужно знать, какие методы доступа определены для этого класса, и похоже, что эта информация недоступна, пока она не будет завершена. Итак, есть ли способ обойти проблему финализации?

Класс не будет продлен (по крайней мере, в этом проекте и не планируется). Создать фиктивный экземпляр не так уж больно... это просто уродливое решение, поэтому я надеялся найти лучшее. Также, возможно, я бы получил больше информации о доработке, что тоже хорошо :)


person Community    schedule 29.07.2013    source источник


Ответы (1)


Забывание обеспечить завершение класса кажется довольно распространенной ошибкой при использовании MOP.

В lisp классы определяются в две «фазы»:

  • Прямое определение класса
  • Эффективное определение класса

Прямое определение класса изоморфно форме defclass. Он имеет имя класса, имена суперклассов, список прямых слотов (т. е. слотов, определенных для этого конкретного класса, но для его суперклассов).

Эффективное определение класса содержит всю информацию, необходимую для компилятора/интерпретатора. Он содержит список всех слотов классов (включая те, которые определены в суперклассах), макет экземпляра класса, ссылки на методы доступа и т. д.

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

Что касается вашей конкретной проблемы: кажется, что optima:match должен гарантировать, что класс завершен, прежде чем пытаться перечислить его слоты. Это можно сделать с помощью двух функций: class-finalized-p (чтобы проверить, нуждается ли класс в финализации) и finalize-inheritance, чтобы фактически выполнить финализацию. Или вы можете использовать вспомогательную функцию closer-mop:ensure-finalized. (closer-mop — библиотека для портативного использования CLOS MOP).

E.g.,:

(c2mop:ensure-finalized (find-class 'expression))
person dmitry_vk    schedule 30.07.2013
comment
Спасибо за ответ! Я считаю, что optima уже импортирует closer-mop, так что использование этой функции не должно быть проблемой. Но еще кое-что: учитывая мою ситуацию и своего рода конфликт интересов. Должен ли optima выполнять проверку и доработку, если это требуется, или это должен делать разработчик? т.е. прямо говоря. Является ли это ошибкой optima, или вы обычно ожидаете, что пользователь библиотеки решит такие проблемы перед использованием библиотеки? - person ; 30.07.2013
comment
В данном случае я считаю, что это ошибка в optima. Мое эмпирическое правило заключается в том, что если я сам не использую MOP, мне не следует беспокоиться о внутренностях MOP. - person dmitry_vk; 30.07.2013