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.