Is there an alternative for boost::phoenix::at_c in combination with boost::spirit::qi::grammar

别等时光非礼了梦想. 提交于 2019-12-21 05:31:11

问题


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?


回答1:


Phoenix allows you to bind data members as well, so you can write:

Parser = 
   *(  aParser [push_back(bind(&MyStruct::m_aList, _val), _1)] 
    |  bParser [push_back(bind(&MyStruct::m_bList, _val), _1)] 
    ); 

Moreover, in this case you don't need the FUSION_ADAPT magic for your structure anymore.



来源:https://stackoverflow.com/questions/3290389/is-there-an-alternative-for-boostphoenixat-c-in-combination-with-boostspir

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!