2014-07-04 64 views
2

以下程序是一個人爲示例(從我工作的較大語法中減少)顯示出奇怪的行爲。提升精神,從語義動作返回的值干擾規則屬性

程序運行的輸出是「hello」,不正確。

如果我從quoted_string規則中刪除(在此示例中無用)語義操作,則輸出爲預期的「foo = hello」。

#define BOOST_RESULT_OF_USE_DECLTYPE 
#define BOOST_SPIRIT_USE_PHOENIX_V3 
#include <vector> 
#include <string> 
#include <iostream> 
#include <boost/spirit/include/qi.hpp> 
#include <boost/spirit/include/phoenix.hpp> 
#include "utils.hpp" 

namespace t { 
    using std::vector; 
    using std::string; 
    namespace qi = boost::spirit::qi; 
    namespace phx = boost::phoenix; 

    template <typename Iterator, typename Skipper=qi::space_type> 
    struct G1 : qi::grammar<Iterator, string(), Skipper> { 

     template <typename T> 
     using rule = qi::rule<Iterator, T, Skipper>; 

     qi::rule<Iterator, string(), qi::locals<char>> quoted_string; 
     rule<string()> start; 

     G1() : G1::base_type(start, "G1") { 
      { 
       using qi::_1; 
       using qi::_a; 

       using attr_signature = vector<char>; 
       auto handler = [](attr_signature const& elements) -> string { 
        string output; 
        for(auto& e : elements) { 
         output += e; 
        } 
        return output; 
       }; 
       quoted_string = (qi::omit[qi::char_("'\"")[_a = _1]] 
        >> +(qi::char_ - qi::char_(_a)) 
        >> qi::lit(_a))[qi::_val = phx::bind(handler, _1)]; 
      } 
      start = qi::string("foo") >> -(qi::string("=") >> quoted_string); 
     } 
    }; 

    string parse(string const input) { 
     G1<string::const_iterator> g; 
     string result; 
     phrase_parse(begin(input), end(input), g, qi::standard::space, result); 
     return result; 
    } 
}; 

int main() { 
    using namespace std; 
    auto r = t::parse("foo='hello'"); 
    cout << r << endl; 
} 

我一定能找到一個解決辦法,但我會找出我錯過

+0

我對不起,我不能提供比解決方法更好的東西,但如果它有幫助,將語義操作更改爲'qi :: _ val + = phx:bind(handler,_1)'使其正常工作。看起來''quoted_string'屬性的字符串是對'start'屬性相同的字符串的引用。如果沒有語義動作,'+(qi :: char_ - qi :: char _(_ a))'只是將一系列字符推回到字符串中,並且您得到正確的值。用你原來的語義動作,'start'字符串被'hello'覆蓋。 – llonesmiz

回答

2

像@cv_and_he解釋,你覆蓋與handler(_1)結果的屬性。由於屬性是通過引用傳遞的,因此會丟失原始值。

自動屬性傳播規則知道如何連接「字符串」容器值,那麼爲什麼你不使用默認實現?

quoted_string %= qi::omit[qi::char_("'\"")[_a = _1]] 
     >> +(qi::char_ - qi::char_(_a)) 
     >> qi::lit(_a); 

%=;這使自動傳播即使在語義動作的存在下)。

或者,你可以回推從SA內:

>> +(qi::char_ - qi::char_(_a)) [ phx::push_back(qi::_val, _1) ] 

而且,如果你真的需要在handler做了一些處理,使其通過引用採取字符串:

auto handler = [](attr_signature const& elements, std::string& attr) { 
    for(auto& e : elements) { 
     attr += e; 
    } 
}; 
quoted_string = (qi::omit[qi::char_("'\"")[_a = _1]] 
    >> +(qi::char_ - qi::char_(_a)) 
    >> qi::lit(_a)) [ phx::bind(handler, _1, qi::_val) ]; 

所有這些方法都有效。

對於真正的重型的事情,我已經在過去使用的boost ::精神::特點定製點自定義字符串類型做轉換:

+0

+1。使'handler'參考的方法看起來非常好(並且很容易推廣)。 – llonesmiz

+0

我完全錯過了「通過參考」的東西......謝謝。 – dvd