简体   繁体   中英

Parsing variant of struct with a single member using Boost Spirit X3 and Fusion

I am trying to parse a std::Variant with a fusion-adapted Struct type that contains a single member. After several hours of trying to figure out the problem, I was able to reproduce the issue with this code:

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"});

This causes the following build error:

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;

What I find baffling, is that if I add a second member to TestStruct , the following code would actually compile and run correctly:

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"});

I've also read on SO, that there were known issues with fusion-adapted single field structs, when trying to parse these with older versions of Spirit.

What is the best way to fix (or go around) this issue with Spirit X3?

I hate to say, but this is a well-known limitation and it keeps cropping up. I've kind of given up on trying to get it fixed. It's an edge case that is usually easy to work-around or avoid.

See eg

A longer list exists for Qi .

In your cause I would probably avoid parsing into TestStruct and rather parse into float . Otherwise, parse it and propagate using a semantic action instead of automatic attribute propagation.

I tried and found it pretty hard to get over the hurdle in this specific case (it appears to be the "inverse" problem, where an already correctly matched rule exposing TestResult is still posing a problem down the road. Clearly the attribute synthesis rules are wrong again).

Brute Force

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

Doing the attribute propagation manually:

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];

Works: Live On Coliru

Indeed, the "official" propagation would look more like¹

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

And it appears to not-work: Live On Coliru


¹ but not exactly, there is a lot of meta-programming conditions before that

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM