/*
Simulating single inheritance and virtual methods in C
2014/04/24
fred
The following Java code will be translated to C:
class A
{
int some_data;
A(int some_data)
{
this.some_data = some_data;
}
void foo()
{
System.out.println("foo in A with " + some_data);
}
void bar()
{
System.out.println("bar in A with " + some_data);
}
}
class B extends A
{
int additional_data;
B(int some_data, int additional_data)
{
super(some_data);
this.additional_data = additional_data;
}
@Override
void bar()
{
System.out.println("bar in B with " + some_data + " and " + additional_data);
}
void baz()
{
System.out.println("baz in B with " + some_data + " and " + additional_data);
}
}
class C extends B
{
C(int some_data, int additional_data)
{
super(some_data, additional_data);
}
@Override
void foo()
{
super.foo();
System.out.println("foo in C with " + some_data + " and " + additional_data);
}
}
*/
#include <stdio.h>
struct A
{
void * vptr;
int some_data;
};
/*
memory layout of an A object:
0 vptr (pointing to the vtable for class A)
4 some_data
*/
struct B
{
struct A parent;
int additional_data;
};
/*
memory layout of a B object:
0 vptr (pointing to the vtable for class B)
4 some_data
8 additional_data
*/
struct C
{
struct B parent;
};
/*
memory layout of a C object:
0 vptr (pointing to the vtable for class C)
4 some_data
8 additional_data
*/
void A__foo(struct A * this)
{
printf("foo in A with %d\n", this
->some_data
); }
void A__bar(struct A * this)
{
printf("bar in A with %d\n", this
->some_data
); }
struct A__vtable_t
{
void (* foo)(struct A *);
void (* bar)(struct A *);
} A__vtable = {
&A__foo,
&A__bar
};
void A__init(struct A * this, int some_data)
{
this->vptr = &A__vtable;
this->some_data = some_data;
}
void invokevirtual__foo(void * this)
{
// Since the vptr is the first data member of each object,
// we can pretend that the this-pointer points to the vptr.
(*(struct A__vtable_t **)this)->foo(this);
// Note how the vtable for A is a prefix of the vtables for B and C,
// so this will also work (and dispatch correctly) on B and C objects.
}
void invokevirtual__bar(void * this)
{
(*(struct A__vtable_t **)this)->bar(this);
}
void B__bar(struct B * this)
{
printf("bar in B with %d and %d\n", this
->parent.
some_data, this
->additional_data
); }
void B__baz(struct B * this)
{
printf("baz in B with %d and %d\n", this
->parent.
some_data, this
->additional_data
); }
struct B__vtable_t
{
void (* foo)(struct A *);
void (* bar)(struct B *);
void (* baz)(struct B *);
} B__vtable = {
&A__foo,
&B__bar,
&B__baz
};
void B__init(struct B * this, int some_data, int additional_data)
{
// statically resolved super constructor call
A__init((struct A *) this, some_data);
// Note that unlike in Java, we could have invoked methods from within the super constructor,
// and they would have dispatched correctly to the A versions,
// because the dynamic type of the object was not B yet...
this->parent.vptr = &B__vtable;
// ...and now it is :)
this->additional_data = additional_data;
}
void invokevirtual__baz(void * this)
{
(*(struct B__vtable_t **)this)->baz(this);
// Again, this will also work correctly for C objects,
// because the vtable for class B is a prefix of the vtable for class C.
// (Actually, they are identical, because class C does not define additional methods.)
// Note that if you try to invoke baz on an A object, the behavior is undefined,
// because the vtable for class A has no entry corresponding to baz; it ends at bar.
// The vtables for classes A, B and C are likely to be arranged next to each other in memory,
// so invoking baz on an A object will probably have the same effect as invoking foo,
// because foo is the first entry in the vtable for class B, which probably comes after A.
}
void C__foo(struct C * this)
{
// statically resolved super method call
A__foo((struct A *) this);
printf("foo in C with %d and %d\n", this
->parent.
parent.
some_data, this
->parent.
additional_data); }
struct C__vtable_t
{
void (* foo)(struct C *);
void (* bar)(struct B *);
void (* baz)(struct B *);
} C__vtable = {
&C__foo,
&B__bar,
&B__baz
};
void C__init(struct C * this, int some_data, int additional_data)
{
B__init((struct B *) this, some_data, additional_data);
this->parent.parent.vptr = &C__vtable;
}
void test_foo_bar(void * this)
{
invokevirtual__foo(this);
invokevirtual__bar(this);
}
void test_foo_bar_baz(void * this)
{
invokevirtual__foo(this);
invokevirtual__bar(this);
invokevirtual__baz(this);
}
int main()
{
struct A a;
struct B b;
struct C c;
A__init(&a, 1);
B__init(&b, 2, 3);
C__init(&c, 4, 5);
test_foo_bar(&a);
test_foo_bar_baz(&b);
test_foo_bar_baz(&c);
return 0;
}