I have created a test application to illustrate my problem. It parses a list of integers preceded by "a=" or "b=" and is separated by "\r\n". The list contains multiple occurrences of those fields in any order.
#include <string>
#include <vector>
#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
typedef std::vector<unsigned int> uint_vector_t;
std::ostream& operator<<(std::ostream& out, const uint_vector_t &data)
{
for (unsigned int i(0); i < data.size(); i++)
{
out << data[i] << '\n';
}
return out;
}
struct MyStruct
{
uint_vector_t m_aList;
uint_vector_t m_bList;
};
BOOST_FUSION_ADAPT_STRUCT
(
MyStruct,
(uint_vector_t, m_aList)
(uint_vector_t, m_bList)
)
;
template<typename Iterator>
struct MyParser : public boost::spirit::qi::grammar<Iterator,
MyStruct()>
{
MyParser() :
MyParser::base_type(Parser, "Parser")
{
using boost::spirit::qi::uint_;
using boost::spirit::qi::_val;
using boost::spirit::qi::_1;
using boost::phoenix::at_c;
using boost::phoenix::push_back;
Parser =
*(
aParser [push_back(at_c<0>(_val), _1)]
|
bParser [push_back(at_c<1>(_val), _1)]
);
aParser = "a=" >> uint_ >> "\r\n";
bParser = "b=" >> uint_ >> "\r\n";
}
boost::spirit::qi::rule<Iterator, MyStruct()> Parser;
boost::spirit::qi::rule<Iterator, unsigned int()> aParser, bParser;
};
int main()
{
using boost::spirit::qi::phrase_parse;
std::string input("a=0\r\nb=7531\r\na=2\r\na=3\r\nb=246\r\n");
std::string::const_iterator begin = input.begin();
std::string::const_iterator end = input.end();
MyParser<std::string::const_iterator> parser;
MyStruct result;
bool succes = phrase_parse(begin, end, parser, "", result);
assert(succes);
std::cout << "===A===\n" <<result.m_aList << "===B===\n" << result.m_bList << std::endl;
}
In practice there are more fields with different types which need to be parsed. My objection with this approach lies in the following expression: [push_back(at_c<0>(_val), _1)] Here is a 'hidden dependency' between the assignment and the first element of MyStruct. This makes the code fragile to changes. If the struct is changed it might still compile, but no longer do what is expected.
I'm hoping for a construction like: [push_back(at_c<0>bind(&MyStruct::aList, arg1)(_val), _1)] See this. So that it is really bound by name.
Is something like this possible? Or should I take a total different approach?