Created
December 14, 2015 15:28
-
-
Save gchudnov/e7592573a762be548299 to your computer and use it in GitHub Desktop.
variant visitor
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 <string> | |
#include <type_traits> | |
#include <typeinfo> | |
#include <tuple> | |
#include <functional> | |
template <typename T> | |
struct function_traits | |
: public function_traits<decltype(&T::operator())> | |
{}; | |
// For generic types, directly use the result of the signature of its 'operator()' | |
template <typename ClassType, typename ReturnType, typename... Args> | |
struct function_traits<ReturnType(ClassType::*)(Args...) const> | |
// we specialize for pointers to member function | |
{ | |
enum { arity = sizeof...(Args) }; // arity is the number of arguments. | |
typedef ReturnType result_type; | |
template <size_t i> | |
struct arg | |
{ | |
typedef typename std::tuple_element<i, std::tuple<Args...>>::type type; | |
// the i-th argument is equivalent to the i-th tuple element of a tuple | |
// composed of those arguments. | |
}; | |
}; | |
// | |
template<typename F, typename BaseInner, typename ArgsT> | |
struct ComposeVariantVisitor { | |
using arg1_type = typename function_traits<F>::template arg<0>::type; | |
struct Inner : BaseInner { | |
Inner(ArgsT &&a) : BaseInner(std::move(a.second)), f_(std::move(a.first)) { /* no-op */ } | |
using BaseInner::operator(); | |
template<typename T> | |
auto operator()(const T &t, typename std::enable_if_t<std::is_convertible<arg1_type, T>::value>* = nullptr) { | |
return f_(t); | |
} | |
private: | |
F f_; | |
}; | |
ComposeVariantVisitor(ArgsT &&args) : args_(std::move(args)) { /* no-op */ } | |
template<typename Fadd> | |
auto on(Fadd &&f) { | |
return ComposeVariantVisitor<Fadd, Inner, std::pair<Fadd, ArgsT>>(std::make_pair(std::move(f), std::move(args_))); | |
} | |
auto end_visitor() { | |
return Inner(std::move(args_)); | |
} | |
private: | |
ArgsT args_; | |
}; | |
// | |
struct EmptyVariantVisitor { | |
struct Inner { | |
struct detail_t { }; | |
Inner(std::nullptr_t) { } | |
void operator()(detail_t &) const { } | |
}; | |
template<typename Fadd> | |
auto on(Fadd &&f) { | |
return ComposeVariantVisitor<Fadd, Inner, std::pair<Fadd, std::nullptr_t>>(std::make_pair(std::move(f), nullptr)); | |
} | |
}; | |
// | |
EmptyVariantVisitor begin_variant_visitor() { | |
return EmptyVariantVisitor(); | |
} | |
int main() { | |
auto visitor = begin_variant_visitor() | |
.on([](int value) { return value * 2; }) | |
.on([](std::string value) { return value + "!"; }) | |
.end_visitor(); | |
auto res1 = visitor(std::string("test")); | |
auto res2 = visitor(42); | |
std::cout << res1 << std::endl; // test! | |
std::cout << res2 << std::endl; // 84 | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment