Object-Oriented Design in ANSI C

Hi guys!
Yes, I know about C++, about Python, Java, and others object-oriented programing language. But now I want talking only about ANSI C - yes, it is not OOP language, but when I read the book Schreiner, Axel-Tobias,.. Object oriented programming with ANSI-C - I am very interested in this idea!

ANSI C program I began write relatively recently. And I'm sorry if something would look corny and stupid. I look forward to your help, perhaps you will share your thoughts about it.

My English very bad - therefore please be patience!

For ease of presentation ideas I will only use the header files.

Okay, we need emulate new operator and delete() function with C++. We should be have initialization point for our objects. Our objects must be have class description. So, - Go!

File objectmode.h has class prototype, and new/delete emulation.
See the description here.

Now, for example, create simple class - human.
See the description here.

It's time to check how it works.
The main program can be found here, and Makefile - here.

Run result:
Code:
% ./oodmain

Manager.
	Age: 32.
	Name: Jim K..
Secretary.
	Age: 32.
	Name: Sara M..

New peaople.
Manager.
	Age: 21.
	Name: Dasha N..
    
%

What I want from you?
I am interested in your opinion about it! I am interested in your advice about it!

Thanks All.

P.S. You want to scream - scream. Do you want to insult - insult. Do you want to cry - it's your business. But do not forget to leave a comment about it.
 
The var_args constructor is a nice idea. In line 65 of objectmode.h you're redundantly checking for the pointers, since line 63 implies obj = p (therefore obj && p && ... <=> obj && ...)

I would put all the function pointers into a vtable-like structure, and add pointers to that structure in the class as member field. That way you have a constant member function price (pointer size) independent of the number of member funtions. It also allows easier checking for child/parent relations by storing the parent vtable pointer in each vtable (required to execute parent constructor/destructors automatically), as well as it allows function overloading by rewriting vtable pointers.
 
xibo said:
In line 65 of objectmode.h you're redundantly checking for the pointers, since line 63 implies obj = p (therefore obj && p && ... <=> obj && ...)

Oh, yes - you are right! Thanks.

xibo said:
I would put all the function pointers into a vtable-like structure, and add pointers to that structure in the class as member field. That way you have a constant member function price (pointer size) independent of the number of member funtions. It also allows easier checking for child/parent relations by storing the parent vtable pointer in each vtable (required to execute parent constructor/destructors automatically), as well as it allows function overloading by rewriting vtable pointers.

Yes, it's a good idea. Thanks, again. But I can not imagine how to do it. It would be nice to see your example.

O, and I find a good cheat sheet (and this) on this topic.

P.S. Come on guys - move, move is fun!.
 
You seem to want to reinvent the wheel, it's not to extinguish enthusiasm but someone already spent many time on that. This page contain some historical documents and sources (CFront). CFront was a tool to translate early C++ in C, but in a static way (large use of preprocessor macros) the variadic functions could be a solution but they are prone to memory corruption, expecially for a basic bunction like *new*. Perhaps a *new* function without initializing the ojects (no C++-like constructor with params, it only allocate memory with the object struct size) should be better, the initialization can be done with a class specific method (init).
 
freethread said:
You seem to want to reinvent the wheel, it's not to extinguish enthusiasm but someone already spent many time on that. This page contain some historical documents and sources (CFront). CFront was a tool to translate early C++ in C, but in a static way (large use of preprocessor macros) the variadic functions could be a solution but they are prone to memory corruption, expecially for a basic bunction like *new*. Perhaps a *new* function without initializing the ojects (no C++-like constructor with params, it only allocate memory with the object struct size) should be better, the initialization can be done with a class specific method (init).

No, I did not invent the wheel all over again. I do not want to use C++, that's all. So I need to learn how to structure your code in ANSI C the most convenient - an object-oriented approach is convenient, I think so. Thank you for the link, and the idea of CFront - I've got something to think about.
 
doorways said:
No, I did not invent the wheel all over again. I do not want to use C++, that's all. So I need to learn how to structure your code in ANSI C the most convenient - an object-oriented approach is convenient, I think so. Thank you for the link, and the idea of CFront - I've got something to think about.

It was not my intention to be offensive, I'm a C++ developer for many years now so my point of view is different and I forgot many things about C. I wrote last C code at the end of '90s for an HC16 micro and at the time I already was a C++ developer. Anyhow, I hope the CFront code and the related documents help you to find a path to a solution, also try a search with 'C with classes' phrase.

Good luck
 
Following is an elaborate functional version of what I wrote in my PM to you. It's a very basic skeleton and doesn't contain vtable, but it should be easily implemented. The macros defines entire class and the *new* operator (NEW_OBJPTR and NEW_OBJREF) are variadic macros that call constructor.

Code:
/**-------------------------------------
 * C with classes (simple attempt)
 *------------------------------------*/

#include <stdlib.h>
#include <memory.h>
 
/** Class definition -----------------*/
#define CLASS \
    typedef struct \
    {

#define CLASS_FIELDS \
        struct \
        {

#define CLASS_FIELD(fieldType, fieldName) \
            fieldType            fieldName;

#define CLASS_FIELDS_END \
        } data_members;

#define CLASS_END(className) \
    } className;
/*------------------------------------*/
	
/** Object definition ----------------*/
#define NEW_OBJPTR(className, classObj, ...) \
    className * classObj = (className *)malloc (sizeof (className)); \
    if (classObj) \
    { \
        memset (classObj, 0, sizeof (className)); \
        className##_ctor (classObj, __VA_ARGS__); \
    }

#define NEW_OBJREF(className, classObj, ...) \
    className classObj; \
    if (classObj) \
    { \
        memset (&classObj, 0, sizeof (className)); \
        className##_ctor (classObj, __VA_ARGS__); \
    }

#define DELETE_OBJ(classObj) \
    if (classObj) \
    { \
        free (classObj); \
        classObj = NULL; \
    }
/*------------------------------------*/

// Class definition: SimpleClass
CLASS
    CLASS_FIELDS
        CLASS_FIELD (int,        i)
        CLASS_FIELD (float,      f)
        CLASS_FIELD (long,       l)
    CLASS_FIELDS_END
CLASS_END (SimpleClass)

/** SimpleClass constructor */
void
SimpleClass_ctor (SimpleClass * pObj, int i1, int i2)
{}

/** SimpleClass destructor */
void
SimpleClass_dtor (SimpleClass * pObj)
{}

/** Entry point */
int
main (int iArgs, char * aArgs[])
{
    int iNum1 = 1;
    int iNum2 = 2;
    // Object creation
    // NEW_OBJPTR
    NEW_OBJPTR (SimpleClass, pObj, iNum1, iNum2)
    // DELETE_OBJ
    DELETE_OBJ (pObj)
}

FYI: this model is more or less similar to the ATL Window message implementation

The resulting (preprocessor expanded) "class object" in the example above is

Code:
typedef struct {
    struct {
        int i;
        float f;
        long l;
    } data_members;
} SimpleClass;


void
SimpleClass_ctor (SimpleClass * pObj, int i1, int i2)
{}


void
SimpleClass_dtor (SimpleClass * pObj)
{}


int
main (int iArgs, char * aArgs[])
{
    int iNum1 = 1;
    int iNum2 = 2;
	
    // NEW_OBJPTR
    SimpleClass * pObj = (SimpleClass *)malloc (sizeof (SimpleClass));
    if (pObj)
    {
        memset (pObj, 0, sizeof (SimpleClass));
        SimpleClass_ctor (pObj, iNum1, iNum2);
    }
    // DELETE_OBJ
    if (pObj)
    {
        free (pObj);
        pObj = ((void *)0);
    }
}

