- instead of setting the same function pointers on structs over and over again, point to a shared (singleton) struct named "vtable" which keeps track of all function pointers for this "type" of structs
- create a factory function that allocates memory for the struct, initializes fields ("vtable" included), let's call it a "constructor"
- make sure all function signatures in the shared struct start with a pointer to the original struct as the first parameter, a good name for this argument would be "this"
- encode parameter types in the function name to support overloading, e.g. "func1_int_int"
- call functions in the form of "obj->vtable->func1_int_int(obj, param1, param2)"