2016-10-04 19 views
4

Мне нужно создать класс во время выполнения, возможно, не прибегая к eval. Зная, что протокол метаклассом не полностью стандартизован в Common-Lisp, после просмотра через The Common Lisp Object System MetaObject Protocol, я попытался следующий код, чтобы создать класс, экземпляр его, и установить значение слота экземпляра на номер:Как портативно создать класс во время выполнения в Common-Lisp CLOS

(defparameter *my-class* 
    (make-instance 'standard-class 
       :name 'my-class 
       :direct-slots '((:name x :readers (get-x) :writers ((setf get-x)))))) 

(defparameter *my-instance* (make-instance *my-class*)) 

(setf (get-x *my-instance*) 42) ;; => 42 

к сожалению, этот код работает правильно SBCL, но не на БКК, где создание класса, кажется, работает, но создание экземпляра (make-instance *my-class*) вызывает следующее сообщение об ошибке:

There is no applicable method for the generic function: 
    #<STANDARD-GENERIC-FUNCTION INITIALIZE-INSTANCE #x30200002481F> 
when called with arguments: 
    (#<error printing CONS #x302001A9F6A3> 
    [Condition of type CCL:NO-APPLICABLE-METHOD-EXISTS] 

Я пытался смотреть на closer-mop пакет, который должен скрыть различия между различными реализациями мета -объект, но я не смог найти какую-либо функцию или класс, полезные для моей области.

Итак, вопрос заключается в следующем: существует ли способ создания класса и создания экземпляра его во время выполнения, используя непосредственно уровень метакласса CLOS?

+0

С более близкого швабра, вы могли бы попробовать [обеспечить класс] (http://mop.lisp.se/dictionary.html#ensure-class), или [обеспечить класс-с использованием класса] (http://mop.lisp.se/dictionary.html#ensure-class-using-class). – coredump

+1

CCL, похоже, работает, если вы также указываете прямые суперклассы: direct-superclasses (list (find-class 'standard-object)) – jkiiski

+0

Спасибо @jkiiski, проблема в том, что я должен был указать суперкласс. Если вы опубликуете его в качестве ответа, я его приму. – Renzo

ответ

3

CCL, похоже, требует, чтобы вы также вручную указали прямые суперклассы.

(defparameter *my-class* 
    (make-instance 'standard-class 
       :name 'my-class 
       :direct-slots '((:name x :readers (get-x) :writers ((setf get-x)))) 
       :direct-superclasses (list (find-class 'standard-object)))) 
3

Обычно можно было бы использовать ENSURE-CLASS создать класс. Назначение ENSURE-CLASS должно быть функциональным эквивалентом DEFCLASS. Минусы, специфичные для конкретной реализации, DEFCLASS - например, для поддержки функций среды разработки.

Вы можете использовать MAKE-INSTANCE, но, к примеру, он не зарегистрирует класс под его именем. Он также не будет называть никаких дополнительных методов ENSURE-CLASS-USING-CLASS.

Поскольку по умолчанию для метакласса является standard-class, CCL следует также вычислить значение по умолчанию для прямого суперкласса, что это не - к сожалению.

Я хотел бы надеяться, что сближающий штифт исправляет эти несовместимости, но я не проверял.

В CCL:

? (ensure-class 'my-class 
       :direct-slots '((:name x 
           :readers (get-x) 
           :writers ((setf get-x)))) 
       :direct-superclasses (list (find-class 'standard-object))) 
#<STANDARD-CLASS MY-CLASS> 
? (find-class 'my-class) 
#<STANDARD-CLASS MY-CLASS> 
? (let ((foo (make-instance 'my-class))) 
    (setf (get-x foo) 10) 
    (incf (get-x foo) 32) 
    (get-x foo)) 
42 

LispWorks на самом деле делает это правильно. Метаклас по умолчанию равен standard-class, а прямой суперкласс - standard-object.

CL-USER 25 > (clos:ensure-class 'foobar 
       :direct-slots '((:name x 
            :readers (get-x) 
            :writers ((setf get-x))))) 
#<STANDARD-CLASS FOOBAR 4020001713> 

CL-USER 26 > (class-direct-superclasses *) 
(#<STANDARD-CLASS STANDARD-OBJECT 40E018E313>) 
+0

Спасибо за ваш полный ответ! – Renzo