2013-08-10 12 views
6

Sto cercando di analizzare qualcosa con la libreria qi di boost.spirit e sto riscontrando un problema. Secondo lo spirit docs, a >> b dovrebbe produrre qualcosa con il tipo tuple<A, B>. Ma questo è un boost::tuple (alias vettore di fusione), e non uno std::tuple (che voglio).Ottenere boost :: spirit :: qi per utilizzare i contenitori stl

C'è un modo semplice per effettuare questa conversione tra boost::tuple =>std::tuple?

La stessa pagina di documentazione dice che *a dovrebbe produrre qualcosa con il tipo vector<A>. Questo sembra produrre un std::vector<A> (o un qualche tipo di boost::vector<A> che può convertire implicitamente in un std::vector<A>). Volevo solo sapere se questo stesso comportamento fosse disponibile per le tuple.

+0

Il tag di fusione e qi non sono collegati a Boost.Fusion e Boost.Spirit. – llonesmiz

+0

whoops, fammi correggere quello –

risposta

13

BREVE RISPOSTA:

Usa #include <boost/fusion/adapted/std_tuple.hpp>.

risposta più completa:

Come si può vedere here:

Nelle tabelle di attributi, useremo vector<A> e tuple<A, B...> come solo segnaposto. La notazione di vector<A> corrisponde a qualsiasi contenitore di tipo STL contenente elementi di tipo A e la notazione tuple<A, B...> corrisponde a qualsiasi sequenza Boost.Fusion che contiene elementi A, B, ... ecc. Infine, Unused sta per unused_type.

Così, quando un parser/generatore ha un attributo di tuple<A,B...> è possibile utilizzare qualsiasi sequenza di fusione (come la fusione :: vector o fusione :: lista) o qualsiasi cosa che può essere adattato ad una sequenza di fusione (come spinta :: array, boost :: tuple, std :: pair, std :: tuple, la propria struct che utilizza BOOST_FUSION_ADAPT_STRUCT).

E quando dispone di vector<A> è possibile utilizzare std :: vector, std :: list e anche std :: map se i propri elementi sono coppie. Puoi anche usare la tua struct se anche specializzi diversi punti di personalizzazione (almeno is_container, container_value e push_back_container in boost :: spirit :: traits).

std :: coppia
Al fine di essere in grado di utilizzare std::pair con lo spirito è sufficiente aggiungere un singolo colpo di testa:

#include <boost/fusion/include/std_pair.hpp> 
... 
qi::rule<Iterator,std::pair<int,double>()> rule = 
    qi::int_ >> qi::lit(',') >> qi::double_; 

std :: tuple
partire con boost 1.48 .0 si può fare lo stesso per std :: tuple:

#include <boost/fusion/adapted/std_tuple.hpp> 
... 
qi::rule<Iterator,std::tuple<int,std::string,double>()> rule = 
    qi::int_ >> qi::lit(',') >> +~qi::char_(',') >> qi::lit(',') >> qi::double_; 

La tua struct
È possibile adattare lo struct personalizzato molto facilmente con l'aiuto di BOOST_FUSION_ADAPT_STRUCT:

#include <boost/fusion/include/adapt_struct.hpp> 
... 
struct normal_struct 
{ 
    int integer; 
    double real; 
}; 

BOOST_FUSION_ADAPT_STRUCT(
    normal_struct, 
    (int, integer) 
    (double, real) 
) 
... 
qi::rule<Iterator,normal_struct()> rule = 
    qi::int_ >> qi::lit(',') >> qi::double_; 

C'è uno tuttavia, la limitazione nota, quando si tenta di utilizzare una struct con un singolo elemento che è anche una compilazione del contenitore, non riesce a meno che non si aggiunga qi::eps >> ... alla regola.

struct struct_with_single_element_container 
{ 
    std::vector<int> cont; 
}; 

BOOST_FUSION_ADAPT_STRUCT(
    struct_with_single_element_container, 
    (std::vector<int>, cont) 
) 
... 
qi::rule<Iterator,struct_with_single_element_container()> rule = 
    qi::eps >> qi::int_%qi::lit(','); 

std :: map
si può semplicemente utilizzare std :: map come contenitore di std :: coppie. Occorre tuttavia tenere presente che se ci sono ripetuti chiavi in ​​ingresso, solo il primo sarà inserito alla mappa (se si utilizza multimap tutto verrà inserito ovviamente):

#include <boost/fusion/include/std_pair.hpp> 
... 
qi::rule<std::string::const_iterator, std::pair<double,int>()> pair_rule = 
    qi::double_ >> qi::lit('=') >> qi::int_; 
qi::rule<std::string::const_iterator, std::map<double,int>()> rule = 
    pair_rule%qi::lit(','); 
//You can also use 
//qi::rule<std::string::const_iterator, std::map<double,int>()> rule = 
    //(qi::double_ >> qi::lit('=') >> qi::int_)%qi::lit(','); 

Il proprio struct come container
Usando lo spirito customization points si può anche fare in modo che la struct si comporti come se fosse un contenitore quando si tratta di attributi. Il minimo che devi specializzare sono is_container, container_value e push_back_container. Ecco un paio di esempi:


Il primo è piuttosto semplice (e sciocco). Rende la tua struct ha un attributo compatibile con std::vector<int>. Ogni volta che un int viene analizzato, viene aggiunto al totale nell'accumulatore. È possibile trovare approcci meno stupidi here e here (nella "risposta precedente").

struct accumulator 
{ 
    accumulator(): total(){} 
    int total; 
}; 

namespace boost{ namespace spirit{ namespace traits 
{ 
    template<> 
    struct is_container<accumulator> : boost::mpl::true_ 
    {}; 

    template<> 
    struct container_value<accumulator> 
    { 
     typedef int type; 
    }; 

    template<> 
    struct push_back_container<accumulator,int> 
    { 
     static bool call(accumulator& c, int val) 
     { 
      c.total+=val; 
      return true; 
     } 
    }; 
}}} 
... 
qi::rule<Iterator,accumulator()> rule = 
    qi::int_%qi::lit(','); 

Il secondo è un po 'più complesso (non molto). Rende la tua struct ha un attributo compatibile con std::vector<boost::variant<int,std::string> >. Quando un int viene analizzato, viene aggiunto al contenitore ints nel distributore, analogamente le stringhe vengono memorizzate nel contenitore strings. Esempi che utilizzano questo (1, 2 e 3).

struct distributor 
{ 
    distributor():ints(),strings(){} 
    std::vector<int> ints; 
    std::vector<std::string> strings; 
}; 

namespace boost{ namespace spirit{ namespace traits 
{ 
    template<> 
    struct is_container<distributor> : boost::mpl::true_ 
    {}; 

    template<> 
    struct container_value<distributor> 
    { 
     typedef boost::variant<int,std::string> type; 
    }; 

    template<> 
    struct push_back_container<distributor,int> 
    { 
     static bool call(distributor& c, int val) 
     { 
      c.ints.push_back(val); 
      return true; 
     } 
    }; 

    template<> 
    struct push_back_container<distributor,std::string> 
    { 
     static bool call(distributor& c, std::string const& val) 
     { 
      c.strings.push_back(val); 
      return true; 
     } 
    }; 
}}} 
... 
qi::rule<std::string::const_iterator, std::string()> string_rule = 
    +~qi::char_(','); 
qi::rule<std::string::const_iterator, distributor()> rule = 
    (qi::int_ | string_rule)%qi::lit(','); 

Tutti i test in un unico file cpp

#include <iostream> 
#include <string> 
#include <utility> 
#include <tuple> 
#include <list> 
#include <vector> 
#include <map> 

#include <boost/fusion/include/std_pair.hpp> 
#include <boost/fusion/include/adapt_struct.hpp> 
#include <boost/fusion/adapted/std_tuple.hpp> 

#include <boost/spirit/include/qi.hpp> 

#include <boost/variant.hpp> 

namespace qi=boost::spirit::qi; 

struct normal_struct 
{ 
    int integer; 
    double real; 
}; 

struct struct_with_single_element_container 
{ 
    std::vector<int> cont; 
}; 

