Created
October 10, 2020 18:14
-
-
Save andreafioraldi/ead77d3cda724377098c0ef4479e6d71 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> | |
#include <assert.h> | |
/* Macro magic, you don't have to understand, just believe. */ | |
#define AFL_MAGIC_FIRST_(a, ...) a | |
#define AFL_MAGIC_SECOND_(a, b, ...) b | |
#define AFL_MAGIC_FIRST(...) AFL_MAGIC_FIRST_(__VA_ARGS__,) | |
#define AFL_MAGIC_SECOND(...) AFL_MAGIC_SECOND_(__VA_ARGS__,) | |
#define AFL_MAGIC_FIRST_SECOND(...) AFL_MAGIC_FIRST(__VA_ARGS__) AFL_MAGIC_SECOND(__VA_ARGS__) | |
#define AFL_MAGIC_IS_PROBE(...) AFL_MAGIC_SECOND(__VA_ARGS__, 0) | |
#define AFL_MAGIC_PROBE() ~, 1 | |
#define AFL_MAGIC_CAT(a, b) a ## b | |
#define AFL_MAGIC_NOT(x) AFL_MAGIC_IS_PROBE(AFL_MAGIC_CAT(AFL_MAGIC__NOT_, x)) | |
#define AFL_MAGIC__NOT_0 AFL_MAGIC_PROBE() | |
#define AFL_MAGIC_BOOL(x) AFL_MAGIC_NOT(AFL_MAGIC_NOT(x)) | |
/* If else on macro constants */ | |
#define AFL_MAGIC_IF_ELSE(condition) AFL_MAGIC__IF_ELSE(AFL_MAGIC_BOOL(condition)) | |
#define AFL_MAGIC__IF_ELSE(condition) AFL_MAGIC_CAT(AFL_MAGIC__IF_, condition) | |
#define AFL_MAGIC__IF_1(...) __VA_ARGS__ AFL_MAGIC__IF_1_ELSE | |
#define AFL_MAGIC__IF_0(...) AFL_MAGIC__IF_0_ELSE | |
#define AFL_MAGIC__IF_1_ELSE(...) | |
#define AFL_MAGIC__IF_0_ELSE(...) __VA_ARGS__ | |
#define AFL_MAGIC_HAS_ARGS(...) AFL_MAGIC_BOOL(AFL_MAGIC_FIRST(AFL_MAGIC__END_OF_ARGUMENTS_ __VA_ARGS__)()) | |
#define AFL_MAGIC__END_OF_ARGUMENTS_() 0 | |
#define AFL_MAGIC_EVAL0(...) __VA_ARGS__ | |
#define AFL_MAGIC_EVAL1(...) AFL_MAGIC_EVAL0(AFL_MAGIC_EVAL0(AFL_MAGIC_EVAL0(__VA_ARGS__))) | |
#define AFL_MAGIC_EVAL2(...) AFL_MAGIC_EVAL1(AFL_MAGIC_EVAL1(AFL_MAGIC_EVAL1(__VA_ARGS__))) | |
#define AFL_MAGIC_EVAL3(...) AFL_MAGIC_EVAL2(AFL_MAGIC_EVAL2(AFL_MAGIC_EVAL2(__VA_ARGS__))) | |
#define AFL_MAGIC_EVAL4(...) AFL_MAGIC_EVAL3(AFL_MAGIC_EVAL3(AFL_MAGIC_EVAL3(__VA_ARGS__))) | |
#define AFL_MAGIC_EVAL(...) AFL_MAGIC_EVAL4(AFL_MAGIC_EVAL4(AFL_MAGIC_EVAL4(__VA_ARGS__))) | |
#define AFL_MAGIC_MAPEND(...) | |
#define AFL_MAGIC_MAPOUT | |
#define AFL_MAGIC_MAPCOMMA , | |
#define AFL_MAGIC_MAPGET_END2() 0, AFL_MAGIC_MAPEND | |
#define AFL_MAGIC_MAPGET_END1(...) AFL_MAGIC_MAPGET_END2 | |
#define AFL_MAGIC_MAPGET_END(...) AFL_MAGIC_MAPGET_END1 | |
#define AFL_MAGIC_MAPNEXT0(test, next, ...) next AFL_MAGIC_MAPOUT | |
#define AFL_MAGIC_MAPNEXT1(test, next) AFL_MAGIC_MAPNEXT0(test, next, 0) | |
#define AFL_MAGIC_MAPNEXT(test, next) AFL_MAGIC_MAPNEXT1(AFL_MAGIC_MAPGET_END test, next) | |
#define AFL_MAGIC_MAP0(f, x, peek, ...) f(x) AFL_MAGIC_MAPNEXT(peek, AFL_MAGIC_MAP1)(f, peek, __VA_ARGS__) | |
#define AFL_MAGIC_MAP1(f, x, peek, ...) f(x) AFL_MAGIC_MAPNEXT(peek, AFL_MAGIC_MAP0)(f, peek, __VA_ARGS__) | |
#define AFL_MAGIC_DOUBLE_MAP0(f, x, y, peek, ...) f(x, y) AFL_MAGIC_MAPNEXT(peek, AFL_MAGIC_DOUBLE_MAP1)(f, peek, __VA_ARGS__) | |
#define AFL_MAGIC_DOUBLE_MAP1(f, x, y, peek, ...) f(x, y) AFL_MAGIC_MAPNEXT(peek, AFL_MAGIC_DOUBLE_MAP0)(f, peek, __VA_ARGS__) | |
#define AFL_MAGIC_MAPLIST_NEXT1(test, next) AFL_MAGIC_MAPNEXT0(test, AFL_MAGIC_MAPCOMMA next, 0) | |
#define AFL_MAGIC_MAPLIST_NEXT(test, next) AFL_MAGIC_MAPLIST_NEXT1(AFL_MAGIC_MAPGET_END test, next) | |
#define AFL_MAGIC_MAPLIST0(f, x, peek, ...) f(x) AFL_MAGIC_MAPLIST_NEXT(peek, AFL_MAGIC_MAPLIST1)(f, peek, __VA_ARGS__) | |
#define AFL_MAGIC_MAPLIST1(f, x, peek, ...) f(x) AFL_MAGIC_MAPLIST_NEXT(peek, AFL_MAGIC_MAPLIST0)(f, peek, __VA_ARGS__) | |
#define AFL_MAGIC_DOUBLE_MAPLIST0(f, x, y, peek, ...) \ | |
f(x, y) AFL_MAGIC_MAPLIST_NEXT(peek, AFL_MAGIC_DOUBLE_MAPLIST1)(f, peek, __VA_ARGS__) | |
#define AFL_MAGIC_DOUBLE_MAPLIST1(f, x, y, peek, ...) \ | |
f(x, y) AFL_MAGIC_MAPLIST_NEXT(peek, AFL_MAGIC_DOUBLE_MAPLIST0)(f, peek, __VA_ARGS__) | |
/** | |
* Applies the function macro `f` to each of the remaining parameters. | |
*/ | |
#define AFL_MAGIC_MAP(f, ...) \ | |
AFL_MAGIC_IF_ELSE(AFL_MAGIC_HAS_ARGS(__VA_ARGS__))( \ | |
AFL_MAGIC_EVAL(AFL_MAGIC_MAP1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) \ | |
)() | |
#define AFL_MAGIC_DOUBLE_MAP(f, ...) \ | |
AFL_MAGIC_IF_ELSE(AFL_MAGIC_HAS_ARGS(__VA_ARGS__))( \ | |
AFL_MAGIC_EVAL(AFL_MAGIC_DOUBLE_MAP1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) \ | |
)() | |
/** | |
* Applies the function macro `f` to each of the remaining parameters and | |
* inserts commas between the results. | |
*/ | |
#define AFL_MAGIC_MAPLIST(f, ...) \ | |
AFL_MAGIC_IF_ELSE(AFL_MAGIC_HAS_ARGS(__VA_ARGS__))( \ | |
AFL_MAGIC_EVAL(AFL_MAGIC_MAPLIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) \ | |
)() | |
#define AFL_MAGIC_DOUBLE_MAPLIST(f, ...) \ | |
AFL_MAGIC_IF_ELSE(AFL_MAGIC_HAS_ARGS(__VA_ARGS__))( \ | |
AFL_MAGIC_EVAL(AFL_MAGIC_DOUBLE_MAPLIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) \ | |
)() | |
#define AFL_MAGIC_VA_COMMA(...) \ | |
AFL_MAGIC_IF_ELSE(AFL_MAGIC_HAS_ARGS(__VA_ARGS__))( \ | |
AFL_MAGIC_MAPCOMMA __VA_ARGS__ \ | |
)() | |
/* | |
Class and vtable macros. | |
*/ | |
#define AFL_END_STRUCT(...) __VA_ARGS__ }; | |
#define AFL_CLASS_BEGIN(name, parent_type) \ | |
struct name##__fields { \ | |
\ | |
struct parent_type##__fields; \ | |
#define AFL_CLASS_END(name) }; \ | |
\ | |
struct name { \ | |
\ | |
struct name##_vtable* _vptr; \ | |
struct name##__fields; \ | |
\ | |
}; \ | |
typedef struct name name##_t; | |
#define AFL_VTABLE(name, parent_type, ...) \ | |
struct name##_vtable; \ | |
extern struct name##_vtable name##_vtable_instance; \ | |
\ | |
struct name##_vtable { \ | |
\ | |
struct parent_type##_vtable; \ | |
\ | |
__VA_ARGS__ \ | |
\ | |
AFL_END_STRUCT | |
#define AFL_LINK_BASE(type) \ | |
._base_vptr = (struct afl_object_vtable*)&type##_vtable_instance | |
#define AFL_BIND_METHOD(type, name) .name = &type##_##name##__nonvirtual | |
#define AFL_VTABLE_FOR(name, parent_type) \ | |
struct name##_vtable name##_vtable_instance = { \ | |
\ | |
AFL_LINK_BASE(parent_type), \ | |
\ | |
AFL_END_STRUCT | |
#define AFL_CLASS(name, parent_type, ...) \ | |
AFL_CLASS_BEGIN(name, parent_type) \ | |
__VA_ARGS__ \ | |
AFL_CLASS_END(name) \ | |
\ | |
AFL_VTABLE(name, parent_type) | |
#define AFL_VTABLEOF(ptr) ((ptr)->_vptr) | |
#define AFL_VTABLE_SET(ptr, vtable) (ptr)->_vptr = &vtable | |
// #define AFL_INSTANCEOF(type, ptr) ((ptr)->v == &type##_vtable_instance) | |
#define AFL_INSTANCEOF(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 AFL_DYNCAST(type, ptr) (AFL_INSTANCEOF(type, ptr) ? (struct type*)(ptr) : NULL) | |
/* | |
Methods wrappers. | |
*/ | |
#define AFL_END_METHOD(...) __VA_ARGS__ } | |
#define AFL_DECLARE_OVERRIDE(class_type, base_type, ret_type, name, ...) \ | |
ret_type class_type##_##name##__nonvirtual(struct base_type* AFL_MAGIC_VA_COMMA( AFL_MAGIC_DOUBLE_MAPLIST(AFL_MAGIC_FIRST, __VA_ARGS__) )); \ | |
\ | |
static inline ret_type class_type##_##name(struct class_type* self AFL_MAGIC_VA_COMMA( AFL_MAGIC_DOUBLE_MAPLIST(AFL_MAGIC_FIRST_SECOND, __VA_ARGS__) )) { \ | |
return base_type##_##name((struct base_type*)self AFL_MAGIC_VA_COMMA( AFL_MAGIC_DOUBLE_MAPLIST(AFL_MAGIC_SECOND, __VA_ARGS__) )); \ | |
} | |
#define AFL_DEFINE_OVERRIDE(class_type, base_type, ret_type, name, ...) \ | |
ret_type class_type##_##name##__nonvirtual(struct base_type* _self AFL_MAGIC_VA_COMMA( AFL_MAGIC_DOUBLE_MAPLIST(AFL_MAGIC_FIRST, __VA_ARGS__) )) { \ | |
\ | |
assert (_self); \ | |
assert (AFL_INSTANCEOF(class_type, _self)); \ | |
struct class_type* self = (struct class_type*)_self; \ | |
\ | |
AFL_END_METHOD | |
#define AFL_OVERRIDE(class_type, base_type, ret_type, name, ...) \ | |
AFL_DECLARE_OVERRIDE(class_type, base_type, ret_type, name, __VA_ARGS__) \ | |
AFL_DEFINE_OVERRIDE(class_type, base_type, ret_type, name, __VA_ARGS__) | |
#define AFL_DECLARE_VIRTUAL(class_type, ret_type, name, ...) \ | |
static inline ret_type class_type##_##name(struct class_type* self AFL_MAGIC_VA_COMMA( AFL_MAGIC_DOUBLE_MAPLIST(AFL_MAGIC_FIRST_SECOND, __VA_ARGS__) )) { \ | |
return AFL_VTABLEOF(self)->print(self AFL_MAGIC_VA_COMMA( AFL_MAGIC_DOUBLE_MAPLIST(AFL_MAGIC_SECOND, __VA_ARGS__) )); \ | |
} | |
// TODO AFL_DECLARE_VIRTUAL_DEFAULT AFL_DEFINE_VIRTUAL_DEFAULT | |
#define AFL_DECLARE_INIT_MODIFIER_BEGIN(modifier, class_type, ...) \ | |
void class_type##_init##modifier(struct class_type* self AFL_MAGIC_VA_COMMA( AFL_MAGIC_DOUBLE_MAPLIST(AFL_MAGIC_FIRST_SECOND, __VA_ARGS__) )) { \ | |
#define AFL_DECLARE_INIT_BEGIN(class_type, ...) AFL_DECLARE_INIT_MODIFIER_BEGIN(, class_type, __VA_ARGS__) | |
#define AFL_DECLARE_INIT_END(class_type) \ | |
AFL_VTABLE_SET(self, class_type##_vtable_instance); } | |
#define AFL_METHOD(class_type, name) class_type##_##name | |
/// 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; | |
struct afl_object__fields { | |
void* _wrapper; // for bindings | |
}; | |
struct afl_object { | |
struct afl_object_vtable* _vptr; | |
struct afl_object__fields; | |
}; | |
static inline void afl_object_deinit(afl_object_t* self) { | |
if (AFL_VTABLEOF(self)->deinit) | |
AFL_VTABLEOF(self)->deinit(self); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/// virtual class A | |
AFL_CLASS(afl_A, afl_object, | |
int x; | |
)( | |
void (*print)(afl_A_t*); | |
) | |
AFL_DECLARE_VIRTUAL(afl_A, void, print) | |
AFL_DECLARE_INIT_MODIFIER_BEGIN(__protected, afl_A, int, x) | |
self->x = x; | |
AFL_DECLARE_INIT_END(afl_A) | |
AFL_OVERRIDE(afl_A, afl_object, void, deinit)( | |
self->x = 0; | |
) | |
// put in C file | |
AFL_VTABLE_FOR(afl_A, afl_object)( | |
AFL_BIND_METHOD(afl_A, deinit), | |
) | |
/// class B | |
AFL_CLASS(afl_B, afl_A)() | |
AFL_DECLARE_INIT_BEGIN(afl_B, int, x) | |
// call super constructor | |
afl_A_init__protected((afl_A_t*)self, x); | |
AFL_DECLARE_INIT_END(afl_B) | |
AFL_OVERRIDE(afl_B, afl_A, void, print)( | |
printf("B: %d\n", self->x); | |
) | |
// put in C file | |
AFL_VTABLE_FOR(afl_B, afl_A)( | |
AFL_BIND_METHOD(afl_A, deinit), | |
AFL_BIND_METHOD(afl_B, print), | |
) | |
int main() { | |
afl_B_t b; | |
afl_B_init(&b, 1); | |
afl_A_t* downcast = (afl_A_t*)&b; | |
afl_B_print(&b); // B: 1 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment