Skip to content

Instantly share code, notes, and snippets.

@MartinBloedorn
Created May 10, 2020 10:11
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 MartinBloedorn/934300062c327121a72bdedd1942bb0d to your computer and use it in GitHub Desktop.
Save MartinBloedorn/934300062c327121a72bdedd1942bb0d to your computer and use it in GitHub Desktop.
#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