【问题标题】:basic boost spirit semantic action doesn't compile基本的提升精神语义动作无法编译
【发布时间】:2012-11-23 14:23:26
【问题描述】:

我正在尝试将大于运算符 > 添加到 ast:代码与文档中的代码 95% 相同。

以下两个兴趣点

  • 我正在尝试编写支持大于的代码块:在下面的代码中注释。
  • term 解析中的一行无法编译,因为我还不了解语义操作:不确定如何通过 phoenix 和语义操作绑定 lhs > rhslhs

解决方案对于 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;
}

【问题讨论】:

  • +1 @llonesmiz 哇——因为使用 Spirit Voodoo 值得 +10——我几乎不敢在 SO 上发布这个。我可能不应该犹豫......
  • 已使用 -std=c++11 验证可与 g++ 4.7.2 一起使用 - @llonesmiz - 如果我们一周没有收到 sehe 的消息,我稍后会向您发送消息,以使用您的解决方案作为答案... 求您的帮助!
  • @llonesmiz (lol...) -- 当然,decltype 在这里作弊 :) 我提供了一个答案,其中包含大量有关问题、原因以及如何解决问题的信息。 (FWIW,这些天我主要和decltype 一起跑步)

标签: c++ boost-spirit decltype result-of


【解决方案1】:

TL;DR 使用

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

binary_expr 结构内。原因如下:


您声明了一个仿函数对象,用作 Phoenix lazy actor

仿函数在 Boost 文档中被称为 Deferred/Polymorphic Calleable Object (PCE)。这意味着构建 Phoenix Actor 的表达式模板实际上只会在实际应用时对函数参数进行实际的重载解析/类型推导。

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

注意在使用 C++11 时,RESULT_OF 协议已过时/冗余,因为 C++11 有 decltype。要启用它,在大多数编译器上,您需要

#define BOOST_RESULT_OF_USE_DECLTPYE

(尽管 v1_52_0 的发行说明清楚地表明 decltype 正在成为支持它的编译器的默认值)。

这个协议需要什么,是这样的:

  • 当您使用带有 n 参数的多态可调用对象时,boost 将查找嵌套的 type typedef inside 嵌套的类模板 result,参数化为n 实际参数类型。 这将是返回类型
  • 实际的仿函数调用调用 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 ));
    }
};

这行得通。但是,由于参数类型的实际重载解析是由编译器完成的,因此您可以更改 operator() 签名以使用固定类型:

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

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

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

甚至根本不是函数模板:

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

一切都很好,只要一个重载与传递的实际参数类型匹配。 重要的一点是,不管operator() 的签名如何,都会对result&lt;...&gt;::type 进行评估,因此需要正是预期的参数数量

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

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&lt;T&gt;::typeresult&lt;T,U&gt;::type 都是具有此定义的有效类型表达式。


奖金说明:

  1. 你说的地方有点奇怪

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

    而不是

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

    这是因为返回类型应该,实际上,会根据实际的参数类型而有所不同。在您的示例中,参数类型 通常是相同的,但在技术上没有意义。

  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)
    

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

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

    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 并在

上进行了测试
  • MSVC 2012,boost 1_52_0,Win64
  • GCC 4.8,boost 1_52_0,Win64

【讨论】:

  • +1 神圣的烟雾 - 有答案 - bb 稍后消化这个!
  • 我很敬畏 - 这需要进入一些教程/文档 - 太有价值了,不能被埋葬,迷失在 SO 中。已加星标。
  • FWIW 它已经在文档中:boost.org/doc/libs/1_52_0/libs/utility/utility.htm#result_of(这也将显示我遗漏的一些信息)。我只是考虑到你的具体用法写的,我猜 :) (另外:只要点赞就足够了:东西不会被埋没。如果它们存在,我可能会用相关的提升标签来标记它)
猜你喜欢
  • 1970-01-01
  • 2011-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多