2
votes

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://stackguides.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;
}
1

1 Answers

2
votes

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