如何用Boost Spirit X3解析带重载构造函数的结构体?
解决Boost Spirit X3解析重载构造函数结构体的类型匹配问题
问题根源
直接使用x3::int_ | (x3::int_ >> x3::lit("-") >> x3::int_)组合规则会编译失败,核心原因有两点:
- 属性类型不匹配:
x3::int_解析返回size_t类型,而规则目标类型是MyRange,两者无法自动转换。 - 规则顺序错误:若先匹配单值规则,范围格式(如
1-2)会被提前匹配为单值1,剩余的-2无法解析,导致逻辑错误。
解决方案
方案1:使用语义动作转换类型(推荐)
通过语义动作将单值解析结果转换为MyRange对象,同时调整规则顺序优先匹配范围格式:
#include <boost/fusion/adapted/struct.hpp> #include <boost/spirit/home/x3.hpp> #include <iostream> namespace x3 = boost::spirit::x3; struct MyRange { size_t start; size_t end; explicit MyRange(size_t start, size_t end = 0) : start(start), end(end == 0 ? start : end) { } }; BOOST_FUSION_ADAPT_STRUCT(MyRange, start, end) int main() { // 优先匹配范围格式,再匹配单值格式,单值通过语义动作构造MyRange auto range_constraint = x3::rule<struct test_struct, MyRange>{} = (x3::int_ >> x3::lit("-") >> x3::int_) | x3::int_[([](auto& ctx) { _val(ctx) = MyRange(_attr(ctx)); })]; for (std::string input : {"1-2", "1", "1-", "garbage"}) { MyRange result{0}; auto iter = input.begin(); // 解析并传入结果对象 auto success = x3::phrase_parse(iter, input.end(), range_constraint, x3::ascii::space, result); // 确保输入被完全解析(避免部分匹配) success &= (iter == input.end()); std::cout << "`" << input << "` -> " << std::boolalpha << success; if (success) { std::cout << " (start: " << result.start << ", end: " << result.end << ")"; } std::cout << std::endl; } return 0; }
方案2:全局类型转换特化
通过特化x3::transform_attribute让Spirit自动完成size_t到MyRange的转换,无需语义动作:
#include <boost/fusion/adapted/struct.hpp> #include <boost/spirit/home/x3.hpp> #include <iostream> namespace x3 = boost::spirit::x3; struct MyRange { size_t start; size_t end; explicit MyRange(size_t start, size_t end = 0) : start(start), end(end == 0 ? start : end) { } }; BOOST_FUSION_ADAPT_STRUCT(MyRange, start, end) // 特化类型转换规则 namespace x3 { template<> struct transform_attribute<MyRange, size_t, void> { static MyRange pre(size_t s) { return MyRange(s); } }; } int main() { auto range_constraint = x3::rule<struct test_struct, MyRange>{} = (x3::int_ >> x3::lit("-") >> x3::int_) | x3::int_; for (std::string input : {"1-2", "1", "1-", "garbage"}) { MyRange result{0}; auto iter = input.begin(); auto success = x3::phrase_parse(iter, input.end(), range_constraint, x3::ascii::space, result); success &= (iter == input.end()); std::cout << "`" << input << "` -> " << std::boolalpha << success; if (success) { std::cout << " (start: " << result.start << ", end: " << result.end << ")"; } std::cout << std::endl; } return 0; }
关键修改说明
- 规则顺序调整:先匹配
start-end格式,避免单值规则提前截取范围的前缀。 - 类型转换:方案1通过局部语义动作构造
MyRange,不会影响其他解析逻辑;方案2通过全局特化实现自动转换,适合全局需要该转换的场景。 - 完全解析检查:添加
iter == input.end()判断,确保输入字符串被完整解析,避免"1-garbage"这类部分匹配的错误判定。
内容的提问来源于stack exchange,提问作者Mikhail
相关产品推荐
相关产品推荐

