2013-08-25 61 views
3

在解析過程中,我只需要在語義動作中設置一些屬性(因爲它們是從正在解析的數據派生的,並且我想避免使用global變量和依賴BOOST_FUSION_ADAPT_STRUCT以及我的代碼應該是通用的,以便我可以重用它的多種類型)。如果我使用多個傳入qi::phrase_parse的變量,我會得到非常長的編譯錯誤列表。我需要幫助嚴重:-)從傳遞到qi :: phrase_parse的表達式的語義動作中設置屬性

#define BOOST_RESULT_OF_USE_DECLTYPE 
#define BOOST_SPIRIT_USE_PHOENIX_V3 

#include <boost/function.hpp> 
#include <boost/phoenix/fusion.hpp> 
#include <boost/spirit/include/phoenix.hpp> 
#include <boost/spirit/include/qi.hpp> 

#include <iostream> 
#include <string> 
#include <climits> 

namespace qi = boost::spirit::qi; 
namespace ph = boost::phoenix; 
namespace ascii = boost::spirit::ascii; 

int main(int argc, char**argv) 
{ 
    bool rc; 
    std::string input(""); 

    //Test case 1 works fine 
    { 
     auto iter(input.begin()); 
     auto last(input.end()); 
     int val1=33; 
     rc = qi::phrase_parse(iter, last, qi::eps[ qi::_val=11 ] , 
        ascii::space, val1) && iter==last; 
     if(rc) 
      std::cout << "val1=" << val1 << std::endl; 
    } 
    //Test case 2 does not compile 
    { 
     auto iter(input.begin()); 
     auto last(input.end()); 
     int val1=33; 
     int val2=0; 
     rc = qi::phrase_parse(iter, last, 
       qi::eps[ ph::at_c<0>(qi::_val)=1,ph::at_c<1>(qi::_val)=2 ], 
       ascii::space, val1,val2) && iter==last; 
     if(rc) 
      std::cout << "val1=" << val1 << 
         " val2=" << val2 << std::endl;   
    } 
    //Test case 3 works fine 
    { 
     auto iter(input.begin()); 
     auto last(input.end()); 
     int val1=33; 
     int val2=0; 
     rc = qi::phrase_parse(iter, last, 
       qi::attr(1)>>qi::attr(2), 
       ascii::space, val1,val2) && iter==last; 
     if(rc) 
      std::cout << "val1=" << val1 << 
         " val2=" << val2 << std::endl; 
    } 

    return 0; 
} 

我把「定製」 my_phrase_parse從cv_and_he但它打破的全面測試案例彙編,我想獲得運行:

template<typename T,typename R> 
void testParser(R rule) 
{ 
    for (const auto input : std::vector<std::string>{ "5 1.0 2.0 3.0 4.0 5.0", "1 1.0", "0" , "", "2 3 ab" }) 
    { 
     bool rc; 
     T maxValue; 
     T minValue; 
     auto iter(input.begin()); 
     auto last(input.end()); 
     std::vector<T> v; 
     rc = my_phrase_parse(iter, last, 
      qi::eps[ 
        ph::at_c<0>(qi::_val)=std::numeric_limits<T>::max(), 
        ph::at_c<1>(qi::_val)=std::numeric_limits<T>::min() 
        ] 
      >> -(qi::omit[ qi::int_] 
      >> *rule[ ph::if_(ph::at_c<0>(qi::_val)>qi::_1)[ ph::at_c<0>(qi::_val)=qi::_1 ], 
         ph::if_(ph::at_c<1>(qi::_val)<qi::_1)[ ph::at_c<1>(qi::_val)=qi::_1 ] 
        ]) 
       ,ascii::space, minValue, maxValue,v) && iter==last; 
     std::cout << (rc ? "ok :`" : "err:`") << input << "` -> "; 
     if(rc) 
     { 
      std::cout << "min=" << minValue << " max=" << maxValue << "\t"; 
      std::copy(v.begin(), v.end(), std::ostream_iterator<T>(std::cout," ")); 
     } 
     else 
      std::cout << *iter; 
     std::cout << std::endl; 
    } 
} 
... 
    testParser<double>(qi::double_); 
+1

