2010-07-20 23 views
3

J'ai créé une application de test pour illustrer mon problème. Il analyse une liste d'entiers précédés de "a =" ou "b =" et est séparé par "\ r \ n". La liste contient plusieurs occurrences de ces champs dans n'importe quel ordre.Y at-il une alternative pour boost :: phoenix :: at_c en combinaison avec boost :: spirit :: qi :: grammar

#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; 
} 

En pratique, il y a plus de champs avec différents types qui doivent être analysés. Mon objection avec cette approche se trouve dans l'expression suivante: [push_back (at_c < 0> (_ val), _1)] Voici une 'dépendance cachée' entre l'assignation et le premier élément de MyStruct. Cela rend le code fragile aux changements. Si la structure est modifiée, elle peut toujours être compilée, mais ne plus faire ce qui est attendu.

J'espère que pour une construction comme: [push_back (at_c < 0> bind (& MyStruct :: aList, arg1) (_ val), _1)] Voir this. Alors qu'il est vraiment lié par son nom.

Est-ce quelque chose comme cela possible? Ou devrais-je adopter une approche totalement différente?

Répondre

7

Phoenix vous permet de lier les membres de données aussi bien, de sorte que vous pouvez écrire:

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

De plus, dans ce cas, vous n'avez pas besoin FUSION_ADAPT magique pour votre structure de plus.