Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@shintakezou
Created April 16, 2019 21:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shintakezou/2f0a03660794968da5d3ef368a36a65e to your computer and use it in GitHub Desktop.
Save shintakezou/2f0a03660794968da5d3ef368a36a65e to your computer and use it in GitHub Desktop.
Dynamic dispatch not just on the first argument (at least C++14)
// 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