libtrbase  1.0.2
Web server and task management solution.
class.h
Go to the documentation of this file.
1 /**
2  * \file
3  * My own class implementation for C. It combines a data structure with
4  * a set of interfaces. Each interface is another structure containing
5  * one or more function pointers to concrete implementations of this
6  * interface for the defined class.
7  *
8  * To each interface a set of caller functions exist, that take an instance
9  * of an object and then in turn call the implementation for the class of
10  * this object. If there is none within the class it looks into its
11  * parent class and so forth.
12  *
13  * This is somewhat similar to late binding in real OOP languages, but
14  * by far not so elaborated. This is not a real object oriented language
15  * and will surely never ever provide all features these have.
16  *
17  * That said it has proven very usefull for me to orgnize code and prevent
18  * code duplication.
19  *
20  * \author Georg Hopp
21  *
22  * \copyright
23  * Copyright © 2014 Georg Hopp
24  *
25  * This program is free software: you can redistribute it and/or modify
26  * it under the terms of the GNU General Public License as published by
27  * the Free Software Foundation, either version 3 of the License, or
28  * (at your option) any later version.
29  *
30  * This program is distributed in the hope that it will be useful,
31  * but WITHOUT ANY WARRANTY; without even the implied warranty of
32  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33  * GNU General Public License for more details.
34  *
35  * You should have received a copy of the GNU General Public License
36  * along with this program. If not, see <http://www.gnu.org/licenses/>.
37  */
38 #ifndef __TR_CLASS_CLASS_H__
39 #define __TR_CLASS_CLASS_H__
40 
41 #include <stdarg.h>
42 #include <sys/types.h>
43 #include <string.h>
44 #include <assert.h>
45 
46 #include "tr/interface.h"
47 
48 /** \cond PRIVATE */
49 #ifndef _ISOC99_SOURCE
50 #define _ISOC99_SOURCE
51 #endif
52 /** \endcond */
53 
54 /**
55  * A magic number that identifies instances of a TR_CLASS class.
56  */
57 #define TR_CLASS_MAGIC 0xFEFE
58 
59 /**
60  * Class declaration macro. This one declares all structures
61  * that are needed to create a new class.
62  */
63 #define TR_CLASS(name) \
64  extern struct TR_class c_##name; \
65  struct c_##name; \
66  typedef struct c_##name * name; \
67  extern struct TR_class * const _##name; \
68  struct c_##name
69 
70 #define TR_INSTANCE_INIT(name) \
71  struct c_##name##_object { \
72  void * TR_class; \
73  struct c_##name data; \
74  }
75 
76 #define TR_CLASSVARS_DECL(name) struct c_##name##_vars
77 #define TR_CLASSVARS(name, class) ((struct c_##name##_vars *)(class)->vars)
78 #define TR_CLASSVARS_BY_NAME(name) \
79  ((struct c_##name##_vars *)(TR_CLASS_BY_NAME(name))->vars)
80 #define TR_CLASSVARS_STATIC(name) \
81  ((struct c_##name##_vars *)(TR_CLASS_BY_NAME_STATIC(name))->vars)
82 
83 #define TR_INHERIT_CLASSVARS(dest, src) \
84  memcpy(TR_CLASSVARS_BY_NAME(dest), \
85  TR_CLASSVARS_BY_NAME(src), \
86  sizeof(TR_CLASSVARS_DECL(src)))
87 
88 
89 /**
90  * Make the new class a child of an existing class.
91  * This is used within the class declaration and can
92  * only be used once. If you do it twice the behaviour
93  * is undefined, but most likely the resulting code won't
94  * even compile.
95  */
96 #define TR_EXTENDS(parent) const char _[sizeof(struct c_##parent)]
97 #define TR_CV_EXTENDS(parent) struct c_##parent##_vars _
98 
99 /**
100  * Some macros might translate a give NULL to _NULL,
101  * this should in turn be a real NULL again.
102  */
103 #define _NULL NULL
104 
105 /**
106  * This will create a new class which previously has
107  * to be defined by TR_CLASS.
108  * This makro will create static variables and functions
109  * used to manage and controll the new class (and its
110  * instances.)
111  * Especially it creates and initializes tha class
112  * structure for this class. This structure contains all
113  * the meta informations of the new class. These are
114  * it's members as well as its interface implementations.
115  * Each class must at least provide an implementation
116  * for the ctor interface else no instances can be
117  * created.
118  */
119 #define TR_CREATE_CLASS(name,_parent,cvInit,...) \
120  struct c_##name##_vars _##name##_vars; \
121  void (* TR_initClassVars##name)(TR_class_ptr) = cvInit; \
122  static TR_class_ptr _classInit##name##_(void) { \
123  c_##name.init = NULL; \
124  c_##name.parent = _##_parent; \
125  if (TR_initClassVars##name) \
126  TR_initClassVars##name(_##name); \
127  return &c_##name; \
128  }; \
129  struct TR_class c_##name = { \
130  TR_CLASS_MAGIC, \
131  NULL, \
132  sizeof(struct c_##name), \
133  _classInit##name##_, \
134  &_##name##_vars, \
135  TR_INIT_IFACE_IMPL(__VA_ARGS__) \
136  }; \
137  struct TR_class * const _##name = &c_##name; \
138  struct c_##name##_vars _##name##_vars
139 
140 /**
141  * Create a static instance of a class.
142  * This is new and currently it is only possible to create
143  * instances for class that does not need a constructor.
144  *
145  * This does not call any constructor with any value...this
146  * has to be fixed. // When this macro is used within a global
147  * (static) context this can't be fixed. No function can be
148  * called from there. Instead I add a TR_objectInit function which
149  * takes an object name (created on either the stack or the
150  * heap) and a variable argument list and calls its constructor
151  * with the arguments.
152  */
153 #define TR_INSTANCE(class, name, ...) \
154  struct c_##class##_object _##name; \
155  class name = &(_##name.data); \
156  struct c_##class##_object _##name = { \
157  &c_##class, \
158  { __VA_ARGS__ } \
159  }
160 
161 #define TR_INSTANCE_CAST(class, name) ((class)&(_##name.data))
162 
163 /**
164  * I initialize _ (the class's base or parent class)
165  * with the identifier of that class.
166  * This identifier is not static and thus can't be used
167  * for a static initialization. As a workaround, until I
168  * find a better solution I create an initialization function
169  * with each class, that will be called once as soon as
170  * an interface implementation will be called. In any case
171  * this is a call to the constructor of this class.
172  * Well, not in any...if I remember correct the static
173  * instance does not call anything at all...
174  *
175  * \cond PRIVATE
176  */
177 #define TR_INIT_CLASS(class) \
178  ((class)->init? (class)->init() : (class))
179 /** \endcond */
180 
181 /**
182  * Returns the pointer to the class structure of the given object.
183  * The class structure is the structure that contains all the
184  * metainformations about our class.
185  *
186  * \see TR_CREATE_CLASS
187  */
188 #define TR_GET_CLASS(object) \
189  (TR_INIT_CLASS(*(TR_class_ptr *)((void*)(object) - sizeof(void*))))
190 
191 #define TR_CLASS_BY_NAME(class_name) (TR_INIT_CLASS(& c_##class_name))
192 #define TR_CLASS_BY_NAME_STATIC(class_name) (& c_##class_name)
193 
194 /**
195  * Returns this class's implementations of the interface
196  * identified by iface.
197  */
198 #define TR_IFACE_GET(class,iface) \
199  (TR_interfaceGet(&((class)->impl),(iface)))
200 
201 /**
202  * Check if the given class is a child of any base class.
203  * That is it has an TR_EXTENDS expression whithin its declaration.
204  *
205  * \see TR_EXTENDS
206  */
207 #define TR_HAS_PARENT(class) \
208  (NULL != ((class)->parent) && TR_INIT_CLASS((class)->parent))
209 
210 /**
211  * Check if obj really points to a class instance.
212  *
213  * /see TR_CLASS_MAGIC
214  */
215 #define TR_IS_OBJECT(obj) \
216  ((TR_GET_CLASS((obj)))->magic == TR_CLASS_MAGIC)
217 
218 /**
219  * Check of obj points to an instance of class.
220  */
221 #define TR_INSTANCE_OF(class,obj) \
222  ((TR_GET_CLASS((obj))) == _##class)
223 
224 /**
225  * Call the class's implementation for the given method of the
226  * given interface _iface.
227  * For this the makro searches through all interfaces defined within
228  * a class for the correct one...from a performance aspect this might
229  * not be ideal but on the other hand...we don't expect more than a
230  * hand full of interfaces per class.
231  *
232  * \cond PRIVATE
233  */
234 #define _TR_CALL(_class,_iface,method) \
235  do { \
236  TR_class_ptr class = _class; \
237  iface = (struct i_##_iface *)TR_IFACE_GET(class, &i_##_iface); \
238  while ((NULL == iface || \
239  NULL == iface->method) && TR_HAS_PARENT(class)) { \
240  class = class->parent; \
241  iface = (struct i_##_iface *)TR_IFACE_GET(class, &i_##_iface); \
242  } \
243  assert(NULL != iface->method); \
244  } while(0)
245 /** \endcond */
246 
247 /**
248  * Call the class's implementation for the given method of the
249  * given interface _iface. This one does not handle a return
250  * value of the called implementation and as such is only suitable
251  * if there is no return value or you are not interested in it.
252  *
253  * \todo actually i use gcc feature ## for variadoc... think about
254  * a way to make this standard.
255  */
256 #define TR_CALL(object,_iface,method,...) \
257  do { \
258  struct i_##_iface * iface; \
259  _TR_CALL(TR_GET_CLASS(object), _iface, method); \
260  iface->method(object, ##__VA_ARGS__); \
261  } while(0)
262 
263 /**
264  * Call the class's implementation for the given method of the
265  * given interface _iface. The return value of the function is assigned
266  * to ret.
267  *
268  * \see TR_CALL
269  */
270 #define TR_RETCALL(object,_iface,method,ret,...) \
271  do { \
272  struct i_##_iface * iface; \
273  _TR_CALL(TR_GET_CLASS(object), _iface, method); \
274  ret = iface->method(object, ##__VA_ARGS__); \
275  } while(0)
276 
277 /**
278  * Like call but this calls the implementation of the direct parent
279  * class of this object.
280  *
281  * \see TR_CALL
282  */
283 #define TR_PARENTCALL(class, object,_iface,method,...) \
284  do { \
285  struct i_##_iface * iface; \
286  TR_class_ptr pc_class = TR_CLASS_BY_NAME(class); \
287  assert(TR_HAS_PARENT(pc_class)); \
288  _TR_CALL(pc_class->parent, _iface, method); \
289  iface->method(object, ##__VA_ARGS__); \
290  } while(0)
291 
292 /*
293  * Like retcall but this calls the implementation of the direct parent
294  * class of this object.
295  *
296  * \see TR_RETCALL
297  */
298 #define TR_PARENTRETCALL(class, object,_iface,method,ret,...) \
299  do { \
300  struct i_##_iface * iface; \
301  TR_class_ptr pc_class = TR_CLASS_BY_NAME(class); \
302  assert(TR_HAS_PARENT(pc_class)); \
303  _TR_CALL(pc_class->parent, _iface, method); \
304  ret = iface->method(object, ##__VA_ARGS__); \
305  } while(0)
306 
307 
308 /**
309  * Definition of the metadata structures and symbols used to
310  * manage classe.
311  *
312  * \cond PRIVATE
313  */
314 struct TR_class;
315 typedef struct TR_class * TR_class_ptr;
316 typedef TR_class_ptr (* TR_fptr_classInit)(void);
317 struct TR_class {
318  const int magic;
319  TR_class_ptr parent;
320  size_t object_size;
321  TR_fptr_classInit init;
322  void * vars;
323  struct TR_iface_impl impl;
324 };
325 /** \endcond */
326 
327 #endif // __TR_CLASS_CLASS_H__
328 
329 // vim: set ts=4 sw=4: