Using std::array as Attribute for boost::spirit::x3
Asked Answered
C

1

6

I'm trying to parse a list of numbers into a fixed sized std::array container using boost::spirit's newest release x3 (as included in boost 1.54). Since std::array has the necessary functions, it is detected as an Container, but it is lacking the insert function which makes it incompatible. Here is a short example of what I am trying to accomplish:

#include <boost/spirit/home/x3.hpp>

#include <array>
#include <iostream>
#include <string>

namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;

typedef std::array<double, 3> Vertex;

int main(int, char**) {
  using x3::double_;
  using ascii::blank;

  std::string input = "3.1415 42 23.5";
  auto iter = input.begin();

  auto vertex = x3::rule<class vertex, Vertex>{} =
    double_ >> double_ >> double_;

  Vertex v;

  bool const res = x3::phrase_parse(iter, input.end(), vertex, blank, v);
  if (!res || iter != input.end()) return EXIT_FAILURE;

  std::cout << "Match:" << std::endl;
  for (auto vi : v) std::cout << vi << std::endl;
  return EXIT_SUCCESS;
}

This won't compile since std::array has no insert function. As a workaround I used semantic actions:

auto vertex() {
  using namespace x3;
  return rule<class vertex_id, Vertex>{} =
    double_[([](auto &c) { _val(c)[0] = _attr(c); })] >>
    double_[([](auto &c) { _val(c)[1] = _attr(c); })] >>
    double_[([](auto &c) { _val(c)[2] = _attr(c); })];
}

and then call

x3::phrase_parse(iter, input.end(), vertex(), blank, v);

instead. This works (using clang 3.6.0 with -std=c++14), but I think this solution is very inelegant and hard to read.

So I tried to adapt std::array as a fusion sequence using BOOST_FUSION_ADAPT_ADT like so:

BOOST_FUSION_ADAPT_ADT(
  Vertex,
  (double, double, obj[0], obj[0] = val)
  (double, double, obj[1], obj[1] = val)
  (double, double, obj[2], obj[2] = val))

and then specializing x3::traits::is_container for Vertex to tell x3 to not treat std::array as a container:

namespace boost { namespace spirit { namespace x3 { namespace traits {
  template<> struct is_container<Vertex> : public mpl::false_ {};
}}}}

But this won't compile in combination with x3. Is this a bug or am I using it wrong? Calling e.g. fusion::front(v) without all the x3 code compiles and works, so I guess my code is not completely wrong.

However I'm sure there is a cleaner solution with x3 not involving any fusion adaptors or semantic actions for this simple problem.

Chondro answered 12/10, 2015 at 13:29 Comment(5)
Is boost::array an option? I believe including "boost/fusion/include/boost_array.hpp" double_ >> double_ >> double_ should work.Niela
I fixed those stupid errors, sorry for that. boost::array is not an options since I am interfacing existing code that relies heavily on std::array.Chondro
This is an awful hack to make your approach compile. You should probably wait for a better answer(or actually a good one).Niela
cv_and_he: I really don't understand what the boost::add_reference part of your code does, or how it solves the issues my compiler reports, but it does. For now I can live with that hack since it also allows adapting any ADT, not just std::array.Chondro
The machinery that Spirit uses to access sequence attributes, forcefully adds a reference to the result of boost::front in order to not make copies. This sadly does not work with the adt_attribute_proxy that BOOST_FUSION_ADAPT_ADT uses. What I did is simply make boost::add_reference not add a reference to an adt_attribute_proxy. I think this may even make a bit of sense since it is basically a view, but messing with such common traits hardly seems like a good idea.Niela
C
0

The code you posted is riddled with sloppy errors. There's no reason to expect anything in there to compile, really.

Regardless, I cleaned that up¹. Of course you should have included

#include <boost/fusion/adapted/array.hpp>

Sadly it simply doesn't work. I reached largely the same conclusion as (after trying the same things you mention, before reading about it :)).

This is a usability issue with Spirit X3 - which is still in experimental phase. You could report it at the [spirit-general] mailing list. Response is usually pretty swift.


¹ in case you want to see what I worked with http://paste.ubuntu.com/12764268/; I've also used x3::repeat(3)[x3::double_] for comparison.

Chirrupy answered 12/10, 2015 at 16:13 Comment(5)
Unfortunately std::array is not adapted by fusion. The header you suggest (or "boost/fusion/include/boost_array.hpp") only affects boost::array. This seems to work with some small modifications. Here is a very old attempt at adapting std::array (I have no idea if it still works).Niela
Huh. It didn't work worth boost array either for me. Will look at the differences when i have time later.Chirrupy
Similar problem exists for std::forward_list. push_back required for class to be container in terms of Boost.Spirit.Summons
According to the release notes std::array will be adapted by fusion starting with Boost 1.63.Niela
@llonesmiz, yes, there is a boost/fusion/adapted/std_array.hpp now.Vertigo

© 2022 - 2024 — McMap. All rights reserved.