chiark / gitweb /
bcaf6f69039eed16bd10ffccf9d4450e4637e57d
[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 *p)
112 {
113   const struct sod_instance *inst = p;
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)
119     return (0);
120   return ((char *)p - vt->_base + chain->off_ichain);
121 }
122
123 /*----- That's all, folks -------------------------------------------------*/