cutes/mirror.h

127 lines
4.1 KiB
C

#ifndef CUTES_MIRROR_H
#define CUTES_MIRROR_H
#include "typeclass_helpers.h"
#include "stdint.h"
#include "string.h"
#include "dictionary.h"
#include "strutil.h" // included because the impl macros require strhash
typedef uintptr_t typeid;
typedef struct {
const char* (*const get_typestring)(void* self);
typeid (*const get_typeid)(void* self);
Dictionary* (*const get_typeclasses)(void* self);
} IMirror;
typedef struct {
void* data;
union {
IMirror const* tc;
// this is cursed, but it allows the TC_CAST macro to work
IMirror const* mirror;
};
} Mirror;
typedef struct {
const void* typeclass;
void* function;
} MirroredTypeclass;
static inline int mirror_is_typeid(const Mirror* mirror, typeid id) {
return mirror->tc->get_typeid(mirror->data) == id;
}
static inline int mirror_is_typestring(const Mirror* mirror, const char* id) {
return strcmp(id, mirror->tc->get_typestring(mirror->data)) == 0;
}
static inline int mirror_eq(const Mirror* lhs, const Mirror* rhs) {
return lhs->tc->get_typeid(lhs->data) == rhs->tc->get_typeid(rhs->data);
}
extern const void* mirror_get_typeclass(void* data, IMirror const* tc, const char* typeclass);
// get the wrapper function for a given typeclass name
// example:
// mirror_get_function(physics_entity.data, physics_entity.mirror, "BehaviourEntity")
extern void* mirror_get_function(void* data, IMirror const* tc, const char* typeclass_name);
// macro reexport of mirror_get_function which will cast the function so it can be called immediately
// example:
// MIRROR_GET_WRAP_FUNC(physics_entity.data, physics_entity.mirror, BehaviourEntity)(physics_entity.data)
#define MIRROR_GET_WRAP_FUNC(Data_, MirrorTc_, Typeclass_)\
((Typeclass_(*)(void*))mirror_get_function(Data_, MirrorTc_, #Typeclass_))
// casting only works if the typeclass in question exposes IMirror as .mirror
// will segfault if the Mirror does not expose To_
// example:
// TC_CAST(physics_entity, BehaviourEntity)
#define TC_CAST(From_, To_)\
MIRROR_GET_WRAP_FUNC(From_.data, From_.mirror, To_)(From_.data)
#define TC_MIRRORS(From_, To_)\
MIRROR_GET_WRAP_FUNC(From_.data, From_.mirror, To_) != NULL
#define MIRROR_TRY_WRAP(Into_, Mirror_, Typeclass_){\
MirroredTypeclassWrapFunc fn_ = mirror_get_typeclass(Mirror_, #Typeclass_);\
if(fn_ != NULL) {\
Into_ = (TypeClass_)fn(Mirror_->data);\
}\
}
#define impl_Mirror_for(T, get_typestring_f, get_typeid_f, get_typeclasses_f)\
Mirror T##_as_Mirror(T* x) {\
TC_FN_TYPECHECK(const char*, get_typestring_f, T*);\
TC_FN_TYPECHECK(typeid, get_typeid_f, T*);\
TC_FN_TYPECHECK(Dictionary*, get_typeclasses_f, T*);\
static IMirror const tc = {\
.get_typestring = (const char*(*const)(void*)) get_typestring_f,\
.get_typeid = (typeid (*const)(void*)) get_typeid_f,\
.get_typeclasses = (Dictionary* (*const)(void*)) get_typeclasses_f,\
};\
return (Mirror){.tc = &tc, .data = x};\
}
#define DECL_REFLECT(T)\
extern const char* T##_get_typestring(T* self);\
extern typeid T##_get_typeid(T* self);\
extern Dictionary* T##_get_typeclasses(T* self);\
decl_typeclass_impl(Mirror, T)
#define START_REFLECT(T)\
const char* T##_get_typestring(T* self) {\
static const char* const typestring = #T;\
return typestring;\
}\
typeid T##_get_typeid(T* self) {\
static char init_flag = 0;\
static typeid id = 0;\
if(!init_flag) {\
init_flag = 1;\
id = strhash(#T);\
}\
return id;\
}\
Dictionary* T##_get_typeclasses(T* self) {\
static char init_flag = 0;\
static Dictionary typeclasses;\
if(!init_flag) {\
init_flag = 1;\
typeclasses = dictionary_new(sizeof(MirroredTypeclass));\
MirroredTypeclass tc;\
REFLECT_TYPECLASS(T, Mirror)
#define REFLECT_TYPECLASS(T, TypeClass_)\
tc = (MirroredTypeclass){\
.typeclass = (void*)T##_as_##TypeClass_(NULL).tc,\
.function = (void*)T##_as_##TypeClass_\
};\
dictionary_set_raw(&typeclasses, #TypeClass_, &tc)
#define END_REFLECT(T)\
}\
return &typeclasses;\
}\
impl_Mirror_for(T, T##_get_typestring, T##_get_typeid, T##_get_typeclasses)
#endif // !CUTES_MIRROR_H