|
| 1 | +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -*- Mode: Lisp -*- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 2 | +;; File - abstract-classes.lisp |
| 3 | +;; Description - Abstract classes in CL |
| 4 | +;; Author - Tim Bradshaw (tfb at lostwithiel) |
| 5 | +;; Created On - Sun Dec 10 18:21:40 2000 |
| 6 | +;; Last Modified On - Sun Jan 17 16:49:17 2021 |
| 7 | +;; Last Modified By - Tim Bradshaw (tfb at kingston.fritz.box) |
| 8 | +;; Update Count - 17 |
| 9 | +;; Status - Unknown |
| 10 | +;; |
| 11 | +;; $Id$ |
| 12 | +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 13 | + |
| 14 | +;;;; Abstract classes |
| 15 | +;;; |
| 16 | +;;; abstract-classes.lisp is copyright 2000-2001, 2021 by me, Tim |
| 17 | +;;; Bradshaw, and may be used for any purpose whatsoever by anyone. It |
| 18 | +;;; has no warranty whatsoever. I would appreciate acknowledgement if |
| 19 | +;;; you use it in anger, and I would also very much appreciate any |
| 20 | +;;; feedback or bug fixes. |
| 21 | +;;; |
| 22 | + |
| 23 | +;;; Get Closer to MOP if this is being loaded as a module: this is |
| 24 | +;;; needed at runtime as well as compile time so a feature expression |
| 25 | +;;; won't work. |
| 26 | +;;; |
| 27 | +#-Lispworks |
| 28 | +(eval-when (:compile-toplevel :load-toplevel :execute) |
| 29 | + (unless (find-package :closer-common-lisp) |
| 30 | + #+quicklisp |
| 31 | + (ql:quickload "closer-mop") |
| 32 | + #-quicklisp |
| 33 | + (error "abstract-classes needs Closer to MOP"))) |
| 34 | + |
| 35 | +(defpackage :org.tfeb.hax.abstract-classes |
| 36 | + (:nicknames :org.tfeb.hax.final-classes) |
| 37 | + #-LispWorks |
| 38 | + (:use :closer-common-lisp) |
| 39 | + #+LispWorks |
| 40 | + (:use :cl :hcl) |
| 41 | + (:export #:abstract-class |
| 42 | + #:define-abstract-class |
| 43 | + #:final-class |
| 44 | + #:define-final-class)) |
| 45 | + |
| 46 | +(in-package :org.tfeb.hax.abstract-classes) |
| 47 | + |
| 48 | +(provide :org.tfeb.hax.abstract-classes) |
| 49 | +(provide :org.tfeb.hax.final-classes) |
| 50 | + |
| 51 | +(eval-when (:compile-toplevel :load-toplevel :execute) |
| 52 | + (unless (eq 'standard-class 'cl:standard-class) |
| 53 | + (warn "STANDARD-CLASS isn't: you're probably doomed"))) |
| 54 | + |
| 55 | +(defclass abstract-class (standard-class) |
| 56 | + () |
| 57 | + (:documentation "The class of abstract classes")) |
| 58 | + |
| 59 | +(defmethod make-instance ((c abstract-class) &rest junk) |
| 60 | + (declare (ignore junk)) |
| 61 | + (error "Trying to make an instance of ~A which is an abstract class" |
| 62 | + (class-name c))) |
| 63 | + |
| 64 | +;;; The MOP requires this, but it's not clear that implementations do. |
| 65 | +;;; VALIDATE-SUPERCLASS specifies when a superclass is suitable for a |
| 66 | +;;; subclass. You have to be pretty specific, It's probably not in |
| 67 | +;;; general safe to do what we do here. |
| 68 | +;;; |
| 69 | + |
| 70 | +(defmethod validate-superclass ((class abstract-class) |
| 71 | + (superclass standard-class)) |
| 72 | + ;; This is, in general, somewhat too permissive, but we are going to |
| 73 | + ;; allow any instance of (a subclass of) STANDARD-CLASS to act as a |
| 74 | + ;; superclass of any instance of ABSTRACT-CLASS... |
| 75 | + t) |
| 76 | + |
| 77 | +(defmethod validate-superclass ((class standard-class) |
| 78 | + (superclass abstract-class)) |
| 79 | + ;; ... and the other way around. |
| 80 | + t) |
| 81 | + |
| 82 | + |
| 83 | +;;; I don't want to have to say ... (:metaclass abstract-class), but |
| 84 | +;;; there is no easy hook into processing the options to DEFCLASS: |
| 85 | +;;; ENSURE-CLASS-USING-CLASS, which would be the logical place to do |
| 86 | +;;; this, is called with a class of NIL if there is no existing class, |
| 87 | +;;; and so can't usefully be specialized. |
| 88 | +;;; |
| 89 | + |
| 90 | +(defmacro define-abstract-class (class supers slots &rest options) |
| 91 | + (when (assoc ':metaclass options) |
| 92 | + (error "Defining an abstract class with a metaclass?")) |
| 93 | + `(defclass ,class ,supers ,slots |
| 94 | + ,@options |
| 95 | + (:metaclass abstract-class))) |
| 96 | + |
| 97 | +;;; Samples of abstract classes |
| 98 | +#|| |
| 99 | +(define-abstract-class abstract-thing () |
| 100 | + ((s :accessor thing-s))) |
| 101 | +
|
| 102 | +(defclass thing (abstract-thing) |
| 103 | + ((s :initform 1))) |
| 104 | +||# |
| 105 | + |
| 106 | +;;; Benchmarks: for ACL 6.0 there is no performance hit. |
| 107 | +#|| |
| 108 | +(define-abstract-class ac () ()) |
| 109 | +(defclass ac-instantiable (ac) ()) |
| 110 | +(defclass nac () ()) |
| 111 | +(defclass nac-instantiable (nac) ()) |
| 112 | +
|
| 113 | +(defun make-n-aci (n) |
| 114 | + (declare (type fixnum n) |
| 115 | + (optimize speed)) |
| 116 | + (loop repeat n |
| 117 | + do (make-instance 'ac-instantiable))) |
| 118 | +
|
| 119 | +(defun make-n-naci (n) |
| 120 | + (declare (type fixnum n) |
| 121 | + (optimize speed)) |
| 122 | + (loop repeat n |
| 123 | + do (make-instance 'nac-instantiable))) |
| 124 | +
|
| 125 | +(defun make-n-general (n cn) |
| 126 | + (declare (type fixnum n) |
| 127 | + (optimize speed)) |
| 128 | + (loop repeat n |
| 129 | + do (make-instance cn))) |
| 130 | +||# |
| 131 | + |
| 132 | + |
| 133 | +;;;; Final classes |
| 134 | +;;; |
| 135 | +;;; Classes which may not be subclassed. |
| 136 | +;;; |
| 137 | +;;; I just know someone is going to ask for an abstract final class. |
| 138 | + |
| 139 | +(defclass final-class (standard-class) |
| 140 | + () |
| 141 | + (:documentation "The class of classes which may not be subclassed")) |
| 142 | + |
| 143 | +;;; The MOP requires this, but it's not clear that implementations do. |
| 144 | +;;; VALIDATE-SUPERCLASS specifies when a superclass is suitable for a |
| 145 | +;;; subclass. You have to be pretty specific, It's probably not in |
| 146 | +;;; general safe to do what we do here. |
| 147 | +;;; |
| 148 | + |
| 149 | +(defmethod validate-superclass ((class final-class) |
| 150 | + (superclass standard-class)) |
| 151 | + ;; This is, in general, somewhat too permissive, but we are going to |
| 152 | + ;; allow any instance of (a subclass of) STANDARD-CLASS to act as a |
| 153 | + ;; superclass of any instance of ABSTRACT-CLASS... |
| 154 | + t) |
| 155 | + |
| 156 | +(defmethod validate-superclass ((class standard-class) |
| 157 | + (superclass final-class)) |
| 158 | + (error "Attempting to subclass a final class")) |
| 159 | + |
| 160 | + |
| 161 | +;;; I don't want to have to say ... (:metaclass final-class), but |
| 162 | +;;; there is no easy hook into processing the options to DEFCLASS: |
| 163 | +;;; ENSURE-CLASS-USING-CLASS, which would be the logical place to do |
| 164 | +;;; this, is called with a class of NIL if there is no existing class, |
| 165 | +;;; and so can't usefully be specialized. |
| 166 | +;;; |
| 167 | + |
| 168 | +(defmacro define-final-class (class supers slots &rest options) |
| 169 | + (when (assoc ':metaclass options) |
| 170 | + (error "Defining a final class with a metaclass?")) |
| 171 | + `(defclass ,class ,supers ,slots |
| 172 | + ,@options |
| 173 | + (:metaclass final-class))) |
0 commit comments