Created
October 7, 2020 12:36
-
-
Save andreafioraldi/baed370b44e64709d2c2ca948143c225 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#define INHERITS(type) struct type _base; | |
#define BASE_CAST(ptr) (&(ptr)->_base) | |
#define VTABLE_INHERITS(type) struct type##_vtable _base; | |
#define VTABLE_INIT_BASE_VPTR(type) \ | |
._base_vptr = (struct afl_object_vtable*)&type##_vtable_instance | |
#define VTABLE_OF(type, ptr) ((struct type##_vtable*)(((struct afl_object*)(ptr))->vptr)) | |
#define VTABLE_SET(ptr, vtable) ((struct afl_object*)(ptr))->vptr = (struct afl_object_vtable*)&vtable | |
// #define INSTANCE_OF(type, ptr) ((ptr)->v == &type##_vtable_instance) | |
#define INSTANCE_OF(type, ptr) ({ \ | |
struct afl_object_vtable* _v = (struct afl_object_vtable*)&type##_vtable_instance; \ | |
while (_v) { \ | |
if (_v == ((struct afl_object*)(ptr))->vptr) break; \ | |
_v = _v->_base_vptr; \ | |
} \ | |
!!_v; \ | |
}) | |
#define DYN_CAST(type, ptr) (INSTANCE_OF(type, ptr) ? (struct type*)(ptr) : NULL) | |
/// class Object | |
typedef struct afl_object afl_object_t; | |
struct afl_object_vtable { | |
struct afl_object_vtable* _base_vptr; | |
void* (*_create_wrapper)(void*); // for bindings | |
/* | |
The deinit() method is optional. | |
It is invoked just before the destroy of the object. | |
*/ | |
void (*deinit)(afl_object_t *); | |
}; | |
struct afl_object_vtable afl_object_vtable_instance = { | |
._base_vptr = NULL, | |
.deinit = NULL | |
}; | |
struct afl_object { | |
void* wrapper; // for bindings | |
struct afl_object_vtable *vptr; | |
}; | |
/// virtual class A | |
typedef struct afl_A afl_A_t; | |
void afl_A_deinit__nonvirtual(afl_object_t* self); | |
struct afl_A_vtable { | |
VTABLE_INHERITS(afl_object) | |
void (*print)(afl_A_t*); | |
}; | |
struct afl_A_vtable afl_A_vtable_instance = { | |
._base = { | |
VTABLE_INIT_BASE_VPTR(afl_object), | |
.deinit = &afl_A_deinit__nonvirtual | |
}, | |
.print = NULL | |
}; | |
struct afl_A { | |
INHERITS(afl_object) | |
int x; | |
}; | |
static inline void afl_object_deinit(afl_object_t* self) { | |
if (VTABLE_OF(afl_object, self)->deinit) | |
VTABLE_OF(afl_object, self)->deinit(self); | |
} | |
// Protected init for virtual classes | |
void afl_A_init__protected(afl_A_t* self, int x) { | |
self->x = x; | |
VTABLE_SET(self, afl_A_vtable_instance); | |
} | |
// Wrap vtable methods | |
static inline void afl_A_print(afl_A_t* self) { | |
VTABLE_OF(afl_A, self)->print(self); | |
} | |
// For the nonvirtuals, maintain the original type of self (afl_object_t) | |
void afl_A_deinit__nonvirtual(afl_object_t* self) { | |
if (INSTANCE_OF(afl_A, self)) // do only for debug builds maybe | |
DYN_CAST(afl_A, self)->x = 0; | |
} | |
// For the wrappers, use the actual self type (afl_A_t) | |
static inline void afl_A_deinit(afl_A_t* self) { | |
afl_object_deinit(BASE_CAST(self)); | |
} | |
/// class B | |
void afl_B_print__nonvirtual(afl_A_t* self); | |
typedef struct afl_B afl_B_t; | |
struct afl_A_vtable afl_B_vtable_instance = { | |
// afl_object_vtable | |
._base = { | |
VTABLE_INIT_BASE_VPTR(afl_A), // declare the parent vtable | |
.deinit = &afl_A_deinit__nonvirtual // reuse A deinit | |
}, | |
.print = &afl_B_print__nonvirtual | |
}; | |
struct afl_B { | |
INHERITS(afl_A) | |
}; | |
// Protected init for virtual classes | |
void afl_B_init(afl_B_t* self, int x) { | |
// call super constructor | |
afl_A_init__protected(BASE_CAST(self), x); | |
VTABLE_SET(self, afl_B_vtable_instance); | |
} | |
void afl_B_print__nonvirtual(afl_A_t* self) { | |
if (INSTANCE_OF(afl_B, self)) | |
printf("B: %d\n", self->x); | |
} | |
// Forward the wrapper | |
static inline void afl_B_print(afl_B_t* self) { | |
afl_A_print(BASE_CAST(self)); | |
} | |
int main() { | |
afl_B_t b; | |
afl_B_init(&b, 1); | |
afl_A_t* downcast = (afl_A_t*)&b; | |
afl_A_print(downcast); // B: 1 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment