【问题标题】:Boost Spirit Qi Custom Syntesized Attribute (Set a specific member of a struct attribute via a semantic action)Boost Spirit Qi Custom Syntesized Attribute(通过语义动作设置结构属性的特定成员)
【发布时间】:2013-09-03 23:02:24
【问题描述】:

假设我有一个我想用灵气解析成的结构,它是这样定义的:

struct data_
{
    bool export;
    std::wstring name;

    data_() : export(false) {}
};

另外,假设结构已经像这样适应融合:

BOOST_FUSION_ADAPT_STRUCT(
    data_,
    (bool, export)
    (std::wstring, name)
)

而相关的规则是:

qi::rule<Iterator, data_(), skipper<Iterator> > rule_data;

rule_data = -lexeme["SpecialText" >> !(alnum | '_')] [ boost::phoenix::at_c<0> = true ] // If this string is found, , set "export" to true
            > lexeme["Name" >> !(alnum | '_')] // this is supposed to go into the "name" member

到目前为止,这编译得很好。但是,“名称”现在保持为空!

所以本质上,我问的是:鉴于“SpecialText”在“Name”之前,我将如何正确合成“export”的布尔属性,而不是字符串?

编辑 在为此拔出头发之后,我偶然发现了“matches []”解析器,它似乎可以满足我的需求。

尽管如此,问题仍然存在于一般形式中,例如,如果我想返回某个字符串或其他数据类型而不是 bool。 本质上,如何通过语义操作设置结构属性的特定成员。

【问题讨论】:

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


【解决方案1】:

如何设置结构成员。

选项 1 (phx::bind)

给定一个结构S

struct S
{
    int         field1;
    std::string field2;
    int         target_field;
    bool        field3;
};

您可以像这样分配给一个字段(例如target_field):

rule<It, S()> p = int_ [ phx::bind(&S::target_field, _val) = _1 ];

现在,您可以通过以下方式使bind 更具可读性:

auto target_field_ = phx::bind(&S::target_field, _val);

p = int_ [ target_field_ = _1 ];

概念证明:live on Coliru

#include "boost/spirit/include/qi.hpp"
#include "boost/spirit/include/phoenix.hpp"

namespace qi  = boost::spirit::qi;
namespace phx = boost::phoenix;
typedef std::string::const_iterator It;

struct S
{
    int         field1;
    std::string field2;
    int         target_field;
    bool        field3;
};

int main()
{
    const std::string input("42");
    It f(begin(input)), l(end(input));

    S instance;

    using namespace qi;
    rule<It, S()> p = int_ [ phx::bind(&S::target_field, _val) = _1 ];

    // or, alternatively:
    auto target_field_ = phx::bind(&S::target_field, _val);
    p = int_ [ target_field_ = _1 ];

    if (parse(f, l, p, instance))
        std::cout << "Parsed: " << instance.target_field;
}

选项 2(融合序列)

您可以使用 adaptation 将结构视为融合序列:

#include "boost/fusion/adapted/struct.hpp"

BOOST_FUSION_ADAPT_STRUCT(S, (int, field1)(std::string, field2)(int, target_field)(bool, field3))

现在您可以在语义操作中对这些序列使用 phoenix lazy 函数:

rule<It, S()> p = int_ [ phx::at_c<2>(_val) = _1 ];

我不喜欢这种风格(因为它把一个富有表现力的结构“降级”成......一个元组),但它可能会派上用场。 Live on Coliru

【讨论】:

  • 请原谅,选项 2 不正是我上面所说的吗?
  • 好的,我在选项 1 中看到我们将 target_field 的构造函数绑定到 _val 并分配属性。所以, [ phx::bind(&S::field2, _val) = "lalaland" ] 应该是一个有效的语义动作,那么,对吗?那么,这会干扰其他字段上的自动属性传播吗?
  • @namezero 呃,是的,它看起来是这样的:/大声笑。这是为 SO 格式化代码的一个强有力的案例(它超过了“右边距”)。无论如何,我认为“自动规则”回答了你真正的问题,所以我很快就拿起了“如何设置结构成员”的松散端......
  • @namezero 是的,语义操作总是会干扰属性传播。 但是你可以同时使用 %= 来实现 auto-rule 行为并在语义操作中_assign to qi::_1...?
  • 是的,这就是为什么我很高兴找到matches[] 解析器。但后来我开始好奇如何做,没有自定义解析器,我认为这也是可能的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多