2013-08-10 8 views
6

Ich versuche etwas mit der Qi-Bibliothek von boost.spirit zu analysieren, und ich stoße auf ein Problem. Nach der spirit docs sollte a >> b etwas mit dem Typ tuple<A, B> produzieren. Aber das ist ein boost::tuple (aka Fusion-Vektor), und nicht ein std::tuple (was ich will).boost :: spirit :: qi stl-container verwenden

Gibt es eine einfache Möglichkeit, diese Konvertierung zwischen boost::tuple =>std::tuple vorzunehmen?

Die gleiche Dokumentationsseite besagt, dass *a etwas mit dem Typ vector<A> produzieren sollte. Dies scheint eine std::vector<A> (oder eine Art boost::vector<A>, die implizit in eine std::vector<A> konvertieren kann) zu produzieren. Ich wollte nur wissen, ob das gleiche Verhalten für Tupel verfügbar war.

+0

Die Tags Fusion und Qi sind nicht auf Boost.Fusion und Boost.Spirit bezogen. – llonesmiz

+0

hoppla, lassen Sie mich das beheben –

Antwort

13

kurze Antwort:

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

vollständigere Antwort:

Wie Sie here sehen: nur

In den Attributtabellen, wir vector<A> und tuple<A, B...> als Platzhalter verwenden. Die Notation vector<A> steht für alle STL-Containerhalteelemente vom Typ A und die Notation tuple<A, B...> steht für jede Boost.Fusion-Sequenz, die Elemente A, B, ... usw. enthält. Schließlich steht Unused für unused_type.

Also, wenn ein Parser/Generator hat ein Attribut von tuple<A,B...> Sie eine Fusionssequenz verwenden können (wie zum Beispiel Fusion :: vector oder Fusion :: Liste) oder alles, was zu einer Fusionssequenz angepasst werden kann (wie boost :: array, boost :: tuple, std :: pair, std :: tuple, Ihre eigene Struktur mit BOOST_FUSION_ADAPT_STRUCT). Wenn Sie vector<A> haben, können Sie std :: vector, std :: list und sogar std :: map verwenden, wenn Ihre Elemente Paare sind. Sie können auch Ihre eigene Struktur verwenden, wenn Sie auch mehrere Anpassungspunkte spezialisieren (mindestens is_container, container_value und push_back_container in boost :: spirit :: traits).

std :: pair
Um in der Lage sein std::pair mit Geist zu verwenden, brauchen Sie nur einen einzigen Header hinzuzufügen:

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

std :: tuple
mit Boost Ab 1.48 .0 Sie können das gleiche tun für 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_; 

Ihre eigene Struktur
Sie können Ihre benutzerdefinierten Struktur sehr leicht mit Hilfe von BOOST_FUSION_ADAPT_STRUCT anpassen:

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

Es gibt eine Wenn Sie versuchen, eine Struktur zu verwenden, die über ein einzelnes Element verfügt, bei dem es sich auch um eine Containerkompilierung handelt, schlägt dies jedoch zu, es sei denn, Sie fügen Ihrer Regel qi::eps >> ... hinzu.

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
Sie können einfach std :: map als Container von std :: Paare verwenden. Beachten Sie aber, dass, wenn es Schlüssel in Ihrer Eingabe wiederholt werden, wird nur die erste wird auf der Karte eingefügt werden (wenn Sie multimap alles verwenden natürlich eingefügt werden):

#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(','); 

Ihre eigene Struktur als container
Mit spirit's customization points können Sie auch Ihre Struktur so verhalten, als wäre sie ein Container, wenn es um Attribute geht. Das Minimum, das Sie spezialisieren müssen, sind is_container, container_value und push_back_container. Hier sind ein paar Beispiele:


Die erste ist ziemlich einfach (und albern). Dadurch erhält Ihre Struktur ein Attribut, das mit std::vector<int> kompatibel ist. Jedes Mal, wenn ein int geparst wird, wird es zur Summe im Akkumulator addiert. Sie finden weniger alberne Ansätze here und here (in der "alten Antwort").

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(','); 

Das zweite ist ein wenig komplexer (nicht viel). Dadurch erhält Ihre Struktur ein Attribut, das mit std::vector<boost::variant<int,std::string> > kompatibel ist. Wenn ein Int geparst wird, wird er zum Container ints im Verteiler hinzugefügt, und Zeichenketten werden im Container strings gespeichert. Beispiele, die dies verwenden (1, 2 und 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(','); 

Alle Tests in einer einzigen CPP-Datei

#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

Und Favorit für zukünftige Attribute Propagation Fragen – sehe

+0

Danke, das war wirklich gründlich. Markiert! –

Verwandte Themen