Click here to Skip to main content
Click here to Skip to main content
Go to top

oocx (oo c programming implementation)

, 20 Jan 2014
Rate this:
Please Sign up or sign in to vote.
this article describes how to do oo programming with c (not c++)

introduction, the why's, and everything else

hello. c has been there for a while. now it seem's more outdated. now is the era of javascript, c#, etc. but here i am talking about c. why? because was the first language i learned. and because it is a classic. and because without it computations era would have not been as it is now. and because read somewhere it was possible to do oo programming with c. and i myself scratched my mind till i found a way to accomplish what somebody said once it was possible but he didn't show it how. finally i got it and i show you here. but it was not easy to accomplish.

so here i will talk about oo and c. if you are interested ok, you are welcome.

by the way, the x in oocx it is for unix, x11 or wahtever you want to be (even could be x of experience as in windows xp). it's purpose it is to make the name of this implementation unique.

what is oo and why c cannot have oo (or it's not easy or well prepared for)?

oo is a paradigm in programming. it means it's a way of programming. c it is a functional language. it means it is a language based on calls to functions. but at difference from javascript, c doesn't allow nested definitions of functions (but it allows of course call other functions inside one function definition). so in c we have for one hand the data structures and for the other the functions. they are separated, not toguether. in oo data structure and function definitions become toguether so is as if function definitions can be placed or stored inside data structures, so when instantiating in memory such a data structure, we will have not only the properties defined but also a bunch of references to the functionalities being defined. but not only this, there is also another important point here that makes oo not possible to apply in c. this other important point is the pointer this. when a functionality it is called through a referenced stored in the instantiated data structure (or object), it is not necessary to pass as a parameter the itself data structure instance (object) in order this functionality operate with data stored in that object. the functionality (code inside it) must have implicitly the address of the object from which it was called. that is not possible in c. in c we would have to pass the object itself as a parameter to the function call when calling through a reference stored in the object itself. that is, we will be repeating code, like this:

object1->funcName(object1);
object2->funcName(object2);

and this is not correct. instead, in pure oo, we should have:

object1->funcName();
object2->funcName();

the way i've found to accomplish oo in c quits that way (because it's a barrier i couldn't surpass) and focus in what is the specific characteristic of oo programming. at my point of view, this key factor about oo programming it is the distinction between static or shared data and none static or data specific to each object. that is, in oo we have types or classes and then instances of those types which are objects. the difference it is information kind. objects holds information specific to an object, while types (or classes) holds information that will be shared or accessed by all objects of a kind. it is with this point of view in mind i developed my own implementation of oo programming with c.

now first i will show the use, and then later, maybe, the implementation (although the source code it is available to download).

doing oo programming with oocx

ok. now i will show you how to use this implementation (oocx) and you will see how it actually does oo programming. for this purpose i will develop a type person and then another employee which derives or inherits from person extending its capabilities (adding other characteristics/properties or/and behaviour/functionality).

pay attention how in person type (class) we will have not only public but also private properties defined. you will see how in this implementation all of the functionality, when defined, it is private and it is only made publicly accessible when loaded through the use of a macro (you will see).

Let's begin by the public interface or header file (that is, information made accessible to the user of the type or class):

Person.h

#ifndef pERSON_H
#define pERSON_H

#include "oocx/oocx.h"

typedef struct person{
	Obj obj;
	int age;
	char *name;
} *Person;

extern Type const person;

#endif

/*
cmds:
	"getThoughts", char **thoughts
	"setThoughts", char *thoughts
	"setName", char *name
	"getName", char **name
	"setAge", int age
	"getAge", int *age
childs:
*/

first we see how we define a data structure which begins by a pointer of type Obj. this will be always like this in this implementation. any data structure beginning by a first member being a pointer of type Obj will be an object. following it, we (i) declare an int variable to store the age and a char * pointer to store the name.

you see. that's all. functionality it is not referenced here, only in a private form in the type (or) class definition (the file ending with .c). so how the user will know what functionality has this type of object? because we tell him through the use of comments. as you will see functionality is asked to objects through the use of commands (with a special function we will see). so in this file (header or ending with .h) we specify the commands and arguments they accept.

there is also another line of code which what it does it is to declare the existence of a pointer of type Type, which in fact it is the name of the type being defined.

Now I am going to show you the type (or class) definition file.

person.c

#include <stdio.h>
#include <stdarg.h>
#include "oocx/oocx.h"
#include "Person.h"

typedef struct this{
	struct person public;
	struct{
		char *thoughts;
	} private;
} *This;

static err setAge(Obj *obj, va_list vAL){
	This this= (This)obj;
	this->public.age= va_arg(vAL, int);
	return err0;
}

static err getAge(Obj *obj, va_list vAL){
	This this= (This)obj;
	int *age= va_arg(vAL, int *);
	*age= this->public.age;
	return err0;
}

static err setName(Obj *obj, va_list vAL){
	This this= (This)obj;
	this->public.name= va_arg(vAL, char *);
	return err0;
}

static err getName(Obj *obj, va_list vAL){
	This this= (This)obj;
	char **name= va_arg(vAL, char **);
	*name= this->public.name;
	return err0;
}

static err getThoughts(Obj *obj, va_list vAL){
	This this= (This)obj;
	char **thoughts= va_arg(vAL, char **);
	*thoughts= this->private.thoughts;
	return err0;
}

static err setThoughts(Obj *obj, va_list vAL){
	This this= (This)obj;
	this->private.thoughts= va_arg(vAL, char *);
	return err0;
}

TYPE(person, this, F(getThoughts) F(setThoughts) F(setAge) F(getAge) F(setName) F(getName) F(NULL), CH(NULL), NULL)</stdarg.h></stdio.h>

as you see functions are defined privately. functions we want to make publicly accessible through the use of commands are loaded in the call to the macro (at the end of the file). the call to the macro is always the same, first the name of the type, then the data structure tag declared in this file (which includes private and public information), then a NULL ending list with functionlities defined in this type that we want to load, then another NULL terminated list with childs we want to have this type or to inherit from (or extend), finally the address of a data structure with static data/variables (usually NULL).

now let's use or test this type (class) we have just defined. next it is the test file code:

personT.c

#include <stdio.h>
#include "oocx/oocx.h"
#include "Person.h"

err main(){
	typeInit();
	f((Obj *)type, "init", person);
	Person p1= NULL;
	f((Obj *)person, "new", &p1, NULL);
	p1->age= 30;
	p1->name= "roger";
	f((Obj *)p1, "setThoughts", "i want a big hamburguer");
	int age;
	char *name, *thoughts;
	f((Obj *)p1, "getAge", &age);
	f((Obj *)p1, "getName", &name);
	f((Obj *)p1, "getThoughts", &thoughts);
	printf("my name is %s and i am %d and now i am thinking: '%s'", name, age, thoughts);
	return err0;
}

/*
my name is roger and i am 30 and now i am thinking: 'i want a big hamburguer'
*/</stdio.h>

as you see it works. we have been able to define a type (or class) with two public variables, one private variable, and functionality to operate with the private and public variables.

you see how we can access (set or get) public variables either directly or through the use of functionality (you will see how when in the case of inherited public variables only through the use of functionality will be possible to set or get, that's the reason we (i) defined also setters and getters for the public vars, because we will need when inherited from this type in next type definition).

you see also, and that's very important, how it is not necessary to explicitly pass as a parameter the object itself when calling functionalities (in fact, that's f's job, that is, f will in fact pass as a parameter the address of the object when calling the functionality referenced by the command; we will talk more about f later).

now it's time to probe inheritance capabilities of this implementation. for that purpose i am going to develop a type employee that inherits from person and which will add some properties and/or functionality to it.

Employee.h

#ifndef eMPLOYEE_H
#define eMPLOYEE_H

#include "oocx/oocx.h"

typedef struct employee{
	Obj obj;
} *Employee;

extern Type const employee;

#endif

/*
cmds: 
	"setSalary", double salary
	"getSalary", double *salary
childs:
	person
*/

in this type we don't have any public property or variable, only the Obj kind pointer, which is mandatory if we want to declare the structure for an object in this implementation.

employee.c

#include <stdio.h>
#include <stdarg.h>
#include "oocx/oocx.h"
#include "Employee.h"
#include "Person.h"

typedef struct this{
	struct employee public;
	struct{
		double salary;
	} private;
} *This;

static err setSalary(Obj *obj, va_list vAL){
	This this= (This)obj;
	this->private.salary=va_arg(vAL,double);
	return err0;
}

static err getSalary(Obj *obj, va_list vAL){
	This this= (This)obj;
	double *salary= va_arg(vAL,double *);
	*salary= this->private.salary;
	return err0;
}

TYPE(employee, this, F(setSalary) F(getSalary) F(NULL), CH(person) CH(NULL), NULL)</stdarg.h></stdio.h>

you see how in the call to the macro we specify from who we inherit or, what it is the same, a NULL terminated list with the childs this type will have (this means in fact this implementation allows for multiple inheritance, each child in the list being a branch of the tree)

employeeT.c

#include <stdio.h>
#include "oocx/oocx.h"
#include "Employee.h"

err main(){
	typeInit();
	f((Obj *)type, "init", employee);
	Employee emp1= NULL;
	f((Obj *)employee, "new", &emp1, NULL);
	f((Obj *)emp1, "setName", "rober");
	f((Obj *)emp1, "setAge", 31);
	f((Obj *)emp1, "setThoughts", "i have a lot of work to do...");
	f((Obj *)emp1, "setSalary", 30000.);
	char *name, *thoughts;
	int age;
	double salary;
	f((Obj *)emp1, "getName", &name);
	f((Obj *)emp1, "getAge", &age);
	f((Obj *)emp1, "getSalary", &salary);
	f((Obj *)emp1, "getThoughts", &thoughts);
	printf("my name is %s and i am %d and i earn %.2f and now i am thinking: '%s'", name, age, salary, thoughts);
}

/*
my name is rober and i am 31 and i earn 30000.00 and now i am thinking: 'i have a lot of work to do...'
*/</stdio.h>

you see how this accomplishes inheritance because we are able to use inherited functionality and data capabilities by only specifying wich childs has this type in the call to the macro, that is, we don't have to redefine again functionality or data. you see the level of handcode it is similar to a classic oo language.

multiple inheritance

following the example series before, i will develop a new type (spyderEmployee) which accomplishes multiple inheritance. it will inherit from employee but also from spyder, which will be a new type i will develop.

first i show you in brieve the code for spyder type (there will not be a specific test file for this type because it will tested when testing spyderEmployee type):

Spyder.h

#ifndef sPYDER_H
#define sPYDER_H

#include "oocx/oocx.h"

typedef struct spyder{
	Obj obj;
} *Spyder;

extern Type const spyder;

#endif

/*
cmds:
	"setPoisonLevel", double poisonLevel
	"getPoisonLevel", double *poisonLevel
childs:
*/

spyder.c

#include <stdio.h>
#include <stdarg.h>
#include "oocx/oocx.h"
#include "Spyder.h"

typedef struct this{
	struct spyder public;
	struct{
		double poisonLevel;
	} private;
} *This;

static err getPoisonLevel(Obj *obj, va_list vAL){
	This this= (This)obj;
	double *poisonLevel= va_arg(vAL, double *);
	*poisonLevel= this->private.poisonLevel;
	return err0;
}

static err setPoisonLevel(Obj *obj, va_list vAL){
	This this= (This)obj;
	this->private.poisonLevel= va_arg(vAL, double);
	return err0;
}

TYPE(spyder, this, F(getPoisonLevel) F(setPoisonLevel) F(NULL), CH(NULL), NULL)</stdarg.h></stdio.h>

now i show you the code for spyderEmployee, the type which inherits from employee (which in turn inherits from person) but also from spyder:

SpyderEmployee.h

#ifndef sPYDEReMPLOYEE_H
#define sPYDEReMPLOYEE_H

#include "oocx/oocx.h"

typedef struct spyderEmployee{
	Obj obj;
} *SpyderEmployee;

extern Type const spyderEmployee;

#endif

/*
cmds:
	"setSuperHeroName", char *name
	"getSuperHeroName", char **name
childs:
	spyder, employee
*/

spyderEmployee.c

#include <stdio.h>
#include <stdarg.h>
#include "oocx/oocx.h"
#include "SpyderEmployee.h"
#include "Spyder.h"
#include "Employee.h"

typedef struct this{
	struct spyderEmployee public;
	struct{
		char *superHeroName;
	} private;
} *This;

static err setSuperHeroName(Obj *obj, va_list vAL){
	This this= (This)obj;
	this->private.superHeroName= va_arg(vAL, char *);
	return err0;
}

static err getSuperHeroName(Obj *obj, va_list vAL){
	This this= (This)obj;
	char **superHeroName= va_arg(vAL, char **);
	*superHeroName= this->private.superHeroName;
	return err0;
}

TYPE(spyderEmployee, this, F(setSuperHeroName) F(getSuperHeroName) F(NULL), CH(spyder) CH(employee) CH(NULL), NULL)</stdarg.h></stdio.h>

it is not mandatory when multiple inheriting to add new code. we can just make a multiple inheritance to joint toguether different capabilities without adding any other functionality or property to the type. in this case, however, we (i) have added one property (private) and setter and getter for this property (we can say this property borns from the union of the two multiple-inherited types, that is if we have the conjunction between spyder and employee, we have a spyderEmployee which happens to be like a super employee and all super something have a nick name).

and now the code for the test file for this type:

spyderEmployeeT.c

#include <stdio.h>
#include "oocx/oocx.h"
#include "SpyderEmployee.h"

err main(){
	typeInit();
	f((Obj *)type, "init", spyderEmployee);
	SpyderEmployee spEmp1= NULL;
	f((Obj *)spyderEmployee, "new", &spEmp1, NULL);
	f((Obj *)spEmp1, "setAge", 24);
	f((Obj *)spEmp1, "setName", "raul");
	f((Obj *)spEmp1, "setThoughts", "I fill great!!!");
	f((Obj *)spEmp1, "setSalary", 24000.);
	f((Obj *)spEmp1, "setPoisonLevel", 56.);
	f((Obj *)spEmp1, "setSuperHeroName", "the terrible employee");
	char *name, *thoughts, *superHeroName;
	int age;
	double salary, poisonLevel;
	f((Obj *)spEmp1, "getName", &name);
	f((Obj *)spEmp1, "getAge", &age);
	f((Obj *)spEmp1, "getThoughts", &thoughts);
	f((Obj *)spEmp1, "getSalary", &salary);
	f((Obj *)spEmp1, "getPoisonLevel", &poisonLevel);
	f((Obj *)spEmp1, "getSuperHeroName", &superHeroName);
	printf("i am %s and i am %d. my poison level is %.2f and my salary is %.2f. now i am thinking: '%s' and I am known as '%s'.\n", name, age, poisonLevel, salary, thoughts, superHeroName);
	return err0;
}

/*
i am raul and i am 24. my poison level is 56.00 and my salary is 24000.00. now i am thinking: 'I fill great!!!' and I am known as 'the terrible employee'.
*/</stdio.h>

so keep in mind that the code for spyderEmployee it is short (and would have been shorter without adding any new property or functionality), but it is capable of all of the multiple-inherited functionality and data coded in spyder, employee and person. so we effectively have achieved multiple-inheritance in this implementation, because we can distribute code definition and data capabilities through a series of layers structured in a tree way.

polymorphism

this implementation use polimorphysm all the time because all the time it uses the same interface to call functionalities, that is, functionalities are asked through commands to objects with the use of f function. So there is no problem in having an array of object references of different types and ask all them, through an iterating loop and with the use of f a same unique command. In the case the object understands the command it will execute functionality associated (whatever it is, could be one in one type of object and another one in an object of a different type, hence polimorphism). In case the object does not understand command, it will do nothing.

how this implementation works

objects are in a layer and types in another, above. the connection between and object and its type its in Obj obj variable declared as a first member of any object. when f is given a command to look for in the type of the object, it can do it because it has the acces through Obj obj variable. In the type of an object it is where all static information or information shared by all objects of a kind it is stored. This are function pointers to functions defined and loaded with the macro in the type, and also commands, which are the same as local function identifiers used in the type definition file. This conversion between function identifiers used in the type definition file and the commands it is done by the macro.

Obj obj it is a pointer wich points to a structure that holds the access to the type but also holds information about childs and parent of an object. That is, an object would be connected in a tree like way to other objects, this means an object can have child objects which at the same time those can have other childs etc. when defining a type, we specify in the macro which kind of objects must be childs of the object type being defined (we do this in the macro).

so at the end we have a tree like structure of objects connected, having a pinnacle. each object of the tree like structure will be connected to its type also (in a layer above). then when a command is given to the pinnacle of this tree like structure, matches for the command in the types of objects are looked for until one is found, then functionality associated (and stored in the type) to that command executed, passing as parameter the object itself. this is done by f function.

work to do

i will keep updating this article (in fact, this is an update). i will update it with new development but also with more explanations about how the implementation work (and also source code). for the moment that's it, a taste. hope you liked it. good bye.

License

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

Share

About the Author

rogerGomez
Web Developer
Spain Spain
it seems now i am a little bit of a software developer. in fact, i like front-end development with javascript, css and html. i like converting an image of a page layout toguether with some func specs into an actual html document with the demanded functionality implemented with javascript.
Follow on   Twitter   Google+   LinkedIn

Comments and Discussions

 
QuestionAnother article attempting similar concept PinmemberKenneth Kasajian28-Jan-14 20:56 
SuggestionProfiling data and reasoning? [modified] PinmemberCity17Citizen17-Jan-14 22:16 
GeneralRe: Profiling data and reasoning? [modified] Pinmemberroger gomez castells20-Jan-14 2:31 
GeneralGood explanation but we should use the right tool. [modified] Pinmemberkwanti17-Dec-13 22:41 
GeneralMy vote of 3 PinmemberJon4-Nov-13 2:58 
GeneralRe: My vote of 3 PinprofessionalMarco Bertschi24-Nov-13 13:24 

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

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

| Advertise | Privacy | Mobile
Web03 | 2.8.140922.1 | Last Updated 20 Jan 2014
Article Copyright 2013 by rogerGomez
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid