1f6ef2e0fb45d7740eb84604daa8a30452356fe2
[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 Sensble Object Design, an object system for C.
11 *
12 * SOD is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * SOD 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 General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with SOD; if not, write to the Free Software Foundation,
24 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26
27 /*----- Header files ------------------------------------------------------*/
28
29 #include "sod.h"
30
31 /*----- Main code ---------------------------------------------------------*/
32
33 /* --- @find_chain@ --- *
34 *
35 * Arguments: @const SodClass *sub, *super@ = pointers to two classes
36 *
37 * Returns: If @sub@ is a subclass of @super@, then a pointer to @sub@'s
38 * chain entry describing @super@'s chain; otherwise null.
39 */
40
41 static const struct sod_chain *find_chain(const SodClass *sub,
42 const SodClass *super)
43 {
44 const SodClass *head = super->cls.head;
45 const struct sod_chain *chain, *limit;
46
47 /* Slightly fancy footwork. Each class carries a table describing its
48 * constituent chains, and each chain has a vector of the classes in that
49 * chain, with the head (least specific) first. Chains are always
50 * non-empty.
51 *
52 * Another useful bit of information in the class is its level, i.e., its
53 * index in its own chain vector. This is invariant because the chains are
54 * linear.
55 *
56 * This suggests the following algorithm. Search @sub@'s chains for one
57 * headed by @super@'s chain head. If we find one, check that the chain's
58 * class vector is long enough, and look at the entry corresponding to
59 * @super@'s level. If it matches @super@ then @sub@ is indeed a subclass
60 * and we're done. Otherwise it isn't, and we lose. We also lose if no
61 * matching chain is found.
62 */
63 for (chain = sub->cls.chains, limit = chain + sub->cls.n_chains;
64 chain < limit; chain++) {
65 if (chain->classes[0] != head)
66 continue;
67 if (super->cls.level < chain->n_classes &&
68 chain->classes[super->cls.level] == super)
69 return (chain);
70 break;
71 }
72 return (0);
73 }
74
75 /* --- @sod_subclassp@ --- *
76 *
77 * Arguments: @const SodClass *sub, *super@ = pointers to two classes
78 *
79 * Returns: Nonzero if @sub@ is a subclass of @super@.
80 */
81
82 int sod_subclassp(const SodClass *sub, const SodClass *super)
83 { return (!!find_chain(sub, super)); }
84
85 /* --- @sod_convert@ --- *
86 *
87 * Arguments: @const SodClass *cls@ = desired class object
88 * @const void *obj@ = pointer to instance
89 *
90 * Returns: Pointer to appropriate ichain of object, or null if the
91 * instance isn't of the specified class.
92 *
93 * Use: General down/cross-casting function.
94 *
95 * Upcasts can be performed efficiently using the automatically
96 * generated macros. In particular, upcasts with a chain are
97 * trivial; cross-chain upcasts require information from vtables
98 * but are fairly fast. This function is rather slower, but is
99 * much more general.
100 *
101 * Suppose we have an instance of a class C, referred to by a
102 * pointer to an instance of one of C's superclasses S. If S'
103 * is some other superclass of C then this function will return
104 * a pointer to C suitable for use as an instance of S'. If S'
105 * is not a superclass of C, then the function returns null.
106 * (If the pointer doesn't point to an instance of some class
107 * then the behaviour is undefined.) Note that you don't need
108 * to know what C or S actually are.
109 */
110
111 void *sod_convert(const SodClass *cls, const void *obj)
112 {
113 const struct sod_instance *inst = obj;
114 const struct sod_vtable *vt = inst->_vt;
115 const SodClass *realcls = vt->_class;
116 const struct sod_chain *chain = find_chain(realcls, cls);
117
118 if (!chain) return (0);
119 return ((char *)obj - vt->_base + chain->off_ichain);
120 }
121
122 /*----- That's all, folks -------------------------------------------------*/