You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Boost Spirit X3 v1.69与VS2017编译兼容问题求助

解决VS2017下Boost Spirit X3解析规则的编译错误问题

问题分析

你遇到的是VS2017对Boost Spirit X3的omit组件与融合适配结构体结合时的模板实例化限制问题。GCC和Clang对模板参数包的处理更宽松,但VS2017的旧版编译器在处理超过一定数量的unused_type元素时,会触发Boost MPL的断言(比如REQUESTED_PUSH_BACK_SPECIALIZATION_FOR_SEQUENCE_DOES_NOT_EXIST)——这是因为你的解析规则生成的属性序列长度超出了VS2017兼容的默认融合vector上限,而omit[int_[item::fn_n_items]]恰好又向序列中多添加了一个unused_type,直接触发了这个限制。

替代解决方案

下面提供两种可在VS2017中正常编译的替代方案,既保留原有解析逻辑,又绕过编译器的模板限制:

方案1:使用状态传递替代全局变量(推荐)

这种方式更符合Spirit X3的设计理念,同时避免全局变量的副作用,从根源上减少属性序列的冗余元素:

#include <vector>
#include <iostream>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/fusion/include/vector.hpp>

namespace x3 = boost::spirit::x3;
namespace definitions {
enum DIRECTION { NOMINAL, REVERSE };
enum SIDE { LEFT, RIGHT };
}
namespace structs {
struct Door {
    std::string label;
    int start_id;
    double start_location;
    definitions::DIRECTION dir;
    std::vector<int> list;
    int end_id;
    double end_location;
    definitions::SIDE side;
};
}

BOOST_FUSION_ADAPT_STRUCT(structs::Door, 
    label, start_id, start_location, dir, list, end_id, end_location, side)

namespace parsers {
using x3::int_;
using x3::char_;
using x3::double_;
using x3::eol;
using x3::omit;
using x3::seek;
using x3::skip;
using x3::blank;
using x3::lexeme;
using x3::ascii::space;
using x3::eps;
using x3::attr;

auto const quoted = '"' >> lexeme[*~char_('"')] >> '"';
auto const comment = -("--" >> omit[*~char_("\r\n")]) >> eol;
auto const skippers = blank | '(' | ',' | ')';

struct direction_ : x3::symbols<definitions::DIRECTION> {
    direction_() {
        add("NOMINAL", definitions::NOMINAL) 
           ("REVERSE", definitions::REVERSE);
    }
} direction;

struct side_ : x3::symbols<definitions::SIDE> {
    side_() {
        add("LEFT", definitions::LEFT) 
           ("RIGHT", definitions::RIGHT);
    }
} side;

// 定义状态结构体保存计数信息
struct ListCountState {
    int n_items = 0;
    int n_read = 0;
};

// 声明状态类型的key
struct list_count_tag;
using list_count_type = x3::rule<list_count_tag, std::vector<int>, decltype(skippers)>;
BOOST_SPIRIT_DECLARE(list_count_type)

namespace door {
    // 整合计数逻辑到列表解析规则中
    auto const list_impl = x3::rule<struct list_impl_tag, std::vector<int>>{} 
        = int_[([](auto& ctx){ 
            x3::get<ListCountState>(ctx).n_items = _attr(ctx); 
            x3::get<ListCountState>(ctx).n_read = 0; 
        })]
        >> *(eps([](auto& ctx){ 
            return x3::get<ListCountState>(ctx).n_read < x3::get<ListCountState>(ctx).n_items; 
        }) 
            >> *comment >> int_[([](auto& ctx){ 
                _val(ctx).push_back(_attr(ctx)); 
                ++x3::get<ListCountState>(ctx).n_read; 
            })]);

    // 替换原规则中的omit计数行
    auto const text = eol >> "[AREA]" >> *comment >> quoted >> *comment >> int_ >> *comment >> double_ >> *comment >> direction 
        >> *comment >> list_impl 
        >> *comment >> int_ >> *comment >> double_ >> *comment >> side;

    // 使用with传递状态
    auto const start = x3::with<ListCountState>(ListCountState{})[skip(skippers)[*seek[text]]];
}

BOOST_SPIRIT_DEFINE(list_count_type)

} // namespace parsers

std::string text = "\n\
[AREA]\n\
\n\
\"LABEL-1\" --Comment\n\
--Start\n\
-- Id Location(m) Dir\n\
-- --------------------------------\n\
374 84.8 NOMINAL -- Comment\n\
\n\
-- List\n\
-- ---------------------------------------------- -\n\
0 --There are none\n\
\n\
-- End\n\
-- Id Location(m)\n\
-- -------------------- -\n\
374 6.7 --Comment\n\
\n\
--------------------------------------------------------------------------------\n\
RIGHT -- Doors\n";

int main() {
    std::vector<structs::Door> doors;
    const bool result = x3::parse(text.begin(), text.end(), parsers::door::start, doors);
    std::cout << std::boolalpha << result << "\n";
    std::cout << doors.size() << "\n";
    for (const auto& door : doors) std::cout << door.label << "\n";
    return 0;
}

方案2:简化属性序列,避免多余unused元素

如果你不想修改状态管理方式,可以把omit[int_[item::fn_n_items]]替换为不产生属性的动作规则,直接读取整数并执行逻辑,不向融合序列中添加元素:

// 在parsers::door命名空间中,替换原有的omit[int_[item::fn_n_items]]行
>> *comment >> x3::raw[int_][([](auto& ctx){ 
    std::string s(_attr(ctx).begin(), _attr(ctx).end());
    item::n_items = std::stoi(s); 
    item::n_read_items = 0; 
})]

这种方式通过raw获取字符串后手动转换为整数,避免了omit带来的unused_type元素,减少属性序列长度,从而绕过VS2017的模板限制。

为什么原代码在GCC/Clang中正常?

GCC和Clang对模板参数的处理更灵活,支持更长的模板参数序列;而VS2017的MSVC编译器在Boost 1.69版本下,对MPL vector的默认上限处理更严格,当序列中的unused_type元素过多时就会触发断言错误。

内容的提问来源于stack exchange,提问作者Pablo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:38:26