2

I'm using boost::spirit to parse text into a struct containing a fixed size array. While following the example in

qi/boost_array.cpp

and trying to use it for parsing into a struct containing a std::array (or a boost::array) i recognised that because of how BOOST_FUSION_ADAPT_STRUCT works i have to place the helper result_of::adapt_array< some_array_type >::type in the struct as well.

I think it should be possible to create a wrapper using BOOST_FUSION_ADAPT_ADT but it seems overkill just to get rid of the little adapter overhead in the struct. As i don't have many instances of it, i can live with it from a memory point of view, but it also introduces some noise.

I also think it could be possible to create an adapter which derives from the array type rather than encapsulating it to hide some of the noise but it would force me to alter all existing structs and it would leak parser overhead to the outside which is not a nice solution.

Is there something obvious I'm approaching wrong or do exist new helpers which route the overhead in the background so that they can be encapsulated in the parser?

I'm aware of Using std::array as Attribute for boost::spirit::x3 .

Live Example

Here's my current code:

#include <string>
#include <array>
#include <boost/spirit/include/qi.hpp>

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;

// ...code from http://www.boost.org/doc/libs/1_60_0/libs/spirit/example/qi/boost_array.cpp here

typedef result_of::adapt_array<std::array<double, 6> >::type AdaptedArrayType;

struct StructWithArray
{
    StructWithArray()
        : adaptedAry_(ary_)
    {}

    double dummy_; // see https://stackoverflow.com/questions/19823413/spirit-qi-attribute-propagation-issue-with-single-member-struct
    std::array<double, 6> ary_;
    AdaptedArrayType adaptedAry_;
};

BOOST_FUSION_ADAPT_STRUCT(
    StructWithArray
    ,
    (double, dummy_)
    (AdaptedArrayType, adaptedAry_)
    )

template <typename Iterator, typename Skipper>
struct StructWithArrayParser
    : qi::grammar<Iterator, StructWithArray(), Skipper>
{
    StructWithArrayParser() : StructWithArrayParser::base_type(start)
    {
        using qi::double_;

        arrayLine %= double_ > double_ > double_ > double_ > double_ > double_;
        start %= double_ > arrayLine;
    }

    qi::rule<Iterator, AdaptedArrayType(), Skipper> arrayLine;
    qi::rule<Iterator, StructWithArray(), Skipper> start;
};

int main() {
    std::string arrayStr = "0 1 2 3 4 5 6";
    std::string::const_iterator it = arrayStr.begin();
    std::string::const_iterator endIt = arrayStr.end();
    StructWithArrayParser<std::string::const_iterator, ascii::space_type> grammar;
    StructWithArray structWithArray;
    bool ret = phrase_parse(it, endIt, grammar, ascii::space, structWithArray);
    return 0;
}
Community
  • 1
  • 1
wonko realtime
  • 545
  • 9
  • 26

1 Answers1

2

You are going places :) You seem to have reached warp speed with Spirit in no time.

EDITED After reading the question closer I noticed that you ask for a shorter, less intrusive way.

I don't know a non-intrusive way to fix it, but you can use boost::array if you specialize is_container for it.

That's pretty unintrusive, but still changes your types.

Live On Coliru

#include <boost/fusion/include/tuple.hpp>
#include <boost/fusion/adapted/boost_array.hpp>
#include <boost/spirit/include/qi.hpp>
#include <string>

namespace boost { namespace spirit { namespace traits {
    template <typename T, size_t N>
        struct is_container<boost::array<T, N>, void> : mpl::false_ { };
} } }

namespace qi    = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;

struct StructWithArray
{
    double dummy_; // see http://stackoverflow.com/questions/19823413/spirit-qi-attribute-propagation-issue-with-single-member-struct
    boost::array<double, 6> ary_;
};

BOOST_FUSION_ADAPT_STRUCT(StructWithArray, dummy_, ary_)

template <typename Iterator, typename Skipper>
struct StructWithArrayParser
    : qi::grammar<Iterator, StructWithArray(), Skipper>
{
    StructWithArrayParser() : StructWithArrayParser::base_type(start)
    {
        using qi::double_;

        arrayLine = double_ > double_ > double_ > double_ > double_ > double_;
        start     = double_ > arrayLine;
    }

  private:
    qi::rule<Iterator, boost::array<double, 6>(), Skipper> arrayLine;
    qi::rule<Iterator, StructWithArray(), Skipper> start;
};

int main() {
    std::string arrayStr = "0 1 2 3 4 5 6";
    using It = std::string::const_iterator;
    It it = arrayStr.begin(), endIt = arrayStr.end();
    StructWithArrayParser<It, ascii::space_type> grammar;

    StructWithArray structWithArray;
    bool ret = phrase_parse(it, endIt, grammar, ascii::space, structWithArray);
    std::cout << std::boolalpha << ret << "\n";

    for (double v : structWithArray.ary_)
        std::cout << v << " ";
}

Prints:

true
1 2 3 4 5 6 
sehe
  • 374,641
  • 47
  • 450
  • 633
  • After testing all permutations of `{g++,clang++} -std={c++03,c++11,c++14}` with boost 1.58,59,60 I found out misread the question. Looking shortly – sehe Dec 23 '15 at 19:50
  • 1
    Updated the answer with the best workaround I could (easily) achieve. Seems to me you might work off this to mimic the same with `std::array` but it's not ready-made – sehe Dec 23 '15 at 20:17
  • Fantastic workaround:) Exactly what i was hoping for. I can happily live with boost::array for now but will make an edit if i get time to make it work with std::array. It's also great the your solution pointed me to BOOST_PP_VARIADICS; way easier that way. – wonko realtime Dec 23 '15 at 22:21
  • Clever, with a small change it works for Spirit X3 as well. Spirit should know better, that `array` is not a container. It is a pity that this workaround doesn't work for `std::array` (because some attempt from Spirit to move the array). `/usr/include/boost/spirit/home/x3/support/traits/move_to.hpp:54:18: fatal error: no viable overloaded '='` – alfC Sep 24 '16 at 20:32