home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / 1990 / 10 / stevens.asc < prev    next >
Text File  |  1990-08-16  |  8KB  |  241 lines

  1. _C PROGRAMMING COLUMN_
  2. by Al Stevens
  3.  
  4. [LISTING ONE]
  5.  
  6. /************ CLASS.H COPYRIGHT 1990 GREGORY COLVIN ************
  7. This program may be distributed free with this copyright notice.
  8. ***************************************************************/
  9.  
  10. #include <stddef.h>
  11. #include <assert.h>
  12.  
  13. /** All objects must be descendants of the Base class, so we
  14.     define the members and methods of Base here. **/
  15. #define Base_MEMBERS
  16. #define Base_METHODS                                           \
  17.     Base *(*create) (void *table);                             \
  18.     Base *(*clone)  (void *self);                              \
  19.     Base *(*copy)   (void *self, Base *other);                 \
  20.     void  (*destroy)(void *self);
  21.  
  22. typedef struct Base_methods Base_Methods;
  23. typedef struct Base_members Base;
  24. struct Base_members {
  25.     Base_Methods *methods;
  26. };
  27. struct Base_methods {
  28.     char *name;
  29.     size_t size;
  30.     Base_Methods *selfTable;
  31.     Base_Methods *nextTable;
  32.     Base_METHODS
  33. };
  34. extern Base_Methods Base_Table, *Class_List;
  35. Base_Methods *TableFromName(char *name);
  36. Base *ObjectFromName(char *name);
  37.  
  38. /** The CLASS macro declares a Child class of the Parent. Note
  39.     that Child##_MEMBERS, Child##_METHODS, Parent##_MEMBERS, and
  40.     Parent##_METHODS must be already defined. All methods except
  41.     create() require the first parameter, self, to be a pointer
  42.     to an object of Child type; it can be declared void to stop
  43.     compiler warnings when Parent methods are invoked, at the
  44.     possible cost of warnings when methods are bound. The method
  45.     table, Child##_Table, must be defined as a global structure. **/
  46. #define CLASS(Parent,Child)                                    \
  47. typedef struct Child##_methods Child##_Methods;                \
  48. typedef struct Child##_members Child;                          \
  49. struct Child##_members {                                       \
  50.     Child##_Methods *methods;                                  \
  51.     Parent##_MEMBERS                                           \
  52.     Child##_MEMBERS                                            \
  53. };                                                             \
  54. struct Child##_methods {                                       \
  55.     char *name;                                                \
  56.     size_t size;                                               \
  57.     Child##_Methods *selfTable;                                \
  58.     Base_Methods *nextTable;                                   \
  59.     Parent##_METHODS                                           \
  60.     Child##_METHODS                                            \
  61. };                                                             \
  62. extern Child##_Methods Child##_Table
  63.  
  64. /** The INHERIT and BIND macros allow for binding functions to
  65.     object methods at run-time. They must be called before
  66.     objects can be created or used. **/
  67. #define INHERIT(Parent,Child)                                  \
  68.     Child##_Table = *(Child##_Methods*)&Parent##_Table;        \
  69.     Child##_Table.name = #Child;                               \
  70.     Child##_Table.size = sizeof(Child);                        \
  71.     Child##_Table.selfTable = &Child##_Table;                  \
  72.     Child##_Table.nextTable = Class_List;                      \
  73.     Class_List = (Base_Methods *)&Child##_Table
  74.  
  75. #define BIND(Class,Method,Function)                            \
  76.     Class##_Table.Method = Function
  77.  
  78. /** The CREATE macro allocates and initializes an object pointer
  79.     by invoking the create() method for the specified Class.
  80.     The DEFINE macro declares an object structure as an
  81.     automatic or external variable; it can take initializers
  82.     after a WITH, and ends with ENDDEF. The NAMED macro creates
  83.     an object pointer from a class name. **/
  84. #define CREATE(Class)                                          \
  85.     (Class *)(*Class##_Table.create)(&Class##_Table)
  86.  
  87. #define DEFINE(Class,ObjectStruct)                             \
  88.     Class ObjectStruct = { &Class##_Table
  89. #define WITH ,
  90. #define ENDDEF }
  91.  
  92. #define NAMED(ClassName) ObjectFromName(ClassName)
  93.  
  94. /** The VALID macro tests the method table self reference. **/
  95. #define VALID(ObjectPtr)                                       \
  96.     ((ObjectPtr)->methods->selfTable == (ObjectPtr)->methods)
  97.  
  98. /** The SEND and CALL macros invoke object methods, through the
  99.     object's methods pointer with SEND, or directly from a
  100.     method table with CALL. Method parameters besides self may
  101.     be sent using WITH, and END terminates the invocation. **/
  102. #define SEND(Message,ObjectPtr)                                \
  103. (   assert(VALID(ObjectPtr)),                                  \
  104.     (*((ObjectPtr)->methods->Message))((ObjectPtr)
  105.  
  106. #define CALL(Class,Method,ObjectPtr)                           \
  107. (   assert(VALID(ObjectPtr)),                                  \
  108.     assert(Class##_Table.selfTable == &Class##_Table),         \
  109.     (*(Class##_Table.Method))((ObjectPtr)
  110.  
  111. #define END ))
  112.  
  113. /** The DESTROY macro invokes an objects destroy() method **/
  114. #define DESTROY(ObjectPtr) SEND(destroy,(ObjectPtr))END
  115.  
  116.  
  117. [LISTING TWO]
  118.  
  119. /************ CLASS.C COPYRIGHT 1990 GREGORY COLVIN ************
  120. This program may be distributed free with this copyright notice.
  121. ***************************************************************/
  122. #include <assert.h>
  123. #include <string.h>
  124. #include <stdlib.h>
  125. #include <stdio.h>
  126. #include "class.h"
  127.  
  128. Base *BaseCreate(Base_Methods *table)
  129. {   Base *new = (Base *)calloc(1,table->size);
  130.     assert(new);
  131.     new->methods = table;
  132.     return new;
  133. }
  134.  
  135. Base *BaseClone(Base *self)
  136. {   Base *new =  (Base *)malloc(self->methods->size);
  137.     assert(new);
  138.     memcpy(new,self,self->methods->size);
  139.     return new;
  140. }
  141.  
  142. Base *BaseCopy(Base *self, Base *other)
  143. {   memcpy(self,other,self->methods->size);
  144.     return self;
  145. }
  146.  
  147. void BaseDestroy(Base *self)
  148. {  if (self)
  149.        free(self);
  150. }
  151.  
  152. Base_Methods Base_Table = {
  153.     "Base",
  154.     sizeof(Base),
  155.     &Base_Table,
  156.     0,
  157.     BaseCreate,
  158.     BaseClone,
  159.     BaseCopy,
  160.     BaseDestroy
  161. };
  162. Base_Methods *Class_List = &Base_Table;
  163.  
  164. Base_Methods *TableFromName(char *name)
  165. {   Base_Methods *table;
  166.     char *pname, *tname=table->name;
  167.     for (table=Class_List; table; table=table->nextTable)
  168.         for (pname=name; *pname == *tname++; pname++ )
  169.             if (!*pname)
  170.                 return table;
  171.     return 0;
  172. }
  173.  
  174. Base *ObjectFromName(char *name)
  175. {   Base_Methods *table = TableFromName(name);
  176.     if (table)
  177.         return table->create(table);
  178.     return 0;
  179. }
  180.  
  181.  
  182. [LISTING THREE]
  183.  
  184. /** TEST.C  Add one to argv[1] the hard way. This program serves
  185. no real purpose except invoking most of the CLASS macros. **/
  186. #include <stdio.h>
  187. #include <stdlib.h>
  188. #include "class.h"
  189.  
  190. /** Define My class members and methods. **/
  191. #define My_MEMBERS int stuff;
  192. #define My_METHODS void (*set)(void*,int); int (*next)(void*);
  193. CLASS(Base,My);
  194.  
  195. /** Define space for My method table. **/
  196. My_Methods My_Table;
  197.  
  198. /** Define functions to implement My methods. **/
  199. void MySet(My *self, int new)
  200. { self->stuff = new;
  201. }
  202.  
  203. int MyNext(My *self)
  204. { return ++self->stuff;
  205. }
  206.  
  207. /** A function to be called in main to initialize My method table. **/
  208. void MyInit( void )
  209. { INHERIT(Base,My);
  210.   BIND(My,set,MySet);
  211.   BIND(My,next,MyNext);
  212. }
  213.  
  214. /** Make My class do something. **/
  215. My *test( int i )
  216. { int j;
  217. #if 1
  218.   My *objectPtr = CREATE(My);   /* One way to make an object */
  219. #else
  220.   DEFINE(My,object)ENDDEF;      /* Another way */
  221.   My *objectPtr= (My *)SEND(clone,&object)END;
  222. #endif
  223.   CALL(My,set,objectPtr) WITH i END;
  224.   if (j = SEND(next,objectPtr)END)
  225.     return objectPtr;
  226.   DESTROY(objectPtr);
  227.   return 0;
  228. }
  229.  
  230. main(int argc, char **argv)
  231. { int arg = atoi(argv[1]);
  232.   My *out;
  233.   MyInit();
  234.   if (out = test(arg)) {
  235.     printf("%d\n",out->stuff);
  236.     DESTROY(out);
  237.   } else
  238.     printf("0\n");
  239. }
  240.  
  241.