1

I'm trying to parse a file and have the data copied into a vector within a class object. I've taken the employee example and modified it to what I'm trying to do. The file being parsed looks like this (but more lines) ...

1 0.2 0.3 0.4

I've added a vector to struct employee and am getting assertion failures on the phrase_parse line.

static assertion failed: Attribute does not have the expected size.
static_assert(
^

I'm kind of thinking the expected size has something to do with the vector. Thoughts on where I'm going wrong?

namespace client {
    namespace ast {

    struct employee
    {
        int id;
        std::vector<double> coords;
    };

    using boost::fusion::operator<<;
}}

BOOST_FUSION_ADAPT_STRUCT(
    client::ast::employee,
    (int, id)
    (std::vector<double>, coords)
)

namespace client
{
    namespace parser
    {
        namespace x3 = boost::spirit::x3;
        namespace ascii = boost::spirit::x3::ascii;

        using x3::int_;
        using x3::double_;

        x3::rule<class employee, ast::employee> const employee = "employee";
        auto const employee_def =
             int_ >> double_ >> double_ >> double_;
        BOOST_SPIRIT_DEFINE(employee)
    }
}

int main()
{
    using boost::spirit::x3::ascii::space;
    using client::parser::employee;

    string fil("test-file.in");

    mapped_file_source map(fil);
    istringstream iss(map.data());
    map.close();

    client::ast::employee emp;

    boost::spirit::istream_iterator iter(iss >> noskipws), eof;

    phrase_parse(iter, eof, employee, space, emp);
    // failure on above line
}
sehe
  • 374,641
  • 47
  • 450
  • 633
Ender
  • 1,652
  • 2
  • 25
  • 50
  • "using" `operator<<` does nothing there. Why on earth do copy a file mappinginto a string stream? Why is your code sample broken and not self-contained? – sehe Jul 09 '17 at 17:10

1 Answers1

1

According to the documentation, double_ >> double_ >> double_ synthesizes a Fusion sequence of double, double, double (so fusion::tuple<double, double, double> or fusion::list<double, double, double> etc).

You want a vector, so you need a repeating parser (operator)

  • the repeat directive will do repeat(3) [double_]
  • the Kleene star (operator *) or plus (operator +) are interesting (but unbounded)
  • the list operator (operator %) is also unbounded but accepts delimiters (e.g. double_ % ','

In this case I'd go the other way: use a proper AST for the grammar:

Live On Coliru

struct coord {
    double x,y,z;
};

struct employee
{
    int id;
    coord coords;
};

Adapting them is simpler than the old fashioned method you used:

BOOST_FUSION_ADAPT_STRUCT(client::ast::coord, x, y, z)
BOOST_FUSION_ADAPT_STRUCT(client::ast::employee, id, coords)

The parser is a clean

auto const coord_def = double_ >> double_ >> double_;
auto const employee_def = int_ >> coord;

Full demo:

Live On Coliru

#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <iostream>

namespace client {
    namespace ast {

    struct coord {
        double x,y,z;
    };

    struct employee
    {
        int id;
        coord coords;
    };

    using boost::fusion::operator<<;
}}

BOOST_FUSION_ADAPT_STRUCT(client::ast::coord, x, y, z)
BOOST_FUSION_ADAPT_STRUCT(client::ast::employee, id, coords)

namespace client
{
    namespace parser
    {
        namespace x3 = boost::spirit::x3;
        namespace ascii = boost::spirit::x3::ascii;

        using x3::int_;
        using x3::double_;

        x3::rule<class employee, ast::coord>    const coord    = "coord";
        x3::rule<class employee, ast::employee> const employee = "employee";

        auto const coord_def = double_ >> double_ >> double_;
        auto const employee_def = int_ >> coord;

        BOOST_SPIRIT_DEFINE(employee, coord);
    }
}

int main()
{
    using boost::spirit::x3::ascii::space;
    using client::parser::employee;

    std::istringstream iss("1 0.2 0.3 0.4");

    client::ast::employee emp;

    boost::spirit::istream_iterator iter(iss >> std::noskipws), eof;

    bool ok = phrase_parse(iter, eof, employee, space, emp);
    if (ok)
        std::cout << "parsed: " 
             << emp.id       << " "
             << emp.coords.x << " "
             << emp.coords.y << " "
             << emp.coords.z << "\n";
}
sehe
  • 374,641
  • 47
  • 450
  • 633
  • What does it take to parse multiple rows of the data? I added another row of 2 2.2 3.3 4.4 to iss and it still only parses one row. – Ender Jul 09 '17 at 22:21
  • Have you read the documentation? My answer points exactly at the stuff needed. See https://stackoverflow.com/questions/17072987/boost-spirit-skipper-issues/17073965#17073965 for some general information about skippers in relation to new lines. Be sure to parse into a different type, because `employee` _[sic]_ can obviously only store 1 line – sehe Jul 09 '17 at 22:27
  • Ok, then. Thanks for the help. I'll keep trying. – Ender Jul 09 '17 at 22:29