home *** CD-ROM | disk | FTP | other *** search
- _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 <stddef.h>
- #include <assert.h>
-
- /** 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 <assert.h>
- #include <string.h>
- #include <stdlib.h>
- #include <stdio.h>
- #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 <stdio.h>
- #include <stdlib.h>
- #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");
- }
-
-
-