#include <iostream> | |
#include <type_traits> | |
#include <string> | |
#include <variant> | |
#include <vector> | |
#include <memory> | |
using variant_t = std::variant<int, double, std::string>; | |
struct AbstractDispatcher | |
{ | |
virtual ~AbstractDispatcher() = default; | |
virtual bool matches(const std::vector<variant_t>& arglist) const noexcept = 0; | |
virtual void dispatch(const std::vector<variant_t>& arglist) = 0; | |
}; | |
template <class Functor, typename... Args> | |
struct Dispatcher : public AbstractDispatcher | |
{ | |
Dispatcher(Functor _f) : f(_f) { | |
static_assert(sizeof...(Args) <= 2, "Cannot dispatch more than 2 arguments."); | |
} | |
bool matches(const std::vector<variant_t>& arglist) const noexcept override { | |
const size_t N = sizeof...(Args); | |
if constexpr(N > 0) { | |
return arglist.size() == N && checkArgumentTypeAt<N, Args...>(arglist); | |
} else { | |
return arglist.empty(); | |
} | |
} | |
void dispatch(const std::vector<variant_t> &arglist) override { | |
const size_t N = sizeof...(Args); | |
if constexpr(N == 0) { | |
(*this)(); | |
} else if constexpr(N == 1) { | |
(*this)(std::get<TypeAt<0>>(arglist.at(0))); | |
} else if constexpr(N == 2) { | |
(*this)(std::get<TypeAt<0>>(arglist.at(0)), | |
std::get<TypeAt<1>>(arglist.at(1))); | |
} | |
} | |
private: | |
void operator()(const Args&... args) { | |
if constexpr(std::is_pointer<Functor>::value) { | |
(*f)(args...); | |
} else { | |
f(args...); | |
} | |
} | |
template <size_t N, typename First, typename Second, typename... Types> | |
bool checkArgumentTypeAt(const std::vector<variant_t>& arglist) const { | |
if constexpr(N > 0) { | |
return checkArgumentTypeAt<N, First>(arglist) | |
&& checkArgumentTypeAt<N-1, Second, Types...>(arglist); | |
} | |
return true; | |
} | |
template <size_t N, typename Type> | |
bool checkArgumentTypeAt(const std::vector<variant_t>& arglist) const { | |
if constexpr(N > 0) { | |
size_t index = arglist.size() - N; | |
return std::get_if<Type>(&arglist.at(index)) != nullptr; | |
} | |
return true; | |
} | |
template<size_t N> using TypeAt = typename std::tuple_element<N, std::tuple<Args...>>::type; | |
Functor f; | |
}; | |
struct AbstractFunctor | |
{ | |
private: | |
std::vector<std::shared_ptr<AbstractDispatcher>> dispatchers; | |
public: | |
virtual ~AbstractFunctor() = default; | |
bool invoke(const std::vector<variant_t>& arglist) { | |
for(auto& d : dispatchers) { | |
if(d->matches(arglist)) { | |
d->dispatch(arglist); | |
return true; | |
} | |
} | |
return false; | |
} | |
protected: | |
template<typename... Args, class Functor> | |
void makeCallable(Functor f) { | |
dispatchers.emplace_back(std::make_shared<Dispatcher<Functor, Args...>>(f)); | |
} | |
}; | |
struct SumFunctor : AbstractFunctor | |
{ | |
SumFunctor() { | |
makeCallable<>(this); | |
makeCallable<int, int>(this); | |
makeCallable<std::string, std::string>(this); | |
} | |
void operator()() const { | |
std::cout << "SumFunctor::operator()\n"; | |
} | |
int operator()(int a, int b) const { | |
int sum = a + b; | |
std::cout << "SumFunctor::operator(int, int) = " << sum << "\n"; | |
return sum; | |
} | |
std::string operator()(const std::string& a, const std::string& b) const { | |
std::string sum = a + b; | |
std::cout << "SumFunctor::operator(string, string) = " << sum << "\n"; | |
return sum; | |
} | |
}; | |
struct SquareFunctor : AbstractFunctor | |
{ | |
SquareFunctor() { | |
makeCallable<double>(std::ref(*this)); | |
} | |
double operator()(double a) const { | |
double square = a*a; | |
std::cout << "SquareFunctor::operator(double) = " << square << "\n"; | |
return square; | |
} | |
}; | |
int main(int, char**) | |
{ | |
SumFunctor sumf; | |
SquareFunctor squaref; | |
sumf.invoke({}); | |
sumf.invoke({1, 5}); | |
sumf.invoke({"foo", "bar"}); | |
squaref.invoke({10.0}); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment