Click here to Skip to main content
15,879,184 members
Articles / Programming Languages / C

Plain C OO Programming (How To)

Rate me:
Please Sign up or sign in to vote.
4.93/5 (20 votes)
29 Oct 2013CPOL13 min read 37.8K   45   21
Plain C, not any macro code definition. C OO rocks!

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:

C++
/*    VarInt.h//////////////////////////////////////////////////// 
*/

#ifndef vARiNT
#define vARiNT

struct varInt;
typedef struct varInt *VarInt;

struct varInt
{    int a;
    /*    destructor
    */
    int (*delete)(VarInt);        
};

/*    constructor
*/
VarInt varInt();

#endif
/*    ////////////////////////////////////////////////////VarInt.h
*/

Now the class definition:

C++
/*    VarInt.c////////////////////////////////////////////////////
*/

#include <stdio.h>
#include <stdlib.h>
#include "constDef.h"
#include "VarInt.h"

/*    we define functions
*/
static int delete(VarInt vInt1)
{    /*    there is no pointers to free in, so we free object.
    */
    free(vInt1);
    return ERR0;
}

/*    define the constructor.
*/
VarInt varInt()
{    VarInt vInt1= malloc(sizeof(struct varInt));
    /*    initzialise pointers in its inside
    */
    vInt1->delete= delete;
    return vInt1;
}

/*    ////////////////////////////////////////////////////VarInt.c
*/

It rests us to define constDef.h which will be global for all files, used around:

C++
/*    constDef.h////////////////////////////////////////////////////
*/

#ifndef CONSTdEF
#define CONSTdEF

#define TRUE 1
#define FALSE 0
#define ERR0 0
#define ERR1 1

#endif

/*    ////////////////////////////////////////////////////constDef.h
*/

Now it's time to test our object:

C++
/*    testVarInt.c////////////////////////////////////////////////////
*/

#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;
}

/*    ////////////////////////////////////////////////////testVarInt.c
*/

Now we see what we get (compiled with MinGW for WindowsXP with 'cc testVarInt.c VarInt.c' and executing 'a.exe'):

C++
/*    output

a in vInt1 is 5
vInt1 freed
*/

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:

C++
/*    Dif.h////////////////////////////////////////////////////////////////
*/

#ifndef dIF
#define dIF

struct dif;
typedef struct dif *Dif;

struct dif
{    int a, b;
    int (*delete)(Dif);
    int (*difference)(Dif);
};

Dif dif();

#endif

/*    /////////////////////////////////////////////////////////Dif.h
*/

Now the type definition:

C++
/*    Dif.c////////////////////////////////////////////////////
*/

#include <stdio.h>
#include <stdlib.h>
#include "constDef.h"
#include "Dif.h"

/*    define functions
*/

static int delete(Dif df1)
{    /*    we free any pointer in its inside,
        apart from function methods.
        there are no, so we free object.
    */
    free(df1);
    return ERR0;
}

static int difference(Dif df1)
{    return df1->b- df1->a;
}

Dif dif()
{    Dif df1= malloc(sizeof(struct dif));
    /*    initzialize pointers
    */    
    df1->delete= delete;
    df1->difference= difference;
    return df1;
}

/*    ////////////////////////////////////////////////////Dif.c
*/

Finally the testing for the object type:

C++
/*    testDif.c////////////////////////////////////////////////////
*/

#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));
    /*    we free object
    */
    if(df1->delete(df1)== ERR0)
    {    df1= NULL;
        printf("\nobject freed");
    }
    return ERR0;
}

/*    ////////////////////////////////////////////////////testDif.c
*/

And the output will be, after compiling:

C++
/*    output

difference is 2
object freed
*/

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:

C++
/*    SumDif.h////////////////////////////////////////////////////
*/

#ifndef sUMdIF
#define sUMdIF

#include "Dif.h"

struct sumDif;
typedef struct sumDif *SumDif;

struct sumDif
{    Dif df;
    int (*delete)(SumDif);
    /*    we add one more method
    */
    int (*sum)(SumDif);
};

SumDif sumDif();

#endif

/*    ////////////////////////////////////////////////////SumDif.h
*/

Second, type definition:

C++
/*    SumDif.c////////////////////////////////////////////////////
*/

#include <stdio.h>
#include <stdlib.h>
#include "constDef.h"
#include "SumDif.h"

/*    define methods
*/

static int delete(SumDif sDf)
{    /*    there is one pointer to free
    */
    Dif df= sDf->df;
    if(df->delete(df)== ERR0)
    {    sDf->df= NULL;
    }
    /*    now we free object itself
    */
    free(sDf);
    return ERR0;
}

static int sum(SumDif sDf)
{    Dif df= sDf->df;
    return df->a+ df->b;
}

/*    the constructor
*/

SumDif sumDif()
{    SumDif sDf= malloc(sizeof(struct sumDif));
    /*    initzialise pointers
    */
    sDf->df= dif();
    sDf->delete= delete;
    sDf->sum= sum;
    return sDf;
}

/*    ////////////////////////////////////////////////////SumDif.c
*/

Third,  test the extended object type:

C++
/*    testSumDif.c////////////////////////////////////////////////////
*/

#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));
    /*    we free object
    */
    if(sDf->delete(sDf)==ERR0)
    {    sDf= NULL;
        printf("\nobject freed");
    }
    return ERR0;
}

/*    ////////////////////////////////////////////////////testSumDif.c
*/

Forth, check the output of the testing file:

C++
/*    output

a is    4
b is    2
difference is   -2
sum is  6
object freed
*/

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:

C++
/*    VarInt2.h////////////////////////////////////////////////////
*/

#ifndef vARiNT2
#define vARiNT2

struct varInt2;
typedef struct varInt2 *VarInt2;

struct varInt2
{    /*    the methods, no public vars
    */
    int (*delete)(VarInt2);
    int (*set)(VarInt2, int);
    int (*get)(VarInt2);

    /*    the private part, a pointer to void.
        the user must not do nothing with it,
        but the object must carry with it to operate.
    */
    void *private;
};

VarInt2 varInt2();

#endif

/*    ////////////////////////////////////////////////////VarInt2.h
*/

Second, the type definition:

C++
/*    VarInt2.c////////////////////////////////////////////////////
*/

#include <stdio.h>
#include <stdlib.h>
#include "constDef.h"
#include "VarInt2.h"

/*    we declare private data
*/

struct private;
typedef struct private *Private;

struct private
{    int a;
};

/*    the methods
*/

static int delete(VarInt2 vInt)
{    /*    we must free the private part (allocated dynamically, as it will see
        in the constructor, more below).
    */
    /*    the private part it has no pointers in it to free, so we directly
        free it. if no, firts free pointers.
    */
    free(vInt->private);
    /*    now we are done and we free the object
    */
    free(vInt);
    return ERR0;
}

static int set(VarInt2 vInt, int a)
{    /*    a handler to private
    */
    Private pr= vInt->private;
    pr->a= a;
    return ERR0;
}

static int get(VarInt2 vInt)
{    Private pr= vInt->private;
    return pr->a;
}

/*    the constructor
*/

VarInt2 varInt2()
{    VarInt2 vInt= malloc(sizeof(struct varInt2));
    /*    initzialise pointers
    */    
    vInt->delete= delete;
    vInt->get= get;
    vInt->set= set;
    Private pr= vInt->private= malloc(sizeof(struct private));
    /*    here we don't use pr because there are no pointers to
        initialize inside private structure.
    */
    return vInt;
}

/*    ////////////////////////////////////////////////////VarInt2.c
*/

Third, the testing of our object type:

C++
/*    testVarInt2.c////////////////////////////////////////////////////
*/

#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));
    /*    we free object.
    */
    if(vInt->delete(vInt)==ERR0)
    {    vInt= NULL;
        printf("\n--object freed--");
    }
    return ERR0;
}

/*    ////////////////////////////////////////////////////testVarInt2.c
*/

Fourth, the compiling command ('cc testVarInt2.c VarInt2.c') and the execution ('a.exe'):

