summaryrefslogtreecommitdiff
path: root/class.h
blob: 90e6c193035c30b17160a0a535f1779b95b17d0f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
/* C class and object types functions.
 *
 * Copyright 2013, Michael Cohen <sucdette@gmail.com>.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef __CLASS_H__
#define __CLASS_H__

/*
  Classes and objects in C

  This file makes it easy to implement classes and objects in C. To
  define a class we need to perform three steps:

  Define the class prototype. This is suitable to go in a .h file for
  general use by other code.

  Note all classes extend Object.

  Example::

CLASS(Foo, Object)
    int x;
    int y;

    //This declares a method of a class Foo, called Con returning a
    //Foo object. In other words it is a constructor.
    Foo METHOD(Foo, Con, int x, int y);
    int METHOD(Foo, add);

END_CLASS

Now we need to define some functions for the constructor and
methods. Note that the constuctor is using ALLOCATE_CLASS to allocate
space for the class structures. Callers may call with self==NULL to
force allocation of a new class. Note that we do not call the
constructor of our superclass implicitly here. (Calling the sperclass
constructor is optional, but ALLOCATE_CLASS is not.).

Foo Foo_Con(Foo self,int x,int y) {
  self->x = x;
  self->y = y;

  return self;
};

int Foo_add(Foo this) {
  return (this->x + this->y);
};

Now we need to define the Virtual function table - These are those
functions and attributes which are defined in this class (over its
superclass). Basically these are all those things in the class
definition above, with real function names binding them. (Note that by
convention we preceed the name of the method with the name of the
class):

VIRTUAL(Foo,Object)
   VMETHOD(Con) = Foo_Con;
   VMETHOD(add) = Foo_add;
END_VIRTUAL

We can use inheritance too:

CLASS(Bar, Foo)
   Bar METHOD(Bar, Con, char *something)
END_CLASS

Here Bar extends Foo and defines a new constructor with a different prototype:

VIRTUAL(Bar,Foo)
   VMETHOD(Con) = Bar_Con
END_VIRTUAL

If there is a function which expects a Foo, we will need to over ride
the Foo constructor in the Bar, so the function will not see the
difference between the Foo and Bar:

CLASS(Bar,Foo)
  int bar_attr;
END_CLASS

Foo Bar_Con(Foo self, int x, int y) {
...
}

VIRTUAL(Bar, Foo)
  VMETHOD(super.Con) = Bar_Con
END_VIRTUAL

Note that in this case we are over riding the Con method defined in
Foo while creating derived Bar classes. The notation in the VIRTUAL
table is to use super.Con, because Foo's Con method (the one we are
over riding), can be located by using super.Con inside a Bar object.

Imagine now that in Bar_Con we wish to use methods and attributes
defined in Bar. Since Bar_Con over rides Bar's base class (Foo) it
must have the prototype described above. Since self is of type Foo its
impossible to use self->bar_attr (There is no bar_attr in Foo - its in
Bar).

In this case, we need to make a type cast to convice C that self is
actually a Bar not a Foo:

Foo Bar_Con(Foo self, int x, int y) {
   Bar this = (Bar)self;

   this->bar_attr=1
};

This allows us to access bars attributes.

This is a general oddity with C style classes, which C++ and Java
hide. In C we must always know which class defines which method and
attribute and reference the right class's method. So for example if we
want to call a Bar's add method:

Bar a;

a->super.add()

because add is defined in Bar's super class (Foo). Constract this with
C++ or Java which hide where methods are defined and simply make all
methods appear like they were defined inside the derived class. This
takes a while to get used to but the compiler will ensure that the
references are correct - otherwise things will generally not compile
properly.

This difference can be used for good and bad. It is possible in C to
call the base class's version of the method at any time (despite the
fact it was over ridden).

For example:

CLASS(Derived, Foo)
      int METHOD(Derived, add);
END_CLASS

VIRTUAL(Derived, Foo)
   VMETHOD(add) = Derived_add
END_VIRTUAL

If d is a Derived object, we can call Foo's version like this:
d->super.add()

But Derived's version is accessed by:
d->add()

Sometimes a derived class may want to over ride the base class's
methods as well, in this case the VIRTUAL section should over ride
super.add as well.

*/
#ifdef __cplusplus
extern "C" {
#endif

#include "misc.h"

#include <talloc.h>

#define CLASS(class,super_class)                                        \
  typedef struct class ## _t *class;                                    \
  DLL_PUBLIC class alloc_ ## class(void); /* Allocates object memory */ \
  DLL_PUBLIC int class ## _init(Object self); /* Class initializer */   \
  DLL_PUBLIC extern struct class ## _t __ ## class; /* Public class template */ \
  struct class ## _t {                                                  \
  struct super_class ## _t super;  /* Superclass Fields we inherit */   \
  class   __class__;       /* Pointer to our own class */               \
  super_class  __super__;  /* Pointer to our superclass */

#define METHOD(cls, name, ... )		\
  (* name)(cls self, ## __VA_ARGS__ )

  // Class methods are attached to the class but are not called with
  // an instance. This is similar to the python class method or java
  // static methods.
#define CLASS_METHOD(name, ... )                \
  (*name)(__VA_ARGS__)

/* This is a convenience macro which may be used if x if really large */
#define CALL(x, method, ... )			\
  (x)->method((x), ## __VA_ARGS__)

#define END_CLASS };

/* This is used to set the classes up for use:
 *
 * class_init = checks the class template (__class) to see if it has
 * been allocated. otherwise allocates it in the global context.
 *
 * class_Alloc = Allocates new memory for an instance of the
 * class. This is a recursive function calling each super class in
 * turn and setting the currently over ridden defaults. So for eample
 * suppose this class (foo) derives from bar, we first fill the
 * template with bars methods, and attributes. Then we over write
 * those with foos methods and attributes.
 */
#define VIRTUAL(class,superclass)                                       \
  struct class ## _t __ ## class;                                       \
                                                                        \
  DLL_PUBLIC  class alloc_ ## class(void) {				\
    class result = talloc_memdup(NULL, &__## class, sizeof(__## class)); \
    return result;                                                      \
  };                                                                    \
                                                                        \
  DLL_PUBLIC int class ## _init(Object this) {                          \
  class self = (class)this;                                             \
  if(self->__super__) return 1;                                         \
  superclass ##_init(this);                                             \
  this->__class__ = (Object)&__ ## class;                               \
  self->__class__ = (class)&__ ## class;                               \
  this->__super__ = (Object)&__ ## superclass;                          \
  self->__super__ = (superclass)&__ ## superclass;                      \
  this->__size = sizeof(struct class ## _t);                            \
  this->__name__ = #class;

#define SET_DOCSTRING(string)			\
  ((Object)self)->__doc__ = string

#define END_VIRTUAL return 1; };

#define VMETHOD(method)				\
  (self)->method

#define VMETHOD_BASE(base, method)		\
  (((base)self)->method)

#define CLASS_ATTR(self, base, method)		\
  (((base)self)->method)

#define VATTR(attribute)			\
  (self)->attribute

#define NAMEOF(obj)				\
  ((Object)obj)->__name__

#define SIZEOF(obj)                             \
  ((Object)obj)->__size

#define DOCSTRING(obj)				\
  ((Object)obj)->__doc__

#define INIT_CLASS(class)                       \
  class ## _init((Object)&__ ## class)

/* This MACRO is used to construct a new Class using a constructor.
 *
 * This is done to try and hide the bare (unbound) method names in
 * order to prevent name space pollution. (Bare methods may be
 * defined as static within the implementation file). This macro
 * ensures that class structures are initialised properly before
 * calling their constructors.
 *
 * We require the following args:
 *  class - the type of class to make
 *  virt_class - The class where the method was defined
 *  constructors - The constructor method to use
 *  context - a talloc context to use.
 *
 *  Note that the class and virt_class do not have to be the same if
 *  the method was not defined in the current class. For example
 *  suppose Foo extends Bar, but method is defined in Bar but
 *  inherited in Foo:
 *
 *  CONSTRUCT(Foo, Bar, super.method, context)
 *
 *  virt_class is Bar because thats where method was defined.
 */

// The following only initialises the class if the __super__ element
// is NULL. This is fast as it wont call the initaliser unnecessaily

  // This requires the class initializers to have been called
  // previously. Therefore they are not exported.
#define CONSTRUCT(class, virt_class, constructor, context, ...) \
    (class)(((virt_class) (&__ ## class))->constructor( \
        (virt_class) _talloc_memdup( \
            context, &__ ## class, \
            sizeof(struct class ## _t), \
            __location__ "(" #class ")"), \
        ## __VA_ARGS__) )

/* _talloc_memdup version
#define CONSTRUCT_CREATE(class, virt_class, context) \
     (virt_class) _talloc_memdup(context, &__ ## class, sizeof(struct class ## _t), __location__ "(" #class ")")
*/

#define CONSTRUCT_CREATE(class, virt_class, context) \
     (virt_class) talloc_memdup(context, &__ ## class, sizeof(struct class ## _t))

#define CONSTRUCT_INITIALIZE(class, virt_class, constructor, object, ...) \
    (class)(((virt_class) (&__ ## class))->constructor(object, ## __VA_ARGS__))

/* This variant is useful when all we have is a class reference
 *   (GETCLASS(Foo)) or &__Foo
 */
#define CONSTRUCT_FROM_REFERENCE(class, constructor, context, ... )	\
  ( (class)->constructor(						\
                       (void *)_talloc_memdup(context, ((Object)class), ((Object)class)->__size,  __location__ "(" #class "." #constructor ")"), \
		      ## __VA_ARGS__) )

/* Finds the size of the class in x */
#define CLASS_SIZE(class)			\
  ((Object)class)->__size

typedef struct Object_t *Object;

struct Object_t {
  //A reference to a class instance - this is useful to be able to
  //tell which class an object really belongs to:
  Object __class__;

  //And its super class:
  Object __super__;

  char *__name__;

  /** Objects may have a doc string associated with them. */
  char *__doc__;

  //How large the class is:
  int __size;

  /* A pointer to an extension - An extension is some other arbitrary
     object which may be linked with this one.
  */
  void *extension;
};

#define SUPER(base, imp, method, ...)            \
  ((base)&__ ## imp)->method((base)self, ## __VA_ARGS__)

#define GETCLASS(class)				\
  (Object)&__ ## class

// Returns true if the obj belongs to the class
#define ISINSTANCE(obj,class)			\
  (((Object)obj)->__class__ == GETCLASS(class))

// This is a string comparison version of ISINSTANCE which works
// across different shared objects.
#define ISNAMEINSTANCE(obj, class)		\
  (obj && !strcmp(class, NAMEOF(obj)))

// We need to ensure that class was properly initialised:
#define ISSUBCLASS(obj,class)			\
  issubclass((Object)obj, (Object)&__ ## class)

#define CLASSOF(obj)				\
  ((Object)obj)->__class__

DLL_PUBLIC void Object_init(Object);

DLL_PUBLIC extern struct Object_t __Object;

/** Find out if obj is an instance of cls or a derived class.

    Use like this:

    if(issubclass(obj, (Object)&__FileLikeObject)) {
       ...
    };


    You can also do this in a faster way if you already know the class
    hierarchy (but it could break if the hierarchy changes):
    {
     Object cls = ((Object)obj)->__class__;

     if(cls == (Object)&__Image || \
        cls == (Object)&__FileLikeObject || \
        cls == (Object)&__AFFObject || ....) {
     ...
     };
    };
 */
int issubclass(Object obj, Object class);

DLL_PUBLIC extern void unimplemented(Object self);

#define UNIMPLEMENTED(class, method)             \
  ((class)self)->method = (void *)unimplemented;

#define ZSTRING_NO_NULL(str) str , (strlen(str))
#define ZSTRING(str) str , (strlen(str)+1)

  // These dont do anything but are useful to indicate when a function
  // parameter is used purely to return a value. They are now used to
  // assist the python binding generator in generating the right sort
  // of code
#define OUT
#define IN

  // This modifier before a class means that the class is abstract and
  // does not have an implementation - we do not generate bindings for
  // that class then.
#define ABSTRACT

  // This modifier indicates that the following pointer is pointing to
  // a borrowed reference - callers must not free the memory after use.
#define BORROWED

  // This tells the autobinder to generated bindings to this struct
#define BOUND

  // This tells the autobinder to ignore this class as it should be
  // private to the implementation - external callers should not
  // access this.
#define PRIVATE

  // This attribute of a method means that this method is a
  // desctructor - the object is no longer valid after this method is
  // run
#define DESTRUCTOR

  // including this after an argument definition will cause the
  // autogenerator to assign default values to that parameter and make
  // it optional
#define DEFAULT(x)

  // This explicitely denote that the type is a null terminated char
  // ptr as opposed to a pointer to char and length.
typedef char * ZString;

  /* The following is a direction for the autogenerator to proxy the
     given class. This is done in the following way:

1) a new python type is created called Proxy_class_name() with a
constructor which takes a surrogate object.

2) The proxy class contains a member "base" of the type of the proxied
C class.

3) The returned python object may be passed to any C functions which
expect the proxied class, and internal C calls will be converted to
python method calls on the proxied object.
  */
#define PROXY_CLASS(name)

  /* This signals the autogenerator to bind the named struct */
#define BIND_STRUCT(name)

  // This means that the memory owned by this pointer is managed
  // externally (not using talloc). It is dangerous to use this
  // keyword too much because we are unable to manage its memory
  // appropriately and it can be free'd from under us.
#define FOREIGN

#ifdef __cplusplus
} /* closing brace for extern "C" */
#endif

#endif /* ifndef __CLASS_H__ */