基本的提升精神语义动作不编译

kfm*_*e04 2 c++ boost-spirit result-of decltype

我试图添加一个大于运算符>到ast:代码与文档中的代码95%相同.

下面有两个兴趣点

  • 我正在尝试编写支持大于的代码块:在下面的代码中注释.
  • 在解析单行的term该编译失败,因为我还没有理解语义动作尚未:不知道如何绑定lhslhs > rhs,通过凤和语义动作.

对于Spirit的常规用户来说,解决方案应该是微不足道的,但我仍在学习,到目前为止只能通过示例.

任何帮助,将不胜感激.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;
}
Run Code Online (Sandbox Code Playgroud)

seh*_*ehe 7

TL; DR使用

template <typename, typename> struct result { typedef expression_ast type; };
Run Code Online (Sandbox Code Playgroud)

内部binary_expr结构.原因如下:


你声明一个仿函数对象,用作凤凰懒惰的角色.

仿函数是Boost文档中的延迟/多态可编辑对象(PCE).这意味着构建Phoenix actor的表达式模板实际上只会在实际应用时对函数参数进行实际的重载解析/类型推导.

无法推导出函数返回类型(就像在常规(非惰性)C++中不能这样).这是boost库使用BOOST_RESULT_OF协议的地方.

注意当使用C++ 11时,RESULT_OF协议已过时/冗余,因为C++ 11具有decltype.要实现这一点,在大多数编译器上都需要

#define BOOST_RESULT_OF_USE_DECLTPYE
Run Code Online (Sandbox Code Playgroud)

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

该协议的含义是:

  • 当您使用带n参数的Polymorphic Calleable Object时,boost将嵌套类模板中查找嵌套的typetypedef ,并使用实际参数类型进行参数化.这将是返回类型resultn
  • 实际调用函数对象调用 operator()这些参数.在这里,C++将留下来进行重载解析.

通常,您需要以完全参数化的样式编写您的仿函数:

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 ));
    }
};
Run Code Online (Sandbox Code Playgroud)

这有效.但是,因为参数类型的实际重载决策是由编译器完成的,所以您可以更改operator()签名以使用固定类型:

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

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

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

甚至根本不是功能模板:

expression_ast operator()(expression_ast const&,expression_ast const&) const;
Run Code Online (Sandbox Code Playgroud)

只要一个重载符合传递的实际参数类型,一切都很好.然而,重要的一点是,无论签名是什么,它都会被评估,因此它将需要精确的预期参数数量.result<...>::typeoperator()

另请注意,您可以通过这种方式组合不同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;
Run Code Online (Sandbox Code Playgroud)

这工作,因为这两个result<T>::typeresult<T,U>::type与此定义有效的类型的表达式.


奖金说明:

  1. 你说的那里有一种陌生感

    template <typename T> struct result { typedef T type; };
    
    Run Code Online (Sandbox Code Playgroud)

    代替

    template <typename> struct result { typedef expression_ast type; };
    
    Run Code Online (Sandbox Code Playgroud)

    这是因为返回类型实际上不会根据实际参数类型而变化.在您的样品,参数类型通常是相同的,但它并没有什么意义技术上.

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

  3. 您还可以将常规功能调整为凤凰演员:

    #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)
    
    Run Code Online (Sandbox Code Playgroud)

    这基本上会为您编写仿函数对象,包括RESULT_OF协议位.

  4. 最后,您可以使用标准的Phoenix actor而不是定义自定义的actor.在这种情况下,不需要上述任何一项:

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

包起来

完整的代码在这里:http://ideone.com/Xv9IH1并进行了测试

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