C89-OOP: Polymorphism
This chapter can be quite short because most of it already has been covered in the C89-OOP: interface inheritance
article. The concept is the same; we use a VMT to store all references of virtual methods in there.
Virtual and override
There isn't much to say here for making virtual methods, as it's exactly the same as making an interface:
typedef struct Foo_vmt Foo_vmt;
typedef struct Foo Foo;
struct Foo_vmt
{
void (*add)(Foo* self);
};
struct Foo
{
Foo_vmt* vptr;
int x;
};
static void Foo_add(Foo* self)
{
++self->x;
}
void Foo_ctor(Foo* self, int x)
{
static Foo_vmt foo_vmt = {
Foo_add
};
self->vptr = &foo_vmt;
self->x = x;
}
Inherited classes can override the VMT members of Foo
by providing their own implementation of the member. In order to access Bar
's fields inside the Foo
's VMT call, we need to downcast Foo
to Bar
first.
typedef struct Bar bar;
struct Bar
{
Foo base;
Foo_vmt* vptr;
int y;
};
static void Bar_add(Foo* self)
{
Bar* bar = (Bar*)self;
++bar->x;
++bar->y;
}
void Bar_ctor(Bar* self, int x, int y)
{
static Foo_vmt bar_vmt = {
Bar_add
};
Foo_ctor(&self->base, x);
self->base.vptr = &bar_vmt;
self->y = y;
}
You can call the method like this:
Bar bar;
Foo* foo;
Bar_ctor(&bar, 1, 2);
foo = (Foo*)&bar;
bar.base.vptr->add(foo);
Note the cast we need to do in order to pass the correct type.
Abstract methods
To mark a member abstract, simply set the member to 0
so it will throw a runtime error whenever someone tries to execute it.
static Foo_vmt foo_vmt = {
0
};
Conclusion
You now know how to make a virtual method, how to override it, and how to make a method abstract. Next up will be a very simple type system to help you keep track of user-defined types on runtime.