-
-
Save lightmare/9ad5f478c60f0ac55283367c63db0583 to your computer and use it in GitHub Desktop.
Lambdas instead of visitors
This file contains hidden or 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 <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