tags:

views:

3932

answers:

26

Can you write object oriented code in C? Especially with regard to polymorphism.


See also: http://stackoverflow.com/questions/415452/object-orientation-in-c

+15  A: 

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++.

tvanfosson
"Eventually, you end up rewriting C++" I wondered if/feared that would be the case.
Dinah
+2  A: 

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.

Uri
The very first C++ compiler did exactly that - it converted the C++ code into equivalent (but ugly and non-human-readable) C code, which was then compiled by the C compiler.
Adam Rosenfield
EDG, Cfront and some others are still capable of doing this. With a very good reason: not every platform has a C++ compiler.
Jasper Bekkers
For some reason I thought that C-front only supported certain C++ extensions (e.g., references) but not full OOP / dynamic dispatch emulation.
Uri
You can also do the same thing with LLVM and the C backend.
Zifre
A: 

Yes, but I have never seen anyone attempt to implement any sort of polymorphism with C.

PaulMorel
You need to look around more :) For instance, Microsoft's Direct X has a polymorphic C interface.
AShelly
Look into linux kernel implementation for example. It is very common and widely used practice in C.
Ilya
also glib is polymorphic, or can be used in a way that allows polymorphism (it's like C++ you have to explicitly say which calls are virtual)
Spudd86
+21  A: 

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 behavior would be slightly different to C++ (core dump at run-time rather than error at compile time).

Here's a piece of sample code that demonstates it:

#include <stdio.h>

// The top-level class.

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

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

// Function for the HTML class.

static int htmlOpen (tCommClass *html, char *fspec) {
    printf ("Opening HTML: %s\n", fspec);
    return 0;
}
static int htmlInit (tCommClass *html) {
    html->open = &htmlOpen;
    return 0;
}

// Test program.

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

    // Same base class but initialized to different sub-classes.
    tcpInit (&commTcp);
    htmlInit (&commHtml);

    // Called in exactly the same manner.

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

    return 0;
}

This produces the output:

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

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

paxdiablo
Encapsulation is pretty easy, polymorphism is doable - but inheritence is tricky
Martin Beckett
+1 for "...slightly different...(core dump at run-time rather than error at compile time)"
Michael Mior
+5  A: 

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.

Robert Gould
+43  A: 

Yes. In fact Axel Schreiner provides his book for free which covers the subject quite thoroughly.

mepcotterell
thanks for the link... looks like some exercise for the brain cells is in the offing.
Gishu
While the concepts in this book are solids, you'll lose type safety.
You can call me Chuck
Before what we know as design patterns, was the design pattern known as "object orientation"; same with garbage collection, and other such. They are so ingrained now, we tend to forget, when they were first being devised, it was in much the same way as with what we think of as design patterns today
George Jempty
+4  A: 

There is an example of inheritance using C in Jim Larson's 1996 talk given at the Section 312 Programming Lunchtime Seminar here: High and Low-Level C.

Judge Maygarden
+9  A: 

Sure that is possible. This is what GObject, the framework where all of gtk+ and gnome are based on, does. Read this: http://en.wikipedia.org/wiki/GObject.

Johannes Schaub - litb
+3  A: 

Yes, you can. People were writing Object Oriented C before C++ or Objective C came on the scene. Both C++ and Objective C were, in parts, attempts to take some of the OO concepts used in C and formalize them as part of the language.

Here's a really simple program that shows how you can make something that looks-like/is a method call (there are better ways to do this, this is just proof the language supports the concepts)

#include<stdio.h>

struct foobarbaz{
    int one;
    int two;
    int three;
    int (*exampleMethod)(int, int);
};

int addTwoNumbers(int a, int b){
    return a+b;
}

int main()
{  
    //define the function pointer    
    int (*pointerToFunction)(int, int) = addTwoNumbers;         

    //lets make sure we can call the pointer
    int test = (*pointerToFunction)(12,12); 
    printf ("test: %u \n",  test);

    //now, define an instance of our struct
    //and add some default values
    struct foobarbaz fbb;
    fbb.one   = 1;
    fbb.two   = 2;
    fbb.three = 3;   

    //now add a "method"
    fbb.exampleMethod = addTwoNumbers;

    //try calling the method
    int test2 = fbb.exampleMethod(13,36);    
    printf ("test2: %u \n",  test2);   

    printf("\nDone\n");
    return 0;
}
Alan Storm
A: 

technically no.

practically yes.

botty
+4  A: 

Trivial example with a Animal and Dog, what you do is 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.

Jasper Bekkers
+2  A: 

Of course, it just won't be a pretty as using a language with built in support. I've even written "object oriented assembler".

Darron
+2  A: 

Object Oriented Programming in C by Laurent Deniau

A: 

I'll grant everyone the benefit of the doubt and accept that it can be done. Other than curiosity though: why?

Gabriel
+12  A: 

If you are convinced that an OOP approach is superior for the problem you are trying to solve, why would you be trying to solve it with a non-OOP language? It seems like you're using the wrong tool for the job. Use C++ or some other object-oriented C variant language.

If you are asking because you are starting to code on an already existing large project written in C, then you shouldn't try to force your own (or anyone else's) OOP paradigms into the project's infrastructure. Follow the guidelines that are already present in the project. In general, clean APIs and isolated libraries and modules will go a long way towards having a clean OOP-ish design.

If, after all this, you really are set on doing OOP C, read this (PDF).

RarrRarrRarr
Not really answering the question...
Brian Postow
@Brian, the link to the PDF would appear to answer the question directly, although I haven't had time to check for myself.
Mark Ransom
The link to the PDF appears to be an entire textbook on the subject... A beautiful proof, but it doesn't fit into the margin...
Brian Postow
yes, answer the question. it's perfectly valid to ask how to use a language in a particular way. there was no request for opinions on other languages....
Tim Ring
@Brian I gave him a link to a *book* that specifically addresses this topic. I also gave my opinion on why the approach to the problem may not be optimal (which I think many people on here seem to agree with, based on votes and other comments/answers). Do you have any suggestions for improving my answer?
RarrRarrRarr
+1 Nice finding.
OscarRyz
+6  A: 

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.

SB
+2  A: 

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.

benzado
+4  A: 

There are several techniques that canbe used. The most important one is more how to split the project. We use in our project an interface 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 *, the .c file is the only module who knows the internas of the structure.

Something like that for a class we name FOO as 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 explicitly the pointer to an object to every function of that module. A C++ compiler does it implicitely, in C we write it explicitly out.

I use really 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 handle already 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.

tristopia
If you do `typedef struct FOO_type FOO_type` instead of a typedef to void in the header you get the added benefit of type checking, while still not exposing your structure.
Scott Wales
Thank you, good tip, that was indeed a problem I had.
tristopia
+12  A: 

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 *

nategoose
You missed `struct` in front of `stack` in the `operator_new`
Chris Lutz
@Chris Lutz: Thanks. Fixed.
nategoose
+1  A: 

One thing you might want to do is look into the implementation of the Xt toolkit for xwindows. Sure it is getting long in the tooth but many of the structures used were designed to work in an OO fashion within traditional C. Generally this means adding an extra layer of indirection here and there and designing structures to lay over each other.

You can really do lots in the way of OO situated in C this way, even though it feels like it some times, OO concepts did not spring fully formed from the mind of #include<favorite_OO_Guru.h> they really constituted many of the established best practice of the time. OO languages and systems only distilled and amplified parts of the programing zeitgeist of the day.

Ukko
A: 

I think that the first thing to say is that (IMHO at least) C's implementation of function pointers is REALLY hard to use. I would jump through a WHOLE lot of hoops to avoid function pointers...

that said, I think that what other people have said is pretty good. you have structures, you have modules, instead of foo->method(a,b,c), you end up with method(foo,a,b,c) If you have more than one type with a "method" method, then you can prefix it with the type, so FOO_method(foo,a,b,c), as others have said... with good use of .h files you can get private and public, etc.

Now, there are a few things that this technique WON'T give you. It won't give you private data fields. that, I think, you have to do with willpower and good coding hygiene... Also, there isn't an easy way to do inheritance with this.

Those are the easy parts at least...the rest, I think is a 90/10 kind of situation. 10% of the benefit will require 90% of the work...

Brian Postow
Single inheritance (without polymorphism, though) can be quite easily implemented with this technique as well. All you need to to is embed the superclass as the first member of the subclass. By the C Standard, the whole structure must necessarily be aligned with the first member, so any method designed for foo (method(foo, a, b, c)) will work when a bar pointer is passed instead (bar being a subclass of foo). This is inheritance.
Miro
@miro. Wow. that's ... that's a serious kludge right there...
Brian Postow
not really... it's used all over the place... glib is pretty much built on this idea, Linux kernel (extensively), also it's essentially the same thing that happens when you use an object oriented language, the compiler uses the same object layout that is described above (multiple inheritance complicates this slightly though, one of the superclasses must be at an offset from the object start)
Spudd86
+1  A: 

You might want to look at the answers to this question: What techniques/strategies do people use for building objects in C (not C++)?

Dale Hagglund
+4  A: 

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

Miro
+1  A: 

Which articles or books are good to use OOP concepts in C?

Dave Hanson's C Interfaces and Implementations is excellent on encapsulation and naming and very good on use of function pointers. Dave does not try to simulate inheritance.

Norman Ramsey
A: 

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.

msw
A: 

I propose to use Objective-C, which is a superset of C.

While Objective-C is 30 years old, it allows to write elegant code.

http://en.wikipedia.org/wiki/Objective-C

Stefan Pantke