73

Is there a way to write OO-like code in the C programming language?


See also:

Found by searching on "[c] oo".

Community
  • 1
  • 1
prinzdezibel
  • 10,529
  • 16
  • 52
  • 61

10 Answers10

60

The first C++ compiler ("C with classes") would actually generate C code, so that's definitely doable.

Basically, your base class is a struct; derived structs must include the base struct at the first position, so that a pointer to the "derived" struct will also be a valid pointer to the base struct.

typedef struct {
   data member_x;
} base;

typedef struct {
   struct base;
   data member_y;
} derived;

void function_on_base(struct base * a); // here I can pass both pointers to derived and to base

void function_on_derived(struct derived * b); // here I must pass a pointer to the derived class

The functions can be part of the structure as function pointers, so that a syntax like p->call(p) becomes possible, but you still have to explicitly pass a pointer to the struct to the function itself.

UncleZeiv
  • 17,080
  • 6
  • 46
  • 74
  • 8
    This doesn't explain how method overriding would work in C. How can you override function_on_base to access derived's memeber_y as you can in C++ polymorphic calls? – John K Oct 30 '12 at 15:50
  • Overriding is not possible in C. – Patrick Collins Jan 08 '14 at 15:40
  • 10
    This answer is incorrect. Passing a `struct derived*` to `function_on_base` will not compile; `struct derived*` is a different type than `struct base*` even if the address is correct; however, if you cast the pointer from `derived*` to `base*`, it will work (but you'll miss out on compile-time type checking and instead get a crash at runtime). @PatrickCollins Overriding _is_ possible in C: http://pastebin.com/W5xEytbv – weberc2 Jul 03 '14 at 18:36
  • @JohnK See above comment. – weberc2 Jul 03 '14 at 18:48
  • @weberc2 Right you are, I'm not really sure what I was thinking when I wrote that. I might have had "overloading" in mind, which your paste also suggests. – Patrick Collins Jul 04 '14 at 04:03
  • @PatrickCollins I actually never remember which phrase is correct, and I use them interchangeably when referring to the mechanism illustrated in my paste. :) – weberc2 Jul 04 '14 at 18:15
  • @weberc2 great example. Thanks! – rr- Oct 18 '14 at 22:00
  • Sadly this involves casting pointers and then dereferencing them, which is a strict aliasing violation and invokes UB – Samuel Peter Feb 27 '17 at 09:31
  • couldn't you use a union for all your descendant types, each having the base struct as the first member in packing order? – Garet Claborn Sep 07 '17 at 03:37
52

Common approach is to define struct with pointers to functions. This defines 'methods' which can be called on any type. Subtypes then set their own functions in this common structure, and return it.

For example, in linux kernel, there is struct:

struct inode_operations {
    int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
    struct dentry * (*lookup) (struct inode *,struct dentry *, 
                               struct nameidata *);
    ...
};

Each registered type of filesystem then registers its own functions for create, lookup, and remaining functions. Rest of code can than use generic inode_operations:

struct inode_operations   *i_op;
i_op -> create(...);
Peter Štibraný
  • 31,128
  • 15
  • 85
  • 114
  • 1
    That's basically how cfront (the original C++ compiler) converted C++ to C that was then compiled with pcc. I learned a lot about how this worked dealing with core files from that mess. – Paul Tomblin Feb 07 '09 at 16:30
  • This isn't C++ style polymorphism. A big advantage of polymorphism over function pointers is that polymorphism allows for data binding, while function pointers does not. In this case, there is no opportunity for data binding, it's just a struct of function pointers. Also, I think the struct is superfluous in this comment. – user2445507 Jun 25 '18 at 19:52
30

C++ is not that far from C.

Classes are structures with a hidden pointer to a table of function pointers called VTable. The Vtable itself is static. When types point to Vtables with the same structure but where pointers point to other implementation, you get polymorphism.

It is recommended to encapsulate the calls logic in function that take the struct as parameter to avoid code clutter.

You should also encapsulcte structures instantiation and initialisation in functions (this is equivalent to a C++ constructor) and deletion (destructor in C++). These are good practice anyway.

typedef struct
{
   int (*SomeFunction)(TheClass* this, int i);
   void (*OtherFunction)(TheClass* this, char* c);
} VTable;

typedef struct
{
   VTable* pVTable;
   int member;

} TheClass;

To call the method:

int CallSomeFunction(TheClass* this, int i)
{
  (this->pVTable->SomeFunction)(this, i);
}
Samuel Peter
  • 3,696
  • 2
  • 29
  • 36
thinkbeforecoding
  • 6,420
  • 1
  • 27
  • 31
  • The example is incomplete. There is no explanation for how to implement a new class with different members (besides just the int). In this case, it seems that all of the "polymorphic" objects will have the same int member. What if you want to define classes with different members? I think in that case your struct should have a void pointer to data members, instead of a concrete int. – user2445507 Jun 25 '18 at 19:55
  • @user2445507 the example implements a vtable which calls functions at runtime, exactly what c++ would do behind the scenes if you create a class. – Niclas Larsson Oct 03 '18 at 08:00
  • @NiclasLarsson In C++ it is possible to create another class inheriting the pure virtual functions, but with different member variables. Then, the virtual function implemented in that class can use those member variables. This is a key aspect of polymorphism. The example does not demonstrate how to do this. For this example, you might as well just use function pointer. – user2445507 Oct 05 '18 at 19:29
17

I looked at everyone elses' answers and came up with this:

#include <stdio.h>

typedef struct
{
    int (*get)(void* this);
    void (*set)(void* this, int i);
    int member;

} TheClass;

int Get(void* this)
{
    TheClass* This = (TheClass*)this;
    return This->member;
}

void Set(void* this, int i)
{
    TheClass* This = (TheClass*)this;
    This->member = i;
}

void init(TheClass* this)
{
    this->get = &Get;
    this->set = &Set;
}

int main(int argc, char **argv)
{
    TheClass name;
    init(&name);
    (name.set)(&name, 10);
    printf("%d\n", (name.get)(&name));
    return 0;
}

I hope that answers some questions.

red_hax0r
  • 179
  • 1
  • 2
  • 3
    Good example. Would be even better if you had 2 "derived" classes with different init / get / set. "private" members / functions can be done with [opaque structs](http://stackoverflow.com/questions/3965279/opaque-c-structs-how-should-they-be-declared). Naming convention is also important: `mylib_someClass_aMethod(this)` is a good possibility. – Ciro Santilli新疆棉花TRUMP BAN BAD Apr 29 '16 at 10:33
  • This looks neat. I put this into a c file and compiled alright for an ARM micro controller. I am not getting far yet but just like to drop a note as an acknowledgement to red_hax0r's post. – coarist Dec 12 '17 at 16:15
  • 1
    where is polymorphism? – Ebru Yener Jan 24 '18 at 15:01
12

Appendix B of the article Open Reusable Object Models, by Ian Piumarta and Alessandro Warth of VPRI is an implementation of an Object model in GNU C, about 140 lines of code. It's a fascinating read !

Here's the uncached version of the macro that sends messages to objects, using a GNU extension to C (statement expression):

struct object;

typedef struct object *oop; 
typedef oop *(*method_t)(oop receiver, ...);

//...

#define send(RCV, MSG, ARGS...) ({ \ 
    oop r = (oop)(RCV); \ 
    method_t method = _bind(r, (MSG)); \ 
    method(r, ##ARGS); \ 
}) 

In the same doc, have a look at the object, vtable, vtable_delegated and symbol structs, and the _bind and vtable_lookup functions.

Cheers!

luser droog
  • 17,793
  • 3
  • 49
  • 96
Sébastien RoccaSerra
  • 15,663
  • 8
  • 46
  • 53
1
#include <stdio.h>

typedef struct {
    int  x;
    int z;
} base;

typedef struct {
    base;
    int y;
    int x;
} derived;

void function_on_base( base * a) // here I can pass both pointers to derived and to base
{
    printf("Class base [%d]\n",a->x);
    printf("Class base [%d]\n",a->z);
}
void function_on_derived( derived * b) // here I must pass a pointer to the derived class
{
    printf("Class derived [%d]\n",b->y);
    printf("Class derived [%d]\n",b->x);
}

int main()
{
    derived d;
    base b;
    printf("Teste de poliformismo\n");

    b.x = 2;
    d.y = 1;
    b.z = 3;
    d.x = 4;
    function_on_base(&b);
    function_on_base(&d);
    function_on_derived(&b);
    function_on_derived(&d);
    return 0;
}

The output was:

Class base [3]
Class base [1]
Class base [4]
Class derived [2]
Class derived [3]
Class derived [1]
Class derived [4]

so it works, its a polymorphic code.

UncleZeiv explained about it at the beginning.

1

The file functions fopen, fclose, fread are examples of OO code in C. Instead of the private data in class, they work on the FILE structure which is used to encapsulate the data and the C functions acts as an member class functions. http://www.amazon.com/File-Structures-Object-Oriented-Approach-C/dp/0201874016

