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.

ithron
  • 310
  • 3
  • 9
  • Is `boost::array` an option? I believe including "boost/fusion/include/boost_array.hpp" `double_ >> double_ >> double_` should work. – llonesmiz Oct 12 '15 at 13:59
  • 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`. – ithron Oct 12 '15 at 17:53
  • [This is an awful hack](http://coliru.stacked-crooked.com/a/412c97fecfa4e4b1) to make your approach compile. You should probably wait for a better answer(or actually a good one). – llonesmiz Oct 12 '15 at 20:21
  • 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`. – ithron Oct 13 '15 at 08:50
  • 1
    [The machinery that Spirit uses to access sequence attributes](https://github.com/boostorg/spirit/blob/boost-1.59.0/include/boost/spirit/home/x3/operator/detail/sequence.hpp#L81), 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. – llonesmiz Oct 13 '15 at 09:41

1 Answers1

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.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Unfortunately `std::array` is not adapted by fusion. The header you suggest (or "boost/fusion/include/boost_array.hpp") only affects `boost::array`. [This](http://coliru.stacked-crooked.com/a/07416373f7d19da3) seems to work with some small modifications. [Here](https://svn.boost.org/trac/boost/ticket/8241) is a very old attempt at adapting `std::array` (I have no idea if it still works). – llonesmiz Oct 12 '15 at 16:39
  • Huh. It didn't work worth boost array either for me. Will look at the differences when i have time later. – sehe Oct 12 '15 at 16:52
  • 1
    Similar problem exists for `std::forward_list`. `push_back` required for class to be container in terms of Boost.Spirit. – Tomilov Anatoliy Oct 12 '15 at 19:03
  • 2
    According to the release notes `std::array` will be adapted by fusion starting with Boost 1.63. – llonesmiz Dec 11 '16 at 16:31
  • @llonesmiz, yes, there is a `boost/fusion/adapted/std_array.hpp` now. – alfC Jun 19 '19 at 19:05