2013-08-27 28 views
4

我還有另一個與精靈氣的阻塞問題。助力精神之氣on_error傳遞error_handler結構參考

我已經在稱爲error_handler的函數結構中實現了錯誤處理。 這通過引用傳遞給語法構造器(請參閱Qi的MiniC示例)。

然後我有語法的構造函數定義on_error<fail> S:

typedef boost::phoenix::function<error_handler<> > error_handler_function; 
on_error<fail>(gr_instruction, 
     error_handler_function(err_handler)(L"Error: Expecting ", _4, _3)); 
     // more on_error<fail>s... 

然而,我的error_handler有私有成員。看起來每次調用on_error時,err_handler對象都被複制,因此一旦函子離開,被改變的局部變量就被銷燬。

我試着通過引用傳遞的處理程序:

typedef boost::phoenix::function<error_handler<>& > error_handler_function; // <--- Note the ampersand! 

on_error<fail>(gr_instruction, 
     error_handler_function(err_handler)(L"Error: Expecting ", _4, _3)); 
     // more on_error<fail>s... 

然而,問題仍然是:對err_handler副本on_error()作品,而不是一個實例!

我也試過​​的變體,只是編譯錯誤。

當然,必須有一個簡單的解決方案來通過引用傳遞處理程序?

我將不勝感激任何輸入。感謝您的幫助。

+0

我剛剛意識到'PHX ::函數'是這是你的'phx :: function &>'思想的明顯替代。看到我的新答案。 – sehe

回答

8

是的,phx :: bind和phx :: function <>默認情況下會按值打包它們的包裝器。然而。

比方說[1],你有一個錯誤處理這樣的...簡約例如:

template <typename=void> struct my_error_handler { 
    my_error_handler() = default; 
    my_error_handler(my_error_handler const&) = delete; 

    template<typename...> struct result { typedef void type; }; 
    template<typename... T> void operator()(T&&...) const { 
     std::cerr << "my_error_handler invoked " << proof++ << "\n"; 
    } 
    mutable int proof = 0; 
}; 

(正如你可以看到我做了明確地不可複製的,以確保編譯器。不會產生默默地在我背後的代碼)

現在,我不知道這是否是你不小心錯過了一個組合,但是:

on_error<fail>(function,  phx::bind(phx::ref(err_handler), L"Error: Expecting ", _4, _3)); 
on_error<fail>(start,   phx::bind(phx::ref(err_handler), L"Error: Expecting ", _4, _3)); 
on_error<fail>(gr_instruction, phx::bind(phx::ref(err_handler), L"Error: Expecting ", _4, _3)); 

工作得很好,一樣

auto ll = phx::bind(phx::ref(err_handler), L"Error: Expecting ", _4, _3); 
on_error<fail>(function,  ll); 
on_error<fail>(start,   ll); 
on_error<fail>(gr_instruction, ll); 

注意,因爲bind需要一個參考,我們需要確保的err_handler匹配壽命(或超過)解析器的,所以我做了err_handler解析器類的成員。

當我將它傳遞輸入失敗,我的程序就能夠打印my_error_handler的調用的proof

std::cout << "The 'proof' in the err_handler instance is: " << p.err_handler.proof << "\n"; 

像往常一樣,一個完全集成的例子程序:

#define BOOST_SPIRIT_USE_PHOENIX_V3 
//#define BOOST_SPIRIT_DEBUG 
#include <boost/spirit/include/qi.hpp> 
#include <boost/spirit/include/phoenix.hpp> 

namespace qi = boost::spirit::qi; 
namespace phx = boost::phoenix; 

namespace asmast 
{ 
    typedef std::string label; 
} 

template <typename=void> struct my_error_handler { 
    my_error_handler() = default; 
    my_error_handler(my_error_handler const&) = delete; 

    template<typename...> struct result { typedef void type; }; 
    template<typename... T> void operator()(T&&...) const { 
     std::cerr << "my_error_handler invoked " << proof++ << "\n"; 
    } 
    mutable int proof = 0; 
}; 

template <typename It, typename Skipper = qi::blank_type> 
    struct parser : qi::grammar<It, Skipper> 
{ 
    parser() : 
     parser::base_type(start) 
    { 
     using namespace qi; 

     start = lexeme["Func" >> !(alnum | '_')] > function; 
     function = gr_identifier 
        >> "{" 
        >> -(
           gr_instruction 
          | gr_label 
          //| gr_vardecl 
          //| gr_paramdecl 
         ) % eol 
        > "}"; 

     gr_instruction_names.add("Mov", unused); 
     gr_instruction_names.add("Push", unused); 
     gr_instruction_names.add("Exit", unused); 

     gr_instruction = lexeme [ gr_instruction_names >> !(alnum|"_") ] > gr_operands; 
     gr_operands = -(gr_operand % ','); 

     gr_identifier = lexeme [ alpha >> *(alnum | '_') ]; 
     gr_operand = gr_identifier | gr_string; 
     gr_string  = lexeme [ '"' >> *("\"\"" | ~char_("\"")) >> '"' ]; 

     gr_newline = +(char_('\r') 
         |char_('\n') 
        ); 

     gr_label = gr_identifier >> ':' > gr_newline; 

#if 1 
     on_error<fail>(function,  phx::bind(phx::ref(err_handler), L"Error: Expecting ", _4, _3)); 
     on_error<fail>(start,   phx::bind(phx::ref(err_handler), L"Error: Expecting ", _4, _3)); 
     on_error<fail>(gr_instruction, phx::bind(phx::ref(err_handler), L"Error: Expecting ", _4, _3)); 
#else 
     auto ll = phx::bind(phx::ref(err_handler), L"Error: Expecting ", _4, _3); 
     on_error<fail>(function,  ll); 
     on_error<fail>(start,   ll); 
     on_error<fail>(gr_instruction, ll); 
#endif 
     // more on_error<fail>s... 

     BOOST_SPIRIT_DEBUG_NODES((start)(function)(gr_instruction)(gr_operands)(gr_identifier)(gr_operand)(gr_string)); 
    } 

    my_error_handler<> err_handler; 
    private: 
    qi::symbols<char, qi::unused_type> gr_instruction_names; 
    qi::rule<It, Skipper> start, function, gr_instruction, gr_operands, gr_operand, gr_string; 
    qi::rule<It, qi::unused_type()> gr_newline; 
    qi::rule<It, asmast::label(), Skipper> gr_label, gr_identifier; 
}; 

int main() 
{ 
    typedef boost::spirit::istream_iterator It; 
    std::cin.unsetf(std::ios::skipws); 
    It f(std::cin), l; 

    parser<It, qi::blank_type> p; 

    try 
    { 
     bool ok = qi::phrase_parse(f,l,p,qi::blank); 
     if (ok) std::cout << "parse success\n"; 
     else  std::cerr << "parse failed: '" << std::string(f,l) << "'\n"; 

     if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n"; 

     std::cout << "The 'proof' in the err_handler instance is: " << p.err_handler.proof << "\n"; 
     return ok; 
    } catch(const qi::expectation_failure<It>& e) 
    { 
     std::string frag(e.first, e.last); 
     std::cerr << e.what() << "'" << frag << "'\n"; 
    } 

    return false; 
} 

當喂入輸入

Func Ident{ 
    Mov name, "hello" 
    Push 5 
    Exit 
} 

它打印(作爲第一/最後一個行)

my_error_handler invoked 0 
my_error_handler invoked 1 
... 
The 'proof' in the err_handler instance is: 2 

[1]這不是我在想象相關的代碼在第一時間

+0

感謝您的回覆和示例。我會在早上第一件事。有趣的是,你如何繼續建立在前面的例子上:]精神是一個陡峭的學習曲線;特別是由於它高度依賴於其他boost庫以及難以解碼編譯器錯誤,但最終非常值得。再次感謝您的耐心等待! – namezero

+0

@namezero哈哈。我只是經濟!我不喜歡從頭開始輸入隨機代碼示例:/ – sehe

+0

另外,請小心那裏的意見偏見。我得出結論,手滾解析器有時是可取的。然而,一旦你掌握了它的靈魂(並且不願意使用Antlr/CoCo/...),Spirit就是......無與倫比的快速原型設計。 – sehe

3

我記得有這個後期的思想和希望檢查:

當然,

my_error_handler<> err_handler; 
phx::function<my_error_handler<> > err_handler_(err_handler); 

會工作(但試圖複製err_handler實例,這不是你想要的)。現在,

phx::function<my_error_handler<> > err_handler_(phx::ref(err_handler)); 

是不會飛(爲my_error<>不能從phx::ref(err_handler)構造),因此,從邏輯上講,你真正需要做的只是:

namespace P = boost::proto; 
phx::function<const phx::actor<P::exprns_::basic_expr< 
    P::tagns_::tag::terminal, 
    P::argsns_::term<boost::reference_wrapper<my_error_handler<> > >, 
    0l> 
> > err_handler_; 

其作品...正是因爲phx::bind會,但更多的語法糖:現在

on_error<fail>(function,  err_handler_(L"Error: Expecting ", _4, _3)); 
    on_error<fail>(start,   err_handler_(L"Error: Expecting ", _4, _3)); 
    on_error<fail>(gr_instruction, err_handler_(L"Error: Expecting ", _4, _3)); 

