Created
April 16, 2019 21:05
-
-
Save shintakezou/2f0a03660794968da5d3ef368a36a65e to your computer and use it in GitHub Desktop.
Dynamic dispatch not just on the first argument (at least C++14)
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
// see https://www.reddit.com/r/cpp_questions/comments/afp4ae/dynamic_argumentbased_dispatch_will_it_ever_be/eeh8qe7/ | |
// credits to https://www.reddit.com/user/alfps | |
// I've modified it so that it can be compiled with a C++14 compliant compiler, plus minor style | |
// modifications, e.g., I've changed the syntax auto x()->ReturnValue in function declaration and defines, | |
// since there's no point in using it. | |
#include <array> // std::array | |
#include <functional> // std::function | |
#include <iostream> | |
#include <map> // std::map | |
#include <stdexcept> // std::exception | |
#include <string> // std::string | |
#include <typeindex> // std::type_index | |
#include <type_traits> // std::is_polymorphic | |
#include <utility> // std::move | |
#define $fail(s) cppx::fail(std::string() + __func__ + " - " + s) | |
namespace std | |
{ | |
// this was added in C++17 | |
template<class T> | |
constexpr bool is_polymorphic_v = is_polymorphic<T>::value; | |
} | |
namespace cppx | |
{ | |
using std::runtime_error; | |
using std::string; | |
template<class Type> using P_ = Type*; | |
bool hopefully(const bool condition) | |
{ | |
return condition; | |
} | |
bool fail(const string& message) | |
{ | |
throw runtime_error(message); | |
} | |
} // namespace cppx | |
namespace app | |
{ | |
using cppx::P_; | |
using cppx::hopefully; | |
using cppx::fail; | |
using std::cout; | |
using std::endl; | |
using std::array; | |
using std::function; | |
using std::map; | |
using std::type_index; | |
using std::is_polymorphic_v; | |
using std::move; | |
void say(const P_<const char> s) | |
{ | |
cout << s << endl; | |
} | |
struct Base | |
{ | |
virtual ~Base() {} | |
}; | |
struct String : Base {}; | |
struct Integer : Base {}; | |
double sum_ss(P_<const String>, P_<const String>) { say("sum(S,S)"); return 1; } | |
double sum_si(P_<const String>, P_<const Integer>) { say("sum(S,I)"); return 2; } | |
double sum_is(P_<const Integer>, P_<const String>) { say("sum(I,S)"); return 3; } | |
double sum_ii(P_<const Integer>, P_<const Integer>) { say("sum(I,I)"); return 4; } | |
class Dispatcher | |
{ | |
using Func = double (P_<const void>, P_<const void>); | |
using Args_id = array<type_index, 2>; | |
using Args_to_func = map<Args_id, function<Func>>; | |
Args_to_func m_functions; | |
public: | |
template<class Arg_1, class Arg_2> | |
double operator()(P_<const Arg_1> a1, P_<const Arg_2> a2) const | |
{ | |
static_assert(is_polymorphic_v<Arg_1>); | |
static_assert(is_polymorphic_v<Arg_2>); | |
const auto it = m_functions.find({typeid(*a1), typeid(*a2)}); | |
hopefully(it != m_functions.end()) | |
or $fail( "No function corresponds to the argument types" ); | |
return it->second(a1, a2); | |
} | |
template<class Arg_1, class Arg_2> | |
void add_wrapped(function<double(P_<const Arg_1>, P_<const Arg_2>)> f) | |
{ | |
static_assert(is_polymorphic_v<Arg_1>); | |
static_assert(is_polymorphic_v<Arg_2>); | |
const Args_id args_id = {typeid(Arg_1), typeid(Arg_2)}; | |
hopefully(m_functions.count(args_id) == 0) | |
or $fail( "That function signature has already been added." ); | |
const auto anon_f = | |
[f](P_<const void> a1, P_<const void> a2) -> double | |
{ | |
return f(static_cast<P_<const Arg_1>>(a1), static_cast<P_<const Arg_2>>(a2)); | |
}; | |
m_functions.insert({ args_id, anon_f }); | |
} | |
// Just an argument type deduction convenience for adding free-standing functions. | |
template<class Arg_1, class Arg_2> | |
void add(const P_<double(P_<const Arg_1>, P_<const Arg_2>)> f) | |
{ | |
add_wrapped<Arg_1, Arg_2>(f); | |
} | |
Dispatcher() | |
{ | |
add(sum_ss); | |
add(sum_si); | |
add(sum_is); | |
add(sum_ii); | |
} | |
}; | |
const Dispatcher sum; | |
void run() | |
{ | |
const Base& s = String(); | |
const Base& i = Integer(); | |
const Base& b = Base(); | |
sum(&s, &s); | |
sum(&s, &i); | |
sum(&i, &s); | |
sum(&i, &i); | |
// Bang | |
sum(&b, &b); | |
} | |
} // namespace app | |
int main() | |
{ | |
using namespace std; | |
try | |
{ | |
app::run(); | |
return EXIT_SUCCESS; | |
} | |
catch (const exception& x) | |
{ | |
cerr << "!oops - " << x.what() << endl; | |
} | |
return EXIT_FAILURE; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment