Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spicing C with classes

Tags:

c

oop

Disclaimer: I am a complete newbie with C, but I've been playing with it trying to mimic some features of classes. Ok, I know that if I want to go that way I should learn C++, but consider the following a little experiment.

Schreiner, in the book Object-oriented programming with ANSI-C suggests a way to use pointers to get object orientation features in C. I must admit I have only skimmed through the book, but I don't like his approach too much. Basically, he uses pointers to functions in order to arrange that

func(foo);

actually results in calling

foo.methods->func();

where foo.methods is a struct containing pointers to functions. The thing I do not like in this approach is that one has to have the global function foo anyway; that is, methods are not namespaced by the class they live in. My feeling is that this will soon lead to clutter: think two objects foo and bar, both having a method func but with a different number of parameters.

So I have tried to get something more fit to my taste. A first attempt is the following (I omit the declarations for sake of brevity)

#include <stdio.h>

//Instances of this struct will be my objects
struct foo {
    //Properties
    int bar;

    //Methods
    void (* print)(struct foo self);
    void (* printSum)(struct foo self, int delta);
};

//Here is the actual implementation of the methods
static void printFoo(struct foo self) {
    printf("This is bar: %d\n", self.bar);
}

static void printSumFoo(struct foo self, int delta) {
    printf("This is bar plus delta: %d\n", self.bar + delta);
}

//This is a sort of constructor
struct foo Foo(int bar) {
    struct foo foo = {
        .bar = bar,
        .print = &printFoo,
        .printSum = &printSumFoo
    };
    return foo;
}

//Finally, this is how one calls the methods
void
main(void) {
    struct foo foo = Foo(14);
    foo.print(foo); // This is bar: 14
    foo.printSum(foo, 2); // This is bar plus delta: 16
}

This is unconvenient but sort of works. What I do not like, though, is that you have to explicitly add the object itself as the first argument. With some preprocessor work I can do a little better:

#include <stdio.h>
#define __(stuff)     stuff.method(* stuff.object)

//Instances of this struct will be my objects
struct foo {
    //Properties
    int bar;

    //Methods
    //Note: these are now struct themselves
    //and they contain a pointer the object...
    struct {
        void (* method)(struct foo self);
        struct foo * object;
    } print;
};

//Here is the actual implementation of the methods
static void printFoo(struct foo self) {
    printf("This is bar: %d\n", self.bar);
}

//This is a sort of constructor
struct foo Foo(int bar) {
    struct foo foo = {
        .bar = bar,
        //...hence initialization is a little bit different
        .print = {
            .method = &printFoo,
            .object = &foo
        }
    };
    return foo;
}

//Finally, this is how one calls the methods
void
main(void) {
    struct foo foo = Foo(14);
    //This is long and unconvenient...
    foo.print.method(* foo.print.object); // This is bar: 14
    //...but it can be shortened by the preprocessor
    __(foo.print); // This is bar: 14
}

This is as far as I can get. The problem here is that it will not work for methods with arguments, as preprocessor macros cannot take a variable number of arguments. Of course one can define macros _0, _1 and so on according to the number of arguments (until one gets tired), but this is hardly a good approach.

Is there any way to improve on this and let C use a more object-oriented syntax?

I should add that actually Schreiner does much more than what I said in his book, but I think the basic construction does not change.

like image 852
Andrea Avatar asked Jan 19 '11 09:01

Andrea


3 Answers

Various frameworks already exists. See for instance http://ldeniau.web.cern.ch/ldeniau/html/oopc.html

like image 134
AProgrammer Avatar answered Oct 20 '22 03:10

AProgrammer


A book (in PDF form) that explains how to do it, is object oriented programming in ANSI C It's old (1993) but still contains some valid ideas and tips, IMHO.

like image 25
Henno Brandsma Avatar answered Oct 20 '22 04:10

Henno Brandsma


Did you have a look at Google's Go? It's basically a modernized C where things are done somewhat in the way you suggested. It has parameter polymorphisms. So you don't have to do this:

static void printFoo(struct foo self) {
    printf("This is bar: %d\n", self.bar);
}

In Go it can be done this way:

static void print(Foo self) {
    printf("This is foo: %d\n", self.foo);
}

static void print(Bar self) {
    printf("This is bar: %d\n", self.bar);
}

In Go Foo and Bar would also be structs. So you are basically on the samet track as the Go language designers.

For an overview of Go see http://golang.org/doc/effective_go.html This is the Go main language description: http://golang.org/doc/effective_go.html

like image 1
OlliP Avatar answered Oct 20 '22 03:10

OlliP