简体   繁体   English

基本的增强精神语义动作无法编译

[英]basic boost spirit semantic action doesn't compile

I am trying to add a greater than operator > to a ast: the code is 95% identical to the code in the docs. 我试图添加一个大于运算符>到ast:代码与文档中的代码95%相同。

Two points of interest below 下面有两个兴趣点

  • 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. 在分析了单行term其中编译失败,因为我还没有理解语义动作尚未:不知道如何将绑定lhslhs > rhs通过凤和语义动作。

The solution should be trivial for regular users of Spirit, but I am still learning, and only getting by through examples so far. 对于Spirit的常规用户来说,解决方案应该是微不足道的,但我仍在学习,到目前为止只能通过示例。

Any help would be appreciated. 任何帮助,将不胜感激。 TIA. TIA。

Code

#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 use TL; DR使用

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

inside binary_expr struct. binary_expr结构中。 Here's why: 原因如下:


You declare a functor object, to be used as a Phoenix lazy actor . 您声明一个仿函数对象,用作Phoenix 懒惰的actor

The functor is what's known as a Deferred/Polymorphic Calleable Object (PCE) in the Boost documentation. 函数是Boost文档中称为“ 延迟/多形可调用对象(PCE) ”的函数。 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 . 这意味着构建Phoenix actor的表达式模板实际上只会在实际应用时对函数参数进行实际的重载解析/类型推导。

The function return type cannot be deduced (just like it cannot in regular (non-lazy) C++). 无法推断出函数的返回类型(就像不能在常规(非惰性)C ++中那样)。 This is where boost libraries employ the BOOST_RESULT_OF protocol. 这是boost库使用BOOST_RESULT_OF协议的地方。

Note The RESULT_OF protocol is obsolete/redundant when using C++11, since C++11 has decltype . 注意使用C ++ 11时,RESULT_OF协议已过时/冗余,因为C ++ 11具有decltype To enable that, on most compilers you need to 要实现这一点,在大多数编译器上都需要

 #define BOOST_RESULT_OF_USE_DECLTPYE 

(allthough the release notes of v1_52_0 make it clear that decltype is becoming the default for compilers that support it well enough). (尽管v1_52_0的发行说明清楚地表明decltype正成为支持它的编译器的默认设置)。

What this protocol entails, is this: 该协议的含义是:

  • 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. 当您使用带有n参数的Polymorphic Calleable Object时,boost将嵌套类模板result查找嵌套type typedef,并使用n实际参数类型进行参数化。 This will be the return type 这将是返回类型
  • The actual functor invocation calls operator() with those arguments. 实际的函子调用使用这些参数调用 operator() Here, C++ will be left to do the overload resolution. 在这里,C ++将留下来进行重载解析。

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

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: 但是,因为参数类型的实际重载决策是由编译器完成的,所以您可以更改operator()签名以使用固定类型:

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;

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 . 但重要的是,无论operator()的签名如何, result<...>::type都会被评估, 因此它将需要精确的预期参数数量

Note also that you can, in this fashion, combine functors of different arity: 另请注意,您可以通过这种方式组合不同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;

This works, because both result<T>::type and result<T,U>::type are valid type expressions with this definition. 之所以有效,是因为result<T>::typeresult<T,U>::type都是具有此定义的有效类型表达式。


Bonus notes: 奖金说明:

  1. You had a strangeness there where you said 你说的那里有一种陌生感

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

    instead of 代替

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

    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. 在您的样品,参数类型通常是相同的,但它并没有什么意义技术上。

  2. You can do things without the BOOST_RESULT_OF protocol if you use decltype. 如果使用decltype,则可以在没有BOOST_RESULT_OF协议的情况下执行操作。 This means you can just drop the nested result struct 这意味着您可以删除嵌套result结构

  3. You can also adapt normal functions as Phoenix actors: 您还可以使普通功能适应Phoenix演员:

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

    This will basically write the functor objects for you , including the RESULT_OF protocol bits. 基本上,这将为编写函子对象,包括RESULT_OF协议位。

  4. Finally, you can use standard Phoenix actors instead of defining custom ones. 最后,您可以使用标准的Phoenix actor而不是定义自定义的actor。 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)]) 

Wrap-up 包起来

The complete code is here: http://ideone.com/Xv9IH1 and was tested on 完整的代码在这里: http : //ideone.com/Xv9IH1 ,并在

  • MSVC 2012, boost 1_52_0, Win64 MSVC 2012,提升1_52_0,Win64
  • GCC 4.8, boost 1_52_0, Win64 GCC 4.8,提升1_52_0,Win64

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM