c++ - Virtual template functions: implementing the Visitor pattern with parameters -
i trying implement visitor pattern walking ast. have defined astnode
can accept visitor
, , allow visitor visit itself. example below contains 1 concrete implementation each of visitor , astnode.
class astnode; template <class p, class r> class visitor { public: virtual ~visitor() {} virtual r visit(astnode& node, p p) const = 0; }; class astnode { public: virtual ~astnode() {} template <class p, class r> virtual r accept(visitor<r, p>& v, p p) { return v.visit(*this); } }; class roman : public astnode { public: roman(numeral n, optional<accidental> a) : numeral(n), alteration(a) {}; const numeral numeral; const optional<accidental> alteration; }; class tostringvisitor : public visitor<string, int> { virtual string visit(roman& node, int param) { string result = numeralstrings[node.numeral]; if (node.alteration.has_value()) result = accidentaltostring(node.alteration.value()) + result; return result; } };
then, can walk ast using this:
roman r; tostringvisitor tsv; // ... return r.accept(tsv, 42);
as can see, trying use templates allow parameter , return value. however, compiler error:
error: templates may not 'virtual' virtual r accept(visitor<r, p>& v, p p) {
i have vague understanding of why error. however, how can accomplish legally?
edit: don't think duplicate of this question, because i'm trying have accept return template type.
you error message because c++ forbids defining virtual template function. removing virtual keyword fix compilartion error.
i've finished writing parser/lexer , found using lambdas great time-saver.
here implementation of lambda visitors. compile in vs 2017, , should compile under gcc.
i got code talk: "c++now 2017: vittorio romeo “implementing variant
visitation using lambdas"
file match.h
#pragma once #include <type_traits> #include <variant> template<typename tf, typename...tfs> struct overload_set : tf, overload_set<tfs...> { using tf::operator(); using overload_set<tfs...>::operator(); template<typename tffwd, typename...tffwds> constexpr overload_set(tffwd&& f, tffwds&&...rest) : tf { std::forward<tffwd>(f) } , overload_set<tfs...>{ std::forward<tffwds>(rest)... } { } }; template<typename tf> struct overload_set<tf> : tf { using tf::operator(); template<typename tffwd> constexpr overload_set(tffwd&& f) : tf { std::forward<tffwd>(f) } { } }; template<typename...tfs> constexpr auto overload(tfs&...fs) { return overload_set<std::remove_reference_t<tfs>...>(std::forward<tfs>(fs)...); } template<typename visitor, typename...tvariants> constexpr decltype(auto) visit_recursively(visitor&& vis, tvariants&&...vars) { return std::visit( std::forward<visitor>(vis), std::forward<tvariants>(variants)._data... ); } template<typename...tvariants> constexpr auto match(tvariants&&...vs) { return [&vs...](auto&&...fs) //-> decltype(auto) { return std::visit(overload(std::forward<decltype(fs)>(fs)...), vs...); }; }
example @ interpreter level:
template<> std::string convertto<std::string>(const variant& v) { return match(v)( [](const std::string& s) { return s; }, [](const auto&) { throw internalerror("cannot convert type string"); return std::string{}; } ); } variant interpreter::evaluate(ast::rvalue & rv) { // maps overloads types held variant type rvalue return match(rv)( [&](auto& x) { return evaluate(x); } ); } // recursion... variant evaluate(std::unique_ptr<ast::spheref>& rv) { return match(rv->vec_) ( [](ast::vector4f& v) { return variant{ std::make_shared<vector4f>(std::move(v)) }; }, [&](std::vector<ast::rvalue>& v) { if (v.size() != 4) { throw internalerror{ "sphere must have 4 parameters" }; } vector4f r; r[0] = convertto<f32>(evaluate(v[0])); r[1] = convertto<f32>(evaluate(v[1])); r[2] = convertto<f32>(evaluate(v[2])); r[3] = convertto<f32>(evaluate(v[3])); return variant{ std::make_shared<vector4f>(std::move(r)) }; } ); } // cascading calls... objectref or = match(o->value_) ( [](identifier& id) -> objectref { return { std::make_shared<ast::identifier>(std::move(id)) }; }, [&](ast::objectvalueblock& bl) -> objectref { return match(std::move(evaluate(bl))) ( [](std::shared_ptr<object>&& x) { return objectref{ x }; }, [](std::shared_ptr<identifier>&& x) { return objectref{ x };}, [](auto&&) { throw internalerror{ "unexpected type in object array expansion" }; return objectref{}; } ); );
Comments
Post a Comment