note from the author:
- This article has been outdated by newest one OO C Programming: OOP Model Definition, Implementation and Application with ANSI C.
- This article stands as a divertimento (amusement), or an easy introduction to the topic, but has serious flaws and still remains further from what would be real OO programming with C#, etc.
- For a serious, real and definitive one on the matter please redirect yourself to the newest one. I (the author) highly recommend it to you.
Introduction or Why another C OO programming stuff
I try here to show direct and straight C OO programming from scratch (not any macro definition of code, nor trying to imitate conventional OO style and words, only plain standard C for C coders). The purpose on doing that it is
manifold:
- To show that it is possible and not so difficult to do OO programming with C if you know how to.
- To take advantage of the reuse code characteristic of OO programming into C.
- To make notice more clearly the differences between C OO programming and
other languages better prepared to for OO (the extra handcode you have to make in C).
- To put more clearly the concept of inheritance or extension capabilities in OO and C, and also the use of multiple inheritance.
- To demonstrate how to do encapsulation within C OO.
- To show how virtual class concept it is more straight forward in C because of the inner separation of declaration from definition intrinsic in C (virtual class declaration allows polymorphism).
- To take a better understanding of OO concept seeing it applied into another language like C.
- To make you have fun with C OO programming and also find it
useful to apply wherever you want or you use C.
It is good code and organized code. This particular code it is not itself the final objective, but it is to show you how to code this way.
To see the contents at a glance pay attention to the next list wich contains the titles of the following sections (to the end):
- Object Type 1: The Simplest Object, a Var Into an Object.
- Object Type 2: More Instance Methods and Public Vars.
- Object Type 3:
Inheriting, Extending From Previous Object Type.
- Object Type 4: Encapsulation of Data and Other Things.
- Take a Breath...
- Object Type 5: Playing Around, Making a Simpler Object From a More Complex One.
- Object Type 6: Multiple Inheritance.
- Object Type 7: Multiple Inheritance In The Private Part and Redoing Interface.
- Second Breath...
- Object Type 8: A Previous Step Into Polymorphism.
- Object Type 9: Polymorphism.
- The Final Breath... Conclusion and Remarks.
Note: You must know C in a procedural way (pointers, etc). I make use of 'malloc
' function from 'stdlib' and also 'sizeof
' function. I use MinGW compiler for windows.
Object Type 1: The Simplest Object, a Var Into an Object.
I will begin by showing you how to make the simplest object possible. Convert a var int in C into an object of type VarInt.
First, the class (type) declaration:
#ifndef vARiNT
#define vARiNT
struct varInt;
typedef struct varInt *VarInt;
struct varInt
{ int a;
int (*delete)(VarInt);
};
VarInt varInt();
#endif
Now the class definition:
#include <stdio.h>
#include <stdlib.h>
#include "constDef.h"
#include "VarInt.h"
static int delete(VarInt vInt1)
{
free(vInt1);
return ERR0;
}
VarInt varInt()
{ VarInt vInt1= malloc(sizeof(struct varInt));
vInt1->delete= delete;
return vInt1;
}
It rests us to define constDef.h which will be global for all files, used around:
#ifndef CONSTdEF
#define CONSTdEF
#define TRUE 1
#define FALSE 0
#define ERR0 0
#define ERR1 1
#endif
Now it's time to test our object:
#include <stdio.h>
#include "constDef.h"
#include "VarInt.h"
int main()
{ VarInt vInt1= varInt();
vInt1->a= 4;
vInt1->a++;
printf("\na in vInt1 is %d", vInt1->a);
if(vInt1->delete(vInt1)== ERR0)
{ vInt1= NULL;
printf("\nvInt1 freed");
}
return ERR0;
}
Now we see what we get (compiled with MinGW for WindowsXP with 'cc testVarInt.c VarInt.c' and executing 'a.exe'):
Object Type 2: More Instance Methods and Public Vars.
I will make an object type
which contains two public vars and one instance methods to find its difference. First the type declaration:
#ifndef dIF
#define dIF
struct dif;
typedef struct dif *Dif;
struct dif
{ int a, b;
int (*delete)(Dif);
int (*difference)(Dif);
};
Dif dif();
#endif
Now the type definition:
#include <stdio.h>
#include <stdlib.h>
#include "constDef.h"
#include "Dif.h"
static int delete(Dif df1)
{
free(df1);
return ERR0;
}
static int difference(Dif df1)
{ return df1->b- df1->a;
}
Dif dif()
{ Dif df1= malloc(sizeof(struct dif));
df1->delete= delete;
df1->difference= difference;
return df1;
}
Finally the testing for the object type:
#include <stdio.h>
#include "constDef.h"
#include "Dif.h"
int main()
{ Dif df1= dif();
df1->a= 4;
df1->b= 6;
printf("\ndifference is %d", df1->difference(df1));
if(df1->delete(df1)== ERR0)
{ df1= NULL;
printf("\nobject freed");
}
return ERR0;
}
And the output will be, after compiling:
Object Type 3: Inheriting, Extending From Previous Object Type.
Here we enter in the concepts of extension capabilities or inheritance. I will extend or make use of previous object type (Dif) to make an object type wich adds one more method to the previous one. Thus we will obtain an object type capable of adding and
subtraction.
First type declaration:
#ifndef sUMdIF
#define sUMdIF
#include "Dif.h"
struct sumDif;
typedef struct sumDif *SumDif;
struct sumDif
{ Dif df;
int (*delete)(SumDif);
int (*sum)(SumDif);
};
SumDif sumDif();
#endif
Second, type definition:
#include <stdio.h>
#include <stdlib.h>
#include "constDef.h"
#include "SumDif.h"
static int delete(SumDif sDf)
{
Dif df= sDf->df;
if(df->delete(df)== ERR0)
{ sDf->df= NULL;
}
free(sDf);
return ERR0;
}
static int sum(SumDif sDf)
{ Dif df= sDf->df;
return df->a+ df->b;
}
SumDif sumDif()
{ SumDif sDf= malloc(sizeof(struct sumDif));
sDf->df= dif();
sDf->delete= delete;
sDf->sum= sum;
return sDf;
}
Third, test the extended object type:
#include <stdio.h>
#include "constDef.h"
#include "SumDif.h"
int main()
{ SumDif sDf= sumDif();
Dif df= sDf->df;
df->a= 4;
df->b= 2;
printf("\na is\t\%d\nb is\t%d\t\ndifference is\t%d\nsum is\t%d",
df->a, df->b, df->difference(df), sDf->sum(sDf));
if(sDf->delete(sDf)==ERR0)
{ sDf= NULL;
printf("\nobject freed");
}
return ERR0;
}
Forth, check the output of the testing file:
Remember that in this case, when compiling we must include 'Dif.c' , that's it, we do: 'cc testSumDif.c SumDif.c Dif.c'. If not, there will be problems when linking.
We have just seen an example (demonstration) of inheritance with C. In fact C has multiple inheritance, just using more objects in its public interface.
Object Type 4: Encapsulation of Data and Other Things.
Now I will show you how to do encapsulation or private data (or objects or anything else). To show you this I am
going to make the first object (the most simple possible one) but with no direct access to the var but putting the var into a private zone and accessing it (setting and getting)
through the use of instance methods.
First, the type declaration:
#ifndef vARiNT2
#define vARiNT2
struct varInt2;
typedef struct varInt2 *VarInt2;
struct varInt2
{
int (*delete)(VarInt2);
int (*set)(VarInt2, int);
int (*get)(VarInt2);
void *private;
};
VarInt2 varInt2();
#endif
Second, the type definition:
#include <stdio.h>
#include <stdlib.h>
#include "constDef.h"
#include "VarInt2.h"
struct private;
typedef struct private *Private;
struct private
{ int a;
};
static int delete(VarInt2 vInt)
{
free(vInt->private);
free(vInt);
return ERR0;
}
static int set(VarInt2 vInt, int a)
{
Private pr= vInt->private;
pr->a= a;
return ERR0;
}
static int get(VarInt2 vInt)
{ Private pr= vInt->private;
return pr->a;
}
VarInt2 varInt2()
{ VarInt2 vInt= malloc(sizeof(struct varInt2));
vInt->delete= delete;
vInt->get= get;
vInt->set= set;
Private pr= vInt->private= malloc(sizeof(struct private));
return vInt;
}
Third, the testing of our object type:
#include <stdio.h>
#include "constDef.h"
#include "VarInt2.h"
int main()
{ VarInt2 vInt= varInt2();
vInt->set(vInt, 2);
printf("\nvInt is\t%d", vInt->get(vInt));
vInt->set(vInt, 3);
printf("\nnow vInt is\t%d", vInt->get(vInt));
if(vInt->delete(vInt)==ERR0)
{ vInt= NULL;
printf("\n--object freed--");
}
return ERR0;
}
Fourth, the compiling command ('cc testVarInt2.c VarInt2.c') and the execution ('a.exe'):
You see, we have made encapsulation without direct manipulation of data.
In the private part we can encapsulate too the use of other objects, and the use of private functions, but this last case has not too much sense since functions intended to be private are not replicated in each instance (different from vars and objects
which are allocated dynamically) and since their use will be guaranteed since they are defined in the same file than the other functions
which make use of them.
Take a Breath...
Till that moment, we have seen:
- the most simple object (a var, a constructor and a delete method).
- the use of other instance methods.
- the use of inheritance or extending capabilities (use of public objects).
- the use of encapsulation (or private part).
It will rests us to show how to use private objects (objects in the private part) and how to implement polymorphism. Also an example of private function will be good. And we'll be done for a first intro into OO C programming (which is use of vars, objects and functions in a private and public part).
Object Type 5: Playing Around, Making a Simpler Object From a More Complex One.
Now suppose we want the kind of Dif object but Sum object. We will do it by
inheriting privately from SumDif and making interface for sum method only.
First, type declaration (interface):
#ifndef sUM
#define sUM
struct sum;
typedef struct sum *Sum;
struct sum
{ int (*delete)(Sum);
int (*setA)(Sum, int);
int (*setB)(Sum, int);
int (*getA)(Sum);
int (*getB)(Sum);
int (*sum)(Sum);
void *private;
};
Sum sum();
#endif
Second, definition:
#include <stdio.h>
#include <stdlib.h>
#include "constDef.h"
#include "Sum.h"
#include "SumDif.h"
struct private;
typedef struct private *Private;
struct private
{ SumDif sD;
};
static int delete(Sum s)
{ Private pr= s->private;
pr->sD->delete(pr->sD);
free(pr);
free(s);
return ERR0;
}
static int setA(Sum s, int a)
{ Private pr= s->private;
SumDif sD= pr->sD;
Dif df= sD->df;
df->a= a;
return ERR0;
}
static int setB(Sum s, int b)
{ Private pr= s->private;
SumDif sD= pr->sD;
Dif df= sD->df;
df->b= b;
return ERR0;
}
static int getA(Sum s)
{ Private pr= s->private;
SumDif sD= pr->sD;
Dif df= sD->df;
return df->a;
}
static int getB(Sum s)
{ Private pr= s->private;
SumDif sD= pr->sD;
Dif df= sD->df;
return df->b;
}
static int sumInner(Sum s)
{ Private pr= s->private;
return pr->sD->sum(pr->sD);
}
Sum sum()
{ Sum s= malloc(sizeof(struct sum));
s->delete= delete;
s->setA= setA;
s->setB= setB;
s->getA= getA;
s->getB= getB;
s->sum= sumInner;
s->private= malloc(sizeof(struct private));
Private pr= s->private;
pr->sD= sumDif();
return s;
}
Third, testing:
#include <stdio.h>
#include "constDef.h"
#include "Sum.h"
int main()
{ Sum s1= sum();
s1->setA(s1, 4);
s1->setB(s1, 4);
printf("\na: %d, b: %d, sum: %d", s1->getA(s1), s1->getB(s1), s1->sum(s1));
if(s1->delete(s1)== ERR0)
{ s1= NULL;
printf("\n// object type Sum freed //");
}
return ERR0;
}
I must make an important remark. When compiling the command will be 'cc testSum.c Sum.c SumDif.c Dif.c'.
As you see, we mention all object file definitions from which Sum type finally derive. Sum derive from
SumDif
which in turn derive from Dif.
Object Type 6: Multiple Inheritance.
Now we can make an object type SumDif2
which will be equal than
SumDif
but obtained using multiple inheritance over the object types Sum and Dif. This multiple inheritance will be made in the public part.
Type declaration:
#ifndef sUMdIF2
#define sUMdIF2
#include "Sum.h"
#include "Dif.h"
struct sumDif2;
typedef struct sumDif2 *SumDif2;
struct sumDif2
{ Sum sum;
Dif dif;
int (*delete)(SumDif2);
};
SumDif2 sumDif2();
#endif
Type (or class) definition:
#include <stdio.h>
#include <stdlib.h>
#include "constDef.h"
#include "SumDif2.h"
static int delete(SumDif2 sD2)
{
if(sD2->sum->delete(sD2->sum)== ERR0)
{ sD2->sum= NULL;
}
if(sD2->dif->delete(sD2->dif)== ERR0)
{ sD2->dif= NULL;
}
free(sD2);
}
SumDif2 sumDif2()
{ SumDif2 sD2= malloc(sizeof(struct sumDif2));
sD2->delete= delete;
sD2->sum= sum();
sD2->dif= dif();
return sD2;
}
Testing our object:
#include <stdio.h>
#include "constDef.h"
#include "SumDif2.h"
int main()
{
SumDif2 sD= sumDif2();
sD->dif->a= 3;
sD->dif->b= 1;
sD->sum->setA(sD->sum, 3);
sD->sum->setB(sD->sum, 1);
printf("\na: %d, b: %d, sum: %d, dif(b- a): %d",
sD->dif->a, sD->dif->b, sD->sum->sum(sD->sum), sD->dif->difference(sD->dif));
if(sD->delete(sD)== ERR0){
sD= NULL;
printf("\n object freed");
}
return ERR0;
}
The command to compile this is 'C:\...>cc Sum.c testSumDif2.c SumDif2.c Dif.c SumDif.c'.
The multiple inheritance has been:
Dif --> SumDif --> Sum --> |
Dif --> |
| --> SumDif2.
Object Type 7: Multiple Inheritance
in the Private Part and Redoing Interface
As we have seen, if we want to employ this multiple inheritance, it is more logical to do it in a private part. In that case we then make the interface to set a's and b's only once, not twice
repeated as in the previous example (which has not too much sense).
Type declaration:
#ifndef sUMdIF2bIS
#define sUMdIF2bIS
struct sumDif2Bis;
typedef struct sumDif2Bis *SumDif2Bis;
struct sumDif2Bis
{ void *private;
int(*delete)(SumDif2Bis);
int (*setA)(SumDif2Bis, int);
int (*setB)(SumDif2Bis, int);
int (*getA)(SumDif2Bis);
int (*getB)(SumDif2Bis);
int (*bMinusA)(SumDif2Bis);
int (*bPlusA)(SumDif2Bis);
};
SumDif2Bis sumDif2Bis();
#endif
Type (or class) definition:
#include <stdio.h>
#include <stdlib.h>
#include "constDef.h"
#include "Sum.h"
#include "Dif.h"
#include "SumDif2Bis.h"
struct private;
typedef struct private *Private;
struct private
{ Sum sum;
Dif dif;
};
static int delete(SumDif2Bis sD)
{ Private pr= sD->private;
pr->sum->delete(pr->sum);
pr->dif->delete(pr->dif);
free(pr);
free(sD);
}
static int setA(SumDif2Bis sD, int a)
{ Private pr= sD->private;
pr->sum->setA(pr->sum, a);
pr->dif->a= a;
return ERR0;
}
static int setB(SumDif2Bis sD, int b)
{ Private pr= sD->private;
pr->sum->setB(pr->sum, b);
pr->dif->b= b;
return ERR0;
}
static int getA(SumDif2Bis sD)
{ Private pr= sD->private;
return pr->dif->a;
}
static int getB(SumDif2Bis sD)
{ Private pr= sD->private;
return pr->dif->b;
}
static int bMinusA(SumDif2Bis sD)
{ Private pr= sD->private;
return pr->dif->difference(pr->dif);
}
static int bPlusA(SumDif2Bis sD)
{ Private pr= sD->private;
return pr->sum->sum(pr->sum);
}
SumDif2Bis sumDif2Bis()
{ SumDif2Bis sD= malloc(sizeof(struct sumDif2Bis));
sD->delete= delete;
sD->setA= setA;
sD->setB= setB;
sD->getA= getA;
sD->getB= getB;
sD->bMinusA= bMinusA;
sD->bPlusA= bPlusA;
sD->private= malloc(sizeof(struct private));
Private pr= sD->private;
pr->dif= dif();
pr->sum= sum();
return sD;
}
Testing:
#include <stdio.h>
#include "constDef.h"
#include "SumDif2Bis.h"
int main()
{
SumDif2Bis sD= sumDif2Bis();
sD->setA(sD, 1);
sD->setB(sD, 4);
printf("\na: %d, b: %d, b- a: %d, b+ a: %d",
sD->getA(sD), sD->getB(sD), sD->bMinusA(sD), sD->bPlusA(sD));
if(sD->delete(sD)== ERR0)
{ sD= NULL;
printf("\nobject freed");
}
return ERR0;
}
Compile: 'C:\...>cc testSumDif2Bis.c SumDif2Bis.c Sum.c Dif.c SumDif.c'.
Second Breath....
As we see, as a conclusion we get than when working with multiple inheritance it is better to use a private multiple inheritance and redo the interface in the public part when the multiple inheritance share data. If not, we can do it in the public part.
Also we see, as a conclusion, that C OO programming presented in this work works and rocks. The only thing to take in mind it is to pass as argument always (when instance methods) the object itself because we don't have pointer 'this'.
Also keep in mind that there is a repetitive structure for the constructor and deletor when freeing and instantiating pointers. So surely we can define a macro to do it.
When long chain pointers for dereferencing the appropriate inherited object, the long chain can be set once to a short one (to a pointer of a type, as in
JavaScript), and then use for the rest of the code the short one.
Finally, it rests us to show you polymorphism (their use or implementation). In this case, as we do and declare the interface in a separate part, will be easy. We only have to declare a handler interface, and then
propose specific definition for each type. When using, we will associate each instance of each type to the same handler type that they share, allowing thus us polymorphism.
So far we have been using either vars, objects and functions in the private or public part. That's not exactly true. With objects and vars it is, but not with functions, because functions while declared in the public part they are defined in the private part. But what if we do not declare a function in the public part but we define it in the separate file? Automatically converts into a private function. It is not necessary to handle it with a handler declared in the private part to use it, because as being defined in the same separate file, it can be used by other functions. So it has not too many sense to define a handler in the private structure for that private functions, because we already can use them, we don't need such a handler. The handler it is necessary in the public part because if not there will be no way to access functions defined as static in a separate file. So I think it is not necessary to show the use of private functions. Just define them in the same separate file and use them in that same file whenever you need. Define them as static also, in that way their names will not interfere in other function names in other files.
Object Type 8: a Previous Step Into Polymorphism.
So, polymorphism: to show you polymorphism (its use) I need two object types
which share the same interface. I will make for this reason the counterpart of Dif type we made at the beginning, that is, an object type Sum but with the same structure (interface) as the Dif type. Then I will make a virtual type or class or interface,
which means will be a structure type to define handlers for that other types of objects (Dif and Sum). That will allow as polymorphism.
Type declaration:
#ifndef sUM2
#define sUM2
struct sum2;
typedef struct sum2 *Sum2;
struct sum2{
int a, b;
int (*delete)(Sum2);
int (*sum)(Sum2);
};
Sum2 sum2();
#endif
Type definition:
#include <stdio.h>
#include <stdlib.h>
#include "constDef.h"
#include "Sum2.h"
static int delete(Sum2 s)
{ free(s);
return ERR0;
}
static int sum(Sum2 s)
{ return s->a+ s->b;
}
Sum2 sum2()
{ Sum2 s= malloc(sizeof(struct sum2));
s->delete= delete;
s->sum= sum;
return s;
}
Testing:
#include <stdio.h>
#include "constDef.h"
#include "Sum2.h"
int main()
{ Sum2 s= sum2();
s->a= 2;
s->b= 3;
printf("\na: %d, b: %d, a+ b: %d", s->a, s->b, s->sum(s));
if(s->delete(s)== ERR0)
{ s= NULL;
printf("\nobject freed");
}
return ERR0;
}
Object Type 9: Polymorphism.
Now, once we made the object type Sum2 (and tested it), counterpart of object type Dif at the beginning, we declare our virtual interface (but we do not define it):
#ifndef voPERATE
#define voPERATE
struct vOperate;
typedef struct vOperate *VOperate;
struct vOperate
{
int a, b;
int (*delete)(VOperate);
int (*operate)(VOperate);
};
#endif
Finally, we test our claim about polymorphism, using the virtual type just defined (or declared) and the object types 2 and 8
which fulfill the structure of the virtual type declared:
#include <stdio.h>
#include "constDef.h"
#include "Dif.h"
#include "Sum2.h"
#include "VOperate.h"
int main()
{ Sum2 s= sum2();
Dif d= dif();
VOperate vOp[]= {(VOperate)s, (VOperate)d, NULL};
int i;
for(i= 0; vOp[i]; i++)
{ vOp[i]->a= 2;
vOp[i]->b= 4;
printf("\na: %d, b: %d, a op b: %d", vOp[i]->a,
vOp[i]->b, vOp[i]->operate(vOp[i]));
}
for(i= 0; vOp[i]; i++)
{ if(vOp[i]->delete(vOp[i])== ERR0)
{ vOp[i]= NULL;
printf("\nobject freed");
}
}
return ERR0;
}
You see, we have made use of polymorphism characteristic
(or behaviour). And it is not necessary even that the method names
coincide, only what regards to the space on memory of the structure
(first an int, then another int, then a pointer, then another one). Of
course, the real meaning of the pointers must coincide. That is, when
dereferencing first pointer it is intended to free and not to operate,
etc.
That is the benefit of declaring the structure
(interface, type) for one hand and the definition for the other, that
polymorphism it is direct and straight (what it is direct and straight
it is the concept of virtual class).
The Final Breath... Conclusion and Remarks
We have seen so far a lot of the concepts of OO applied into C in a direct and straight manner, from scratch and plain C. The use of macros could be maybe available to the use or definition of constructors and deleters because it is somehow an automated process of programming, but it is not strictly necessary.
It is this last part from C which differentiates it from other programming languages regarding OO. C has not automated coupling of function definitions to its handlers, we must define for one hand the functions, somehow in a private, enclosed manner with static concept, and then the handlers in the structure interface, and then branch them
together in the constructor. Not only this, the use of any pointer needs to be branched accordingly in the constructor and freed in the deleter in the case of objects used by other objects. So this is basically the difference, the process of declaring the structure interface for one hand and defining elements for the other and branching them
together, which is automated in other OO languages (when you define functions inside class definition automatically there are handlers to use them (the dot terminology) and they are branched or coupled
together). This concept of separation of the structure interface and the
definition also allows the most direct appliance of the concept of virtual class or type, that in C it is more straight.
You have seen so far how we have been using several object types regarding the same kind of concept or operations (in that case sum and rest of two numbers), and played around composing different kinds of object types from the previous defined ones. So we see here clearly the concept of reuse of code or object types,
which it is inherent to OO programming. We can take whatever object type we have at hand and compose other object types we need using the previous ones, extending their capabilities either in the public or private part. We have seen also encapsulation of data or object functionality and reuse (black box). We have seen also multiple inheritance, extending or reusing from previous objects (more than one type) and how when
this multiple inheritance share data among the different types extended it is not a good idea to do it in the public part, but better to do it in the private part and define the structure for the public part. When
inheriting from different kinds of objects which not share data concepts, it is not necessary this step.
At the end, this is real C programming and real OO programming, all plain and from scratch. It is the first time I see C OO programming exposed in this manner,
and ever people who claims to be it possible to do OO programming with C or other languages not prepared to and not show the way, maybe it is because really they don't know how.
Here I show you. It is possible. It is not so difficult. It has its benefits (allows the benefits of OO into C). What it is really difficult it is to know how.