如何在 Boost::Spirit::x3 中执行通用的可重用规则?

问题描述 投票:0回答:1

在我目前正在开发的解析器中,我正在编写一些大小相当重要的规则/“子解析器”。问题是:由于这个解析器可能需要编译器付出一定的努力并且可以具有一定的大小,因此我只想翻译和实例化它一次,从而编写像 boost 教程中解释的那样的规则。到目前为止,一切都很好。但是,该规则将被属性类型始终不同的不同解析器频繁重用。 我将用下面的简单示例来说明我的问题,该示例已经尝试过但无法构建。

my_rule

是必须可重用的规则。我希望它可用于

Foo
Bar
Baz
的三种解析规则。它们各自代表了我可能遇到的可重用性的可能用例。到目前为止,如示例所示,我尝试对所有用例使用
boost::fusion::vector
通用属性,但这并不那么容易。构建了
Foo
Baz
的解析规则,但
Bar
的解析规则没有构建。
因此,根据这个例子,我的问题是:如何使 

my_rule

在每个用例中工作?使用什么作为属性?至少有可能吗?我准备好听到“不”。

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/binary.hpp>

namespace x3 = boost::spirit::x3;

struct Foo
{
  int a;
  int b;
};

struct Bar
{
  int c;
  int d;
  int e;
};

struct Baz
{
  int f;
  Foo foo;
};

BOOST_FUSION_ADAPT_STRUCT(Foo, a, b);
BOOST_FUSION_ADAPT_STRUCT(Bar, c, d, e);
BOOST_FUSION_ADAPT_STRUCT(Baz, f, foo);

const auto my_rule =
  x3::rule<class My_rule_tag, boost::fusion::vector<int, int>>{ "my-rule" } %=
  x3::byte_ >> x3::byte_;

const auto foo_rule = x3::rule<class Foo_rule_tag, Foo>{ "foo-rule" } %=
  my_rule;

const auto bar_rule = x3::rule<class Bar_rule_tag, Bar>{ "bar-rule" } %=
  x3::byte_ >> my_rule;

const auto baz_rule = x3::rule<class Bar_rule_tag, Baz>{ "baz-rule" } %=
  x3::byte_ >> my_rule;

int
main()
{
  std::vector<std::uint8_t> frame{ 0x03, 0x06, 0x09 };
  // Build OK
  Foo foo1;
  x3::parse(frame.begin(), frame.end(), foo_rule, foo1);
  // Build NOK
  Bar bar1;
  x3::parse(frame.begin(), frame.end(), bar_rule, bar1);
  // Build OK
  Baz baz1;
  x3::parse(frame.begin(), frame.end(), baz_rule, baz1);
  return 0;
}


c++ boost boost-spirit boost-spirit-x3 boost-fusion
1个回答
0
投票
generic

reusable,您希望规则与调用位于相同的 TU 中。 正如您已经弄清楚的(请参阅

Baz

聚合

Foo
),关键是使属性类型与属性传播兼容。
我能想到的最简单的事情:

住在Coliru #include <boost/fusion/include/adapt_struct.hpp> #include <boost/spirit/home/x3.hpp> #include <boost/spirit/home/x3/binary.hpp> template <typename T> constexpr static T parse(auto const& frame, auto rule) { T val; parse(begin(frame), end(frame), rule, val); return val; } struct Foo { int a, b; constexpr auto operator<=>(Foo const&) const = default; }; struct Bar { int c, d, e; constexpr auto operator<=>(Bar const&) const = default; }; struct Baz { int f; Foo foo; constexpr auto operator<=>(Baz const&) const = default; }; BOOST_FUSION_ADAPT_STRUCT(Foo, a, b) BOOST_FUSION_ADAPT_STRUCT(Bar, c, d, e) BOOST_FUSION_ADAPT_STRUCT(Baz, f, foo.a, foo.b) // using Vec2 = boost::fusion::vector<int, int>; namespace x3 = boost::spirit::x3; auto const vec2_ = x3::byte_ >> x3::byte_; auto const foo_rule = vec2_; auto const bar_rule = x3::byte_ >> vec2_; auto const baz_rule = x3::byte_ >> foo_rule; int main() { constexpr std::array<std::uint8_t, 3> frame{0x03, 0x06, 0x09}; assert((parse<Foo>(frame, foo_rule) == Foo{ 3, 6 })); assert((parse<Bar>(frame, bar_rule) == Bar{ 3, 6, 9 })); assert((parse<Baz>(frame, baz_rule) == Baz{ 3, {6, 9} })); }

注意
Baz

的改编!


如果你想要
{foo,bar,baz}_rule

正确编译防火墙:

住在科里鲁

#include <boost/fusion/include/adapt_struct.hpp> #include <boost/spirit/home/x3.hpp> #include <boost/spirit/home/x3/binary.hpp> template <typename T> constexpr static T parse(auto const& frame, auto rule) { T val; parse(begin(frame), end(frame), rule, val); return val; } struct Foo { int a, b; constexpr auto operator<=>(Foo const&) const = default; }; struct Bar { int c, d, e; constexpr auto operator<=>(Bar const&) const = default; }; struct Baz { int f; Foo foo; constexpr auto operator<=>(Baz const&) const = default; }; BOOST_FUSION_ADAPT_STRUCT(Foo, a, b) BOOST_FUSION_ADAPT_STRUCT(Bar, c, d, e) BOOST_FUSION_ADAPT_STRUCT(Baz, f, foo) // using Vec2 = boost::fusion::vector<int, int>; namespace x3 = boost::spirit::x3; auto const vec2_ = x3::byte_ >> x3::byte_; auto const foo_rule = x3::rule<class Foo_rule_tag, Foo>{"foo_rule"} = vec2_; auto const bar_rule = x3::rule<class Bar_rule_tag, Bar>{"bar_rule"} = x3::byte_ >> vec2_; auto const baz_rule = x3::rule<class Baz_rule_tag, Baz>{"baz_rule"} = x3::byte_ >> foo_rule; int main() { constexpr std::array<std::uint8_t, 3> frame{0x03, 0x06, 0x09}; assert((parse<Foo>(frame, foo_rule) == Foo{ 3, 6 })); assert((parse<Bar>(frame, bar_rule) == Bar{ 3, 6, 9 })); assert((parse<Baz>(frame, baz_rule) == Baz{ 3, {6, 9} })); }

如果“涉及”
vec2_

(可能是某种数据包标头),那么


要么像在 Baz 中那样进行聚合
  • 在每个需要它的 TU 中包含它的
  • 通用
  • 定义
  • 说实话,将解析器跨 TU 分开是

永远不值得的 它,特别是对于X3。编译时间已经非常短了,而且 显式实例化硬锁迭代器以及上下文类型, 通常会导致难以理解的链接器错误。 参见例如

    混合来自分离翻译单元的非终结符规则
  • BOOST_SPIRIT_DEFINE 不懂
  • 开箱即用

因为看起来您正在将二进制线路协议反序列化为 (非打包?)使用一些特定于应用程序的表示的结构,我会 考虑是否可以使用例如生成解析器增强描述。

© www.soinside.com 2019 - 2024. All rights reserved.