Class and Objects in C
Class and Objects in C
C
lass AndObjectsinC
1.1Requirements
Inheritanceletsusevolvegeneraldatatypesintomorespecializedonesandspares
usrecodingbasicfunctionality.DynamicLinkagehelpsusrepairtheshortcomings
thatamoregeneraldatatypemighthave.Whatwestillneedisacleanglobal
organizationtosimplifymaintainingalargersystemofclasses:
(1)alldynamiclinkshavetopointtothecorrectmethods—e.g.,aconstructor
shouldnotbeinsertedinthewrongplaceinaclassdescription;
(2)weneedacoherentwaytoadd,remove,orchangetheorderofdynamically
linkedmethodsforasuperclasswhileguaranteeingcorrectinheritancetoits
subclasses;
(3)thereshouldbenoloopholessuchasmissingdynamiclinksorundefined
methods;
(4)ifweinheritadynamicallylinkedmethod,theimplementationofthesuperclass
fromwhichweinheritmustremainabsolutelyunchanged,i.e.,inheritancemust
bepossibleusingbinaryinformationonly;
(5)differentsetsofclassesshouldbeabletohavedifferentsetsofdynamically
linkedmethods.
Mostly,thislistindicatesthatmaintainingdynamiclinkageisdifficultanderrorprone—ifwecannot
substantiallyimprovethesituationwemaywellhavecreated
awhiteelephant.
Sofarwehaveworkedwithasinglelistofdynamicallylinkedmethods,regardlessofwhetherornot
itmadesenseforaparticularclass.Thelistwasdefinedas
structClassanditwasincludedwhereverdynamiclinkageneededtobeinitialized.
Thankstofunctionprototypes,ANSI-Cwillcheckthatfunctionnameslike
Point_ctorfittheslotsintheclassdescription,wheretheyareusedasstaticinitializers.(1)aboveis
onlyaproblemifseveralmethodshavetypecompatibleinterfacesorifwechangestructClassanddo
asloppyrecompilation.
Item(2),changingstructClass,soundslikeanightmare—weneedtomanuallyaccesseveryclass
implementationtoupdatethestaticinitializationoftheclass
description,andwecaneasilyforgettoaddanewmethodinsomeclass,thus
causingproblem(3).
IfmaintainingasinglestructClasssoundslikeachallengealready,(5)above
suggeststhatweshouldhavedifferentversionsofstructClassfordifferentsetsof
classes!Therequirementisperfectlyreasonable,however:everyclassneedsa
constructorandadestructor;forpoints,circles,andothergraphicalobjectsweadd
drawingfacilities;atomsandstringsneedcomparisons;collectionslikesets,bags,
orlistshavemethodstoadd,find,andremoveobjects;andsoon.
1.2Metaclasses
Itturnsoutthatrequirement(5)doesnotcompoundourproblems—itactually
pointsthewaytosolvingthem.Justlikeacircleaddsinformationtoapoint,sodo
theclassdescriptionsforpointsandcirclestogetheraddinformation—a
polymorphicdraw()—totheclassdescriptionforbothofthesetwoclasses.
Putdifferently:Aslongastwoclasseshavethesamedynamicallylinked
methods,albeitwithdifferentimplementations,theycanusethesamestruct
2
Classtostorethelinks—thisisthecaseforPointandCircle.Onceweadd
anotherdynamicallylinkedmethod,weneedtolengthenstructClasstoprovide
roomforthenewlink—thisishowwegetfromaclasswithonlyaconstructor
andadestructortoaclasslikePointwitha.drawcomponentthrownin.
Lengtheningstructuresiswhatwecalledinheritance,i.e.,wediscoverthat
classdescriptionswiththesamesetofmethodsformaclass,andthatthereis
inheritanceamongtheclassesofclassdescriptions!
Wecallaclassofclassdescriptionsametaclass.Ametaclassbehavesjustlike
aclass:PointandCircle,thedescriptionsforallpointsandallcircles,aretwo
objectsinametaclassPointClass,becausetheycanbothdescribehowtodraw.A
metaclasshasmethods:wecanaskanobjectlikePointorCircleforthesizeof
theobjects,pointsorcircles,thatitdescribes,orwecouldasktheobjectCircleif
Point,indeed,describesthesuperclassofthecircles.
Dynamicallylinkedmethodscandodifferentthingsforobjectsfromdifferent
classes.Doesametaclassneeddynamicallylinkedmethods?Thedestructorin
PointClasswouldbecalledasaconsequenceofdelete(Point)ordelete(Circle),
i.e.,whenwetrytoeliminatetheclassdescriptionforpointsorcircles.Thisdestructoroughttoreturn
anullpointerbecauseitisclearlynotagoodideatoeliminateaclassdescription.Ametaclass
constructorismuchmoreuseful:
Circle=new(PointClass,/*askthemetaclass*/
"Circle",/*tomakeaclassdescription*/
Point,/*withthissuperclass,*/
sizeof(structCircle),/*thissizefortheobjects,*/
ctor,Circle_ctor,/*thisconstructor,*/
draw,Circle_draw,/*andthisdrawingmethod.*/
0);/*endoflist*/
Thiscallshouldproduceaclassdescriptionforaclasswhoseobjectscanbeconstructed,destroyed,
anddrawn.Becausedrawingisthenewideacommontoall
classdescriptionsinPointClass,itseemsonlyreasonabletoexpectthatthe
PointClassconstructorwouldatleastknowhowtodepositalinktoadrawing
methodinthenewdescription.
Evenmoreispossible:ifwepassthesuperclassdescriptionPointtothe
PointClassconstructor,itshouldbeabletofirstcopyalltheinheritedlinksfrom
PointtoCircleandthenoverwritethosewhichareredefinedforCircle.This,however,completely
solvestheproblemofbinaryinheritance:whenwecreateCircle
weonlyspecifythenewmethodsspecifictocircles;methodsforpointsareimplicitlyinherited
becausetheiraddressescanbecopiedbythePointClassconstructor.
1.3Roots—ObjectandClass
Classdescriptionswiththesamesetofmethodsaretheobjectsofametaclass.A
metaclassassuchisaclassand,therefore,hasaclassdescription.Wemust
assumethattheclassdescriptionsformetaclassesonceagainareobjectsofmeta
(metameta?)classes,whichinturnareclassesand...
Itseemsunwisetocontinuethistrainofthought.Instead,letusstartwiththe
mosttrivialobjectsimaginable.WedefineaclassObjectwiththeabilitytocreate,
destroy,compare,anddisplayobjects.
InterfaceObject.h:
3
externconstvoid*Object;/*new(Object);*/
void*new(constvoid*class,...);
voiddelete(void*self);
intdiffer(constvoid*self,constvoid*b);
intputo(constvoid*self,FILE*fp);
RepresentationObject.r:
structObject{
conststructClass*class;/*object’sdescription*/
};
Nextwedefinetherepresentationfortheclassdescriptionforobjects,i.e.,the
structuretowhichthecomponent.classinstructObjectforourtrivialobjects
points.Bothstructuresareneededinthesameplaces,soweaddtoObject.h:
externconstvoid*Class;/*new(Class,"name",super,size
sel,meth,...0);*/
andtoObject.r:
structClass{
conststructObject_;/*class’description*/
constchar*name;/*class’name*/
conststructClass*super;/*class’superclass*/
size_tsize;/*class’object’ssize*/
void*(*ctor)(void*self,va_list*app);
void*(*dtor)(void*self);
int(*differ)(constvoid*self,constvoid*b);
int(*puto)(constvoid*self,FILE*fp);
};
structClassistherepresentationofeachelementofthefirstmetaclassClass.
Thismetaclassisaclass;therefore,itselementspointtoaclassdescription.Pointingtoaclass
descriptionisexactlywhatanObjectcando,i.e.,structClass
extendsstructObject,i.e.,ClassisasubclassofObject!
Thisdoesnotcausegrief:objects,i.e.,instancesoftheclassObject,canbe
created,destroyed,compared,anddisplayed.Wehavedecidedthatwewantto
createclassdescriptions,andwecanwriteadestructorthatsilentlypreventsthata
classdescriptionisdestroyed.Itmaybequiteusefultobeabletocompareand
displayclassdescriptions.However,thismeansthatthemetaclassClasshasthe
samesetofmethods,andthereforethesametypeofdescription,astheclass
Object,i.e.,thechainfromobjectstotheirclassdescriptionandfromtheretothe
descriptionoftheclassdescriptionendsrightthere.Properlyinitialized.
1.4Subclassing—Any
GiventhedescriptionsClassandObject,wecanalreadymakenewobjectsand
evenanewsubclass.Asanexample,considerasubclassAnywhichclaimsthat
allitsobjectsareequaltoanyotherobject,i.e.,Anyoverwritesdiffer()toalways
returnzero.HereistheimplementationofAny,andaquicktest,allinonefile
any.c:
#include"Object.h"
4
staticintAny_differ(constvoid*_self,constvoid*b)
{
return0;/*Anyequalsanything...*/
}
intmain()
{void*o=new(Object);
constvoid*Any=
new(Class,"Any",Object,sizeOf(o),
differ,Any_differ,
0);
void*a=new(Any);
puto(Any,stdout);
puto(o,stdout);
puto(a,stdout);
if(differ(o,o)==differ(a,a))
puts("ok");
if(differ(o,a)!=differ(a,o))
puts("notcommutative");
delete(o),delete(a);
delete(Any);
return0;
}
Ifweimplementanewclassweneedtoincludetheinterfaceofitssuperclass.
AnyhasthesamerepresentationasObjectandtheclassissosimplethatwedo
notevenneedtoincludethesuperclassrepresentationfile.Theclassdescription
AnyiscreatedbyrequestinganewinstancefromitsmetaclassClassandconstructingitwiththenew
classname,thesuperclassdescription,andthesizeofan
objectofthenewclass:
constvoid*Any=
new(Class,"Any",Object,sizeOf(o),
differ,Any_differ,
0);
Additionally,wespecifyexactlythosedynamicallylinkedmethods,whichwe
overwriteforthenewclass.Themethodnamescanappearinanyorder,eachis
precededbyitsselectorname.Azeroterminatesthelist.
TheprogramgeneratesoneinstanceoofObjectandoneinstanceaofAny,
anddisplaysthenewclassdescriptionandthetwoinstances.Eitherinstancecannotdifferfromitself,
sotheprogramprintsok.Themethoddiffer()hasbeen
overwrittenforAny;therefore,wegetdifferentresultsifwecompareotoa,and
viceversa:
$any
Classat0x101fc
Objectat0x101f4
Anyat0x10220
ok
notcommutative
Any:cannotdestroyclass
5
Clearly,weshouldnotbeabletodeleteaclassdescription.Thiserrorisalready
detectedduringcompilation,becausedelete()doesnotacceptapointertoanarea
protectedwithconst.
1.5Implementation—Object
ImplementingtheObjectclassisstraightforward:theconstructoranddestructor
returnself,anddiffer()checksifitstwoargumentpointersareequal.Defining
thesetrivialimplementationsisveryimportant,however:weuseasingletreeof
classesandmakeObjecttheultimatesuperclassofeveryotherclass;ifaclass
doesnotoverwriteamethodsuchasdiffer()itinheritsitfromObject,i.e.,every
classhasatleastarudimentarydefinitionforeverydynamicallylinkedmethod
alreadyapplicabletoObject.
Thisisageneralsafetyprinciple:wheneverweintroduceanewdynamically
linkedmethod,wewillimmediatelyimplementitforitsfirstclass.Inthisfashion
wecanneverbecaughtselectingatotallyundefinedmethod.Acaseinpointisthe
puto()methodforObject:
staticintObject_puto(constvoid*_self,FILE*fp)
{conststructClass*class=classOf(_self);
returnfprintf(fp,"%sat%p\n",class—>name,_self);
}
Everyobjectpointstoaclassdescriptionandwehavestoredtheclassnamewith
thedescription.Therefore,foranyobjectwecanatleastdisplaytheclassname
andtheaddressoftheobject.Thefirstthreelinesofoutputfromthetrivialtest
program indicatethatwehavenotbotheredtooverwritethismethod
forClassorAny.
puto()reliesonanaccessfunctionclassOf()whichdoessomesafetychecks
andreturnstheclassdescriptorforanobject:
constvoid*classOf(constvoid*_self)
{conststructObject*self=_self;
assert(self&&self—>class);
returnself—>class;
}
Similarly,wecanaskanobjectforitssize*—rememberthat,technically,anobject
isaplainvoid*inANSI-C:
size_tsizeOf(constvoid*_self)
{conststructClass*class=classOf(_self);
returnclass—>size;
}
Itisdebatableifweshouldasktheobjectforthesize,orifweshouldonlyaskitfor
theclassandthenexplicitlyasktheclassforthesize.IfweimplementsizeOf()for
*Thespellingislikelytobeerror-prone,butIjustcouldnotresistthepun.Inventinggoodmethod
namesisanart.
6
objects,wecannotapplyittoaclassdescriptiontogetthecorrespondingobject
size—wewillgetthesizeoftheclassdescriptionitself.However,practicaluse
indicatesthatdefiningsizeOf()forobjectsispreferable.Incontrast,super()isa
staticallylinkedmethodwhichreturnsthesuperclassofaclass,notofanobject.
1.6Implementation—Class
ClassisasubclassofObject,sowecansimplyinheritthemethodsforcomparison
anddisplay.Thedestructorreturnsanullpointertokeepdelete()fromactually
reclaimingthespaceoccupiedbyaclassdescription:
staticvoid*Class_dtor(void*_self)
{structClass*self=_self;
fprintf(stderr,"%s:cannotdestroyclass\n",self—>name);
return0;
}
Hereistheaccessfunctiontogetthesuperclassfromaclassdescription:
constvoid*super(constvoid*_self)
{conststructClass*self=_self;
assert(self&&self—>super);
returnself—>super;
}
TheonlydifficultpartistheimplementationoftheClassconstructorbecause
thisiswhereanewclassdescriptionisinitialized,whereinheritancetakesplace,
andwhereourfourbasicmethodscanbeoverwritten.Werecallhowanewclassdescriptionis
created:
constvoid*Any=
new(Class,"Any",Object,sizeOf(o),
differ,Any_differ,
0);
ThismeansthatourClassconstructorreceivesthename,superclass,andobject
sizeforanewclassdescription.Westartbytransferringthesefromtheargument
list:
staticvoid*Class_ctor(void*_self,va_list*app)
{structClass*self=_self;
self—>name=va_arg(*app,char*);
self—>super=va_arg(*app,structClass*);
self—>size=va_arg(*app,size_t);
assert(self—>super);
selfcannotbeanullpointerbecausewewouldnothaveotherwisefoundthis
method.super,however,couldbezeroandthatwouldbeaverybadidea.
Thenextstepisinheritance.Wemustcopytheconstructorandallother
methodsfromthesuperclassdescriptionatsupertoournewclassdescriptionat
self:
constsize_toffset=offsetof(structClass,ctor);
...
memcpy((char*)self+offset,(char*)self—>super
+offset,sizeOf(self—>super)—offset);
7
AssumingthattheconstructoristhefirstmethodinstructClass,weusetheANSICmacrooffsetof()
todeterminewhereourcopyistostart.Fortunately,theclass
descriptionatsuperissubclassedfromObjectandhasinheritedsizeOf()sowe
cancomputehowmanybytestocopy.
Whilethissolutionisnotentirelyfoolproof,itseemstobethebestcompromise.Ofcourse,wecould
copytheentireareaatsuperandstorethenewname
etc.afterwards;however,wewouldstillhavetorescuethestructObjectatthe
beginningofthenewclassdescription,becausenew()hasalreadystoredtheclass
description’sclassdescriptionpointerthere.
ThelastpartoftheClassconstructorisresponsibleforoverwritingwhatever
methodshavebeenspecifiedintheargumentlisttonew().ANSI-Cdoesnotletus
assignfunctionpointerstoandfromvoid*,soacertainamountofcastingis
necessary:
{
typedefvoid(*voidf)();/*genericfunctionpointer*/
voidfselector;
va_listap=*app;
while((selector=va_arg(ap,voidf)))
{voidfmethod=va_arg(ap,voidf);
if(selector==(voidf)ctor)
*(voidf*)&self—>ctor=method;
elseif(selector==(voidf)dtor)
*(voidf*)&self—>dtor=method;
elseif(selector==(voidf)differ)
*(voidf*)&self—>differ=method;
elseif(selector==(voidf)puto)
*(voidf*)&self—>puto=method;
}
returnself;
}}
Asweshallsee,thispartoftheargumentlistisbestsharedamong
allclassconstructorssothattheselector/methodpairsmaybespecifiedinany
order.Weaccomplishthisbynolongerincrementing*app;insteadwepassa
copyapofthisvaluetova_arg().
Storingthemethodsinthisfashionhasafewconsequences:Ifnoclassconstructorisinterestedina
selector,aselector/methodpairissilentlyignored,butat
leastitisnotaddedtoaclassdescriptionwhereitdoesnotbelong.Ifamethod
doesnothavethepropertype,theANSI-Ccompilerwillnotdetecttheerror
becausethevariableargumentlistandourcastingpreventtypechecks.Herewe
relyontheprogrammertomatchtheselectortothemethodsuppliedwithit,but
theymustbespecifiedasapairandthatshouldresultinacertainamountofplausibility.
1.7Initialization
Normallyweobtainaclassdescriptionbysendingnew()toametaclassdescription.
InthecaseofClassandObjectwewouldissuethefollowingcalls:
constvoid*Object=new(Class,
"Object",Object,sizeof(structObject),
8
ctor,Object_ctor,
dtor,Object_dtor,
differ,Object_differ,
puto,Object_puto,
0);
constvoid*Class=new(Class,
"Class",Object,sizeof(structClass),
ctor,Class_ctor,
dtor,Class_dtor,
0);
Unfortunately,eithercallreliesontheotheralreadyhavingbeencompleted.Therefore,the
implementationofClassandObjectinObject.crequiresstaticinitialization
oftheclassdescriptions.Thisistheonlypointwhereweexplicitlyinitializeastruct
Class:
staticconststructClassobject[]={
{{object+1},
"Object",object,sizeof(structObject),
Object_ctor,Object_dtor,Object_differ,Object_puto
},
{{object+1},
"Class",object,sizeof(structClass),
Class_ctor,Class_dtor,Object_differ,Object_puto
}
};
constvoid*Object=object;
constvoid*Class=object+1;
Anarraynameistheaddressofthefirstarrayelementandcanalreadybeusedto
initializecomponentsoftheelements.Wefullyparenthesizethisinitializationin
casestructObjectischangedlateron.
1.8Selectors
Oneargument_selfis
theobjectfordynamiclinkage.Weverifythatitexistsandthattherequired
methodexistsfortheobject.Thenwecallthemethodandpassallargumentstoit;
therefore,themethodcanassumethat_selfisaproperobjectforit.Finally,we
returntheresultvalueofthemethod,ifany,astheresultoftheselector.
Everydynamicallylinkedmethodmusthaveaselector.Sofar,wehavehidden
callstotheconstructorandthedestructorbehindnew()anddelete(),butwestill
needthefunctionnamesctoranddtorfortheselector/methodpairspassedtothe
Classconstructor.Wemaylaterdecidetobindnew()anddelete()dynamically;
therefore,itwouldnotbeagoodideatousetheirnamesinplaceofctoranddtor.
WehaveintroducedacommonsuperclassObjectforallourclassesandwe
havegivenitsomefunctionalitythatsimplifiesimplementingselectorfunctions.
classOf()inspectsanobjectandreturnsanon-zeropointertoitsclassdescription.
9
Thispermitsthefollowingimplementationfordelete():
voiddelete(void*_self)
{
if(_self)
free(dtor(_self));
}
void*dtor(void*_self)
{conststructClass*class=classOf(_self);
assert(class—>dtor);
returnclass—>dtor(_self);
}
new()mustbeimplementedverycarefullybutitworkssimilarly:
void*new(constvoid*_class,...)
{conststructClass*class=_class;
structObject*object;
va_listap;
assert(class&&class—>size);
object=calloc(1,class—>size);
assert(object);
object—>class=class;
va_start(ap,_class);
object=ctor(object,&ap);
va_end(ap);
returnobject;
}
Weverifytheclassdescriptionandwemakesurethatwecancreateazero-filled
object.Thenweinitializetheclassdescriptionoftheobjectandwearereadytolet
thenormalselectorctor()findandexecutetheconstructor:
void*ctor(void*_self,va_list*app)
{conststructClass*class=classOf(_self);
assert(class—>ctor);
returnclass—>ctor(_self,app);
}
Thereisperhapsabittoomuchcheckinggoingon,butwehaveauniformand
robustinterface.
1.9SuperclassSelectors
Beforeasubclassconstructorperformsitsowninitialization,itisrequiredtocallthe
superclassconstructor.Similarly,asubclassdestructormustcallitssuperclass
destructorafterithascompleteditsownresourcereclamation.Whenweare
implementingselectorfunctions,weshouldalsosupplyselectorsforthesuperclass
calls:
void*super_ctor(constvoid*_class,
void*_self,va_list*app)
{conststructClass*superclass=super(_class);
assert(_self&&superclass—>ctor);
returnsuperclass—>ctor(_self,app);
10
}
void*super_dtor(constvoid*_class,void*_self)
{conststructClass*superclass=super(_class);
assert(_self&&superclass—>dtor);
returnsuperclass—>dtor(_self);
}
Theseselectorsshouldonlybecalledbyasubclassimplementation;therefore,we
includetheirdeclarationsintotherepresentationfileandnotintotheinterfacefile.
Tobeonthesafeside,wesupplysuperclassselectorsforalldynamicallylinked
methods,i.e.,everyselectorhasacorrespondingsuperclassselector.Thisway,
everydynamicallylinkedmethodhasasimplewaytocallitssuperclassmethod.
Actually,thereisasubtletrapluring.Considerhowamethodofanarbitrary
classXwouldcallitssuperclassmethod.Thisisthecorrectway:
staticvoid*X_method(void*_self,va_list*app)
{void*p=super_method(X,_self,app);
...
Lookingatthesuperclassselectorsshownaboveweseethatsuper_method()in
thiscasecalls
super(X)—>method(_self,app);
i.e.,themethodinthesuperclassoftheclassXforwhichwejustdefined
X_method().ThesamemethodisstillreachedevenifsomesubclassYinherited
X_method()becausetheimplementationisindependentofanyfutureinheritance.
ThefollowingcodeforX_method()maylookmoreplausible,butitwillbreak
oncethemethodisinherited:
staticvoid*X_method(void*_self,va_list*app)
{void*p=/*WRONG*/
super_method(classOf(_self),_self,app);
...
Thesuperclassselectordefinitionnowproduces
super(classOf(_self))—>method(_self,app);
If_selfisinclassX,wereachthesamemethodasbefore.However,if_selfisina
subclassYofXweget
super(Y)—>method(_self,app);
andthatisstillX_method(),i.e.,insteadofcallingasuperclassmethod,weget
stuckinasequenceofrecursivecalls!
1.10ANewMetaclass—PointClass
ObjectandClassaretherootofourclasshierarchy.Everyclassisasubclassof
Objectandinheritsitsmethods,everymetaclassisasubclassofClassand
cooperateswithitsconstructor.WeknowHowasimplesubclasscanbemadebyreplacing
dynamicallylinkedmethodsofitssuperclassand,
possibly,definingnewstaticallylinkedmethods.
Wenowturntobuildingclasseswithmorefunctionality.Asanexamplewe
connectPointandCircletoourclasshierarchy.Theseclasseshaveanewdynamicallylinkedmethod
draw();therefore,weneedanewmetaclasstoaccommodate
thelink.HereistheinterfacefilePoint.h:
#include"Object.h"
11
externconstvoid*Point;/*new(Point,x,y);*/
voiddraw(constvoid*self);
voidmove(void*point,intdx,intdy);
externconstvoid*PointClass;/*addsdraw*/
Thesubclassalwaysincludesthesuperclassanddefinesapointertotheclass
descriptionandtothemetaclassdescriptionifthereisanewone.Onceweintroducemetaclasses,we
canfinallydeclaretheselectorforadynamicallylinked
methodwhereitbelongs:inthesameinterfacefileasthemetaclasspointer.
TherepresentationfilePoint.rcontainstheobjectstructurestructPointwithits
accessmacrosasbefore,anditcontainsthesuperclassselectorstogetherwiththe
structureforthemetaclass:
#include"Object.r"
structPoint{conststructObject_;/*Point:Object*/
intx,y;/*coordinates*/
};
#definex(p)(((conststructPoint*)(p))—>x)
#definey(p)(((conststructPoint*)(p))—>y)
voidsuper_draw(constvoid*class,constvoid*self);
structPointClass{
conststructClass_;/*PointClass:Class*/
void(*draw)(constvoid*self);
};
TheimplementationfilePoint.ccontainsmove(),Point_draw(),draw(),and
super_draw().Thesemethodsarewrittenasbefore;wesawthetechniqueforthe
superclassselectorintheprevioussection.Theconstructormustcallthesuperclassconstructor:
staticvoid*Point_ctor(void*_self,va_list*app)
{structPoint*self=super_ctor(Point,_self,app);
self—>x=va_arg(*app,int);
self—>y=va_arg(*app,int);
returnself;
}
Onenewideainthisfileistheconstructorforthemetaclass.Itcallsthesuperclassconstructorto
performinheritanceandthenusesthesameloopas
Class_ctor()tooverwritethenewdynamicallylinkedmethoddraw():
staticvoid*PointClass_ctor(void*_self,va_list*app)
{structPointClass*self
=super_ctor(PointClass,_self,app);
typedefvoid(*voidf)();
voidfselector;
va_listap=*app;
while((selector=va_arg(ap,voidf)))
{voidfmethod=va_arg(ap,voidf);
if(selector==(voidf)draw)
*(voidf*)&self—>draw=method;
}
returnself;
12
}
Notethatwesharetheselector/methodpairsintheargumentlistwiththesuperclassconstructor:ap
takeswhateverClass_ctor()returnsin*appandstartsthe
loopfromthere.
Withthisconstructorinplacewecandynamicallyinitializethenewclass
descriptions:PointClassismadebyClassandthenPointismadewiththeclass
descriptionPointClass:
voidinitPoint(void)
{
if(!PointClass)
PointClass=new(Class,"PointClass",
Class,sizeof(structPointClass),
ctor,PointClass_ctor,
0);
if(!Point)
Point=new(PointClass,"Point",
Object,sizeof(structPoint),
ctor,Point_ctor,
draw,Point_draw,
0);
}
Writingtheinitializationisstraightforward:wespecifytheclassnames,inheritance
relationships,andthesizeoftheobjectstructures,andthenweadd
selector/methodpairsforalldynamicallylinkedmethodsdefinedinthefile.Azero
completeseachargumentlist.
Fornow,initPoint()isaddedtotheinterfaceinPoint.handthefunctionmustdefinitelybecalled
beforewecanmakepointsorsubclasses.Thefunctionisinterlockedsothatitcan
becalledmorethanonce—itwillproduceexactlyoneclassdescriptionPointClass
andPoint.
AslongaswecallinitPoint()frommain()wecanreusethetestprogramPoints
$pointsp
"."at1,2
"."at11,22
Wecanremovetheuglycodeintheconstructor.
staticvoid*Circle_ctor(void*_self,va_list*app)
{structCircle*self=super_ctor(Circle,_self,app);
self—>rad=va_arg(*app,int);
returnself;
}
Ofcourse,weneedtoaddaninitializationfunctioninitCircle()tobecalledfrom
main()beforecirclescanbemade:
voidinitCircle(void)
{
if(!Circle)
{initPoint();
13
Circle=new(PointClass,"Circle",
Point,sizeof(structCircle),
ctor,Circle_ctor,
draw,Circle_draw,
0);
}
}
BecauseCircledependsonPoint,wecalloninitPoint()beforeweinitializeCircle.
Allofthesefunctionsdotheirrealworkonlyonce,andwecancalltheminany
orderaslongaswetakecareoftheinterdependenceinsidethefunctionitself.
1.11Summary
Objectspointtotheirclassdescriptionswhich,forthemostpart,containpointers
todynamicallylinkedmethods.Classdescriptionswiththesamesetofmethod
pointersconstituteametaclass—classdescriptionsareobjects,too.Ametaclass,
again,hasaclassdescription.
ThingsremainfinitebecausewestartwithatrivialclassObjectandwithafirst
metaclassClasswhichhasObjectasasuperclass.Ifthesamesetofmethods—
constructor,destructor,comparison,anddisplay—canbeappliedtoobjectsand
classdescriptions,thenthemetaclassdescriptionClasswhichdescribestheclass
descriptionObjectalsodescribesitself.
Ametaclassconstructorfillsaclassdescriptionandthusimplementsbinary
inheritance,thedestructorreturnszerotoprotecttheclassdescriptionfrombeing
destroyed,thedisplayfunctioncouldshowmethodpointers,etc.Twoclass
descriptionsarethesameifandonlyiftheiraddressesareequal.
14
REFERENCES
[ANSI]AmericanNationalStandardforInformationSystems—Programming
LanguageCX3.159-1989.
[AWK88]A.V.Aho,B.W.KernighanundP.J.WeinbergerTheawkProgramming
LanguageAddison-Wesley1988,ISBN0-201-07981-X.
AUTHOR
GuruprasadDavangave
SoftwareEngineer
ComputerScienceandEngineering.[2021]