chiark / gitweb /
src/method-impl.lisp: Initialize `suppliedp' flags properly.
[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 -------------------------------------------------*/