4

I have yet another blocker issue with Spirit Qi.

I have implemented error handling in a functor struct called error_handler. This is passed to the grammar constructor by reference (see the MiniC example for Qi).

I then have on_error<fail>s defined in the grammar's constructor:

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

However, my error_handler has private members. It seems every time on_error is invoked, the err_handler object is copied, hence once the functor leaves, the local variables changed are destroyed.

I tried passing the handler by reference:

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

However, the problem remains: on_error() works on copies of err_handler, not a single instance!!

I have also tried variations of boost::phoenix::ref(err_handler) with nothing but compile errors.

Surely, there must be an easy solution to passing the handler via reference?

I would appreciate any input. Thank you for your help.

sehe
  • 374,641
  • 47
  • 450
  • 633
namezero
  • 2,203
  • 3
  • 24
  • 37
  • I just realized that `phx::function` is the obvious alternative to your `phx::function& >` thought. See my new answer. – sehe Aug 31 '13 at 10:29

2 Answers2

8

Yes, phx::bind and phx::function<> will take their wrapper calleables by value by default. However.

Let's say[1], you have an error handler like this... minimalist example:

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;
};

(As you can see I made it explicitely non-copyable to ensure the compiler wouldn't silently generate the code behind my back.)

Now, I'm not sure whether this is a combination you accidentally missed, but:

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));

works nicely, as does

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);

Note that because bind takes a reference, we need to ensure lifetime of err_handler matches (or exceeds) that of the parser, so I made err_handler a member of the parser class.

When I pass it input to fail, my program will be able to print proof of the invocations of my_error_handler:

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

As always, a completely integrated sample program:

#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;
}

When fed the input

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

It prints (as the first/last lines)

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

[1] It's not the first time I'm having to imagine relevant code

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thank you for your reply and the example. I will try that first thing in the morning. Kinda funny how you keep building upon the previous examples :] Spirit is really a steep learning curve; especially due to its high reliance on other boost libraries and difficult to decode compiler errors, but in the end well worth it. Thanks again for your patience! – namezero Aug 29 '13 at 01:33
  • @namezero Lol. I'm just being economic! I don't like typing up random code samples from scratch :/ – sehe Aug 29 '13 at 07:07
  • Also, be careful with opinion bias there. I've come to the conclusion that hand rolling parsers is sometimes preferrable. However, Spirit is... unbeatable for rapid prototyping once you get the hang of it (and prefer not to use Antlr/CoCo/...). – sehe Aug 29 '13 at 07:07
  • Thank you, that does indeed work! I hadn't stumbled on an example using phoenix::bind for this. As for [1], the error_handler is just like the one in the MiniC sample, only I'm not outputting to std::cout. I know it would have been easier to not use Spirit for this parser, but we got something way more complicated coming up, so I figured this would be a great way to start understanding the library. Thanks again! – namezero Aug 29 '13 at 13:21
  • @namezero I don't really care about a sample that I have to lookup. At the very least, you said it was stateful. So I wrote the most minimal stateful functor I could think of :) – sehe Aug 29 '13 at 13:23
3

I remembered having a late thought about this and wanted to check it:

Of course,

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

would work (but tries to copy the err_handler instance, which is not what you want). Now,

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

isn't going to fly (as a my_error<> cannot construct from a phx::ref(err_handler)) so, logically, what you actually needed to do was just:

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_;

which... works exactly as the phx::bind would, but with more syntactic sugar:

    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));

Now, with some C++11 this can be written slightly less verbose:

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

See it working Live on Coliru with the code below:

#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;
}
sehe
  • 374,641
  • 47
  • 450
  • 633
  • +1. I think that you can also use `phx::function< phx::expression::reference< my_error_handler<> >::type > err_handler_;`. I think I prefer simply having `proof` as a reference that is initialized in the constructor, but this way is very interesting. – llonesmiz Aug 31 '13 at 11:50