_C PROGRAMMING COLUMN_ by Al Stevens [LISTING ONE] /************ CLASS.H COPYRIGHT 1990 GREGORY COLVIN ************ This program may be distributed free with this copyright notice. ***************************************************************/ #include #include /** All objects must be descendants of the Base class, so we define the members and methods of Base here. **/ #define Base_MEMBERS #define Base_METHODS \ Base *(*create) (void *table); \ Base *(*clone) (void *self); \ Base *(*copy) (void *self, Base *other); \ void (*destroy)(void *self); typedef struct Base_methods Base_Methods; typedef struct Base_members Base; struct Base_members { Base_Methods *methods; }; struct Base_methods { char *name; size_t size; Base_Methods *selfTable; Base_Methods *nextTable; Base_METHODS }; extern Base_Methods Base_Table, *Class_List; Base_Methods *TableFromName(char *name); Base *ObjectFromName(char *name); /** The CLASS macro declares a Child class of the Parent. Note that Child##_MEMBERS, Child##_METHODS, Parent##_MEMBERS, and Parent##_METHODS must be already defined. All methods except create() require the first parameter, self, to be a pointer to an object of Child type; it can be declared void to stop compiler warnings when Parent methods are invoked, at the possible cost of warnings when methods are bound. The method table, Child##_Table, must be defined as a global structure. **/ #define CLASS(Parent,Child) \ typedef struct Child##_methods Child##_Methods; \ typedef struct Child##_members Child; \ struct Child##_members { \ Child##_Methods *methods; \ Parent##_MEMBERS \ Child##_MEMBERS \ }; \ struct Child##_methods { \ char *name; \ size_t size; \ Child##_Methods *selfTable; \ Base_Methods *nextTable; \ Parent##_METHODS \ Child##_METHODS \ }; \ extern Child##_Methods Child##_Table /** The INHERIT and BIND macros allow for binding functions to object methods at run-time. They must be called before objects can be created or used. **/ #define INHERIT(Parent,Child) \ Child##_Table = *(Child##_Methods*)&Parent##_Table; \ Child##_Table.name = #Child; \ Child##_Table.size = sizeof(Child); \ Child##_Table.selfTable = &Child##_Table; \ Child##_Table.nextTable = Class_List; \ Class_List = (Base_Methods *)&Child##_Table #define BIND(Class,Method,Function) \ Class##_Table.Method = Function /** The CREATE macro allocates and initializes an object pointer by invoking the create() method for the specified Class. The DEFINE macro declares an object structure as an automatic or external variable; it can take initializers after a WITH, and ends with ENDDEF. The NAMED macro creates an object pointer from a class name. **/ #define CREATE(Class) \ (Class *)(*Class##_Table.create)(&Class##_Table) #define DEFINE(Class,ObjectStruct) \ Class ObjectStruct = { &Class##_Table #define WITH , #define ENDDEF } #define NAMED(ClassName) ObjectFromName(ClassName) /** The VALID macro tests the method table self reference. **/ #define VALID(ObjectPtr) \ ((ObjectPtr)->methods->selfTable == (ObjectPtr)->methods) /** The SEND and CALL macros invoke object methods, through the object's methods pointer with SEND, or directly from a method table with CALL. Method parameters besides self may be sent using WITH, and END terminates the invocation. **/ #define SEND(Message,ObjectPtr) \ ( assert(VALID(ObjectPtr)), \ (*((ObjectPtr)->methods->Message))((ObjectPtr) #define CALL(Class,Method,ObjectPtr) \ ( assert(VALID(ObjectPtr)), \ assert(Class##_Table.selfTable == &Class##_Table), \ (*(Class##_Table.Method))((ObjectPtr) #define END )) /** The DESTROY macro invokes an objects destroy() method **/ #define DESTROY(ObjectPtr) SEND(destroy,(ObjectPtr))END [LISTING TWO] /************ CLASS.C COPYRIGHT 1990 GREGORY COLVIN ************ This program may be distributed free with this copyright notice. ***************************************************************/ #include #include #include #include #include "class.h" Base *BaseCreate(Base_Methods *table) { Base *new = (Base *)calloc(1,table->size); assert(new); new->methods = table; return new; } Base *BaseClone(Base *self) { Base *new = (Base *)malloc(self->methods->size); assert(new); memcpy(new,self,self->methods->size); return new; } Base *BaseCopy(Base *self, Base *other) { memcpy(self,other,self->methods->size); return self; } void BaseDestroy(Base *self) { if (self) free(self); } Base_Methods Base_Table = { "Base", sizeof(Base), &Base_Table, 0, BaseCreate, BaseClone, BaseCopy, BaseDestroy }; Base_Methods *Class_List = &Base_Table; Base_Methods *TableFromName(char *name) { Base_Methods *table; char *pname, *tname=table->name; for (table=Class_List; table; table=table->nextTable) for (pname=name; *pname == *tname++; pname++ ) if (!*pname) return table; return 0; } Base *ObjectFromName(char *name) { Base_Methods *table = TableFromName(name); if (table) return table->create(table); return 0; } [LISTING THREE] /** TEST.C Add one to argv[1] the hard way. This program serves no real purpose except invoking most of the CLASS macros. **/ #include #include #include "class.h" /** Define My class members and methods. **/ #define My_MEMBERS int stuff; #define My_METHODS void (*set)(void*,int); int (*next)(void*); CLASS(Base,My); /** Define space for My method table. **/ My_Methods My_Table; /** Define functions to implement My methods. **/ void MySet(My *self, int new) { self->stuff = new; } int MyNext(My *self) { return ++self->stuff; } /** A function to be called in main to initialize My method table. **/ void MyInit( void ) { INHERIT(Base,My); BIND(My,set,MySet); BIND(My,next,MyNext); } /** Make My class do something. **/ My *test( int i ) { int j; #if 1 My *objectPtr = CREATE(My); /* One way to make an object */ #else DEFINE(My,object)ENDDEF; /* Another way */ My *objectPtr= (My *)SEND(clone,&object)END; #endif CALL(My,set,objectPtr) WITH i END; if (j = SEND(next,objectPtr)END) return objectPtr; DESTROY(objectPtr); return 0; } main(int argc, char **argv) { int arg = atoi(argv[1]); My *out; MyInit(); if (out = test(arg)) { printf("%d\n",out->stuff); DESTROY(out); } else printf("0\n"); }