Jonathan said:
To do OO in C, you usually do something like this:
[snip]
#include <stdio.h>
typedef struct foo {
const
void* vtbl;
int a;
} foo;
typedef struct vtable_foo {
void (*bar)(foo*);
void (*abc)(foo*);
} vtable_foo;
void actual_foo_bar(foo* p) {
p->a = 3;
}
inline /* virtual */
void foo_bar(foo* p) {
((vtable_foo*)(p->vtbl))->bar(p);
}
void actual_foo_abc(foo* p) {
p->a = 4;
}
inline /* virtual */
void foo_abc(foo* p) {
((vtable_foo*)(p->vtbl))->abc(p);
}
// [Unnecessary] class initializer --
// must be run EXACTLY ONCE
// need *never* be run.
// before creating any instances of foo
//void initialize_foo_class(void) {
// __vtable_foo.bar = foo_bar;
// __vtable_foo.abc = foo_abc;
// }
const
vtable_foo __vtable_foo = {actual_foo_bar,
actual_foo_abc};
// [No. This is not a] constructor.
// This is an initializer.
inline
void foo_initialize(foo *p) {
p->vtbl = (void*)(&__vtable_foo);
p->a = 0;
}
// This is a [pseudo] constructor.
inline
foo foo_create(void) {
foo f;
foo_initialize(&f);
return f;
}
// Always define a destructor
// even if it doesn't do anything.
inline
void foo_destroy(const foo* p) { }
//typedef struct doo {
// vtable_doo* vtbl;
// int a; // from foo
// int b;
// } doo;
typedef struct doo {
foo f;
int b;
} doo;
typedef struct vtable_doo {
void (*bar)(doo*);
void (*abc)(foo*);
void (*xyz)(doo*);
} vtable_doo;
void actual_doo_bar(doo* p) {
p->b = 5;
}
inline /* virtual */
void doo_bar(doo* p) {
((vtable_doo*)(p->f.vtbl))->bar(p);
}
inline /* virtual */
void doo_abc(doo* p) {
((vtable_doo*)(p->f.vtbl))->abc((foo*)p);
}
void actual_doo_xyz(doo* p) {
p->b = 6;
}
inline /* virtual */
void doo_xyz(doo* p) {
((vtable_doo*)(p->f.vtbl))->xyz(p);
}
// Unnecessary "class" initializer.
//void initialize_doo_class(void) {
// // inherited from foo
// __vtable_doo.bar = doo_bar; /* overriden in doo */
// __vtable_doo.abc = foo_abc;
//
// // new in doo
// __vtable_doo.xyz = doo_xyz;
// }
const
vtable_doo __vtable_doo = {actual_doo_bar,
actual_foo_abc,
actual_doo_xyz};
// This is an initializer.
inline
void doo_initialize(doo* p) {
p->f.vtbl = (void*)(&__vtable_doo);
p->f.a = 0;
p->b = 0;
}
// This is a [pseudo] constructor.
inline
doo doo_create(void) {
doo d;
doo_initialize(&d);
return d;
}
// Always define a destructor
// even if it doesn't do anything.
inline
void doo_destroy(const doo* p) { }
int main(int argc, char* argv[]) {
doo d1 = doo_create();
foo f1 = foo_create();
foo* p = &(d1.f);
doo_xyz(&d1); // This sets d1.b to 6.
printf("d1.b is %d\n", d1.b);
foo_bar(p); // This sets d1.b to 5
// because it is invoked via vtable.
printf("d1.b is %d\n", d1.b);
foo_bar(&f1); // This sets f1.a to 3.
printf("f1.a is %d\n", f1.a);
foo_destroy(&f1); // Always call the destructors
doo_destroy(&d1); // even if they don't do anything.
return 0;
}
> gcc -Wall -std=c99 -pedantic -o main main.c
> ./main
d1.b is 6
d1.b is 5
f1.a is 3