BOOST_FUSION_ADAPT_STRUCT(
    normal_struct, 
    (int, integer) 
    (double, real) 
) 

BOOST_FUSION_ADAPT_STRUCT(
    struct_with_single_element_container, 
    (std::vector<int>, cont) 
) 

struct accumulator 
{ 
    accumulator(): total(){} 
    int total; 
}; 

namespace boost{ namespace spirit{ namespace traits 
{ 
    template<> 
    struct is_container<accumulator> : boost::mpl::true_ 
    {}; 

    template<> 
    struct container_value<accumulator> 
    { 
     typedef int type; 
    }; 

    template<> 
    struct push_back_container<accumulator,int> 
    { 
     static bool call(accumulator& c, int val) 
     { 
      c.total+=val; 
      return true; 
     } 
    }; 
}}} 

struct distributor 
{ 
    distributor():ints(),strings(){} 
    std::vector<int> ints; 
    std::vector<std::string> strings; 
}; 

namespace boost{ namespace spirit{ namespace traits 
{ 
    template<> 
    struct is_container<distributor> : boost::mpl::true_ 
    {}; 

    template<> 
    struct container_value<distributor> 
    { 
     typedef boost::variant<int,std::string> type; 
    }; 

    template<> 
    struct push_back_container<distributor,int> 
    { 
     static bool call(distributor& c, int val) 
     { 
      c.ints.push_back(val); 
      return true; 
     } 
    }; 

    template<> 
    struct push_back_container<distributor,std::string> 
    { 
     static bool call(distributor& c, std::string const& val) 
     { 
      c.strings.push_back(val); 
      return true; 
     } 
    }; 
}}} 


int main() 
{ 
    { 
     std::pair<int,double> parsed; 
     qi::rule<std::string::const_iterator, std::pair<int,double>()> rule = 
        qi::int_ >> qi::lit(',') >> qi::double_; 
     std::string test="1,2.5"; 
     std::string::const_iterator iter=test.begin(), end=test.end(); 
     bool result = qi::parse(iter,end,rule,parsed); 
     if(result && iter==end) 
     { 
      std::cout << "Success." << std::endl; 
      std::cout << "First: " << parsed.first << ", Second: " << parsed.second << std::endl; 
     } 
     else 
     { 
      std::cout << "Failure." << std::endl; 
      std::cout << "Unparsed: " << std::string(iter,end) << std::endl; 
     } 
    } 

    { 
     std::tuple<int,std::string,double> parsed; 
     qi::rule<std::string::const_iterator, std::tuple<int,std::string,double>()> rule = 
        qi::int_ >> qi::lit(',') >> +~qi::char_(',') >> qi::lit(',') >> qi::double_; 
     std::string test="1,abc,2.5"; 
     std::string::const_iterator iter=test.begin(), end=test.end(); 
     bool result = qi::parse(iter,end,rule,parsed); 
     if(result && iter==end) 
     { 
      std::cout << "Success." << std::endl; 
      std::cout << "get<0>: " << std::get<0>(parsed) << ", get<1>: " << std::get<1>(parsed) << ", get<2>: " << std::get<2>(parsed) << std::endl; 
     } 
     else 
     { 
      std::cout << "Failure." << std::endl; 
      std::cout << "Unparsed: " << std::string(iter,end) << std::endl; 
     } 
    } 

    { 
     normal_struct parsed; 
     qi::rule<std::string::const_iterator, normal_struct()> rule = 
        qi::int_ >> qi::lit(',') >> qi::double_; 
     std::string test="1,2.5"; 
     std::string::const_iterator iter=test.begin(), end=test.end(); 
     bool result = qi::parse(iter,end,rule,parsed); 
     if(result && iter==end) 
     { 
      std::cout << "Success." << std::endl; 
      std::cout << "integer: " << parsed.integer << ", real: " << parsed.real << std::endl; 
     } 
     else 
     { 
      std::cout << "Failure." << std::endl; 
      std::cout << "Unparsed: " << std::string(iter,end) << std::endl; 
     } 
    } 

    { 
     struct_with_single_element_container parsed; 
     //there is a problem when you have a struct with a single element container, the workaround is simply adding qi::eps to the rule 
     qi::rule<std::string::const_iterator, struct_with_single_element_container()> rule = 
        qi::eps >> qi::int_%qi::lit(','); 
     std::string test="1,2"; 
     std::string::const_iterator iter=test.begin(), end=test.end(); 
     bool result = qi::parse(iter,end,rule,parsed); 
     if(result && iter==end) 
     { 
      std::cout << "Success." << std::endl; 
      std::cout << "[0]: " << parsed.cont[0] << ", [1]: " << parsed.cont[1] << std::endl; 
     } 
     else 
     { 
      std::cout << "Failure." << std::endl; 
      std::cout << "Unparsed: " << std::string(iter,end) << std::endl; 
     } 
    } 

    { 
     std::list<int> parsed; 
     qi::rule<std::string::const_iterator, std::list<int>()> rule = 
        qi::int_%qi::lit(','); 
     std::string test="1,2"; 
     std::string::const_iterator iter=test.begin(), end=test.end(); 
     bool result = qi::parse(iter,end,rule,parsed); 
     if(result && iter==end) 
     { 
      std::cout << "Success." << std::endl; 
      std::cout << "front: " << parsed.front() << ", back: " << parsed.back() << std::endl; 
     } 
     else 
     { 
      std::cout << "Failure." << std::endl; 
      std::cout << "Unparsed: " << std::string(iter,end) << std::endl; 
     } 
    } 

    { 
     std::map<double,int> parsed; 
     qi::rule<std::string::const_iterator, std::pair<double,int>()> pair_rule = 
        qi::double_ >> qi::lit('=') >> qi::int_; 
     qi::rule<std::string::const_iterator, std::map<double,int>()> rule = 
        pair_rule%qi::lit(','); 
     std::string test="2.5=1,3.5=2"; 
     std::string::const_iterator iter=test.begin(), end=test.end(); 
     bool result = qi::parse(iter,end,rule,parsed); 
     if(result && iter==end) 
     { 
      std::cout << "Success." << std::endl; 
      std::cout << "map[2.5]: " << parsed[2.5] << ", map[3.5]: " << parsed[3.5] << std::endl; 
     } 
     else 
     { 
      std::cout << "Failure." << std::endl; 
      std::cout << "Unparsed: " << std::string(iter,end) << std::endl; 
     } 
    } 

    { 
     accumulator parsed; 
     qi::rule<std::string::const_iterator, accumulator()> rule = 
        qi::int_%qi::lit(','); 
     std::string test="1,2,3"; 
     std::string::const_iterator iter=test.begin(), end=test.end(); 
     bool result = qi::parse(iter,end,rule,parsed); 
     if(result && iter==end) 
     { 
      std::cout << "Success." << std::endl; 
      std::cout << "total: " << parsed.total << std::endl; 
     } 
     else 
     { 
      std::cout << "Failure." << std::endl; 
      std::cout << "Unparsed: " << std::string(iter,end) << std::endl; 
     } 
    } 

    { 
     distributor parsed; 
     qi::rule<std::string::const_iterator, std::string()> string_rule = 
        +~qi::char_(','); 
     qi::rule<std::string::const_iterator, distributor()> rule = 
        (qi::int_ | string_rule)%qi::lit(','); 
     std::string test="abc,1,2,def,ghi,3,jkl"; 
     std::string::const_iterator iter=test.begin(), end=test.end(); 
     bool result = qi::parse(iter,end,rule,parsed); 
     if(result && iter==end) 
     { 
      std::cout << "Success." << std::endl; 
      std::cout << "ints" << std::endl; 
      for(auto val: parsed.ints) 
       std::cout << val << std::endl; 
      std::cout << "strings" << std::endl; 
      for(const auto& val: parsed.strings) 
       std::cout << val << std::endl; 
     } 
     else 
     { 
      std::cout << "Failure." << std::endl; 
      std::cout << "Unparsed: " << std::string(iter,end) << std::endl; 
     } 
    } 

} 
+0

E preferito per le future domande di propagazione degli attributi – sehe

+0

Grazie, è stato davvero approfondito. Segnalibro! –

Problemi correlati