3 * Runtime support for the Sensible Object Design
5 * (c) 2009 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the Sensible Object Design, an object system for C.
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.
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.
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,
28 /*----- Header files ------------------------------------------------------*/
32 /*----- Main code ---------------------------------------------------------*/
34 /* --- @find_chain@ --- *
36 * Arguments: @const SodClass *sub, *super@ = pointers to two classes
38 * Returns: If @sub@ is a subclass of @super@, then a pointer to @sub@'s
39 * chain entry describing @super@'s chain; otherwise null.
42 static const struct sod_chain *find_chain(const SodClass *sub,
43 const SodClass *super)
45 const SodClass *head = super->cls.head;
46 const struct sod_chain *chain, *limit;
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
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
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.
64 for (chain = sub->cls.chains, limit = chain + sub->cls.n_chains;
65 chain < limit; chain++) {
66 if (chain->classes[0] != head)
68 if (super->cls.level < chain->n_classes &&
69 chain->classes[super->cls.level] == super)
76 /* --- @sod_subclassp@ --- *
78 * Arguments: @const SodClass *sub, *super@ = pointers to two classes
80 * Returns: Nonzero if @sub@ is a subclass of @super@.
83 int sod_subclassp(const SodClass *sub, const SodClass *super)
84 { return (!!find_chain(sub, super)); }
86 /* --- @sod_convert@ --- *
88 * Arguments: @const SodClass *cls@ = desired class object
89 * @const void *obj@ = pointer to instance
91 * Returns: Pointer to appropriate ichain of object, or null if the
92 * instance isn't of the specified class.
94 * Use: General down/cross-casting function.
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
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.
112 void *sod_convert(const SodClass *cls, const void *obj)
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);
119 if (!chain) return (0);
120 return ((char *)obj - vt->_base + chain->off_ichain);
123 /* --- @sod_init@, @sod_initv@ --- *
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
129 * Returns: Pointer to the initialized instance.
131 * Use: Initializes an instance in pre-allocated storage, and returns
134 * This function will imprint the storage, and then send an
135 * `initialize' message to the fresh instance containing the
136 * provided keyword arguments.
138 * It's usually convenient to use the macro @SOD_INIT@ rather
139 * than calling @sod_init@ directly.
142 void *sod_init(const SodClass *cls, void *p, ...)
147 sod_initv(cls, p, ap);
152 void *sod_initv(const SodClass *cls, void *p, va_list ap)
157 obj = SOD_CONVERT(SodObject, p);
158 SodObject_init__v(obj, ap);
162 /*----- That's all, folks -------------------------------------------------*/