Last active
August 29, 2015 13:57
-
-
Save EricWF/9540803 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
#ifndef ENTITY_HPP | |
#define ENTITY_HPP | |
# include <elib/aux.hpp> | |
# include <elib/any.hpp> | |
# include <unordered_map> | |
# include <typeinfo> | |
# include <typeindex> | |
namespace example | |
{ | |
using namespace elib; | |
// fwd | |
class entity; | |
// all tags have to inherit from this for safety | |
// prevents things like int or std::string from being used as tags | |
struct method_base {}; | |
namespace detail | |
{ | |
//////////////////////////////////////////////////////////////////////// | |
// | |
template <class T> | |
struct deduce_result | |
{ | |
static_assert( | |
elib::aux::never<T>::value | |
, "cannot deduce result for non-function type" | |
); | |
}; | |
template <class Ret, class ...Args> | |
struct deduce_result<Ret(Args...)> | |
{ | |
using type = Ret; | |
}; | |
//////////////////////////////////////////////////////////////////////// | |
// | |
template <class T> | |
struct method_traits | |
{ | |
static_assert( | |
aux::never<T>::value | |
, "cannot check method on non function type" | |
); | |
}; | |
template <class Tag, class ...Args> | |
struct method_traits<Tag(Args...)> | |
{ | |
// the type used to call the function | |
using type = Tag(Args...); | |
// The tag containing meta-information | |
using tag = Tag; | |
// the actual type (without pointer or ref) of the actual method pointer | |
using method_type = typename Tag::type; | |
// the return type of the method | |
using result_type = typename deduce_result<method_type>::type; | |
// the signature of the method externally | |
using signature = result_type(Args...); | |
// true if we require a "this" entity to be passed as the first param | |
static constexpr bool requires_this = | |
elib::or_< | |
aux::is_same<method_type, result_type(entity &, Args...)> | |
, aux::is_same<method_type, result_type(entity const &, Args...)> | |
>::value; | |
// true if the method is a "const" method | |
static constexpr bool is_const = | |
elib::or_< | |
aux::is_same<method_type, result_type(Args...)> | |
, aux::is_same<method_type, result_type(entity const &, Args...)> | |
>::value; | |
// if the call does not require this, this_type = void. | |
// otherwise it is entity & if non-const and entity const & if const | |
using this_type = | |
elib::if_c_t< | |
requires_this | |
, elib::if_c_t<is_const, entity const &, entity &> | |
, void | |
>; | |
// this may seem dumb, but in order to run the static_assert's | |
// we need to reach in and touch something. this is what we touch | |
static constexpr bool good = true; | |
private: | |
static_assert( | |
aux::is_base_of<method_base, Tag>::value | |
, "Tag must derive from method base" | |
); | |
// the signature and the method type can either have the sames arguments | |
// or the method_type can require a reference to "this" entity. | |
static_assert( | |
aux::is_same<method_type, signature>::value | |
|| aux::is_same<method_type, result_type(entity const &, Args...)>::value | |
|| aux::is_same<method_type, result_type(entity &, Args...)>::value | |
, "Invalid call signature for method" | |
); | |
}; | |
template <class Tag, class ...Args> | |
constexpr bool method_traits<Tag(Args...)>::requires_this; | |
template <class Tag, class ...Args> | |
constexpr bool method_traits<Tag(Args...)>::is_const; | |
template <class Tag, class ...Args> | |
constexpr bool method_traits<Tag(Args...)>::good; | |
template <class TaggedMethod, class ...InvokeArgs> | |
struct check_call : elib::false_ {}; | |
template <class Tag, class ...Args> | |
struct check_call<Tag(Args...), Args...> : elib::true_ {}; | |
template < | |
class TaggedMethod | |
, bool = method_traits<TaggedMethod>::requires_this | |
> struct method_adapter; | |
template <class Tag, class ...Args> | |
struct method_adapter<Tag(Args...), true> | |
{ | |
using traits = method_traits<Tag(Args...)>; | |
static_assert(traits::good, "Bad method traits"); | |
using method_type = typename traits::method_type; | |
using result_type = typename traits::result_type; | |
using entity_type = | |
elib::if_c_t< | |
traits::is_const | |
, const entity | |
, entity | |
>; | |
public: | |
method_adapter(entity_type & e, method_type* mptr) | |
: m_entity(&e), m_method_ptr(mptr) | |
{} | |
template < ELIB_ENABLE_IF(traits::requires_this) > | |
result_type operator()(Args &&... args) | |
{ | |
return (*m_method_ptr)(*m_entity, elib::forward<Args>(args)...); | |
} | |
private: | |
entity_type *m_entity; | |
method_type *m_method_ptr; | |
}; | |
template <class Tag, class ...Args> | |
struct method_adapter<Tag(Args...), false> | |
{ | |
using traits = method_traits<Tag(Args...)>; | |
static_assert(traits::good, "Bad method traits"); | |
using method_type = typename traits::method_type; | |
using result_type = typename traits::result_type; | |
using entity_type = | |
elib::if_c_t< | |
traits::is_const | |
, const entity | |
, entity | |
>; | |
public: | |
explicit method_adapter(method_type *mptr) | |
: m_method_ptr(mptr) | |
{} | |
method_adapter(entity_type &, method_type* mptr) | |
: m_method_ptr(mptr) | |
{} | |
result_type operator()(Args &&... args) | |
{ | |
return (*m_method_ptr)(elib::forward<Args>(args)...); | |
} | |
private: | |
method_type *m_method_ptr; | |
}; | |
} // namespace detail | |
// this is a sample implementation of an entity that accepts methods | |
// WARNING: the type index used as the key is different that the type it holds | |
class entity | |
{ | |
public: | |
template < | |
class TaggedMethod | |
, ELIB_ENABLE_IF(detail::method_traits<TaggedMethod>::good) | |
> | |
bool has_method() | |
{ | |
return m_methods.count(std::type_index(typeid(TaggedMethod))); | |
} | |
template< | |
class TaggedMethod | |
, ELIB_ENABLE_IF(detail::method_traits<TaggedMethod>::good) | |
> | |
void set_method(typename detail::method_traits<TaggedMethod>::method_type* method_ptr) | |
{ | |
using method_ptr_type = typename detail::method_traits<TaggedMethod>::method_type*; | |
m_methods[std::type_index(typeid(TaggedMethod))] = elib::any(method_ptr); | |
} | |
template < | |
class TaggedMethod | |
, ELIB_ENABLE_IF(detail::method_traits<TaggedMethod>::good) | |
> | |
void remove_method() | |
{ | |
m_methods.erase(std::type_index(typeid(TaggedMethod))); | |
} | |
template < | |
class TaggedMethod | |
, class ...Args | |
, ELIB_ENABLE_IF(detail::method_traits<TaggedMethod>::is_const == false) | |
, ELIB_ENABLE_IF(detail::check_call<TaggedMethod, Args...>::value) | |
> | |
typename detail::method_traits<TaggedMethod>::result_type | |
call(Args &&... args) | |
{ | |
using traits = detail::method_traits<TaggedMethod>; | |
auto pos = m_methods.find(std::type_index(typeid(TaggedMethod))); | |
if (pos == m_methods.end()) | |
throw "TODO: No such method"; | |
auto fn_ptr = elib::any_cast<typename traits::method_type*>(pos->second); | |
detail::method_adapter<TaggedMethod> adapter(*this, fn_ptr); | |
return adapter(elib::forward<Args>(args)...); | |
} | |
template < | |
class TaggedMethod | |
, class ...Args | |
, ELIB_ENABLE_IF(detail::method_traits<TaggedMethod>::is_const) | |
, ELIB_ENABLE_IF(detail::check_call<TaggedMethod, Args...>::value) | |
> | |
typename detail::method_traits<TaggedMethod>::result_type | |
call(Args &&... args) const | |
{ | |
using traits = detail::method_traits<TaggedMethod>; | |
auto pos = m_methods.find(std::type_index(typeid(TaggedMethod))); | |
if (pos == m_methods.end()) | |
throw "TODO: no such method"; | |
auto fn_ptr = elib::any_cast<typename traits::method_type*>(pos->second); | |
detail::method_adapter<TaggedMethod> adapter(*this, fn_ptr); | |
return adapter(elib::forward<Args>(args)...); | |
} | |
private: | |
std::unordered_map<std::type_index, elib::any> m_methods; | |
}; | |
} | |
#endif |
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
#include "entity.hpp" | |
#include <iostream> | |
namespace example | |
{ | |
// this is a tag | |
struct move_ : method_base | |
{ | |
using type = void(entity const &, int x, int y); | |
}; | |
void run() | |
{ | |
entity e; | |
std::cout << e.has_method<move_(int, int)>() << std::endl; | |
auto move_fn = [](entity &, int, int){ std::cout << "Here!" << std::endl; }; | |
e.set_method<move_(int, int)>(move_fn); | |
std::cout << e.has_method<move_(int, int)>() << std::endl; | |
e.call<move_(int, int)>(1, 2); | |
e.remove_method<move_(int, int)>(); | |
std::cout << e.has_method<move_(int, int)>() << std::endl; | |
} | |
} | |
int main() | |
{ | |
std::cout << std::boolalpha; | |
example::run(); | |
} |
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
// example one, using tag dispatching. | |
// in this example you need to define one tag for every type of function you want access to. | |
// NOTE: it would be pretty easy to generate this with a macro. | |
constexpr struct ActiveTextureARB_tag_t | |
{ | |
using type = GL_ActiveTextureARB_Func; | |
static const char* name = "glActiveTextureARB"; | |
} ActiveTextureARB_tag; | |
const char *ActiveTextureARB_tag_t::name; | |
template <class Tag, class ...Args> | |
typename std::result_of<typename Tag::type(std::declval<Args>()...)>::type | |
call_ogl(Tag, Args &&... args) | |
{ | |
using fn_type = typename Tag::type; | |
fn_type fn = (fn_type) SDL_GL_GetProcAddress(Tag::name); | |
return (*fn)(std::forward<Args>(args)...); | |
} | |
sample usage: | |
auto ret = call_ogl(ActiveTextureARB_tag); | |
other possible signatures: | |
auto ret = call_ogl<ActiveTextureARB_tag>(params...); | |
// NOTE: this version does not need any struct tag definitions | |
auto ret = call_ogl<ActiveTextureARB_Func>(String name, params...); | |
// A definition would look like | |
// Helper function to deduce return type | |
template <class T> | |
struct detect_return_type; | |
template <class Ret,class ...Args> | |
struct detect_return_type<Ret(Args...)> | |
{ | |
using type = Ret; | |
}; | |
template <class Ret,class ...Args> | |
struct detect_return_type<Ret(*)(Args...)> | |
{ | |
using type = Ret; | |
}; | |
template <class FunctionType, class ...Args> | |
typename detect_return_type<FunctionType>::type | |
call_ogl(const char *name, Args &&...args) | |
{ | |
FunctionType fn = (FunctionType) SDL_GetProcAddr(name); | |
return fn(std::forward<Args>(args)...); | |
} | |
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
#include "entity.hpp" | |
#include <iostream> | |
namespace example | |
{ | |
// this is a tag | |
struct move_ : method_base | |
{ | |
using type = void(entity const &, int x, int y); | |
}; | |
void run() | |
{ | |
entity e; | |
e.has_method<move_>(); | |
e.set_method<move_>([](){}); | |
e.call<move_>(0, 0); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment