Skip to content

Instantly share code, notes, and snippets.

@gchudnov
Created December 14, 2015 15:28
Show Gist options
  • Save gchudnov/e7592573a762be548299 to your computer and use it in GitHub Desktop.
Save gchudnov/e7592573a762be548299 to your computer and use it in GitHub Desktop.
variant visitor
#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