[升壓1.47.0之前,你的第一個例子是行不通的(HTTP:// boost-spirit.com/home/2011/02/26/using-_val-in-top-level-semantic-actions/)。在那個版本中,一個修復被應用於「正常」的'parse'和'phrase_parse',但顯然不適用於「可變參數」。如果創建了一個規則,如:qi :: rule (),ascii :: space_type> parser = qi :: eps [ph :: at_c <0>(qi :: _val)= 1 ,ph :: at_c <1>(qi :: _val)= 2];'它會起作用。 – llonesmiz

+0

@cv_and_he嗨,感謝您的反饋。我正在使用1_52。如果單個變量的情況不起作用,我不會問這個問題,因爲它會很明顯:-),因爲我可以避免使用attr的'測試用例3'生成值的問題。 –

+0

我知道它的工作原理。如果你閱讀這個鏈接,你會發現它唯一的原因是因爲它只改變了'parse'和'phrase_parse'的唯一屬性。 – llonesmiz

回答

1

Phoenix placeholders that Spirit uses允許你可以操縱規則上下文中的重要信息。當您在撥打parsephrase_parse的表達式中直接使用它們時,沒有規則,因此沒有上下文。在版本Boost 1.47.0之前沒有工作,爲了使這種行爲保持一致,一個修復程序被應用到這些功能的單一參數版本,但顯然不適用於可變參數版本。

避免此問題的一種方法是創建一條規則,該規則的屬性爲fusion::vector,其中引用了您在致電phrase_parse時使用的類型。

編輯:刪除「my_phrase_parse」,因爲我不相信這是正確的

#define BOOST_RESULT_OF_USE_DECLTYPE 
#define BOOST_SPIRIT_USE_PHOENIX_V3 

#include <boost/function.hpp> 
#include <boost/phoenix/fusion.hpp> 
#include <boost/spirit/include/phoenix.hpp> 
#include <boost/spirit/include/qi.hpp> 

#include <iostream> 
#include <string> 

namespace qi = boost::spirit::qi; 
namespace ph = boost::phoenix; 
namespace ascii = boost::spirit::ascii; 
namespace fusion = boost::fusion; 

int main(int argc, char**argv) 
{ 
    bool rc; 
    std::string input(""); 

    //Test case works fine 
    { 
     auto iter(input.begin()); 
     auto last(input.end()); 
     int val1=33; 
     rc = qi::phrase_parse(iter, last, qi::eps[ qi::_val=11 ] , 
        ascii::space, val1) && iter==last; 
     if(rc) 
      std::cout << "val1=" << val1 << std::endl; 
    } 
    //You can use a rule 
    { 
     auto iter(input.begin()); 
     auto last(input.end()); 
     int val1=33; 
     int val2=0; 

     qi::rule<decltype(iter),fusion::vector<int&, int&>(),ascii::space_type> parser=qi::eps[ ph::at_c<0>(qi::_val)=1,ph::at_c<1>(qi::_val)=2 ]; 

     rc = qi::phrase_parse(iter, last, 
       parser, 
       ascii::space, val1,val2) && iter==last; 
     if(rc) 
      std::cout << "val1=" << val1 << 
         " val2=" << val2 << std::endl;  
    } 

    return 0; 
} 

編輯2:增加了另一種方式來接近你想在你的編輯來解決這個問題

#define BOOST_RESULT_OF_USE_DECLTYPE 
#define BOOST_SPIRIT_USE_PHOENIX_V3 

#include <boost/spirit/include/qi_core.hpp> 
#include <boost/spirit/include/qi_omit.hpp> 

#include <iostream> 
#include <string> 
#include <climits> 

namespace qi = boost::spirit::qi; 
namespace ascii = boost::spirit::ascii; 

template <typename T> 
struct min_max_set 
{ 
    min_max_set():min(std::numeric_limits<T>::max()),max(std::numeric_limits<T>::min()),set(){} 
    T min; 
    T max; 
    std::vector<T> set; 
}; 

namespace boost{ namespace spirit { namespace traits 
{ 
    template <typename T> 
    struct is_container<min_max_set<T>> 
     : boost::mpl::true_ 
    {}; 

    template <typename T> 
    struct container_value<min_max_set<T>> 
    { 
     typedef T type; 
    }; 

    template <typename T> 
    struct push_back_container<min_max_set<T>,T> 
    { 
     static bool call(min_max_set<T>& cont, const T& val) 
     { 
      if(cont.min>val) 
       cont.min=val; 
      if(cont.max<val) 
       cont.max=val; 
      cont.set.push_back(val); 
      return true; 
     } 
    }; 

}}} 

template<typename T,typename R> 
void testParser(R rule) 
{ 
    for (const auto input : std::vector<std::string>{ "5 1.0 2.0 3.0 4.0 5.0", "1 1.0", "0" , "", "2 3 ab" }) 
    { 
     bool rc; 

     auto iter(input.begin()); 
     auto last(input.end()); 
     min_max_set<T> accum; 

     rc = qi::phrase_parse(iter, last, 
       qi::omit[qi::int_] >> *rule 
       ,ascii::space, accum) && iter==last; 
     std::cout << (rc ? "ok :`" : "err:`") << input << "` -> "; 
     if(rc) 
     { 
      std::cout << "min=" << accum.min << " max=" << accum.max << "\t"; 
      std::copy(accum.set.begin(), accum.set.end(), std::ostream_iterator<T>(std::cout," ")); 
     } 
     else 
      std::cout << *iter; 
     std::cout << std::endl; 
    } 
} 


int main(int argc, char**argv) 
{ 
    testParser<double>(qi::double_); 
    return 0; 
} 
+0

感謝您提供'詳細'的回覆。它讓我從星期天下午的沮喪中解救出來:-) –

+0

不要相信'my_phrase_parse'是100%正確的,因爲我基本上不知道自己在做什麼。 「這似乎工作」是我能做的唯一保證。 – llonesmiz

+0

你介意如果我延長這個問題?我採用了你的代碼,但是如果我添加一些規則到應該自動合成attrs的eps表達式中,它不會再次編譯:-(或者我應該打開一個新問題嗎? –

0

我希望我能正確地看到問題,但即使情況稍有不同,您也可以不加任何麻煩地存儲多個值:

struct data { 
    int _a; 
    int _b; 
    data(int a, int b) : _a(a), _b(b) {}; 
}; 

,構建它:

qi::eps[qi::_val = phx::construct<data>(1, 2)] 
+0

我遇到的問題是使用了qi :: phrase_parse。如果你檢查三個簡單的測試用例我的問題......我感覺像語義行爲應該有工作! :-)但他們沒有。 'cv_and_he'的信息顯示它不應該像我預期的那樣工作。他嘗試了一些「詭計」,但這並不夠普遍,這一系列的實驗已經幫助我理解它是死衚衕的。我能夠使用qi :: grammar和繼承的屬性找到解決方案,我可以在語義規則中使用它。 –

1

這是workaround我最初的問題:

#define BOOST_RESULT_OF_USE_DECLTYPE 
#define BOOST_SPIRIT_USE_PHOENIX_V3 

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

#include <iostream> 
#include <algorithm> 
#include <iterator> 
#include <string> 
#include <climits> 

namespace qi = boost::spirit::qi; 
namespace ph = boost::phoenix; 
namespace ascii = boost::spirit::ascii; 

template<typename T,typename R> 
void testParser(R rule) 
{ 
    for (const auto &input : std::vector<std::string>{ "5 1.0 2.0 3.0 4.0 5.0" }) 
    { 
     bool rc=false; 
     T maxValue, minValue; 
     auto iter(input.begin()), last(input.end()); 
     std::vector<T> data; 

     qi::rule< decltype(iter),std::vector<T>(T&, T&),qi::space_type > mrule; 

     mrule %= 
      qi::eps[ qi::_r1=std::numeric_limits<T>::max(), 
        qi::_r2=std::numeric_limits<T>::min() ] >> 
      -(qi::omit[ qi::int_[ ph::reserve(qi::_val,qi::_1) ] ] 
       >> *rule[ ph::if_(qi::_r1>qi::_1)[ qi::_r1=qi::_1 ], 
          ph::if_(qi::_r2<qi::_1)[ qi::_r2=qi::_1 ] 
         ]); 

     rc = qi::phrase_parse(iter, last, mrule(ph::ref(minValue), ph::ref(maxValue)), qi::space, data) && iter==last; 

    std::cout << (rc ? "ok :`" : "err:`") << input << "` -> "; 
    if(rc) 
    { 
     std::cout << "min=" << minValue << " max=" << maxValue << "\t"; 
     std::copy(data.begin(), data.end(), std::ostream_iterator<T>(std::cout," ")); 
     } 
     else 
      std::cout << *iter; 
     std::cout << std::endl; 
    } 
} 

int main(int argc, char**argv) 
{ 
    testParser<double>(qi::double_); 
    return 0; 
} 
+2

as sehe does [here](http://stackoverflow.com/a/18391326/2417774),您可以輕鬆地將'qi :: _ r1'和'qi :: _ r2'重命名爲'min'和'max'分別幫助規則的可讀性通過使用:'qi :: _ r1_type min; qi :: _ r2_type max; mrule%= qi :: eps [min = std :: numeric_limits :: max(),max = std :: numeric_limits :: min()] >> - (qi :: omit [qi :: int_ [ph: :保留(qi :: _val,qi :: _1)]] >> * rule [ph :: if_(min> qi :: _1)[min = qi :: _ 1],ph :: if_(max llonesmiz

+0

@cv_and_he代碼可讀性很好。 –

相關問題