Last active
July 12, 2016 09:56
-
-
Save elliotpotts/344c27ea7f0cf23c98ba 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
#include <iostream> | |
#include <type_traits> | |
#include <typeindex> | |
#include <map> | |
#include <vector> | |
#include <tuple> | |
#include <utility> | |
#include <functional> | |
#include <memory> | |
#include <stdexcept> | |
#include <boost/any.hpp> | |
template <typename T> | |
struct function_traits : public function_traits<decltype(&T::operator())>{ | |
}; | |
template <typename ClassType, typename ReturnType, typename... Args> | |
struct function_traits<ReturnType(ClassType::*)(Args...) const> { | |
static constexpr auto arity = sizeof...(Args); | |
typedef ReturnType result_type; | |
template <size_t i> | |
using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; | |
using args = std::tuple<Args...>; | |
}; | |
template <class ReturnT> | |
class DynamicFunction { | |
private: | |
template<class... ArgsT, size_t... Is> | |
ReturnT op_call_helper(std::index_sequence<Is...> arg_indices, ArgsT&&... args) { | |
auto arg_storage = std::make_tuple(args...); | |
return call({std::get<Is>(arg_storage)...}); | |
} | |
protected: | |
virtual ReturnT call(std::vector<boost::any> args) = 0; | |
public: | |
template <class... ArgsT> | |
ReturnT operator()(ArgsT&&... args) { | |
return op_call_helper(std::index_sequence_for<ArgsT...>(), args...); | |
} | |
}; | |
template <class FT> | |
class DynamicFunctionImpl : public DynamicFunction<typename function_traits<FT>::result_type> { | |
private: | |
using ReturnT = typename function_traits<FT>::result_type; | |
FT f_storage; | |
template<class... ArgsT, size_t... Is> | |
ReturnT call_helper(std::vector<boost::any> args, std::index_sequence<Is...> indices) { | |
auto argument_transport = std::make_tuple(boost::any_cast<typename std::tuple_element<Is, typename function_traits<FT>::args>::type>(args[Is])...); | |
return f_storage(std::get<Is>(argument_transport)...); | |
} | |
protected: | |
virtual ReturnT call(std::vector<boost::any> args) { | |
return call_helper(args, std::make_index_sequence<std::tuple_size<typename function_traits<FT>::args>::value>()); | |
} | |
public: | |
DynamicFunctionImpl(FT f) : f_storage(f) { | |
} | |
}; | |
using runtime_signature = std::vector<std::type_index>; | |
template <class ReturnT> | |
class OpenMultiMethod { | |
private: | |
std::map<runtime_signature, std::unique_ptr<DynamicFunction<ReturnT>>> methods; | |
template<class FT, size_t... Is> | |
void add_method(FT function, std::index_sequence<Is...>) { | |
runtime_signature args_t = { typeid(typename std::tuple_element<Is, typename function_traits<FT>::args>::type)... }; | |
methods[args_t] = std::unique_ptr<DynamicFunction<ReturnT>>(new DynamicFunctionImpl<FT>(function)); | |
} | |
public: | |
template<class FT> | |
void operator+=(FT function) { | |
add_method(function, std::make_index_sequence<std::tuple_size<typename function_traits<FT>::args>::value>()); | |
} | |
template<class... ArgsT> | |
ReturnT operator()(ArgsT... args) { | |
runtime_signature signature = { typeid(args)... }; | |
if(methods.find(signature) == methods.end()) { | |
throw std::runtime_error("Could not match dynamic signature"); | |
} else { | |
return (*methods[signature])(args...); | |
} | |
} | |
}; | |
class Parent { | |
virtual void make_vtable() {} | |
}; | |
struct ChildA : Parent {}; | |
struct ChildB : Parent {}; | |
int main() { | |
OpenMultiMethod<void> colliding; | |
colliding += [](ChildA& a, ChildA& b) { std::cout << "AA\n";}; | |
colliding += [](ChildA* a, ChildB* b) { std::cout << "AB\n";}; | |
colliding += [](ChildB* a, ChildA* b) { std::cout << "BA\n";}; | |
colliding += [](ChildB* a, ChildB* b) { std::cout << "BB\n";}; | |
Parent* a = new ChildA(); | |
Parent* b = new ChildB(); | |
colliding(*a, *a); | |
colliding(a, b); | |
colliding(b, a); | |
colliding(b, b); | |
} |
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
- Allow references | |
- Allow base pointers | |
- Allow ordering of suitability of arguments based on inheritance distance & other factors | |
- Disallow duplicate signature handlers |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I tried using this, but as it stand it throws a "Could not match dynamic signature" all the time.
This specific error is due to the fact that
OpenMultiMethod::operator()
doesn't correctly forward the type of its parameters, losing information.I believe lines 88 and 89 should look like this:
With this change it matches the
colliding(*a, *a)
call (won't match the subsequent ones, though, because of the pointers), but will still fail with this exception: