1

I am trying to write a parser for csv file using the boost.spirit library. I encounter the following compilation error. I am new in boost.spirit, so could someone identify the reasons?

The error message is:

Error C2664: 'bool boost::spirit::qi::rule::parse(Iterator &,const Iterator &,Context &,const Skipper &,Attribute &) const': cannot convert argument 1 from 'const char *' to 'std::_String_iterator>> &'

And my code is :

#pragma once
#define BOOST_SPIRIT_USE_PHOENIX_V3

#include<vector>
#include<string>
#include<memory>
#include<boost/iostreams/device/mapped_file.hpp> // for mmap
#include<boost/utility/string_ref.hpp>
#include<boost/spirit/include/qi.hpp>
#include<boost/spirit/include/qi_grammar.hpp>
#include<boost/spirit/include/qi_real.hpp>
#include<boost/spirit/include/phoenix.hpp>
#include<boost/spirit/include/qi_symbols.hpp>

typedef boost::string_ref CsvField;
typedef std::vector<CsvField> CsvLine;
typedef std::vector<CsvLine> CsvFile; 
namespace qi = boost::spirit::qi;

template <typename T> struct CsvParser : qi::grammar<T, CsvFile()> {
    CsvParser() : CsvParser::base_type(lines) {
        using namespace qi;
        using boost::phoenix::construct;
        using boost::phoenix::size;
        using boost::phoenix::begin;
        using boost::spirit::qi::float_;

        field = raw[*~char_(",\r\n")][_val = construct<CsvField>(begin(qi::_1), size(qi::_1))]; // semantic action
        //field = qi::float_;
        line = field % ',';
        lines = line  % eol;
    }
    // declare: line, field, fields
    qi::rule<T, CsvFile()> lines;
    qi::rule<T, CsvLine()> line;
    qi::rule<T, CsvField()> field;
};

The code is really adopted from Simplest way to read a CSV file mapped to memory? , so I don't have any clue. I am using Microsoft Visual Studio 2015 and boost 1.16.0 .

This same error occurs if I replace typedef boost::string_ref CsvField by typedef std::string, or replace the parser for field by field = *(~char_(",\r\n")).

Also, the file I am parsing is really a standard csv file, so any suggestion of alternative parsing methods is welcome. The only catch is that the file has millions of rows, so the standard line-by-line parsing does not work for me.

Community
  • 1
  • 1
Chandler
  • 21
  • 4

1 Answers1

0

You're not showing the relevant code. All you have is a template class, but whether any instantiation is even well-formed depends on what you instantiate it with.

Right now I'd assume you are trying to instantiate with std::string::const_iterator as the iterator type - which is a bit funny w.r.t. the mention of memory mapping and string_ref (which imply you wish to be do everything zero-copy).

Nevertheless, the issue then is that raw[] exposes an iterator_range of the source iterator type, meaning you are passing std::string::const_iterator as the first argument of the string_ref (alias CsvField) constructor. That won't work.

In a fix:

field = raw[*~char_(",\r\n")][_val = construct<CsvField>(&*begin(qi::_1), size(qi::_1))]; // semantic action

To be really nice you should wrap std::addressof in a phoenix actor and use it instead of operator&. I'll leave that as an exercise for the reader.

Live On Coliru

#define BOOST_SPIRIT_USE_PHOENIX_V3
#include<boost/utility/string_ref.hpp>
#include<boost/spirit/include/qi.hpp>
#include<boost/spirit/include/phoenix.hpp>

typedef boost::string_ref CsvField;
typedef std::vector<CsvField> CsvLine;
typedef std::vector<CsvLine> CsvFile; 

namespace qi = boost::spirit::qi;

template <typename T> struct CsvParser : qi::grammar<T, CsvFile()> {
    CsvParser() : CsvParser::base_type(lines) {
        using namespace qi;
        using boost::phoenix::construct;
        using boost::phoenix::size;
        using boost::phoenix::begin;
        using boost::spirit::qi::float_;

        field = raw[*~char_(",\r\n")][_val = construct<CsvField>(&*begin(qi::_1), size(qi::_1))]; // semantic action
        //field = qi::float_;
        line = field % ',';
        lines = line  % eol;
    }
    // declare: line, field, fields
    qi::rule<T, CsvFile()> lines;
    qi::rule<T, CsvLine()> line;
    qi::rule<T, CsvField()> field;
};

int main()
{
    using It = std::string::const_iterator;
    CsvParser<It> p;

    std::string const input = R"([section1]
key1=value1
key2=value2
[section2]
key3=value3
key4=value4
)";

    CsvFile parsed;
    auto f = input.begin(), l = input.end();
    bool ok = parse(f, l, p, parsed);

    if (ok) {
        std::cout << "Parsed: " << parsed.size() << " stuffs\n";
    } else {
        std::cout << "Parse failed\n";
    }

    if (f != l)
        std::cout << "Remaining input: '" << std::string(f, l) << "'\n";
}

Printing:

Parsed: 7 stuffs
sehe
  • 374,641
  • 47
  • 450
  • 633