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