You can simulate virtual functions with function pointers. For instance,
struct foo
{
void(*bar)(struct foo*, int, int);
};
void default_bar ( struct foo * f, int a, int b )
{
printf("bar(%d,%d)\n", a, b);
}
void setup_foo ( struct foo * f )
{
f->bar = &default_bar;
}
Then, you can "subclasss" the structure with something like:
struct meh
{
/* inherit from "class foo". MUST be first. */
struct foo base;
int more_data;
};
/* override "method bar". */
struct custom_bar ( struct foo * f, int a, int b )
{
struct meh * m = (struct meh*)f;
printf("custom_bar(%d,%d)\n", a, b);
}
void setup_meh ( struct meh * m )
{
setup_foo(&m->base);
m->bar = &custom_bar;
}
All of this is labor-intensive and error prone, but it can be done. This type of "inheritance" and "override" implementation is common practice in some well-known C libraries, including jpeglib
and libpng
. They use this technique to allow you to override the I/O procedures if you're not satisfied with standard C I/O.
Edit: as noted in the comments, some of this code relies on (officially) non-standard behavior that "just happens" to work on most compilers. The main issue is that the code assumes that &m.base == &m
(e.g. the offset of the base
member is 0). If that is not the case, then the cast in custom_bar()
results in undefined behavior. To work around this issue, you can add an extra pointer in struct foo
as such:
struct foo
{
/* same as before ...*/
/* extra pointer. */
void * hook;
};
Then, modify the stuff that touches the cast,
void setup_meh ( struct meh * m )
{
m->base.hook = m;
/* set up function pointers as usual... */
}
void custom_bar ( struct foo * f, int a, int b )
{
struct meh * m = (struct meh*)f->hook;
/* override. */
}
This technique is more reliable, especially if you plan to write the "derived struct" in C++ and use virtual functions. In that case, the offset of the first member is often non-0 as compilers store run-time type information and the class' v-table there.