2013-08-10 36 views
6

Tôi đang cố gắng phân tích cú pháp gì đó với thư viện qi của boost.spirit và tôi đang gặp sự cố. Theo số spirit docs, a >> b sẽ tạo ra thứ gì đó với loại tuple<A, B>. Nhưng đây là boost::tuple (còn gọi là vector tổng hợp), và không phải là std::tuple (mà tôi muốn).Tăng cường :: tinh thần :: qi để sử dụng các thùng chứa stl

Có cách nào dễ dàng để thực hiện chuyển đổi này giữa boost::tuple =>std::tuple không?

Trang tài liệu tương tự cho biết rằng *a sẽ tạo một thứ gì đó với loại vector<A>. Điều này dường như đang tạo ra một std::vector<A> (hoặc một số loại boost::vector<A> có thể chuyển đổi hoàn toàn thành std::vector<A>). Tôi chỉ muốn biết liệu hành vi tương tự này có sẵn cho các bộ dữ liệu không.

+0

Các thẻ fusion và khí không liên quan đến Boost.Fusion và Boost.Spirit. – llonesmiz

+0

Rất tiếc, hãy để tôi sửa chữa –

Trả lời

13

trả lời ngắn gọn:

Sử dụng #include <boost/fusion/adapted/std_tuple.hpp>.

THÊM ĐẦY ĐỦ ĐÁP:

Như bạn thấy here:

Trong bảng thuộc tính, chúng tôi sẽ sử dụng vector<A>tuple<A, B...> như chỉ placeholders. Ký hiệu của vector<A> là viết tắt của bất kỳ phần tử giữ STL nào của loại A và ký hiệu tuple<A, B...> là viết tắt của bất kỳ chuỗi Boost.Fusion nào giữ các thành phần A, B, ... v.v. Cuối cùng, không sử dụng là viết tắt của unused_type.

Vì vậy, khi một phân tích cú pháp/máy phát điện có một thuộc tính của tuple<A,B...> bạn có thể sử dụng bất kỳ chuỗi phản ứng tổng hợp (chẳng hạn như phản ứng tổng hợp :: vector hoặc fusion :: list) hoặc bất cứ điều gì có thể được thích nghi với một chuỗi phản ứng tổng hợp (chẳng hạn như tăng :: mảng, tăng :: tuple, std :: pair, std :: tuple, cấu trúc của riêng bạn sử dụng BOOST_FUSION_ADAPT_STRUCT).

Và khi nó có vector<A> bạn có thể sử dụng tiêu chuẩn :: vector, std :: list và thậm chí std :: map nếu các phần tử của bạn là cặp. Bạn cũng có thể sử dụng cấu trúc của riêng mình nếu bạn cũng chuyên một số điểm tùy chỉnh (ít nhất là is_container, container_value và push_back_container trong boost :: spirit :: traits).

std :: cặp
Để có thể sử dụng std::pair với tinh thần bạn chỉ cần thêm một tiêu đề duy nhất:

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

std :: tuple
Bắt đầu với tăng 1,48 .0 bạn có thể làm tương tự cho 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_; 

struct riêng của bạn
Bạn có thể thích ứng struct tùy chỉnh của bạn rất dễ dàng với sự giúp đỡ của 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ó một hạn chế được biết đến mặc dù, khi bạn cố gắng sử dụng một cấu trúc có một phần tử duy nhất cũng là một trình biên dịch vùng chứa không thành công trừ khi bạn thêm qi::eps >> ... vào quy tắc của bạn.

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
Bạn chỉ có thể sử dụng std :: bản đồ như một container của std :: cặp. Cũng nên lưu ý rằng nếu có được lặp lại phím trong đầu vào của bạn, chỉ có người đầu tiên sẽ được chèn vào bản đồ (nếu bạn sử dụng tất cả mọi thứ Multimap sẽ được chèn tất nhiên):

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

struct riêng của bạn như một vùng chứa
Sử dụng tinh thần customization points bạn cũng có thể làm cho cấu trúc của bạn hoạt động như thể nó là một vùng chứa khi xử lý các thuộc tính. Số tiền tối thiểu bạn cần để chuyên là is_container, container_valuepush_back_container. Dưới đây là một vài ví dụ:


Điều đầu tiên khá đơn giản (và ngớ ngẩn). Nó làm cho cấu trúc của bạn có thuộc tính tương thích với std::vector<int>. Mỗi khi một int được phân tích cú pháp nó được thêm vào tổng số trong bộ tích lũy. Bạn có thể tìm cách tiếp cận ít ngớ ngẩn hơn herehere (trong "câu trả lời cũ").

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

Điều thứ hai là một chút phức tạp hơn (không nhiều). Nó làm cho cấu trúc của bạn có thuộc tính tương thích với std::vector<boost::variant<int,std::string> >. Khi một int được phân tích cú pháp nó được thêm vào thùng chứa ints trong nhà phân phối, các chuỗi tương tự được lưu trữ trong vùng chứa strings. Ví dụ sử dụng thông tin này (1, 23).

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

Tất cả các bài kiểm tra trong một file cpp đơn

#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

Và yêu thích cho các câu hỏi tuyên truyền thuộc tính trong tương lai – sehe

+0

Cảm ơn, điều đó thực sự kỹ lưỡng. Đã đánh dấu trang! –

Các vấn đề liên quan