j@252
|
1 (in-package #:amuse)
|
j@252
|
2
|
j@252
|
3 ;;;=====================================================================
|
j@252
|
4 ;;; Taken from: Strandh, R., Hamer, J., and Baumann, G., (2007). Using
|
j@252
|
5 ;;; Stealth Mixins to Achieve Modularity. In Proceedings of the 2007
|
j@252
|
6 ;;; Australian Software Engineering Conference (ASWEC'07).
|
j@252
|
7 ;;; =====================================================================
|
j@252
|
8
|
j@252
|
9 (defvar *stealth-mixins* (make-hash-table))
|
j@252
|
10
|
j@252
|
11 (defmacro class-stealth-mixins (class)
|
j@252
|
12 `(gethash ,class *stealth-mixins*))
|
j@252
|
13
|
j@252
|
14 (defmacro define-stealth-mixin (name super-classes victim-class
|
j@252
|
15 &rest for-defclass)
|
j@252
|
16 "Like DEFCLASS but adds the newly defined class to the super classes
|
j@252
|
17 of `victim−class'."
|
j@252
|
18 `(progn
|
j@252
|
19 ;; First define the class we talk about
|
j@252
|
20 (defclass ,name ,super-classes ,@for-defclass)
|
j@252
|
21 ;; Add the class to the mixins of the victim
|
j@252
|
22 (sb-mop:ensure-class
|
j@252
|
23 ',victim-class
|
j@252
|
24 :direct-superclasses (adjoin ',name
|
j@252
|
25 (and (find-class ',victim-class nil)
|
j@252
|
26 (sb-mop:class-direct-superclasses
|
j@252
|
27 (find-class ',victim-class)))
|
j@252
|
28 :test #'class-equalp))
|
j@252
|
29 ;; Register it as a new mixin for the victim class
|
j@252
|
30 (pushnew ',name (class-stealth-mixins ',victim-class))
|
j@252
|
31 ;; When one wants to [re] define the victim class the new mixin
|
j@252
|
32 ;; should be present too . We do this by `patching' ensure−class:
|
j@252
|
33 (defmethod sb-mop:ensure-class-using-class :around
|
j@252
|
34 (class (name (eql ',victim-class)) &rest arguments
|
j@252
|
35 &key (direct-superclasses nil direct-superclasses-p)
|
j@252
|
36 &allow-other-keys)
|
j@252
|
37 (cond (direct-superclasses-p
|
j@252
|
38 ;; Silently modify the super classes to include our new
|
j@252
|
39 ;; mixin.
|
j@252
|
40 (dolist (k (class-stealth-mixins name))
|
j@252
|
41 (pushnew k direct-superclasses
|
j@252
|
42 :test #'class-equalp))
|
j@252
|
43 (apply #'call-next-method class name
|
j@252
|
44 :direct-superclasses direct-superclasses
|
j@252
|
45 arguments))
|
j@252
|
46 (t
|
j@252
|
47 (call-next-method))))
|
j@252
|
48 ',name))
|
j@252
|
49
|
j@252
|
50 ;; The `direct−superclasses' argument to ensure−class is a list of
|
j@252
|
51 ;; either classes or their names. Since we want to avoid duplicates,
|
j@252
|
52 ;; we need an appropriate equivalence predicate:
|
j@252
|
53 (defun class-equalp (c1 c2)
|
j@252
|
54 (when (symbolp c1) (setf c1 (find-class c1)))
|
j@252
|
55 (when (symbolp c2) (setf c2 (find-class c2)))
|
j@252
|
56 (eq c1 c2))
|