【问题标题】:How to combine Boost.Spirit customization points with Nabialek?如何将Boost.Spirit自定义点与Nabialek结合起来?
【发布时间】:2025-12-31 10:15:11
【问题描述】:

首先要做的事:我正在使用 MS Visual Studio 2017 (v15.19.11)、C++14 和 Boost v1.65.1 的默认 C++ 语言标准

我有一个格式如下的输入文件:

IterName        SomeName
IterDesc        Some Description
COLUMNS fname.XCT               posn.detect.R           posn.source.z
        expt_KF000000.raw       0.0                     27.639320225002102
        expt_KF000180.raw       216.34457142857138      30.584848796430673
        expt_KF000360.raw       72.68914285714277       33.530377367859245

我正在尝试将其解析并加载到如下结构中:

struct Stax { std::string stage, axis; };
struct Configuration {
    std::map<std::string, std::string> filenames; // "expt_KF000000.raw": "XCT", "other_file.raw": "OCT"
    std::map<double, Stax> positions;  // 0.0: ["Detect", "R"], 27.639: ["Source", "Z"]
};
struct Iteration {
    std::string name;
    std::string descript;
    std::vector<Configuration> configs;
};

包含“COLUMNS”的行之后的每一行都应生成一个新的Configuration 实例以添加到Iteration.configs

正如代码中的 cmets 所暗示的,某些列可能会丢失(在本例中为“fname.OCT”)...但列的顺序可能会有所不同(数字也会有所不同) ) 所以我尝试使用本地规则状态来保存可变数量的解析器,每列一个,如下所示:https://*.com/a/31382602/1206102

Configuration.positions 的元素由“posn.”标头字符串后面的信息以及从单元格本身解析的任何内容组成。同样,Configuration.filenames 的元素由标题中“fname.”后面的任何内容以及单元格内容组成。所以我必须以某种方式保存我从标题行解析的信息,以便稍后在解析单元格时使用......所以我将它与单元格解析器一起存储在规则本地状态变量中。

我已经尝试过各种各样的事情,从规则本地人到 Phoenix 包装的数据成员,从直接属性传播到适应 Fusion 的结构和“自定义点”,甚至是 attr_casttransform_attribute 专业化...但尚未成功编译。我的尝试列表如下:

  1. https://wandbox.org/permlink/fU1WOazzxOVPbh9n
  2. https://wandbox.org/permlink/Itlq3HfydUByUSaH
  3. https://wandbox.org/permlink/96yDzVpCpI0K4ujs
  4. https://wandbox.org/permlink/u8iWv61jJSQv01bU

我的最后一次尝试仍然产生 11 个编译时错误,其中第一个 ('const_iterator': is not a member of 'Configuration') 说明:某些东西 (Qi/Fusion/Phoenix) 期望 Configuration 是可迭代的...可能是因为解析的类型填充它的是(元组向量)。

这可能是我的问题的症结所在:我正在尝试将已解析的可迭代对象填充到不可迭代 * 复杂结构中:已解析向量中的一些元组将进入该结构的一个数据成员,而其他元组将进入不同的数据成员。 (*我说不可迭代,因为在 Configuration 上进行迭代是没有意义的,但如果 Qi/Fusion/Phoenix 坚持我可以尝试适应。事实上,你会看到 [注释掉] 尝试定义 const_iterator输入Configuration。)

如果可能的话,我想使用 Boost.Spirit 来实现这一点......有什么想法我做错了什么吗?

【问题讨论】:

    标签: c++ boost-spirit boost-spirit-qi


    【解决方案1】:
    1. &amp;cell_stage 不采用cell_stage 的地址,因为operator&amp; 已超载。使用boost/std::addressof获取地址。
    2. qi::rule&lt;Iterator, Skipper, double()&gt;* 无法转换为 qi::rule&lt;Iterator, Skipper, boost::variant&lt;std::string, double, bool&gt;&gt;*
    3. qi::eps[_offset = -1, px::ref(_ncols) = px::size(_tstore)] 中,您只需在初始化时将-1 分配给_offset,它必须是px::ref(_offset) = -1
    4. qi::repeat(_ncols) 不会重复_ncols 次,而是_ncols 在规则创建时包含的数字,即零。
    5. qi::eps[_offset = ++_offset % _ncols] 中,与之前的一样,您不会创建Phoenix lambda,而只是创建qi::eps[0] 之类的东西。必须是qi::eps[px::ref(_offset) = ++px::ref(_offset) % px::cref(_ncols)]
    6. 我没有深入了解qi::attr(_tstore[_offset]) &gt;&gt; *_pstore[_offset] 应该做什么,但这显然不是您的实际意思,可能是qi::lazy(px::cref(_tstore)[_offset]) &gt;&gt; qi::lazy(px::cref(_pstore)[_offset])

    此时我不得不停下来问:你真的要让它工作吗?

    不要将 Spirit 用作 Birmingham screwdriver。您不必在一个 Spirit 解析器中完成所有事情。例如,您可以在通常的 for 循环中解析行,这样编写和维护起来会简单得多。

    【讨论】:

    • 感谢 Nikita,但在我早期的一个工作示例 (coliru.stacked-crooked.com/a/2116fc892b9ecaf2) 中,#1 和 #4 不会导致问题,但也许我通过使用数据成员而不是本地人破坏了它们。并指出了最适合这项工作的工具,但我已经看到 Spirit 成功地钉了一些非常复杂的钉子。
    • 琐事:我认为qi::repeat(px::ref(_ncols)) 是受支持的,并且执行了 OP 看似预期的操作