【问题标题】:Parsing variant of struct with a single member using Boost Spirit X3 and Fusion使用 Boost Spirit X3 和 Fusion 解析具有单个成员的结构变体
【发布时间】:2020-06-28 21:16:49
【问题描述】:

我正在尝试使用包含单个成员的融合适应 Struct 类型解析 std::Variant。经过几个小时的尝试找出问题后,我能够使用以下代码重现问题:

struct TestStruct {
    float value;
};

BOOST_FUSION_ADAPT_STRUCT(TestStruct, value)

typedef std::variant<TestStruct, std:string> TestVariant;

auto TestStructRule = x3::rule<struct test_struct, TestStruct>{} = x3::float_ >> ",";

auto TestVariantRule = x3::rule<struct test_variant, TestVariant>{} = TestStruct | "default" >> x3::attr(std::String{"default"});

这会导致以下构建错误:

boost/boost/spirit/home/x3/support/traits/move_to.hpp:67:18: error: no viable overloaded '='

            dest = std::move(fusion::front(src));

boost/boost/spirit/home/x3/support/traits/move_to.hpp:79:13: note: in instantiation of function template specialization 'boost::spirit::x3::traits::detail::move_to_plain<TestStruct, std::__1::variant<TestStruct, std::string> >' requested here

            move_to_plain(std::forward<Source>(src), dest, is_single_element_sequence);

...

/Library/Developer/CommandLineTools/usr/include/c++/v1/variant:1214:12: note: candidate function not viable: no known conversion from 'typename remove_reference<float &>::type' (aka 'float') to 'const std::__1::variant<TestStruct, std::String>' for 1st argument

  variant& operator=(const variant&) = default;
       ^
/Library/Developer/CommandLineTools/usr/include/c++/v1/variant:1215:12: note: candidate function not viable: no known conversion from 'typename remove_reference<float &>::type' (aka 'float') to 'std::__1::variant<TestStruct, std::String>' for 1st argument

  variant& operator=(variant&&) = default;

我觉得莫名其妙的是,如果我向TestStruct 添加第二个成员,以下代码实际上会正确编译和运行:

struct TestStruct {
    float value1;
    float value2; // <-- Added second member
};

BOOST_FUSION_ADAPT_STRUCT(TestStruct, value1, value2)

typedef std::variant<TestStruct, std:string> TestVariant;

auto TestStructRule = x3::rule<struct test_struct, TestStruct>{} = x3::float_ >> "," >> x3::float_ >> ",";

auto TestVariantRule = x3::rule<struct test_variant, TestVariant>{} = TestStruct | "default" >> x3::attr(std::String{"default"});

我也读过 SO,当尝试用旧版本的 Spirit 解析这些时,融合适应的单字段结构存在已知问题。

解决(或绕过)Spirit X3 这个问题的最佳方法是什么?

【问题讨论】:

  • 只需添加一个赋值运算符即可。 “适应”使结构表现为元组。

标签: c++ boost boost-spirit boost-spirit-x3 boost-fusion


【解决方案1】:

我不想说,但这是一个众所周知的限制,并且不断出现。我已经放弃了尝试修复它。这是一种边缘情况,通常很容易解决或避免。

参见例如

一个longer list exists for Qi

出于您的原因,我可能会避免解析为TestStruct,而是解析为float。否则,解析它并使用语义操作而不是自动属性传播来传播。

我尝试并发现在这种特定情况下很难克服障碍(这似乎是“逆”问题,其中一个已经正确匹配的规则暴露了 TestResult 仍然构成问题。显然属性合成规则又错了)。

蛮力

auto assign = [](auto& ctx) { _val(ctx) = _attr(ctx); };

手动进行属性传播:

auto test_struct
    = x3::rule<struct test_struct, TestStruct>{}
    = x3::float_ >> ",";

auto test_variant
    = x3::rule<struct test_variant, TestVariant>{}
    = test_struct [assign] | "default" >> x3::attr("default"s)[assign];

作品:Live On Coliru

确实,“官方”传播看起来更像¹

auto assign = [](auto& ctx) {
    x3::traits::move_to(_attr(ctx), _val(ctx));
};

它似乎不起作用:Live On Coliru


¹但不完全是,在此之前有很多元编程条件

【讨论】:

  • 感谢您的回答@sehe,我最终对TestStruct 使用了语义操作:[](auto&amp;ctx){x3::_val(ctx).value = x3::_attr(ctx);};
猜你喜欢
  • 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
相关资源
最近更新 更多