Skip to content

Instantly share code, notes, and snippets.

@rhysd
Last active August 29, 2015 14:01
Show Gist options
  • Save rhysd/3b73599071bb6876c2ea to your computer and use it in GitHub Desktop.
Save rhysd/3b73599071bb6876c2ea to your computer and use it in GitHub Desktop.
enable to return value by visitor
#include <memory>
#include <string>
#include <type_traits>
#include <cassert>
#include <iostream>
#include <boost/any.hpp>
// visitor のコンストラクタで visit してはどうか
template<class T>
using enable_if_void = typename std::enable_if<
std::is_void<T>::value
, T
>::type;
template<class T>
using disable_if_void = typename std::enable_if<
!std::is_void<T>::value
, T
>::type;
// Forward declaration for test nodes
struct char_literal;
struct float_literal;
struct visitor_holder_base {
virtual void visit(char_literal const& lit) = 0;
virtual void visit(char_literal &lit) = 0;
virtual void visit(float_literal const& lit) = 0;
virtual void visit(float_literal &lit) = 0;
// ...
// FIXME: So many pure virtual functions
virtual ~visitor_holder_base()
{}
};
// Node: ResultType should be default constructible
template<class Visitor, class ResultType>
struct visitor_holder : visitor_holder_base {
Visitor &v;
ResultType result;
void visit(char_literal const& lit) override { result = v(lit); }
void visit(char_literal &lit) override { result = v(lit); }
void visit(float_literal const& lit) override { result = v(lit); }
void visit(float_literal &lit) override { result = v(lit); }
// ...
// FIXME: So many virtual functions
visitor_holder(Visitor &v)
: v(v)
{}
~visitor_holder() override
{}
};
template<class Visitor>
struct visitor_holder<Visitor, void> : visitor_holder_base {
Visitor &v;
void visit(char_literal const& lit) override { v(lit); }
void visit(char_literal &lit) override { v(lit); }
void visit(float_literal const& lit) override { v(lit); }
void visit(float_literal &lit) override { v(lit); }
// ...
// FIXME: So many virtual functions
visitor_holder(Visitor &v)
: v(v)
{}
~visitor_holder() override
{}
};
// Node: ResultType should be default constructible
template<class Visitor, class ResultType>
struct const_visitor_holder : visitor_holder_base {
Visitor const& v;
ResultType result;
void visit(char_literal const& lit) override { result = v(lit); }
void visit(char_literal &lit) override { result = v(lit); }
void visit(float_literal const& lit) override { result = v(lit); }
void visit(float_literal &lit) override { result = v(lit); }
// ...
// FIXME: So many virtual functions
const_visitor_holder(Visitor const& v)
: v(v)
{}
~const_visitor_holder() override
{}
};
template<class Visitor>
struct const_visitor_holder<Visitor, void> : visitor_holder_base {
Visitor const& v;
void visit(char_literal const& lit) override { v(lit); }
void visit(char_literal &lit) override { v(lit); }
void visit(float_literal const& lit) override { v(lit); }
void visit(float_literal &lit) override { v(lit); }
// ...
// FIXME: So many virtual functions
const_visitor_holder(Visitor const& v)
: v(v)
{}
~const_visitor_holder() override
{}
};
class visitor {
public:
std::shared_ptr<visitor_holder_base> const holder;
template<class AnyVisitor>
explicit visitor(AnyVisitor const& v)
: holder(std::make_shared<const_visitor_holder<AnyVisitor, typename AnyVisitor::result_type>>(v))
{}
template<class AnyVisitor>
explicit visitor(AnyVisitor &v)
: holder(std::make_shared<visitor_holder<AnyVisitor, typename AnyVisitor::result_type>>(v))
{}
// Note: remain the result of visit in a holder
template<class T>
void visit(T &node)
{
holder->visit(node);
}
// Note: remain the result of visit in a holder
template<class T>
void visit(T const& node) const
{
holder->visit(node);
}
};
class node {
struct node_holder_base {
// Interfaces here
virtual void apply(visitor const& v) const = 0;
virtual void apply(visitor &v) = 0;
virtual ~node_holder_base()
{}
};
template<class Node>
struct node_holder : node_holder_base {
Node node;
node_holder(Node const& node)
: node(node)
{}
node_holder(Node && node)
: node(node)
{}
void apply(visitor const& v) const override
{
v.visit(node);
}
void apply(visitor &v) override
{
v.visit(node);
}
~node_holder() override
{}
};
std::shared_ptr<node_holder_base> holder;
public:
template<class AnyNode>
node(AnyNode const& n)
: holder(std::make_shared<node_holder<AnyNode>>(n))
{}
template<class V>
enable_if_void<typename V::result_type>
apply(V const& v) const
{
holder->apply(visitor{v});
}
template<class V>
disable_if_void<typename V::result_type>
apply(V const& v) const
{
visitor internal_visitor{v};
holder->apply(internal_visitor);
auto const dispatched = std::dynamic_pointer_cast<const_visitor_holder<V, typename V::result_type>>(internal_visitor.holder);
assert(dispatched);
return dispatched->result;
}
template<class V>
enable_if_void<typename V::result_type>
apply(V &v)
{
holder->apply(visitor{v});
}
template<class V>
disable_if_void<typename V::result_type>
apply(V &v)
{
visitor internal_visitor{v};
holder->apply(internal_visitor);
auto const dispatched = std::dynamic_pointer_cast<visitor_holder<V, typename V::result_type>>(internal_visitor.holder);
assert(dispatched);
return dispatched->result;
}
};
#include <iostream>
// Test nodes
struct char_literal {
char value;
};
struct float_literal {
float value;
};
template<class T>
struct ast_visitor {
using result_type = T;
};
// Test visitors
struct printer : ast_visitor<void> {
int i = 0;
template<class T>
void operator()(T const& t) noexcept
{
std::cout << "print node: " << t.value << std::endl;
i++;
}
};
struct stringizer : ast_visitor<std::string> {
template<class T>
std::string operator()(T const& t) const noexcept
{
return std::to_string(t.value);
}
};
int main()
{
node n1 = char_literal{'c'};
node n2 = float_literal{3.14};
// visitor which returns void
printer p;
n1.apply(p);
n2.apply(p);
std::cout << p.i << std::endl;
n1.apply(printer{});
std::cout << n1.apply(stringizer{}) << std::endl;
std::cout << n2.apply(stringizer{}) << std::endl;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment