Created
March 14, 2018 05:46
-
-
Save veryjos/2ab605d90f964775175c738e29b33dca to your computer and use it in GitHub Desktop.
This file contains 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
#pragma once | |
#include <string> | |
#include <functional> | |
#include <unordered_map> | |
#include <vector> | |
#include <type_traits> | |
namespace Reflect { | |
/** | |
* Forward declarations.. | |
*/ | |
namespace internal { | |
template <typename... A> | |
void PackArgList(char** out, A... args); | |
}; | |
/** | |
* Used to call getters, setters, function invokers.. | |
*/ | |
using _meta_Setter = void (*)(void*, void*); | |
using _meta_Getter = void* (*)(void*); | |
using _meta_Invoker = void* (*)(void*, void*); | |
struct MemberVariable { | |
std::string name; | |
size_t offset; | |
template <typename T, typename V> | |
void Set(T* instance, V value) { | |
*((V*)((char*)instance) + offset) = value; | |
}; | |
template <typename R, typename T> | |
R Get(T* instance) { | |
return *((R*)((char*)instance) + offset); | |
}; | |
}; | |
struct MemberFunction { | |
std::string name; | |
_meta_Invoker invoker; | |
template <typename T, typename... A> | |
void Invoke(T* instance, A... a) { | |
char* packedArgs; | |
internal::PackArgList<A...>(&packedArgs, a...); | |
invoker((void*)instance, packedArgs); | |
}; | |
}; | |
struct Class { | |
std::string name; | |
std::unordered_map<std::string, MemberVariable> memberVariables; | |
std::unordered_map<std::string, MemberFunction> memberFunctions; | |
}; | |
inline std::unordered_map<std::string, Class> classes; | |
namespace internal { | |
/** | |
* Some types wrapped for use at compile time | |
*/ | |
template <int i> | |
struct MetaIndex { | |
static constexpr const int value = i; | |
}; | |
template <bool v> | |
struct MetaFlag { | |
static constexpr const int value = v; | |
}; | |
/** | |
* Helpers for counting arguments | |
*/ | |
template <typename... A> | |
struct ArgCounter { | |
static constexpr int value = sizeof...(A); | |
}; | |
template <typename R, typename... A> | |
const int constexpr GetArgCount(R (A...)) { | |
return ArgCounter<A...>::value; | |
}; | |
template <typename R, typename U, typename... A> | |
const int constexpr GetArgCount(R (U::*) (A...)) { | |
return ArgCounter<A...>::argCount; | |
}; | |
/** | |
* Helpers for summing sizeof(argument list[n]) | |
*/ | |
template <typename... A> | |
struct ArgListSizeCounter; | |
template <typename U, typename... A> | |
struct ArgListSizeCounter<U, A...> { | |
static const int constexpr GetSize() { | |
return sizeof(U) + ArgListSizeCounter<A...>::GetSize(); | |
}; | |
}; | |
template <> | |
struct ArgListSizeCounter<> { | |
static const int constexpr GetSize() { | |
return 0; | |
}; | |
}; | |
template <typename... A> | |
const int constexpr GetArgListSize() { | |
return ArgListSizeCounter<A...>::GetSize(); | |
}; | |
/** | |
* Helpers for getting a list of argument ID's | |
*/ | |
template <typename... A> | |
struct ArgIdentifier; | |
template <typename A, typename... R> | |
struct ArgIdentifier<A, R...> { | |
static void GetArgIdsImpl(std::vector<size_t>& list) { | |
list.push_back(typeid(A).hash_code()); | |
ArgIdentifier<R...>::GetArgIdsImpl(list); | |
}; | |
}; | |
template<> | |
struct ArgIdentifier<> { | |
static void GetArgIdsImpl(std::vector<size_t>& list) { | |
}; | |
}; | |
template <typename R, typename... A> | |
std::vector<size_t> GetArgIds(R (A...)) { | |
std::vector<size_t> list; | |
ArgIdentifier<A...>::GetArgIdsImpl(list); | |
return list; | |
}; | |
template <typename R, typename U, typename... A> | |
std::vector<size_t> GetArgIds(R (U::*) (A...)) { | |
std::vector<size_t> list; | |
ArgIdentifier<A...>::GetArgIdsImpl(list); | |
return list; | |
}; | |
/** | |
* Helpers for packing/unpacking arugments | |
*/ | |
template <typename... A> | |
struct ArgPacker; | |
template <typename U, typename... A> | |
struct ArgPacker<U, A...> { | |
static void Pack(char* out, U c, A... a) { | |
using nakedType = typename std::remove_reference<U>::type; | |
*((nakedType*)out) = c; | |
out += sizeof(U); | |
ArgPacker<A...>::Pack(out, a...); | |
}; | |
}; | |
template <> | |
struct ArgPacker<> { | |
static void Pack(char* out) { | |
}; | |
}; | |
template <typename... A> | |
void PackArgList(char** out, A... args) { | |
*out = (char*)malloc(GetArgListSize<A...>()); | |
ArgPacker<A...>::Pack(*out, args...); | |
}; | |
template <typename I, typename T, typename... A> | |
struct ArgUnpacker; | |
template <typename I, typename T, typename U, typename... A> | |
struct ArgUnpacker<I, T, U, A...> { | |
static void Unpack(T& tuple, char* args) { | |
std::get<I::value>(tuple) = *((U*)args); | |
args += sizeof(U); | |
ArgUnpacker<MetaIndex<I::value + 1>, T, A...>::Unpack(tuple, args); | |
}; | |
}; | |
template <typename I, typename T> | |
struct ArgUnpacker<I, T> { | |
static void Unpack(T& tuple, char* args) { | |
}; | |
}; | |
template <typename R, typename U, typename... A> | |
void MemberInvokeProxy(R (U::*fun) (A...), U* instance, A... args) { | |
(instance->*fun)(args...); | |
}; | |
template <typename R, typename U, typename... A> | |
R Invoke(R (U::*fun) (A...), U* instance, A... args) { | |
return std::invoke( | |
&MemberInvokeProxy<R, U, A...>, | |
args... | |
); | |
}; | |
template <typename R, typename U, typename... A> | |
R InvokeWithPackedArgs(R (U::*fun) (A...), U* instance, char* args) { | |
std::tuple<A...> unpackedArgs; | |
ArgUnpacker<MetaIndex<0>, decltype(unpackedArgs), A...>::Unpack(unpackedArgs, args); | |
return std::apply( | |
&MemberInvokeProxy<R, U, A...>, | |
std::tuple_cat( | |
std::make_tuple(fun, instance), | |
unpackedArgs | |
) | |
); | |
}; | |
/** | |
* ClassMetaStore will be specialized for each type at compile time and store | |
* the metadata for that type | |
*/ | |
template <typename T> | |
struct ClassMetaStore { | |
static inline ClassMetaStore<T>* instance; | |
Class cl; | |
}; | |
/** | |
* Registers a ClassMetaStore at alloc time (before main) | |
*/ | |
template <typename T> | |
struct Registrar { | |
Registrar(const char* name) { | |
ClassMetaStore<T>::instance = new ClassMetaStore<T>(); | |
ClassMetaStore<T>::instance->cl.name = name; | |
T::_meta_InitClassMetaStore(); | |
Reflect::classes[name] = ClassMetaStore<T>::instance->cl; | |
}; | |
}; | |
/** | |
* Gets the next value in a counter. Will be called recursively at | |
* compile time, checking if the definition for "_meta_coounter" has been | |
* overloaded for the index that was passed in | |
*/ | |
template <typename T, typename I, typename invalidator> | |
static constexpr const int NextValueForCounter() { | |
if constexpr (decltype(T::_meta_counter(I{}))::value == false) { | |
return I::value; | |
} else { | |
return NextValueForCounter<T, MetaIndex<I::value + 1>, invalidator>(); | |
} | |
}; | |
}; | |
}; | |
#define REFLECT_ENABLE(type) \ | |
using _meta_myType = type; \ | |
friend struct Reflect::internal::ClassMetaStore<_meta_myType>; \ | |
friend struct Reflect::internal::Registrar<_meta_myType>; \ | |
\ | |
static inline Reflect::internal::Registrar<_meta_myType> _meta_registrar = Reflect::internal::Registrar<_meta_myType>(#type); \ | |
\ | |
template <typename I> \ | |
static constexpr Reflect::internal::MetaFlag<false> _meta_counter(I); \ | |
\ | |
template <typename I> \ | |
static void _meta_InitNext(I) { }; \ | |
\ | |
static void _meta_InitClassMetaStore() { \ | |
_meta_InitNext(Reflect::internal::MetaIndex<0>{}); \ | |
} \ | |
#define CONCAT_(x,y) x##y | |
#define CONCAT(x,y) CONCAT_(x,y) | |
#define REFLECT(name) _REFLECT_IMPL(name, CONCAT(_meta_index_, name), __LINE__) | |
#define _REFLECT_IMPL(name, ident, line) \ | |
static constexpr const int ident = Reflect::internal::NextValueForCounter< \ | |
_meta_myType, \ | |
Reflect::internal::MetaIndex<0>, \ | |
Reflect::internal::MetaIndex<line> \ | |
>(); \ | |
static constexpr const Reflect::internal::MetaFlag<true> _meta_counter(Reflect::internal::MetaIndex<ident>); \ | |
\ | |
template <typename T> \ | |
static typename std::enable_if< \ | |
std::is_member_function_pointer<decltype(&T::name)>::value, \ | |
void \ | |
>::type \ | |
_meta_InitMemberFunction(Reflect::internal::MetaIndex<ident>) { \ | |
auto meta = Reflect::internal::ClassMetaStore<_meta_myType>::instance; \ | |
\ | |
auto invoker = [](void* instance, void* args) -> void* { \ | |
T* specialized = (T*)instance ; \ | |
\ | |
Reflect::internal::InvokeWithPackedArgs(&T::name, specialized, (char*)args); \ | |
\ | |
return nullptr; \ | |
}; \ | |
\ | |
Reflect::MemberFunction memberFunction = { \ | |
#name, \ | |
invoker \ | |
}; \ | |
\ | |
meta->cl.memberFunctions[#name] = memberFunction; \ | |
} \ | |
\ | |
template <typename T> \ | |
static typename std::enable_if< \ | |
!std::is_member_function_pointer<decltype(&T::name)>::value, \ | |
void \ | |
>::type \ | |
_meta_InitMemberFunction(Reflect::internal::MetaIndex<ident>) { \ | |
} \ | |
\ | |
template <typename T> \ | |
static typename std::enable_if< \ | |
std::is_member_object_pointer<decltype(&T::name)>::value, \ | |
void \ | |
>::type \ | |
_meta_InitMemberVariable(Reflect::internal::MetaIndex<ident>) { \ | |
auto meta = Reflect::internal::ClassMetaStore<_meta_myType>::instance; \ | |
Reflect::MemberVariable memberVariable = { \ | |
#name, \ | |
offsetof(T, name) \ | |
}; \ | |
\ | |
meta->cl.memberVariables[#name] = memberVariable; \ | |
} \ | |
\ | |
template <typename T> \ | |
static typename std::enable_if< \ | |
!std::is_member_object_pointer<decltype(&T::name)>::value, \ | |
void \ | |
>::type \ | |
_meta_InitMemberVariable(Reflect::internal::MetaIndex<ident>) { \ | |
} \ | |
\ | |
static void _meta_InitNext(Reflect::internal::MetaIndex<ident>) { \ | |
_meta_InitMemberVariable<_meta_myType>(Reflect::internal::MetaIndex<ident>{}); \ | |
_meta_InitMemberFunction<_meta_myType>(Reflect::internal::MetaIndex<ident>{}); \ | |
\ | |
_meta_InitNext(Reflect::internal::MetaIndex<ident + 1>{}); \ | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment