基本的增强精神语义动作无法编译 [英] basic boost spirit semantic action doesn't compile

查看:92
本文介绍了基本的增强精神语义动作无法编译的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为ast添加一个大于运算符>:该代码与文档中的代码95%相同.

I am trying to add a greater than operator > to a ast: the code is 95% identical to the code in the docs.

下面有两个景点

  • 我尝试编写对大于以下内容的支持的代码块:在下面的代码中注释了.
  • term的解析中的一行无法编译,因为我还不了解语义动作:不确定如何通过凤凰和语义动作来绑定lhs > rhslhs.
  • A block of code where I'm trying to write support for greater than: commented in the code below.
  • A single line in the parse for term which fails to compile because I don't yet understand semantic actions yet: not sure how to bind the lhs of lhs > rhs through phoenix and semantic actions.

该解决方案对于Spirit的普通用户而言应该是微不足道的,但我仍在学习,并且到目前为止仅通过示例进行了解.

任何帮助将不胜感激. TIA.

Any help would be appreciated. TIA.

代码

#define BOOST_SPIRIT_DEBUG

#include <boost/spirit/include/qi.hpp>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/classic_symbols.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/regex.hpp>   // std::regex not fully implemented in stdc++ yet

#include <string>
#include <map>
#include <utility>
#include <functional>
#include <iostream>
#include <string>
#include <vector>

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

    struct binary_op;
    struct unary_op;
    struct nil {};

    struct expression_ast
    {
        typedef
        boost::variant<
        nil // can't happen!
        , double
        , std::string
        , boost::recursive_wrapper<expression_ast>
        , boost::recursive_wrapper<binary_op>
        , boost::recursive_wrapper<unary_op>
        >
        type;

        expression_ast()
            : m_expr(nil()) {}

        template <typename Expr>
        expression_ast(Expr const& expr)
            : m_expr(expr) {}

        expression_ast& operator+=(expression_ast const& rhs);
        expression_ast& operator-=(expression_ast const& rhs);
        expression_ast& operator*=(expression_ast const& rhs);
        expression_ast& operator/=(expression_ast const& rhs);


        type m_expr;
    };

    struct binary_op
    {
        binary_op(
            char op
            , expression_ast const& left
            , expression_ast const& right)
            : m_op(op), m_left(left), m_right(right) {}

        char m_op;
        expression_ast m_left;
        expression_ast m_right;
    };

    struct unary_op
    {
        unary_op(
            char op
            , expression_ast const& subject)
            : m_op(op), m_subject(subject) {}

        char m_op;
        expression_ast m_subject;
    };

    expression_ast& expression_ast::operator+=(expression_ast const& rhs)
    {
        m_expr = binary_op('+', m_expr, rhs);
        return *this;
    }

    expression_ast& expression_ast::operator-=(expression_ast const& rhs)
    {
        m_expr = binary_op('-', m_expr, rhs);
        return *this;
    }

    expression_ast& expression_ast::operator*=(expression_ast const& rhs)
    {
        m_expr = binary_op('*', m_expr, rhs);
        return *this;
    }

    expression_ast& expression_ast::operator/=(expression_ast const& rhs)
    {
        m_expr = binary_op('/', m_expr, rhs);
        return *this;
    }

    // We should be using expression_ast::operator-. There's a bug
    // in phoenix type deduction mechanism that prevents us from
    // doing so. Phoenix will be switching to BOOST_TYPEOF. In the
    // meantime, we will use a phoenix::function below:
    struct negate_expr
    {
        template <typename T>
        struct result
        {
            typedef T type;
        };

        expression_ast operator()(expression_ast const& expr) const
        {
            return expression_ast(unary_op('-', expr));
        }
    };

    static boost::phoenix::function<negate_expr> neg;

    struct ast_print
    {
        typedef std::string result_type;

        std::string operator()(qi::info::nil) const
        {
            return "";
        }
        std::string operator()(std::string const& str) const
        {
            return str;
        }
        std::string operator()(double d) const
        {
            std::ostringstream oss;
            oss << d;
            return oss.str();
        }

        std::string operator()(expression_ast const& ast) const
        {
            return boost::apply_visitor(*this, ast.m_expr);
        }

        std::string operator()(binary_op const& expr) const
        {
            std::ostringstream oss;
            oss << "op:" << expr.m_op << "(";
            oss << boost::apply_visitor(*this, expr.m_left.m_expr);
            oss << ", ";
            oss << boost::apply_visitor(*this, expr.m_right.m_expr);
            oss << ')';
            return oss.str();
        }

        std::string operator()(unary_op const& expr) const
        {
            std::ostringstream oss;
            oss << "op:" << expr.m_op << "(";
            oss << boost::apply_visitor(*this, expr.m_subject.m_expr);
            oss << ')';
            return oss.str();
        }
    };

    std::ostream& operator << (std::ostream& stream, const expression_ast& expr)
    {
        ast_print printer;
        stream << printer(expr) << std::endl;
        return stream;
    }

    // CODE ADDED HERE ------------------------------------------------------------
    template< char OP >
    struct binary_expr
    {
        template <typename T>
        struct result
        {
            typedef T type;
        };

        expression_ast operator()(expression_ast const& lhs,expression_ast const& rhs) const
        {
            return expression_ast(binary_op( OP, lhs, rhs ));
        }
    };

    static boost::phoenix::function<binary_expr<'>'>> gt;
    // CODE ADDED END HERE -------------------------------------------------------

    template <typename Iterator>
    struct ParserGenerator : qi::grammar<Iterator, expression_ast(), ascii::space_type>
    {
        ParserGenerator() : ParserGenerator::base_type(expression)
    {
        using qi::_val;
        using qi::_1;
        using qi::double_;
        using qi::iso8859_1::char_;
        using qi::iso8859_1::space;
        using qi::eol;
        using boost::spirit::ascii::string;

        comment =
            space >> ("//" >> *(char_ - eol) >> eol)
            ;

        expression =
            term                            [_val = _1]
            >> *(   ('+' >> term            [_val += _1])
                |   ('-' >> term            [_val -= _1])
                )
            ;

        term =
            factor                          [_val = _1]
            >> *(   ('*' >> factor          [_val *= _1])
                |   ('/' >> factor          [_val /= _1])
//          |   ('>' >> factor          [_val = gt(qi::_val,_1)]) // PROBLEM HERE!
                )
            ;

        factor =
            symbol                          [_val = _1]
            | double_                       [_val = _1]
            |   '(' >> expression           [_val = _1] >> ')'
            |   ('-' >> factor              [_val = neg(_1)])
            |   ('+' >> factor              [_val = _1])
            ;

        symbol %= 
            (symbol_raw 
            >> *( string("[") >> +qi::digit >> string("]"))
            >> *( string(".") >> symbol ))
            ;

        symbol_raw %= 
            +(qi::alpha | qi::char_( "_" ))
            ;

        BOOST_SPIRIT_DEBUG_NODE(expression);
        BOOST_SPIRIT_DEBUG_NODE(term);
        BOOST_SPIRIT_DEBUG_NODE(factor);
        BOOST_SPIRIT_DEBUG_NODE(comment);
        BOOST_SPIRIT_DEBUG_NODE(symbol);
        BOOST_SPIRIT_DEBUG_NODE(symbol_raw);
    }

    qi::rule<Iterator, expression_ast(), ascii::space_type>
        expression, term, factor, comment;

    qi::rule<Iterator, std::string(), ascii::space_type>
        symbol, symbol_raw;
    };
}

int main(int argc, char* argv[])
{
    using boost::spirit::ascii::space;
    using client::expression_ast;
    using client::ast_print;

    typedef std::string::const_iterator iterator_type;
    typedef client::ParserGenerator<iterator_type> ParserGenerator;

    ParserGenerator pg;   // our grammar
    std::string predicate( "i_.c>x[0]" );
    expression_ast  ast;
    ast_print       printer;

    iterator_type iter = predicate.begin(), end = predicate.end();
    if ( phrase_parse( iter, end, pg, space, ast ))
    {
        std::cerr << printer( ast ) << std::endl;
    }

    return 0;
}

推荐答案

TL; DR使用

template <typename, typename> struct result { typedef expression_ast type; };

binary_expr内部的

结构.原因如下:

inside binary_expr struct. Here's why:

您声明一个仿函数对象,用作Phoenix的懒惰演员.

You declare a functor object, to be used as a Phoenix lazy actor.

函子是Boost文档中称为延迟/多形可调用对象(PCE)的函数.这意味着构建Phoenix角色的表达式模板实际上只会在实际应用时对函数参数进行实际的重载解析/类型推断.

The functor is what's known as a Deferred/Polymorphic Calleable Object (PCE) in the Boost documentation. This means that the expression templates building the Phoenix actors will actually only do actual overload resolution/type deduction for the function arguments at the actual time of application.

无法推断出函数返回类型 (就像在常规(非惰性)C ++中无法推断出)一样.这是Boost库采用BOOST_RESULT_OF协议的地方.

The function return type cannot be deduced (just like it cannot in regular (non-lazy) C++). This is where boost libraries employ the BOOST_RESULT_OF protocol.

注意,因为C ++ 11具有decltype,所以使用C ++ 11时RESULT_OF协议已过时/多余.为此,在大多数编译器上,您需要

Note The RESULT_OF protocol is obsolete/redundant when using C++11, since C++11 has decltype. To enable that, on most compilers you need to

#define BOOST_RESULT_OF_USE_DECLTPYE

(尽管v1_52_0的发行说明清楚地表明decltype正在成为足够支持它的编译器的默认设置).

(allthough the release notes of v1_52_0 make it clear that decltype is becoming the default for compilers that support it well enough).

此协议包含的内容是:

  • 当您将多态可调用对象与 n 参数一起使用时,boost将在嵌套的类模板result中使用嵌套的type typedef 查找嵌套的type typedef n 实际参数类型. 这将是返回类型
  • 使用这些参数的实际函子调用调用 operator().在这里,将保留C ++来进行重载解析.
  • When you use Polymorphic Calleable Object with n parameters, boost will look for a nested type typedef inside a nested class template result, parameterized with the n actual argument types. This will be the return type
  • The actual functor invocation calls operator() with those arguments. Here, C++ will be left to do the overload resolution.

通常,您应该以完全参数化的样式编写函子:

In general, you are expected to write your functor in fully parameterized style:

template<char OP>
struct binary_expr
{
    template <typename, typename> struct result { typedef expression_ast type; };

    template <typename A, typename B>
    typename result<A,B>::type operator()(A const&a,B const&b) const {
        return expression_ast(binary_op( OP, a, b ));
    }
};

这有效.但是,由于对参数类型的实际重载解析是由编译器完成的,因此可以将operator()签名更改为使用固定类型:

This works. However, because the actual overload resolution on argument types is done by the compiler, you can change the operator() signature to use fixed types:

template <typename A, typename B>
expression_ast operator()(A const&a,B const&b) const {
    return binary_op(OP, a, b);
}

如果愿意,您可以取消(一些)模板参数:

If you wish, you can do away with (some) template parameters:

template <typename E>
expression_ast operator()(E const&a,E const&b) const {
    return binary_op(OP, a, b);
}

甚至根本没有功能模板:

Or even not a function template at all:

expression_ast operator()(expression_ast const&,expression_ast const&) const;

一切都很好,只要一个重载与传递的 actual 参数类型匹配即可. 重要的一点 是,无论operator()的签名如何,result<...>::type都将得到评估,因此 准确地期望的参数数量 .

Everything is fine, as long as one overload matches the actual argument types passed. The important bit, though, is that the result<...>::type is getting evaluated regardless of the signature of operator(), and as such it will need precisely the number of arguments expected.

还请注意,您可以通过这种方式组合不同Arity的函子:

Note also that you can, in this fashion, combine functors of different arity:

template <char OP> struct operator_expr 
{
    template <typename T, typename=T> struct result 
        { typedef expression_ast type; };

    expression_ast operator()(expression_ast const& expr) const
    { return expression_ast(unary_op(OP, expr)); }

    expression_ast operator()(expression_ast const&a, expression_ast const&b) const 
    { return binary_op(OP, a, b); }
};

static boost::phoenix::function<operator_expr<'-'>> neg;
static boost::phoenix::function<operator_expr<'>'>> gt;

之所以可行,是因为result<T>::typeresult<T,U>::type都是具有此定义的有效类型表达式.

This works, because both result<T>::type and result<T,U>::type are valid type expressions with this definition.

  1. 你说的地方很陌生

  1. You had a strangeness there where you said

template <typename T> struct result { typedef T type; };

代替

template <typename> struct result { typedef expression_ast type; };

这是因为实际上返回类型应该 not 有所不同,具体取决于实际的参数类型.在您的示例中,参数类型通常会是,但是在技术上没有意义.

This is because the return type shouls, in fact, not vary depending on the actual argument type. In your sample, the argument type would normally be the same, but it didn't make sense technically.

如果使用decltype,则可以在没有BOOST_RESULT_OF协议的情况下执行操作.这意味着您只需删除嵌套的result结构

You can do things without the BOOST_RESULT_OF protocol if you use decltype. This means you can just drop the nested result struct

您还可以使普通功能适应Phoenix演员:

You can also adapt normal functions as Phoenix actors:

#define BOOST_SPIRIT_USE_PHOENIX_V3

// ...

expression_ast neg_expr(expression_ast const&a)                         { return unary_op ('-', a); }
expression_ast gt_expr (expression_ast const&a, expression_ast const&b) { return binary_op('>', a, b); }

BOOST_PHOENIX_ADAPT_FUNCTION(expression_ast, neg, neg_expr, 1)
BOOST_PHOENIX_ADAPT_FUNCTION(expression_ast, gt,  gt_expr,  2)

这基本上将为您写函子对象 ,包括RESULT_OF协议位.

This will basically write the functor objects for you, including the RESULT_OF protocol bits.

最后,您可以使用标准Phoenix演员,而不是定义自定义演员.在这种情况下,无需执行以下任何操作:

Finally, you can use standard Phoenix actors instead of defining custom ones. In that case, there is no need for any of the above:

using phx = boost::phoenix;
// ...
   |   ('>' >> factor [_val = phx::construct<binary_op>('>', _val, _1)]) // PROBLEM HERE!
// ...
   |   ('-' >> factor [_val = phx::construct<unary_op>('-', _1)])

总结

完整的代码在这里: http://ideone.com/Xv9IH1 并在

Wrap-up

The complete code is here: http://ideone.com/Xv9IH1 and was tested on

  • MSVC 2012,增强版1_52_0,Win64
  • GCC 4.8,boost 1_52_0,Win64

这篇关于基本的增强精神语义动作无法编译的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