How would one write object-oriented code in C? [closed]

Loading...

How would one write object-oriented code in C? [closed]






Closed. This question needs to be more focused. It is not currently accepting answers.
                            
                        










Want to improve this question? Update the question so it focuses on one problem only by editing this post.
                        
Closed 3 years ago.



What are some ways to write object-oriented code in C? Especially with regard to polymorphism.

See also this Stack Overflow question Object-orientation in C.

Solutions/Answers:

Answer 1:

Yes. In fact Axel Schreiner provides his book “Object-oriented Programming in ANSI-C” for free which covers the subject quite thoroughly.

Answer 2:

Since you’re talking about polymorphism then yes, you can, we were doing that sort of stuff years before C++ came about.

Basically you use a struct to hold both the data and a list of function pointers to point to the relevant functions for that data.

So, in a communications class, you would have an open, read, write and close call which would be maintained as four function pointers in the structure, alongside the data for an object, something like:

typedef struct {
    int (*open)(void *self, char *fspec);
    int (*close)(void *self);
    int (*read)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
    int (*write)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
    // And data goes here.
} tCommClass;

tCommClass commRs232;
commRs232.open = &rs232Open;
: :
commRs232.write = &rs232Write;

tCommClass commTcp;
commTcp.open = &tcpOpen;
: :
commTcp.write = &tcpWrite;

Of course, those code segments above would actually be in a “constructor” such as rs232Init().

When you ‘inherit’ from that class, you just change the pointers to point to your own functions. Everyone that called those functions would do it through the function pointers, giving you your polymorphism:

int stat = (commTcp.open)(commTcp, "bigiron.box.com:5000");

Sort of like a manual vtable.

You could even have virtual classes by setting the pointers to NULL -the behaviour would be slightly different to C++ (a core dump at run-time rather than an error at compile time).

Here’s a piece of sample code that demonstrates it. First the top-level class structure:

#include <stdio.h>

// The top-level class.

typedef struct sCommClass {
    int (*open)(struct sCommClass *self, char *fspec);
} tCommClass;

Then we have the functions for the TCP ‘subclass’:

// Function for the TCP 'class'.

static int tcpOpen (tCommClass *tcp, char *fspec) {
    printf ("Opening TCP: %s\n", fspec);
    return 0;
}
static int tcpInit (tCommClass *tcp) {
    tcp->open = &tcpOpen;
    return 0;
}

And the HTTP one as well:

// Function for the HTTP 'class'.

static int httpOpen (tCommClass *http, char *fspec) {
    printf ("Opening HTTP: %s\n", fspec);
    return 0;
}
static int httpInit (tCommClass *http) {
    http->open = &httpOpen;
    return 0;
}

And finally a test program to show it in action:

// Test program.

int main (void) {
    int status;
    tCommClass commTcp, commHttp;

    // Same 'base' class but initialised to different sub-classes.

    tcpInit (&commTcp);
    httpInit (&commHttp);

    // Called in exactly the same manner.

    status = (commTcp.open)(&commTcp, "bigiron.box.com:5000");
    status = (commHttp.open)(&commHttp, "http://www.microsoft.com");

    return 0;
}

This produces the output:

Opening TCP: bigiron.box.com:5000
Opening HTTP: http://www.microsoft.com

so you can see that the different functions are being called, depending on the sub-class.

Answer 3:

Namespaces are often done by doing:

stack_push(thing *)

instead of

stack::push(thing *)

To make a C struct into something like a C++ class you can turn:

class stack {
     public:
        stack();
        void push(thing *);
        thing * pop();
        static int this_is_here_as_an_example_only;
     private:
        ...
};

Into

struct stack {
     struct stack_type * my_type;
     // Put the stuff that you put after private: here
};
struct stack_type {
     void (* construct)(struct stack * this); // This takes uninitialized memory
     struct stack * (* operator_new)(); // This allocates a new struct, passes it to construct, and then returns it
     void (*push)(struct stack * this, thing * t); // Pushing t onto this stack
     thing * (*pop)(struct stack * this); // Pops the top thing off the stack and returns it
     int this_is_here_as_an_example_only;
}Stack = {
    .construct = stack_construct,
    .operator_new = stack_operator_new,
    .push = stack_push,
    .pop = stack_pop
};
// All of these functions are assumed to be defined somewhere else

And do:

struct stack * st = Stack.operator_new(); // Make a new stack
if (!st) {
   // Do something about it
} else {
   // You can use the stack
   stack_push(st, thing0); // This is a non-virtual call
   Stack.push(st, thing1); // This is like casting *st to a Stack (which it already is) and doing the push
   st->my_type.push(st, thing2); // This is a virtual call
}

I didn’t do the destructor or delete, but it follows the same pattern.

this_is_here_as_an_example_only is like a static class variable — shared among all instances of a type. All methods are really static, except that some take a this *

Answer 4:

I believe that besides being useful in its own right, implementing OOP in C is an excellent way to learn OOP and understand its inner workings. Experience of many programmers has shown that to use a technique efficiently and confidently, a programmer must understand how the underlying concepts are ultimately implemented. Emulating classes, inheritance, and polymorphism in C teaches just this.

To answer the original question, here are a couple resources that teach how to do OOP in C:

EmbeddedGurus.com blog post “Object-based programming in C” shows how to implement classes and single inheritance in portable C:
http://embeddedgurus.com/state-space/2008/01/object-based-programming-in-c/

Application Note “”C+”—Object Oriented Programming in C” shows how to implement classes, single inheritance, and late binding (polymorphism) in C using preprocessor macros:
http://www.state-machine.com/resources/cplus_3.0_manual.pdf, the example code is available from http://www.state-machine.com/resources/cplus_3.0.zip

Answer 5:

I’ve seen it done. I wouldn’t recommend it. C++ originally started this way as a preprocessor that produced C code as an intermediate step.

Essentially what you end up doing is create a dispatch table for all of your methods where you store your function references. Deriving a class would entail copying this dispatch table and replacing the entries that you wanted to override, with your new “methods” having to call the original method if it wants to invoke the base method. Eventually, you end up rewriting C++.

Answer 6:

Sure that is possible. This is what GObject, the framework that all of GTK+ and GNOME is based on, does.

Answer 7:

The C stdio FILE sub-library is an excellent example of how to create abstraction, encapsulation, and modularity in unadulterated C.

Inheritance and polymorphism – the other aspects often considered essential to OOP – do not necessarily provide the productivity gains they promise and reasonable arguments have been made that they can actually hinder development and thinking about the problem domain.

Answer 8:

Trivial example with an Animal and Dog: You mirror C++’s vtable mechanism (largely anyway). You also separate allocation and instantiation (Animal_Alloc, Animal_New) so we don’t call malloc() multiple times. We must also explicitly pass the this pointer around.

If you were to do non-virtual functions, that’s trival. You just don’t add them to the vtable and static functions don’t require a this pointer. Multiple inheritance generally requires multiple vtables to resolve ambiguities.

Also, you should be able to use setjmp/longjmp to do exception handling.

struct Animal_Vtable{
    typedef void (*Walk_Fun)(struct Animal *a_This);
    typedef struct Animal * (*Dtor_Fun)(struct Animal *a_This);

    Walk_Fun Walk;
    Dtor_Fun Dtor;
};

struct Animal{
    Animal_Vtable vtable;

    char *Name;
};

struct Dog{
    Animal_Vtable vtable;

    char *Name; // Mirror member variables for easy access
    char *Type;
};

void Animal_Walk(struct Animal *a_This){
    printf("Animal (%s) walking\n", a_This->Name);
}

struct Animal* Animal_Dtor(struct Animal *a_This){
    printf("animal::dtor\n");
    return a_This;
}

Animal *Animal_Alloc(){
    return (Animal*)malloc(sizeof(Animal));
}

Animal *Animal_New(Animal *a_Animal){
    a_Animal->vtable.Walk = Animal_Walk;
    a_Animal->vtable.Dtor = Animal_Dtor;
    a_Animal->Name = "Anonymous";
    return a_Animal;
}

void Animal_Free(Animal *a_This){
    a_This->vtable.Dtor(a_This);

    free(a_This);
}

void Dog_Walk(struct Dog *a_This){
    printf("Dog walking %s (%s)\n", a_This->Type, a_This->Name);
}

Dog* Dog_Dtor(struct Dog *a_This){
    // Explicit call to parent destructor
    Animal_Dtor((Animal*)a_This);

    printf("dog::dtor\n");

    return a_This;
}

Dog *Dog_Alloc(){
    return (Dog*)malloc(sizeof(Dog));
}

Dog *Dog_New(Dog *a_Dog){
    // Explict call to parent constructor
    Animal_New((Animal*)a_Dog);

    a_Dog->Type = "Dog type";
    a_Dog->vtable.Walk = (Animal_Vtable::Walk_Fun) Dog_Walk;
    a_Dog->vtable.Dtor = (Animal_Vtable::Dtor_Fun) Dog_Dtor;

    return a_Dog;
}

int main(int argc, char **argv){
    /*
      Base class:

        Animal *a_Animal = Animal_New(Animal_Alloc());
    */
    Animal *a_Animal = (Animal*)Dog_New(Dog_Alloc());

    a_Animal->vtable.Walk(a_Animal);

    Animal_Free(a_Animal);
}

PS. This is tested on a C++ compiler, but it should be easy to make it work on a C compiler.

Answer 9:

Check out GObject. It’s meant to be OO in C and one implementation of what you’re looking for. If you really want OO though, go with C++ or some other OOP language. GObject can be really tough to work with at times if you’re used to dealing with OO languages, but like anything, you’ll get used to the conventions and flow.

Answer 10:

This has been interesting to read. I have been pondering the same question myself, and the benefits of thinking about it are this:

  • Trying to imagine how to implement OOP concepts in a non-OOP language helps me understand the strengths of the OOp language (in my case, C++). This helps give me better judgement about whether to use C or C++ for a given type of application — where the benefits of one out-weighs the other.

  • In my browsing the web for information and opinions on this I found an author who was writing code for an embedded processor and only had a C compiler available:
    http://www.eetimes.com/discussion/other/4024626/Object-Oriented-C-Creating-Foundation-Classes-Part-1

In his case, analyzing and adapting OOP concepts in plain C was a valid pursuit. It appears he was open to sacrificing some OOP concepts due to the performance overhead hit resulting from attempting to implement them in C.

The lesson I’ve taken is, yes it can be done to a certain degree, and yes, there are some good reasons to attempt it.

In the end, the machine is twiddling stack pointer bits, making the program counter jump around and calculating memory access operations. From the efficiency standpoint, the fewer of these calculations done by your program, the better… but sometimes we have to pay this tax simply so we can organize our program in a way that makes it least susceptible to human error. The OOP language compiler strives to optimize both aspects. The programmer has to be much more careful implementing these concepts in a language like C.

Answer 11:

You may find it helpful to look at Apple’s documentation for its Core Foundation set of APIs. It is a pure C API, but many of the types are bridged to Objective-C object equivalents.

You may also find it helpful to look at the design of Objective-C itself. It’s a bit different from C++ in that the object system is defined in terms of C functions, e.g. objc_msg_send to call a method on an object. The compiler translates the square bracket syntax into those function calls, so you don’t have to know it, but considering your question you may find it useful to learn how it works under the hood.

Answer 12:

There are several techniques that can be used. The most important one is more how to split the project. We use an interface in our project that is declared in a .h file and the implementation of the object in a .c file. The important part is that all modules that include the .h file see only an object as a void *, and the .c file is the only module who knows the internals of the structure.

Something like this for a class we name FOO as an example:

In the .h file

#ifndef FOO_H_
#define FOO_H_

...
 typedef struct FOO_type FOO_type;     /* That's all the rest of the program knows about FOO */

/* Declaration of accessors, functions */
FOO_type *FOO_new(void);
void FOO_free(FOO_type *this);
...
void FOO_dosomething(FOO_type *this, param ...):
char *FOO_getName(FOO_type *this, etc);
#endif

The C implementation file will be something like that.

#include <stdlib.h>
...
#include "FOO.h"

struct FOO_type {
    whatever...
};


FOO_type *FOO_new(void)
{
    FOO_type *this = calloc(1, sizeof (FOO_type));

    ...
    FOO_dosomething(this, );
    return this;
}

So I give the pointer explicitly to an object to every function of that module. A C++ compiler does it implicitly, and in C we write it explicitly out.

I really use this in my programs, to make sure that my program does not compile in C++, and it has the fine property of being in another color in my syntax highlighting editor.

The fields of the FOO_struct can be modified in one module and another module doesn’t even need to be recompiled to be still usable.

With that style I already handle a big part of the advantages of OOP (data encapsulation). By using function pointers, it’s even easy to implement something like inheritance, but honestly, it’s really only rarely useful.

Answer 13:

You can fake it using function pointers, and in fact, I think it is theoretically possible to compile C++ programs into C.

However, it rarely makes sense to force a paradigm on a language rather than to pick a language that uses a paradigm.

Answer 14:

Object oriented C, can be done, I’ve seen that type of code in production in Korea, and it was the most horrible monster I’d seen in years (this was like last year(2007) that I saw the code).
So yes it can be done, and yes people have done it before, and still do it even in this day and age. But I’d recommend C++ or Objective-C, both are languages born from C, with the purpose of providing object orientation with different paradigms.

References

Loading...