I compiled with cl, not tested in gcc but it should work as variadic macros are supported from version 3.x and above (if I'm not wrong).

The reason of the nested structs is to leave space for vtable (or struct-implemented methods), that is

Code:
typedef struct {
    struct {
        int i;
        float f;
        long l;
    } data_members;
    struct {
        void (* SimpleClass_ctor) (SimpleClass * pObj, int i1, int i2);
        void (* SimpleClass_dtor) (SimpleClass * pObj);
        ...
    } vtable; 
} SimpleClass;

PS: I don't know if there is C/C++ syntax highlight here.
 
Implementing constructors/destructors is simple and straightforward. But I recon this would get very messy with implementation of polymorphism/inheritance and typing templates (two most useful features of C++) syntax wise. Will make code harder to read extremely difficult to debug, probably major reason why C++ got created in first place.

P.S.
There is also Objective-C with its dynamic object interface that is a strict superset of ANSI C (not like C++).
 
expl said:
Implementing constructors/destructors is simple and straightforward. But I recon this would get very messy with implementation of polymorphism/inheritance and typing templates (two most useful features of C++) syntax wise. Will make code harder to read extremely difficult to debug, probably major reason why C++ got created in first place.

Agree. Perhaps simple hand-maintained classes, with no macros and one single struct without nested data and vtable structs, is better to directly access data and function members and to read source as real C, I added too much unwanted sugar in my last post.

Polymorphism and templates are too hard to implement, I never was in need to implement them in C and I'm not sure if there's a way to, CFront was designed for that reason. However with simple classes the code is the same as the original doorways's code, with the only difference in using variadic macros instead of variadic functions (in my opinion), in this way if you create an object to a class with wrong number of arguments you get a compile-time error, with variadic functions the result is undefined and it happens at run-time.
 
So, guys,
I read your comments, suggestions and criticism. Thanks. I read some articles on the Internet, too.

We can use two styles of OOD in ANSI C.
1-style, a style of C++ - there is a new operator and delete() function (see my first example). The disadvantage of this method has an unknown number of arguments to the constructor, and the universality of the new operator - which can lead to memory leakage.

2-style, a Object Pascal style - where the constructor and destructor are invoked directly from the object type. It is more convenient in this case. We do not have universal functions, and each object itself controls the memory leak. Constructor clearly indicate what arguments he wants to get - we do not need to use something like va_list.

The second style I like more.

As they say a lot of tutorials on Java Programming Language - access to the properties of an object is better carried out through methods of this object. Agree, so we can control to the object properties have always been the correct information.

Based on this recommendation - all properties of an object (instance variables) are always considered "Private". In the "Public", we describe the methods that control the properties of the object, etc. methods that are described in the "Private" - are not described in the vtable.

The example is the same, and see human.h human.c files. How to use it - oodmain.c and Makefile.

We can use something like inheritance.
See files employee.h and employee.c. How to use it - oodmain.c and Makefile.

Yes, ANS-C is not very suited to Object-Oriented Design. But I do not like C++ and Objective-C (a very bad syntax, as for me). Very good idea to use define to define the keywords, as shown freethread.

P.S. If you have a criticism, advice or suggestions - I'm glad to hear it.http://pastebin.com/9qAfxvpd
 
Main reason why people are not using objective implementations in C is that the compiler is not aware of your objective structure. This makes compiler optimizations weaker and debugging much harder. Unless you write your own translator on top of C Compiler (like Objective-C does) this is not going to be very practical. Besides C++ and Objective-C are both very refined languages with two very different ways of implementing object-oriented paradigm. If you expect to do any serious programming in future I would advice you to give yourself a chance to understand them from non critical point of view.
 
Hello again.
I want to show another way that I like.
The disadvantage of this method is the fact that the inheritance of the classes need to re-declare parent class methods .

Advantage is that when using the classes we do not need to duplicate the pieces of code, as in the previous example, example: e0->human->getage(e0->human) now it is always the case: w_employee.getage(e1).
And the virtual method table has same address for all objects, and dynamic memory is allocated only for the object's properties.

Implementation of the parent class (human): human.h and human.c
The implementation of the child-class (employee): employee.h and employee.c
Use this as an example: oodmain.c and Makefile

expl said:
Main reason why people are not using objective implementations in C is that the compiler is not aware of your objective structure. This makes compiler optimizations weaker and debugging much harder. Unless you write your own translator on top of C Compiler (like Objective-C does) this is not going to be very practical. Besides C++ and Objective-C are both very refined languages with two very different ways of implementing object-oriented paradigm. If you expect to do any serious programming in future I would advice you to give yourself a chance to understand them from non critical point of view.

Yes, I understand that. I understand that the ANSI-C procedural rather than object-oriented programming language. But now I am only interested in ANSI-C. And I want to somehow emulate OOP in ANSI-C to be able to write programs with a large number of included files and prietom function names do not conflict.

Opinion on the language C + +, Objective-C, Java and etc. - Yes, a subjective opinion - for each particular purpose should be to select a specific development language. Now I am only interested in ANSI-C.

Thank you all. If I have another idea - I'll let you know about them here. If you have ideas - tell me about them, please. Thank you.
 
Your OO solution is very elegant (in my opinion) and doesn't create confusion. I'm not an expert about C99 standard (and standards in general), so I had to dig a bit to find something about C struct member initialization as in:

Code:
/* Initialization (constructor / destructor). */
const struct w_human_vtable w_human = {
    .create  = create,  .destroy = destroy,
    ...

This doesn't works on all compilers, however this works in C99 compliant compilers as GCC and derived.
 
freethread said:
Your OO solution is very elegant (in my opinion) and don't create confusion. I'm not an expert about C99 standard (and standards in general), so I had to dig a bit to find something about C struct member initialization as in:

Code:
/* Initialization (constructor / destructor). */
const struct w_human_vtable w_human = {
    .create  = create,  .destroy = destroy,
    ...

this doesn't works on all compiler, however this works in C99 compliant compilers as gcc and derived.

I don't know why it did not work. Hmm. :\
I compiled this project as a standard c89 - it work.
Code:
    cc -Wall -std=c89 -c oodmain.c
    cc -Wall -std=c89 -c human.c
    cc -Wall -std=c89 -c employee.c
    cc -Wall -std=c89 -o oodmain oodmain.o human.o employee.o -O2

This initialize way provides in c89 standard. It works when I use the gcc compiler, too.

Code:
    gcc -Wall -std=c89 -c oodmain.c
    gcc -Wall -std=c89 -c human.c
    gcc -Wall -std=c89 -c employee.c
    gcc -Wall -std=c89 -o oodmain oodmain.o human.o employee.o -O2

Maybe you forgot to create a human.h header file? To make sure that you have a problem with the initialization, let's try to run this code.
Code:
%cc -Wall -std=c89 -o test test.c
%./test
If it works, then you probably made a mistake when copying the project files. If you can not solve this problem, I can send you the entire project by mail.

And thank you for your opinion, about my OOD solution!
 
doorways said:
I don't know why it did not work.

Wait, I've not tested it on a Unix machine and my knowledge of C is not up to date, really it's quite old. In other words I guess that the dot labeled struct members are not recognized by cl (MS) compiler.

Update:
The test.c doesn't compile in cl, as expected. The workaround is to initialize the structs without dot labels, paying attention to the right function reference order:
Code:
const struct my_struct courtesy = {
  .morning = hello,
  .evening = goodbye
};
become
Code:
const struct my_struct courtesy = {
  hello,
  goodbye
};
Your OO solution in C still remain valuable, anyway. I guess this only affects cl compiler, so unless you want a universal solution, this is not an issue but a specific compiler compliance lack.
 
freethread said:
Wait, I've not tested it on a Unix machine and my knowledge of C is not up to date, really it's quite old. In other words I guess that the dot labeled struct members are not recognized by cl (MS) compiler.

Update:
The test.c doesn't compile in cl, as expected. The workaround is to initialize the structs without dot labels, paying attention to the right function reference order:
Code:
const struct my_struct courtesy = {
  .morning = hello,
  .evening = goodbye
};
become
Code:
const struct my_struct courtesy = {
  hello,
  goodbye
};
Your OO solution in C still remain valuable, anyway. I guess this only affects cl compiler, so unless you want a universal solution, this is not an issue but a specific compiler compliance lack.

Perhaps this is a problem for your compiler. The с99 standard allows both to initialize the structure. сc compiler does not have a strict and allows you to do this even when using standard с89, even the use of single-line comments.

Your method of installing a bit uncomfortable ...
Code:
const struct my_struct courtesy = {
  hello, goodbye
};
that if the structure is to have a lot of arguments?
Code:
struct my_struct {
    char *name;
    int age;
    char *post;
    void (*morning)(char *);
    void (*evening)(char *);
};

You will need to initialize all elements in the correct order.

Code:
const struct my_struct courtesy = {
  NULL, 0, NULL, hello, goodbye
};

To me - only methods.
Code:
const struct my_struct courtesy = {
  .morning = hello,
  .evening = goodbye
};

This is useful if the structure has many elements.

On initialization of structures read more here.
 
The thing with making C act too OO using macros and things that only work on certain compilers is that it really does seem to be a better solution to use C++.
That said however, a typical OO approach with C works very well with things like GUI libraries such as Motif but also games. (Not many modern games are made using C these days which is a shame. :/

The following is how I would do an OO entity / component type system for a simple game complete with a solution for "virtual functions" for the update and draw.

It seems like a lot, but tbh even in C++ it would still need *some* boilerplate code. The cool thing is that once the Entity "class"/context is set up, the rest of the game objects are trivial to keep adding until you have the next Half-Life..

(May be buggy, no checks for failure etc...)

Entity
Code:
struct Entity
{
  int x;
  int y;
  void* context;
  void (*update_func)(void*);
  void (*draw_func)(void*);
};

struct Entity* entity_create(void* context)
{
  struct Entity* entity = 0;

  entity = (struct Entity*)malloc(sizeof(struct Entity));
  entity->x = 10;
  entity->y = 10;
  entity->context = context;
  entity->update_func = 0;
  entity->draw_func = 0;

  return entity;
}

void entity_update(struct Entity* entity)
{
  if(entity->update_func != 0)
  {
    entity->update_func(entity->context);
  }
}

void entity_draw(struct Entity* entity)
{
  if(entity->draw_func != 0)
  {
    entity->draw_func(entity->context);
  }
}

void entity_destroy(struct Entity* entity)
{
  free(entity);
}

Player
Code:
struct Player
{
  struct Entity* entity;
  int health;
};

void player_update(void* player);
void player_draw(void* player);

struct Player* player_create()
{
  struct Player* player = 0;

  player = (struct Player*)malloc(sizeof(struct Player));
  player->entity = entity_create(player);
  player->entity->update_func = player_update;
  player->entity->draw_func = player_draw;
  player->health = 100;

  return player;
}

void player_update(void* player)
{
  printf("Player Updating\n");
}

void player_draw(void* player)
{
  printf("Player Drawing\n");
}

void player_destroy(struct Player* player)
{
  entity_destroy(player->entity);
  free(player);
}

main
Code:
int main(int argc, char* argv[])
{
  struct Player* player = 0;

  player = player_create();
  entity_update(player->entity);
  entity_draw(player->entity);
  player_destroy(player);

  return 0;
}
 
kpedersen said:
The following is how I would do an OO entity / component type system for a simple game complete with a solution for "virtual functions" for the update and draw.

Thank you for your example, it is interesting.
 
GTK+ and Gnome libraries have been written with pure C in an OOP way, much as you are doing with your code, but are a very good piece of code to look for inspiration.
Having said that, your solution seems pretty good to me, and a few years ago I was teaching how to use C in a C++-like way using structs, casts, and static functions to make things private/public.
 
fluca1978 said:
GTK+ and Gnome libraries have been written with pure C in an OOP way, much as you are doing with your code, but are a very good piece of code to look for inspiration.

I always found it an inspiration to go and live in a cave!
:)
 
Just to chime in with a quick comment:

C can do pretty much the same object orientation C++ can, using things like void pointers, pointers to functions and whatnot. However, trying to emulate true object orientation (a la C++ or Java) with C can get quite messy, especially when things like inheritance come into play. If you really need this kind of thing you're probably better off using C++ (or Java) in the first place. Use the right language for the job.

Regards,

Fonz
 
Back
Top