如何基于Boost Spirit将含可选部分的解析结果映射到变体向量?
一、将查询结果映射到std::variant向量
要实现必填/可选查询部分到std::variant向量的映射,结合qi::grammar和Boost Fusion适配的步骤如下:
- 定义目标类型与variant
先明确路径部件的可能类型(比如字面量、通配符、变量等),定义std::variant及顶层查询结构体:
namespace ast { struct Literal { std::string value; }; struct Wildcard {}; struct Variable { std::string name; }; using PathPart = std::variant<Literal, Wildcard, Variable>; struct Query { bool rooted; std::vector<PathPart> parts; }; }
- 适配结构体到Fusion序列
使用BOOST_FUSION_ADAPT_STRUCT将结构体成员暴露给Spirit,确保解析结果能正确绑定到对应字段:
BOOST_FUSION_ADAPT_STRUCT(ast::Literal, value) BOOST_FUSION_ADAPT_STRUCT(ast::Variable, name) // Wildcard无成员,无需适配 BOOST_FUSION_ADAPT_STRUCT(ast::Query, rooted, parts)
- 在grammar中定义规则并绑定属性
在qi::grammar子类中,拆分规则并绑定到对应属性,处理必填/可选逻辑:
template <typename Iterator> struct QueryGrammar : qi::grammar<Iterator, ast::Query(), qi::space_type> { QueryGrammar() : QueryGrammar::base_type(query) { // 基础路径部件规则 literal = qi::lexeme[+(qi::char_ - '/' - '*' - '$')]; wildcard = qi::lit("*") >> qi::attr(ast::Wildcard{}); variable = qi::lit('$') >> qi::lexeme[+(qi::alnum | '_')]; // 生成PathPart variant的规则 path_part = literal | wildcard | variable; // 根路径标识规则:必填的rooted状态(/开头为true,否则false) rooted = qi::lit('/') >> qi::attr(true) | qi::attr(false); // 完整查询规则:rooted + 可选的路径部件序列(用/分隔) query = rooted >> -(path_part % qi::lit('/')); } qi::rule<Iterator, ast::Literal(), qi::space_type> literal; qi::rule<Iterator, ast::Wildcard(), qi::space_type> wildcard; qi::rule<Iterator, ast::Variable(), qi::space_type> variable; qi::rule<Iterator, ast::PathPart(), qi::space_type> path_part; qi::rule<Iterator, bool(), qi::space_type> rooted; qi::rule<Iterator, ast::Query(), qi::space_type> query; };
关键注意点:
- 用
qi::attr处理默认值,比如非根路径的rooted默认为false - 用
-(...)标记可选部分,%表示分隔符分隔的重复项 - 各部件规则会自动匹配并生成对应的
PathPartvariant类型
二、修复container.hpp编译错误
替换Query结构体后出现的container.hpp编译错误,大多源于属性不匹配或Fusion适配错误,按以下步骤排查修复:
严格匹配Fusion适配的成员顺序
确保BOOST_FUSION_ADAPT_STRUCT中成员的顺序和Query结构体定义顺序完全一致,比如结构体是bool rooted; std::vector<PathPart> parts;,适配时必须写(rooted, parts),顺序错误会导致属性绑定混乱,触发容器相关编译错误。确保向量规则的属性类型一致
路径部件序列规则path_part % qi::lit('/')的属性是std::vector<ast::PathPart>,必须和Query中parts成员的类型完全匹配。若规则返回其他容器类型(如std::list),Spirit无法自动转换,会触发容器适配错误。显式指定规则属性(可选)
若自动属性转换引发冲突,可显式定义序列规则的属性类型:
qi::rule<Iterator, std::vector<ast::PathPart>(), qi::space_type> path_sequence; path_sequence = path_part % qi::lit('/'); query = rooted >> -path_sequence;
升级Boost版本
旧版本Boost Spirit对std::variant和容器的组合支持存在bug,建议升级到Boost 1.70+版本,新版本对C++17特性的兼容性更好,能减少此类编译问题。校验规则的属性签名
确保所有规则的属性签名(如qi::rule<Iterator, X(), qi::space_type>)与实际返回类型一致,比如wildcard规则必须返回ast::Wildcard,不能写错类型,否则会导致属性传递失败。
内容的提问来源于stack exchange,提问作者llm