Priyank Bolia
  • 12,942
  • 13
  • 57
  • 81
  • 3
    The book pointed to is C++ only. Note: I have a copy and would not recommend it today. If I had one OO recommendation in C to give today it would be C Interfaces and Implementations: Techniques for Creating Reusable Software by David Hanson (http://amzn.com/0201498413). Absolutely brilliant book and most programmers would do well to understand it, most examples in it are taken from compiler backends so the code is exemplary. – Harry Aug 26 '14 at 17:38
0

From Wikipedia: In programming languages and type theory, polymorphism (from Greek πολύς, polys, "many, much" and μορφή, morphē, "form, shape") is the provision of a single interface to entities of different types.

So I would say the only way to implement it in C is by using variadic arguments along with some (semi)automatic type info management. For example in C++ you can write (sorry for trivialness):

void add( int& result, int a1, int a2 );
void add( float& result, float a1, float a2 );
void add( double& result, double a1, double a2 );

In C, among other solutions, the best you can do is something like this:

int int_add( int a1, int a2 );
float float_add( float a1, fload a2 );
double double_add( double a1, double a2 );

void add( int typeinfo, void* result, ... );

Then you need:

  1. to implement the "typeinfo" with enums/macros
  2. to implement the latter function with stdarg.h stuff
  3. to say goodbye to C static type checking

I am almost sure that any other implementation of polymorphism should look much like this very one. The above answers, instead, seems to try to address inheritance more than polymorphism!

EnzoR
  • 2,523
  • 2
  • 16
  • 19
  • In C11, the new [_Generic](http://en.cppreference.com/w/c/language/generic) keyword simplifies this design pattern a great deal. I highly recommend it for those choosing this approach. – Caleb Gray Nov 13 '16 at 09:23
0

In order too build OO functionality in C, you can look at previous answers.

But, (as it has been asked in other questions redirected to this one) if you want to understand what polymorphism is, by examples in C language. Maybe I am wrong, but I can't think of anything as easy to understand as C pointers arithmetic. In my opinion, pointer arithmetic is inherently polymorphic in C. In the following example the same function (method in OO), namely the addition (+), will produce a different behavior depending on the properties of the input structures.

Example:

double a*;
char str*;

a=(double*)malloc(2*sizeof(double));
str=(char*)malloc(2*sizeof(char)); 

a=a+2; // make the pointer a, point 2*8 bytes ahead.

str=str+2; // make the pointer str, point 2*1 bytes ahead.

Disclaimer: I am very new at C and very much looking forward to being corrected and learn from other user's comments, or even completely erase this answer, should it be wrong. Many thanks,

johnnybegood
  • 31
  • 1
  • 3
0

What I usually like to do is to wrap the structs in another which contain meta information about the wrapped class and then build visitor like function lists acting on the generic struct. The advantage of this approach is that you don't need to modify the existing structures and you can create visitors for any subset of structs.

Take the usual example:

typedef struct {
    char call[7] = "MIAO!\n";
} Cat;
    
typedef struct {
    char call[6] = "BAU!\n";
} Dog;

We can wrap the 2 strutures in this new structure:

typedef struct {
    const void * animal;
    AnimalType type;
} Animal;

The type can be a simple int but let's not be lazy:

typedef enum  {
    ANIMAL_CAT = 0,
    ANIMAL_DOG,
    ANIMAL_COUNT
} AnimalType;

It would be nice to have some wrapping functions:

Animal catAsAnimal(const Cat * c) {
    return (Animal){(void *)c, ANIMAL_CAT};
}

Animal dogAsAnimal(const Dog * d) {
    return (Animal){(void *)d, ANIMAL_DOG};
}

Now we can define our "visitor":

void catCall ( Animal a ) {
    Cat * c = (Cat *)a.animal;
    printf(c->call);
}

void dogCall ( Animal a ) {
    Dog * d = (Dog *)a.animal;
    printf(d->call);
}

void (*animalCalls[ANIMAL_COUNT])(Animal)={&catCall, &dogCall};

Then the actual usage will be:

Cat cat;
Dog dog;

Animal animals[2];
animals[0] = catAsAnimal(&cat);
animals[1] = dogAsAnimal(&dog);

for (int i = 0; i < 2; i++) {
    Animal a = animals[i];
    animalCalls[a.type](a);
}

The disadvantage of this approach is that you have to wrap the structures every time you want to use it as a generic type.

fede. dev
  • 23
  • 1
  • 5