src/method-{proto,impl}.lisp: Abstract out the receiver type.
[sod] / lib / sod.c
1 /* -*-c-*-
2 *
3 * Runtime support for the Sensible Object Design
4 *
5 * (c) 2009 Straylight/Edgeware
6 */
7
8 /*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of the Sensible Object Design, an object system for C.
11 *
12 * The SOD Runtime Library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Library General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) any later version.
16 *
17 * The SOD Runtime is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with SOD; if not, write to the Free
24 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25 * MA 02111-1307, USA.
26 */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 #include "sod.h"
31
32 /*----- Main code ---------------------------------------------------------*/
33
34 /* --- @find_chain@ --- *
35 *
36 * Arguments: @const SodClass *sub, *super@ = pointers to two classes
37 *
38 * Returns: If @sub@ is a subclass of @super@, then a pointer to @sub@'s
39 * chain entry describing @super@'s chain; otherwise null.
40 */
41
42 static const struct sod_chain *find_chain(const SodClass *sub,
43 const SodClass *super)
44 {
45 const SodClass *head = super->cls.head;
46 const struct sod_chain *chain, *limit;
47
48 /* Slightly fancy footwork. Each class carries a table describing its
49 * constituent chains, and each chain has a vector of the classes in that
50 * chain, with the head (least specific) first. Chains are always
51 * non-empty.
52 *
53 * Another useful bit of information in the class is its level, i.e., its
54 * index in its own chain vector. This is invariant because the chains are
55 * linear.
56 *
57 * This suggests the following algorithm. Search @sub@'s chains for one
58 * headed by @super@'s chain head. If we find one, check that the chain's
59 * class vector is long enough, and look at the entry corresponding to
60 * @super@'s level. If it matches @super@ then @sub@ is indeed a subclass
61 * and we're done. Otherwise it isn't, and we lose. We also lose if no
62 * matching chain is found.
63 */
64 for (chain = sub->cls.chains, limit = chain + sub->cls.n_chains;
65 chain < limit; chain++) {
66 if (chain->classes[0] != head)
67 continue;
68 if (super->cls.level < chain->n_classes &&
69 chain->classes[super->cls.level] == super)
70 return (chain);
71 break;
72 }
73 return (0);
74 }
75
76 /* --- @sod_subclassp@ --- *
77 *
78 * Arguments: @const SodClass *sub, *super@ = pointers to two classes
79 *
80 * Returns: Nonzero if @sub@ is a subclass of @super@.
81 */
82
83 int sod_subclassp(const SodClass *sub, const SodClass *super)
84 { return (!!find_chain(sub, super)); }
85
86 /* --- @sod_convert@ --- *
87 *
88 * Arguments: @const SodClass *cls@ = desired class object
89 * @const void *obj@ = pointer to instance
90 *
91 * Returns: Pointer to appropriate ichain of object, or null if the
92 * instance isn't of the specified class.
93 *
94 * Use: General down/cross-casting function.
95 *
96 * Upcasts can be performed efficiently using the automatically
97 * generated macros. In particular, upcasts with a chain are
98 * trivial; cross-chain upcasts require information from vtables
99 * but are fairly fast. This function is rather slower, but is
100 * much more general.
101 *
102 * Suppose we have an instance of a class C, referred to by a
103 * pointer to an instance of one of C's superclasses S. If S'
104 * is some other superclass of C then this function will return
105 * a pointer to C suitable for use as an instance of S'. If S'
106 * is not a superclass of C, then the function returns null.
107 * (If the pointer doesn't point to an instance of some class
108 * then the behaviour is undefined.) Note that you don't need
109 * to know what C or S actually are.
110 */
111
112 void *sod_convert(const SodClass *cls, const void *obj)
113 {
114 const struct sod_instance *inst = obj;
115 const struct sod_vtable *vt = inst->_vt;
116 const SodClass *realcls = vt->_class;
117 const struct sod_chain *chain = find_chain(realcls, cls);
118
119 if (!chain) return (0);
120 return ((char *)obj - vt->_base + chain->off_ichain);
121 }
122
123 /* --- @sod_init@, @sod_initv@ --- *
124 *
125 * Arguments: @const SodClass *cls@ = class object for new instance
126 * @void *p@ = pointer to storage for new instance
127 * @va_list ap, ...@ = initialization keyword arguments
128 *
129 * Returns: Pointer to the initialized instance.
130 *
131 * Use: Initializes an instance in pre-allocated storage, and returns
132 * a pointer to it.
133 *
134 * This function will imprint the storage, and then send an
135 * `initialize' message to the fresh instance containing the
136 * provided keyword arguments.
137 *
138 * It's usually convenient to use the macro @SOD_INIT@ rather
139 * than calling @sod_init@ directly.
140 */
141
142 void *sod_init(const SodClass *cls, void *p, ...)
143 {
144 va_list ap;
145
146 va_start(ap, p);
147 sod_initv(cls, p, ap);
148 va_end(ap);
149 return (p);
150 }
151
152 void *sod_initv(const SodClass *cls, void *p, va_list ap)
153 {
154 SodObject *obj;
155
156 cls->cls.imprint(p);
157 obj = SOD_CONVERT(SodObject, p);
158 SodObject_init__v(obj, ap);
159 return (p);
160 }
161
162 /* --- @sod_teardown@ --- *
163 *
164 * Arguments: @void *p@ = pointer to an instance to be torn down
165 *
166 * Returns: Zero if the object is torn down; nonzero if it refused for
167 * some reason.
168 *
169 * Use: Invokes the instance's `teardown' method to release any held
170 * resources.
171 *
172 * If this function returns nonzero, then the object is still
173 * active, and may still hold important resources. This is not
174 * intended to be a failure condition: failures in teardown are
175 * usually unrecoverable (or very hard to recover from) and
176 * should probably cause the program to abort. A refusal, on
177 * the other hand, means that the object is still live and
178 * shouldn't be deallocated, but that this is a normal situation
179 * and the caller shouldn't worry about it.
180 */
181
182 int sod_teardown(void *p)
183 {
184 SodObject *obj;
185
186 obj = SOD_CONVERT(SodObject, p);
187 return (SodObject_teardown(obj));
188 }
189
190 /*----- That's all, folks -------------------------------------------------*/