,一些C++ 11這個可以寫略少冗長:

my_error_handler<> err_handler; 
phx::function<decltype(phx::ref(err_handler))> err_handler_; 

見它的工作Live on Coliru用下面的代碼:

#define BOOST_SPIRIT_USE_PHOENIX_V3 
//#define BOOST_SPIRIT_DEBUG 
#include <boost/spirit/include/qi.hpp> 
#include <boost/spirit/include/phoenix.hpp> 

namespace qi = boost::spirit::qi; 
namespace phx = boost::phoenix; 

namespace asmast 
{ 
    typedef std::string label; 
} 

template <typename=void> struct my_error_handler { 
    my_error_handler() = default; 
    my_error_handler(my_error_handler const&) = delete; 

    template<typename...> struct result { typedef void type; }; 
    template<typename... T> void operator()(T&&...) const { 
     std::cerr << "my_error_handler invoked " << proof++ << "\n"; 
    } 
    mutable int proof = 0; 
}; 

template <typename It, typename Skipper = qi::blank_type> 
    struct parser : qi::grammar<It, Skipper> 
{ 
    parser() : 
     parser::base_type(start), 
     err_handler(), 
     err_handler_(phx::ref(err_handler)) 
    { 
     using namespace qi; 

     start = lexeme["Func" >> !(alnum | '_')] > function; 
     function = gr_identifier 
        >> "{" 
        >> -(
           gr_instruction 
          | gr_label 
          //| gr_vardecl 
          //| gr_paramdecl 
         ) % eol 
        > "}"; 

     gr_instruction_names.add("Mov", unused); 
     gr_instruction_names.add("Push", unused); 
     gr_instruction_names.add("Exit", unused); 

     gr_instruction = lexeme [ gr_instruction_names >> !(alnum|"_") ] > gr_operands; 
     gr_operands = -(gr_operand % ','); 

     gr_identifier = lexeme [ alpha >> *(alnum | '_') ]; 
     gr_operand = gr_identifier | gr_string; 
     gr_string  = lexeme [ '"' >> *("\"\"" | ~char_("\"")) >> '"' ]; 

     gr_newline = +(char_('\r') 
         |char_('\n') 
        ); 

     gr_label = gr_identifier >> ':' > gr_newline; 

#if 0 
     on_error<fail>(function,  phx::bind(phx::ref(err_handler), L"Error: Expecting ", _4, _3)); 
     on_error<fail>(start,   phx::bind(phx::ref(err_handler), L"Error: Expecting ", _4, _3)); 
     on_error<fail>(gr_instruction, phx::bind(phx::ref(err_handler), L"Error: Expecting ", _4, _3)); 
#else 
     on_error<fail>(function,  err_handler_(L"Error: Expecting ", _4, _3)); 
     on_error<fail>(start,   err_handler_(L"Error: Expecting ", _4, _3)); 
     on_error<fail>(gr_instruction, err_handler_(L"Error: Expecting ", _4, _3)); 
#endif 
     // more on_error<fail>s... 

     BOOST_SPIRIT_DEBUG_NODES((start)(function)(gr_instruction)(gr_operands)(gr_identifier)(gr_operand)(gr_string)); 
    } 

    my_error_handler<> err_handler; 
    phx::function<decltype(phx::ref(err_handler))> err_handler_; 
    private: 
    qi::symbols<char, qi::unused_type> gr_instruction_names; 
    qi::rule<It, Skipper> start, function, gr_instruction, gr_operands, gr_operand, gr_string; 
    qi::rule<It, qi::unused_type()> gr_newline; 
    qi::rule<It, asmast::label(), Skipper> gr_label, gr_identifier; 
}; 

int main() 
{ 
    typedef boost::spirit::istream_iterator It; 
    std::cin.unsetf(std::ios::skipws); 
    It f(std::cin), l; 

    parser<It, qi::blank_type> p; 

    try 
    { 
     bool ok = qi::phrase_parse(f,l,p,qi::blank); 
     if (ok) std::cout << "parse success\n"; 
     else  std::cerr << "parse failed: '" << std::string(f,l) << "'\n"; 

     if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n"; 

     std::cout << "The 'proof' in the err_handler instance is: " << p.err_handler.proof << "\n"; 
     return ok; 
    } catch(const qi::expectation_failure<It>& e) 
    { 
     std::string frag(e.first, e.last); 
     std::cerr << e.what() << "'" << frag << "'\n"; 
    } 

    return false; 
} 
+0

+1。我認爲你也可以使用'phx :: function > :: type> err_handler_;'。我認爲我更喜歡簡單地將'proof'作爲在構造函數中初始化的引用,但這種方式非常有趣。 – llonesmiz