New feature: proper object lifecycle protocol; init and teardown fragments.
[sod] / src / class-make-impl.lisp
index aef6948..bd2407e 100644 (file)
 ;;;--------------------------------------------------------------------------
 ;;; Classes.
 
-(defun maximum (items order what)
-  "Return a maximum item according to the non-strict partial ORDER."
-  (reduce (lambda (best this)
-           (cond ((funcall order best this) best)
-                 ((funcall order this best) this)
-                 (t (error "Unable to choose best ~A." what))))
-         items))
-
-(defmethod guess-metaclass ((class sod-class))
-  "Default metaclass-guessing function for classes.
-
-   Return the most specific metaclass of any of the CLASS's direct
-   superclasses."
-  (maximum (mapcar #'sod-class-metaclass
-                  (sod-class-direct-superclasses class))
-          #'sod-subclass-p
-          (format nil "metaclass for `~A'" class)))
-
 (defmethod shared-initialize :after ((class sod-class) slot-names &key pset)
   "Specific behaviour for SOD class initialization.
 
       (pset :nick :id)
     (string-downcase (slot-value class 'name)))
 
-  ;; If no metaclass, guess one in a (Lisp) class-specific way.
+  ;; Set the metaclass if the appropriate property has been provided;
+  ;; otherwise leave it unbound for now, and we'll sort out the mess during
+  ;; finalization.
   (default-slot-from-property (class 'metaclass slot-names)
-      (pset :metaclass :id meta (find-sod-class meta))
-    (and (sod-class-direct-superclasses class)
-        (guess-metaclass class)))
+      (pset :metaclass :id meta (find-sod-class meta)))
 
   ;; If no chain-link, then start a new chain here.
   (default-slot-from-property (class 'chain-link slot-names)
 ;;; Slot initializers.
 
 (defmethod make-sod-instance-initializer
-    ((class sod-class) nick name value-kind value-form pset
-     &optional location)
+    ((class sod-class) nick name value pset &optional location)
   (with-default-error-location (location)
     (let* ((slot (find-instance-slot-by-name class nick name))
-          (initializer (make-sod-initializer-using-slot
-                        class slot 'sod-instance-initializer
-                        value-kind value-form pset
-                        (file-location location))))
+          (initializer (and value
+                            (make-sod-initializer-using-slot
+                             class slot 'sod-instance-initializer
+                             value pset (file-location location)))))
       (with-slots (instance-initializers) class
+
        (setf instance-initializers
              (append instance-initializers (list initializer))))
       initializer)))
 
 (defmethod make-sod-class-initializer
-    ((class sod-class) nick name value-kind value-form pset
-     &optional location)
+    ((class sod-class) nick name value pset &optional location)
   (with-default-error-location (location)
     (let* ((slot (find-class-slot-by-name class nick name))
           (initializer (make-sod-initializer-using-slot
                         class slot 'sod-class-initializer
-                        value-kind value-form pset
-                        (file-location location))))
+                        value pset (file-location location))))
       (with-slots (class-initializers) class
        (setf class-initializers
              (append class-initializers (list initializer))))
       initializer)))
 
 (defmethod make-sod-initializer-using-slot
-    ((class sod-class) (slot sod-slot)
-     init-class value-kind value-form pset location)
+    ((class sod-class) (slot sod-slot) init-class value pset location)
   (make-instance (get-property pset :initializer-class :symbol init-class)
                 :class class
                 :slot slot
-                :value-kind value-kind
-                :value-form value-form
+                :value value
                 :location (file-location location)
                 :pset pset))
 
   nil)
 
 ;;;--------------------------------------------------------------------------
+;;; Initialization and teardown fragments.
+
+(defmethod make-sod-class-initfrag
+    ((class sod-class) frag pset &optional location)
+  (declare (ignore pset location))
+  (with-slots (initfrags) class
+    (setf initfrags (append initfrags (list frag)))))
+
+(defmethod make-sod-class-tearfrag
+    ((class sod-class) frag pset &optional location)
+  (declare (ignore pset location))
+  (with-slots (tearfrags) class
+    (setf tearfrags (append tearfrags (list frag)))))
+
+;;;--------------------------------------------------------------------------
 ;;; Messages.
 
 (defmethod make-sod-message
     ((method sod-method) (message sod-message) (type c-type))
   (error "Methods must have function type, not ~A" type))
 
+(export 'check-method-return-type)
+(defun check-method-return-type (method-type wanted-type)
+  "Signal an error unless METHOD-TYPE does not return the WANTED-TYPE."
+  (let ((method-returns (c-type-subtype method-type)))
+    (unless (c-type-equal-p method-returns wanted-type)
+      (error "Method return type ~A should be ~A"
+            method-returns wanted-type))))
+
+(export 'check-method-return-type-against-message)
+(defun check-method-return-type-against-message (method-type message-type)
+  "Signal an error unless METHOD-TYPE and MESSAGE-TYPE return the same type."
+  (let ((message-returns (c-type-subtype message-type))
+       (method-returns (c-type-subtype method-type)))
+    (unless (c-type-equal-p message-returns method-returns)
+      (error "Method return type ~A doesn't match message ~A"
+            method-returns message-returns))))
+
+(export 'check-method-argument-lists)
+(defun check-method-argument-lists (method-type message-type)
+  "Signal an error unless METHOD-TYPE and MESSAGE-TYPE have matching argument
+   lists.
+
+  This checks that (a) the two types have matching lists of mandatory
+  arguments, and (b) that either both or neither types accept keyword
+  arguments."
+  (let ((message-keywords-p (typep message-type 'c-keyword-function-type))
+       (method-keywords-p (typep method-type 'c-keyword-function-type)))
+    (cond (message-keywords-p
+          (unless method-keywords-p
+            (error "Method must declare a keyword argument list")))
+         (method-keywords-p
+          (error "Method must not declare a keyword argument list"))))
+  (unless (argument-lists-compatible-p (c-function-arguments message-type)
+                                      (c-function-arguments method-type))
+    (error "Method arguments ~A don't match message ~A"
+          method-type message-type)))
+
 (defmethod check-method-type
     ((method sod-method) (message sod-message) (type c-function-type))
   (with-slots ((msgtype %type)) message
-    (unless (c-type-equal-p (c-type-subtype msgtype)
-                           (c-type-subtype type))
-      (error "Method return type ~A doesn't match message ~A"
-             (c-type-subtype msgtype) (c-type-subtype type)))
-    (unless (argument-lists-compatible-p (c-function-arguments msgtype)
-                                        (c-function-arguments type))
-      (error "Method arguments ~A don't match message ~A" type msgtype))))
+    (check-method-return-type-against-message type msgtype)
+    (check-method-argument-lists type msgtype)))
 
 ;;;----- That's all, folks --------------------------------------------------