0

First things first: I'm using the default C++ language standard for MS Visual Studio 2017 (v15.19.11), C++14, along with Boost v1.65.1

I have an input file formatted like this:

IterName        SomeName
IterDesc        Some Description
COLUMNS fname.XCT               posn.detect.R           posn.source.z
        expt_KF000000.raw       0.0                     27.639320225002102
        expt_KF000180.raw       216.34457142857138      30.584848796430673
        expt_KF000360.raw       72.68914285714277       33.530377367859245

I am trying to parse & load this into a structure like the following:

struct Stax { std::string stage, axis; };
struct Configuration {
    std::map<std::string, std::string> filenames; // "expt_KF000000.raw": "XCT", "other_file.raw": "OCT"
    std::map<double, Stax> positions;  // 0.0: ["Detect", "R"], 27.639: ["Source", "Z"]
};
struct Iteration {
    std::string name;
    std::string descript;
    std::vector<Configuration> configs;
};

Each line after the one containing "COLUMNS" should generate a new Configuration instance to be added to Iteration.configs.

As implied by the comments in the code, some columns may be missing ("fname.OCT" in this example)... but the order of the columns may vary (as will the number) so I've tried using local rule state to hold a variable number of parsers, one for each column, as suggested here: https://stackoverflow.com/a/31382602/1206102

The elements of Configuration.positions are composed of info following the "posn." header string plus any content parsed from the cell itself. Likewise, elements of Configuration.filenames are composed of whatever follows "fname." in the header plus the cell content. So I have to somehow save the info I parse from the header row in order to use later when the cells are parsed... so I store this alongside the cell parsers in the rule local state var.

I've tried all manner of things from rule locals to Phoenix-wrapped data members, from direct attribute propagation to Fusion-adapted structs & "customization points", even attr_cast's with transform_attribute specializations... but nothing has successfully compiled yet. My list of attempts follows:

  1. https://wandbox.org/permlink/fU1WOazzxOVPbh9n
  2. https://wandbox.org/permlink/Itlq3HfydUByUSaH
  3. https://wandbox.org/permlink/96yDzVpCpI0K4ujs
  4. https://wandbox.org/permlink/u8iWv61jJSQv01bU

My last attempt still produces 11 compile-time errors, the 1st of which ('const_iterator': is not a member of 'Configuration') is telling: something (Qi/Fusion/Phoenix) is expecting Configuration to be iterable... probably because the parsed type that populates it is (a vector of tuples).

That may be the crux of my problem: I'm trying to stuff a parsed iterable into a non-iterable* complex struct: some of the tuples in the parsed vector will go into one data member of the struct, while others will go into a different data member. (*I say non-iterable because it makes no sense to iterate on Configuration but if Qi/Fusion/Phoenix insists I could try to accomodate. In fact you'll see a [commented out] attempt to define the const_iterator type for Configuration.)

I want to accomplish this using Boost.Spirit if at all possible so... any ideas what I'm doing wrong?

textral
  • 1,029
  • 8
  • 13

1 Answers1

1
  1. &cell_stage does not take an address of cell_stage because operator& is overloaded. Use boost/std::addressof to obtain the address.
  2. qi::rule<Iterator, Skipper, double()>* cannot be converted to qi::rule<Iterator, Skipper, boost::variant<std::string, double, bool>>*
  3. In qi::eps[_offset = -1, px::ref(_ncols) = px::size(_tstore)] you just assign -1 to _offset at initialization, it has to be px::ref(_offset) = -1
  4. The qi::repeat(_ncols) will repeat not _ncols times, but the number that _ncols contained at the rule creation time, which is zero.
  5. In qi::eps[_offset = ++_offset % _ncols], like the in previous ones, you do not create a Phoenix lambda, but just something like qi::eps[0]. It has to be qi::eps[px::ref(_offset) = ++px::ref(_offset) % px::cref(_ncols)].
  6. I did not dig what qi::attr(_tstore[_offset]) >> *_pstore[_offset] should be doing, but it is clearly not what you actually meant, probably qi::lazy(px::cref(_tstore)[_offset]) >> qi::lazy(px::cref(_pstore)[_offset]).

At this point I has to stop and ask: Do you really want to make this working?

Do not use Spirit as a Birmingham screwdriver. You are not forced to make all the things in a single Spirit parser. For example, you can parse rows in a usual for-loop and it will be much simpler to write and maintain.

Nikita Kniazev
  • 3,728
  • 2
  • 16
  • 30
  • Thanks Nikita, but #1 and #4 don't cause problems in one of my earlier working examples (http://coliru.stacked-crooked.com/a/2116fc892b9ecaf2) but maybe I broke them by using data members instead of locals. And point taken about the best tool for the job, but I've seen Spirit successfully hammer some pretty complicated nails. – textral May 15 '19 at 04:08
  • Trivia: I think `qi::repeat(px::ref(_ncols))` is supported and does what OP seemingly expected – sehe May 15 '19 at 22:20