2013-08-27 4 views
4

У меня есть еще одна проблема с блоком с Spirit Qi.boost spirit qi on_error pass error_handler struct by reference

Я реализовал обработку ошибок в functor-структуре с именем error_handler. Это передается конструктору грамматики по ссылке (см. Пример MiniC для Qi).

Я тогда on_error<fail> сек, определенные в конструкторе грамматики:

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... 

Однако проблема остается: on_error() работы по копии err_handler, ни один экземпляр !!

Я также пробовал варианты boost::phoenix::ref(err_handler) с ошибками компиляции.

Несомненно, должно быть простое решение для передачи обработчика по ссылке?

Буду признателен за любой ввод. Спасибо за помощь.

+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 член класса анализатора ,

Когда я прохожу его вход к сбою, моя программа будет иметь возможность печатать proof из призываний my_error_handler:

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

Благодарим вас за ответ и пример. Я попробую это первым делом с утра. Как ни странно, как вы продолжаете строить предыдущие примеры:] Дух - действительно крутая кривая обучения; особенно из-за его высокой зависимости от других ускорительных библиотек и трудностей для декодирования ошибок компилятора, но в конечном итоге это того стоит. Еще раз спасибо за терпение! – namezero

+0

@namezero Lol. Я просто экономичен! Мне не нравится набирать образцы случайных кодов с нуля:/ – sehe

+0

Также будьте осторожны с предубеждением. Я пришел к выводу, что раздача парсеров иногда бывает предпочтительной. Тем не менее, Spirit ... непревзойден для быстрого прототипирования, как только вы его повесите (и предпочитаете не использовать Antlr/CoCo/...). – 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_;'. Я думаю, что я предпочитаю просто иметь «доказательство» как ссылку, которая инициализируется в конструкторе, но этот способ очень интересен. – llonesmiz