Skip to content

Instantly share code, notes, and snippets.

@lightmare
Forked from daniel-j-h/LICENSE.md
Last active September 12, 2016 15:14
Show Gist options
  • Save lightmare/9ad5f478c60f0ac55283367c63db0583 to your computer and use it in GitHub Desktop.
Save lightmare/9ad5f478c60f0ac55283367c63db0583 to your computer and use it in GitHub Desktop.
Lambdas instead of visitors
#include <string>
#include <iostream>
#if USE_BOOSTVAR
#include <boost/variant.hpp>
using boost::variant;
using boost::apply_visitor;
#else
#include <mapbox/variant.hpp>
using mapbox::util::variant;
using mapbox::util::apply_visitor;
//using namespace mapbox::util;
#endif
struct Book {};
struct Movie {};
// Option 1: write visitor and feel the pain in your fingers
struct Match {
using result_type = std::string;
std::string operator()(Book) const { return "Book"; }
std::string operator()(Movie) const { return "Movie"; }
};
// Option 2: let compiler synthesize a visitor based on lambdas
template <typename... Functions> struct unary_visitor;
template <typename F>
struct unary_visitor<F> : F
{
using result_type = std::string; // for boost::apply_visitor
using F::operator();
unary_visitor(F f) : F(f) { }
};
template <typename F, typename... Functions>
struct unary_visitor<F, Functions...> : F, unary_visitor<Functions...>
{
using F::operator();
using unary_visitor<Functions...>::operator();
unary_visitor(F f, Functions... fs) : F(f), unary_visitor<Functions...>(fs...)
{
std::cout << "constructing " << typeid(*this).name() << " at " << this << '\n';
}
unary_visitor(unary_visitor<F, Functions...> const& other)
: F(other), unary_visitor<Functions...>(other)
{
std::cout << "copying " << typeid(*this).name() << " to " << this << '\n';
}
unary_visitor(unary_visitor<F, Functions...> && other)
: F(std::move(other)), unary_visitor<Functions...>(std::move(other))
{
std::cout << "moving " << typeid(*this).name() << " to " << this << '\n';
}
};
template <typename... Functions>
auto make_visitor(Functions... functions) -> unary_visitor<Functions...>
{
return unary_visitor<Functions...>(functions...);
}
template <typename... Types>
struct Either : variant<Types...>
{
using variant<Types...>::variant;
using variant<Types...>::operator=;
template <typename F>
auto wisit(F && visitor) const
-> decltype(apply_visitor(std::forward<F>(visitor), *this))
{
return apply_visitor(std::forward<F>(visitor), *this);
}
template <typename F>
auto wisit(F && visitor)
-> decltype(apply_visitor(std::forward<F>(visitor), *this))
{
return apply_visitor(std::forward<F>(visitor), *this);
}
template <typename... Fs>
auto wisit(Fs &&... functions) const
-> decltype(apply_visitor(make_visitor(std::forward<Fs>(functions)...), *this))
{
//return apply_visitor(make_visitor(std::forward<Fs>(functions)...), *this);
auto visitor = make_visitor(std::forward<Fs>(functions)...);
std::cout << " (vis is " << &visitor << ' ' << typeid(visitor).name() << ") ";
return apply_visitor(visitor, *this);
}
template <typename... Fs>
auto wisit(Fs &&... functions)
-> decltype(apply_visitor(make_visitor(std::forward<Fs>(functions)...), *this))
{
//return apply_visitor(make_visitor(std::forward<Fs>(functions)...), *this);
auto visitor = make_visitor(std::forward<Fs>(functions)...);
std::cout << " (vis is " << &visitor << ' ' << typeid(visitor).name() << ") ";
return apply_visitor(visitor, *this);
}
};
// Usage
int main(int argc, char** argv)
{
#if USE_BOOSTVAR
std::cout << "==============\nBOOST VARIANT\n";
#else
std::cout << "==============\nMAPBOX VARIANT\n";
#endif
for (int argi = 1; argi < argc; ++argi)
{
Either<Book, Movie> rv;
switch (argv[argi][0]) {
case 'b': rv = Book(); break;
case 'm': rv = Movie(); break;
default:
std::cerr << "invalid arg '" << argv[argi] << "', must be either 'b' or 'm'\n";
continue;
}
// For Option 1, the visitor and its invocation is separated
std::cout << "============\n";
std::cout << apply_visitor(Match{}, rv) << std::endl;
std::cout << "var.wisit(Match{}) => "
<< rv.wisit(Match{})
<< std::endl;
// Option 2, hey this almost looks like Haskell/Swift/Rust :)
auto handler = make_visitor([] (Book) { return "Book"; },
[] (Movie) { return "Movie"; });
std::cout << "handler " << &handler << '\n';
std::cout << "apply_visitor(handler, var) => "
<< apply_visitor(handler, rv)
<< std::endl;
std::cout << "var.wisit(handler) => "
<< rv.wisit(handler)
<< std::endl;
std::cout << "var.wisit(lambdas...) => "
<< rv.wisit([] (Book) { return "Book"; },
[] (Movie) { return "Movie"; })
<< std::endl;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment