编译Boost Qi解析器报错:无法跳过PHP代码中的注释与空格
Boost Spirit Qi编译错误修复:迭代器类型不匹配
以下是用于提取PHP代码非注释部分的Boost Spirit Qi实现,但编译时出现错误:
#include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/qi_char_class.hpp> #include <boost/spirit/include/qi_string.hpp> #include <boost/spirit/include/qi_operator.hpp> #include <boost/spirit/include/qi_action.hpp> #include <boost/spirit/include/qi_auxiliary.hpp> namespace qi = boost::spirit::qi; using namespace std; int main(int argc, char *argv[]) { std::string contents = "<?php echo \"Soy español\";?>"; using Iterator = std::string::const_iterator; struct StripCommentParser : qi::grammar<Iterator, std::string()> { StripCommentParser() : StripCommentParser::base_type(start) { using namespace qi; single_line_comment = "//" >> *(qi::char_ - eol) >> (eol | eoi); block_comment = ("/*" >> *(block_comment | qi::char_ - "*/")) > ("*/" | eoi); php_comment = '#' >> *(qi::char_ - eol) >> (eol | eoi); start = qi::skip(space | single_line_comment | block_comment | php_comment)[*qi::char_]; } private: qi::rule<Iterator, std::string()> start; qi::rule<Iterator> block_comment, single_line_comment, php_comment; }; string non_comments_code; // 触发编译错误的行 parse(begin(contents), end(contents), StripCommentParser{}, non_comments_code); }
编译时的错误信息:
In file included from /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:36, from /usr/include/boost/spirit/home/qi/nonterminal.hpp:14, from /usr/include/boost/spirit/home/qi.hpp:21, from /usr/include/boost/spirit/include/qi.hpp:16, from ../../src/capel-tool.cpp:4: /usr/include/boost/spirit/home/qi/reference.hpp: In instantiation of ‘bool boost::spirit::qi::reference<Subject>::parse(Iterator&, const Iterator&, Context&, const Skipper&, Attribute&) const [with Iterator = __gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >; Context = boost::spirit::context<boost::fusion::cons<std::__cxx11::basic_string<char>&, boost::fusion::nil_>, boost::spirit::locals<> >; Skipper = boost::spirit::unused_type; Attribute = std::__cxx11::basic_string<char>; Subject = const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, std::__cxx11::basic_string<char>(), boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type>]’: /usr/include/boost/spirit/home/qi/parse.hpp:88:47: required from ‘bool boost::spirit::qi::parse(Iterator&, Iterator, const Expr&, Attr&) [with Iterator = __gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >; Expr = main(int, char**)::StripCommentParser; Attr = std::__cxx11::basic_string<char>]’ /usr/include/boost/spirit/home/qi/parse.hpp:100:25: required from ‘bool boost::spirit::qi::parse(const Iterator&, Iterator, const Expr&, Attr&) [with Iterator = __gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >; Expr = main(int, char**)::StripCommentParser; Attr = std::__cxx11::basic_string<char>]’ ../../src/capel-tool.cpp:106:80: required from here /usr/include/boost/spirit/home/qi/reference.hpp:43:35: error: no matching function for call to ‘boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, std::__cxx11::basic_string<char>(), boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type>::parse(__gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >&, const __gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >&, boost::spirit::context<boost::fusion::cons<std::__cxx11::basic_string<char>&, boost::fusion::nil_>, boost::spirit::locals<> >&, const boost::spirit::unused_type&, std::__cxx11::basic_string<char>&) const’ 43 | return ref.get().parse(first, last, context, skipper, attr_); | ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In file included from /usr/include/boost/spirit/home/qi/nonterminal.hpp:14, from /usr/include/boost/spirit/home/qi.hpp:21, from /usr/include/boost/spirit/include/qi.hpp:16, from ../../src/capel-tool.cpp:4: /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:282:14: note: candidate: ‘template<class Context, class Skipper, class Attribute> bool boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>::parse(Iterator&, const Iterator&, Context&, const Skipper&, Attribute&) const [with Context = Context; Skipper = Skipper; Attribute = Attribute; Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >; T1 = std::__cxx11::basic_string<char>(); T2 = boost::spirit::unused_type; T3 = boost::spirit::unused_type; T4 = boost::spirit::unused_type]’ 282 | bool parse(Iterator& first, Iterator const& last | ^~~~~ /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:282:14: note: template argument deduction/substitution failed: In file included from /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:36, from /usr/include/boost/spirit/home/qi/nonterminal.hpp:14, from /usr/include/boost/spirit/home/qi.hpp:21, from /usr/include/boost/spirit/include/qi.hpp:16, from ../../src/capel-tool.cpp:4: /usr/include/boost/spirit/home/qi/reference.hpp:43:35: note: cannot convert ‘first’ (type ‘__gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >’) to type ‘__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&’ 43 | return ref.get().parse(first, last, context, skipper, attr_); | ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In file included from /usr/include/boost/spirit/home/qi/nonterminal.hpp:14, from /usr/include/boost/spirit/home/qi.hpp:21, from /usr/include/boost/spirit/include/qi.hpp:16, from ../../src/capel-tool.cpp:4: /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:333:14: note: candidate: ‘template<class Context, class Skipper, class Attribute, class Params> bool boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>::parse(Iterator&, const Iterator&, Context&, const Skipper&, Attribute&, const Params&) const [with Context = Context; Skipper = Skipper; Attribute = Attribute; Params = Params; Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >; T1 = std::__cxx11::basic_string<char>(); T2 = boost::spirit::unused_type; T3 = boost::spirit::unused_type; T4 = boost::spirit::unused_type]’ 333 | bool parse(Iterator& first, Iterator const& last | ^~~~~ /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:333:14: note: template argument deduction/substitution failed: In file included from /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:36, from /usr/include/boost/spirit/home/qi/nonterminal.hpp:14, from /usr/include/boost/spirit/home/qi.hpp:21, from /usr/include/boost/spirit/include/qi.hpp:16, from ../../src/capel-tool.cpp:4: /usr/include/boost/spirit/home/qi/reference.hpp:43:35: note: candidate expects 6 arguments, 5 provided 43 | return ref.get().parse(first, last, context, skipper, attr_); | ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
错误原因
编译报错的核心是迭代器类型不匹配:
- 代码中定义的
Iterator是std::string::const_iterator(只读迭代器) - 调用
parse时传入的begin(contents)和end(contents)是std::string::iterator(可写迭代器) - Boost Spirit要求grammar/rule的迭代器类型与parse调用时的迭代器类型完全一致,否则会触发类型推导失败
修复方案
有两种简单的修复方式:
方式1:使用const迭代器调用parse
把触发错误的行修改为:
parse(cbegin(contents), cend(contents), StripCommentParser{}, non_comments_code);
cbegin()和cend()会返回const_iterator,与grammar定义的迭代器类型匹配。
方式2:修改Iterator类型为非const
把using Iterator = std::string::const_iterator;改为:
using Iterator = std::string::iterator;
这样grammar的迭代器类型就和begin()/end()返回的类型一致。
另外,代码中重复包含了<boost/spirit/include/qi.hpp>,可以去掉重复的一行,避免冗余。
内容的提问来源于stack exchange,提问作者Santilín
相关产品推荐
相关产品推荐

