Boost Spirit 结构 boost::fusion::extension::adt_attribute_proxy<Ast::Term, 0, false> 中没有类型名称 'value_type'

Boost Spirit No type names 'value_type' in struct boost::fusion::extension::adt_attribute_proxy<Ast::Term, 0, false>

在此 之后,我将结构更改为 class 并添加了其他复制构造函数并重载了 = 运算符。我还为解析器添加了识别换行符、制表符等的功能。但是,由于一些错误,代码没有 运行。我知道如何修复它。下面是错误消息和代码的片段。

***/main.cpp:130:24:   required from ‘Parser::BNF<Iterator>::BNF() [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >]’
****/main.cpp:156:52:   required from here
/usr/include/boost/spirit/home/qi/detail/assign_to.hpp:41:16: error: no type named ‘value_type’ in ‘struct boost::fusion::extension::adt_attribute_proxy<Ast::Term, 0, false>’
   41 |         struct is_container_of_ranges
      |                ^~~~~~~~~~~~~~~~~~~~~~
......

***/main.cpp:130:24:   required from ‘Parser::BNF<Iterator>::BNF() [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >]’
***/main.cpp:156:52:   required from here
/usr/include/boost/spirit/home/qi/detail/assign_to.hpp:72:17: error: no matching function for call to ‘boost::spirit::traits::assign_to_attribute_from_iterators<boost::fusion::extension::adt_attribute_proxy<Ast::Term, 0, false>, __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, void>::call(const __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, const __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, boost::fusion::extension::adt_attribute_proxy<Ast::Term, 0, false>&, boost::spirit::traits::detail::is_container_of_ranges<boost::fusion::extension::adt_attribute_proxy<Ast::Term, 0, false> >)’
   72 |             call(first, last, attr, detail::is_container_of_ranges<Attribute>());
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted.hpp>
#include <iomanip>

namespace qi = boost::spirit::qi;

namespace Ast {
    enum class TermType {
        literal, rule_name
    };

    class Term {
    private:
        std::string data;
        TermType term_type;

    public:
        Term() = default;

        void set_data(std::string const &data) {
            this->data = data;
        }

        std::string const &get_data() const {
            return data;
        }

        void set_term_type(TermType term_type) {
            this->term_type = term_type;
        }

        TermType const &get_term_type() const {
            return term_type;
        }

        bool operator==(const Term &term) {
            if (!data.compare(term.data) && term_type == term.term_type)
                return true;
            return false;
        }

        Term &operator=(const Term &term) {
            if (this != &term) {
                data = term.data;
                term_type = term.term_type;
            }
            return *this;
        }

        Term(const Term &term) {
            data = term.data;
            term_type = term.term_type;
        }
    };

    class List : public std::vector<Term> {
        // attributes not required for parsing
        // constructor, copy constructor, = operator overloading
    };

    using Expression = std::vector<List>;

    class Rule {
    private:
        Term name; // lhs
        Expression rhs;

    public:
        void set_rule_name(const Term &rule_name) {
            this->name = rule_name;
        }

        Term const &get_rule_name() const {
            return name;
        }

        void set_expression(const Expression &expression) {
            rhs = expression;
        }

        Expression const &get_expression() const {
            return rhs;
        }

        // constructor, copy constructor, = operator overloading
    };

    using Syntax = std::list<Rule>;
}
BOOST_FUSION_ADAPT_ADT(Ast::Term,
                       (obj.get_data(), obj.set_data(val))
                               (obj.get_term_type(), obj.set_term_type(val)))

BOOST_FUSION_ADAPT_ADT(Ast::Rule,
                       (obj.get_rule_name(), obj.set_rule_name(val))
                               (obj.get_expression(), obj.set_expression(val)))

namespace Parser {
    template<typename Iterator>
    struct BNF : qi::grammar<Iterator, Ast::Syntax()> {
        BNF()
                : BNF::base_type(start) {
            using namespace qi;
            _blank = blank;
            unesc_char.add
                    ("\a", '\a')
                    ("\b", '\b')
                    ("\f", '\f')
                    ("\n", '\n')
                    ("\r", '\r')
                    ("\t", '\t')
                    ("\v", '\v')
                    ("\\", '\')
                    ("\'", '\'')
                    ("\\"", '\"');
            _skipper = blank | (eol >> !skip(_blank.alias())[_rule]);
            start = skip(_skipper.alias())[_rule % +eol];

            _rule = _rule_name >> "::=" >> _expression;
            _expression = _list % '|';
            _list = +_term;
            _term = _literal >> attr(Ast::TermType::literal)
                    | _rule_name;
            _literal = unesc_char | '"' >> *(_character - '"') >> '"'
                       | "'" >> *(_character - "'") >> "'";

            _character = alnum | char_("\"'| !#$%&()*+,./:;>=<?@]\^_`{}~[-");
            _rule_name = '<' >> qi::raw[(alpha >> *(alnum | char_('-')))] >> '>'
                             >> attr(Ast::TermType::rule_name);

            // clang-format on
            BOOST_SPIRIT_DEBUG_NODES(
                    (_rule)(_expression)(_list)(_literal)(_character)(_rule_name))
        }

    private:
        using Skipper = qi::rule<Iterator>;
        Skipper _skipper, _blank;

        qi::rule<Iterator, Ast::Syntax()> start;
        qi::rule<Iterator, Ast::Rule(), Skipper> _rule;
        qi::rule<Iterator, Ast::Expression(), Skipper> _expression;
        qi::rule<Iterator, Ast::List(), Skipper> _list;
        // lexemes
        qi::rule<Iterator, Ast::Term()> _term;
        qi::rule<Iterator, std::string()> _literal;
        qi::rule<Iterator, Ast::Term()> _rule_name;
        qi::rule<Iterator, char()> _character;
        qi::symbols<char const, char const> unesc_char;
    };
}

int main() {
    Parser::BNF<std::string::const_iterator> const parser;

    std::string const input = R"(<code>   ::=  <letter><digit> | <letter><digit><code>
<letter> ::= "a" | "b" | "c" | "d" | "e"
           | "f" | "g" | "h" | "i"
<digit>  ::= "0" | "1 \n 3" | "2" | "3 \t yy" |
             "4"
    )";

    auto it = input.begin(), itEnd = input.end();

    Ast::Syntax syntax;
    if (parse(it, itEnd, parser, syntax)) {
        for (auto &rule : syntax) {
            std::cout << rule.get_rule_name().get_data() << " ::= ";
            std::string sep;
            for (auto &list : rule.get_expression()) {
                std::cout << sep;
                for (auto &term: list) { std::cout << term.get_data(); }
                sep = " | ";
            };
            std::cout << "\n";
        }
    } else {
        std::cout << "Failed\n";
    }

    if (it != itEnd)
        std::cout << "Remaining: " << std::quoted(std::string(it, itEnd)) << "\n";
}

由于某些原因,属性兼容性规则在调用 setter 方法时不适用。您可以在顶部强制使用 as_string 问题:

_rule_name = '<' >> qi::as_string [ qi::raw[ (alpha >> *(alnum | char_('-'))) ] ] >>
             '>' >> attr(Ast::TermType::rule_name);

侧面观察

2 月 27 日 23:35 :

My other concern is the inheritance from string class. I thought was bad to do that.

显然,你改变了主意。我真的不知道为什么。在这种情况下,您可能不应该这样做。快捷方式是“好的”,除非它们不是。参见例如More silent behaviour changes with C++20 three-way comparison and https://quuxplusone.github.io/blog/2018/12/11/dont-inherit-from-std-types/

另外,提防quasi-classes (PDF)。你能告诉我为什么结构不是更好吗?您只是重新定义了很多标准行为(copy ctor/assignment 应该是默认的,就像 C++20 上的 operator<=> 一样)。