C++
/*    output

vInt is 2
now vInt is     3
--object freed--
*/

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):

C++
/*    Sum.h/////////////////////////////////////////////////
*/

#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;
};

/*    constructor
*/
Sum sum();

#endif

/*    /////////////////////////////////////////////////Sum.h
*/

Second, definition:

C++
/*    Sum.c////////////////////////////////////////////////
*/

#include <stdio.h>
#include <stdlib.h>
#include "constDef.h"
#include "Sum.h"
#include "SumDif.h"

/*    We declare the private structure
*/

struct private;
typedef struct private *Private;

struct private
{    SumDif sD;
};

/*    define methods
*/

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);
}

/*    constructor
*/

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;
}
/*    /////////////////////////////////////////////////Sum.c
*/

Third, testing:

C++
/*    testSum.c///////////////////////////////////////
*/

#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;
}

/*    testSum.c////////////////////////////////////////////
*/

/*      output

a: 4, b: 4, sum: 8
// object type Sum freed //
*/

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:

C++
/*    SumDif2.h/////////////////////////////////////////////////////
*/

#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

/*    //////////////////////////////////////////////////////SumDif2.h
*/

Type (or class) definition:

C++
/*    SumDif2.c///////////////////////////////////////
*/

#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;
}

/*    //////////////////////////////////////SumDif2.c
*/

Testing our object:

C++
/*    testSumDif2.c/////////////////////////////////////
*/

#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;
}    

/*    /////////////////////////////////////////testSumDif2.c
*/

/*      output

a: 3, b: 1, sum: 4, dif(b- a): -2
*/

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:

C++
/*    SumDif2Bis.h//////////////////////////////////////////
*/

#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

/*    ///////////////////////////////////////////SumDif2Bis.h
*/

Type (or class) definition:

C++
/*    SumDif2Bis.c///////////////////////////////////////////////
*/

#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;
}

/*    ///////////////////////////////////////////////SumDif2Bis.c
*/

Testing:

C++
/*    testSumDif2Bis.c////////////////////////////////////////
*/

#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;
}

/*    ////////////////////////////////////////testSumDif2Bis.c
*/

Compile: 'C:\...>cc testSumDif2Bis.c SumDif2Bis.c Sum.c Dif.c SumDif.c'.

C++
/*    output:

a: 1, b: 4, b- a: 3, b+ a: 5
*/

 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:

C++
 /*    Sum2.h/////////////////////////////
*/

#ifndef sUM2
#define sUM2

struct sum2;
typedef struct sum2 *Sum2;
struct sum2{
    int a, b;
    int (*delete)(Sum2);
    int (*sum)(Sum2);
};

Sum2 sum2();

#endif

/*    //////////////////////////////Sum2.h
*/

Type definition:

C++
/*    Sum2.c///////////////////////////////
*/

#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;
}

/*    //////////////////////////////Sum2.c
*/

Testing:

C++
/*    testSum2.c//////////////////////
*/

#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;
}

/*    ////////////////////////////testSum2.c
*/

/*    output:

a: 2, b: 3, a+ b: 5
object freed
*/

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): 

C++
/*    VOperate.h//////////////////////////////
*/

#ifndef voPERATE
#define voPERATE

struct vOperate;
typedef struct vOperate *VOperate;

struct vOperate
{
    int a, b;
    int (*delete)(VOperate);
    int (*operate)(VOperate);
};

/*    there is no constructor
*/

#endif

/*    //////////////////////////VOperate.h
*/

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:

C++
/*    testVOperate.c/////////////////////
*/

#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;
}

/*    ///////////////////////testVOperate.c
*/

/*    output:

a: 2, b: 4, a op b: 6
a: 2, b: 4, a op b: 2
object freed
object freed
*/

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Spain Spain
bla bla bla bla bla bla bla bla bla

Comments and Discussions

 
QuestionA second article on that, much more powerfull and thorough, have been published (by me) Pin
rog_gc25-Oct-13 21:41
professionalrog_gc25-Oct-13 21:41 
GeneralImmense overhead Pin
waleri8-Jul-13 20:06
waleri8-Jul-13 20:06 
GeneralRe: Immense overhead Pin
rog_gc8-Jul-13 21:24
professionalrog_gc8-Jul-13 21:24 
excuse me, about the points you mention, the most to have to worry about for me would be third. but at first hand i am not conscious about passing any structure by value. all structures are allocated dinamically and hence, passed as pointer to that mem space. can you show me the code where you think a structure has been passed by value? I will revise, but as i have said, i don't think this occurs.

So your third would be very big problem if that really happened in my code, but I don't think it happens.

The other points I don't think they are something to worry much about. At the end what I do it is to allocate some memory (yes, but I think it is something common in nowadays computing...) and assign to some variables wich coincid are pointers a value. That's all, terrifying all. So please.... measure your astonishing surprise about terrible overhead. I don't think so!!!

To be more precise i would make a critic or remark a flaw i see in what i have presented. First notice, as i have already said in some comment, it lacks static vars and methods (static as in modern OO terminology), that is, class members. I will resolve this in next article (yes, there will be a continuation of that if possible fixing what i am going to mention now).

Second, the "big" flaw at my understanding. In the concept of inheritance presented in this work, or use of other objects by objects, I must remark it's different from conventional OO in the sense that in other languages you can attack or use methods inherited directly, you don't have to make a long derreference chain of pointers till arrive at the object wich has the method inheritted you want. Here you have, you cannot derreference or use a method directly if that has been inherited. So in next article, if that occurs, i will resolve this, fix it, toguether with the lack of class static members.

Thanks for reading, but sincerely i don't catch your worry about overhead. Maybe if tested in a production environment we would can chech if you are right or not about it.
GeneralRe: Immense overhead Pin
waleri12-Jul-13 9:04
waleri12-Jul-13 9:04 
GeneralRe: Immense overhead Pin
rog_gc12-Jul-13 21:45
professionalrog_gc12-Jul-13 21:45 
GeneralRe: Immense overhead Pin
studleylee14-Jul-13 8:53
studleylee14-Jul-13 8:53 
GeneralRe: Immense overhead Pin
waleri14-Jul-13 19:10
waleri14-Jul-13 19:10 
GeneralRe: Immense overhead Pin
studleylee15-Jul-13 9:35
studleylee15-Jul-13 9:35 
GeneralRe: Immense overhead Pin
studleylee15-Jul-13 9:43
studleylee15-Jul-13 9:43 
GeneralRe: Immense overhead Pin
waleri21-Jul-13 21:51
waleri21-Jul-13 21:51 
GeneralRe: Immense overhead Pin
studleylee14-Jul-13 9:03
studleylee14-Jul-13 9:03 
GeneralRe: Immense overhead Pin
waleri21-Jul-13 22:00
waleri21-Jul-13 22:00 
GeneralRe: Immense overhead Pin
rog_gc21-Jul-13 23:18
professionalrog_gc21-Jul-13 23:18 
GeneralRe: Immense overhead Pin
Rob Grainger29-Oct-13 23:20
Rob Grainger29-Oct-13 23:20 
GeneralRe: Immense overhead Pin
rog_gc3-Nov-13 1:00
professionalrog_gc3-Nov-13 1:00 
GeneralMy vote of 4 Pin
Brainhack6-Jul-13 0:51
Brainhack6-Jul-13 0:51 
GeneralRe: My vote of 4 Pin
rog_gc6-Jul-13 7:03
professionalrog_gc6-Jul-13 7:03 
QuestionThanks for good FeedBack!!! Pin
rog_gc5-Jul-13 23:58
professionalrog_gc5-Jul-13 23:58 
GeneralMy vote of 5 Pin
Manfred Rudolf Bihy5-Jul-13 11:14
professionalManfred Rudolf Bihy5-Jul-13 11:14 
GeneralMy vote of 5 Pin
damian-piatkowski5-Jul-13 11:10
damian-piatkowski5-Jul-13 11:10 
GeneralRe: My vote of 5 Pin
rog_gc6-Jul-13 7:04
professionalrog_gc6-Jul-13 7:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.