2013-02-25 46 views
5

在Boost Phoenix文章“转换表达式树”here中,使用了一组自定义类invert_actions的专业化语言来转换二进制算术表达式。例如a+b变成a-b; a*b变成a/b;反之亦然。转换Boost C++ Phoenix表达式树

这涉及表达式树的递归遍历 - 但是,当遇到涉及操作符未明确处理的表达式时,此遍历​​停止。例如,_1+_2-_3将变为_1-_2+_3,但_1+_1&_2将保持原样(&没有处理程序)。 let(_a = 1, _b = 2) [ _a+_b ]也将保持不变。

我原以为这是文章的打算,但看看最后列出的测试,我看到if_(_1 * _4)[_2 - _3]预计会改变;与提供的代码(here),我发现它没有。

那么我怎样才能定义一个通用的Boost Phoenix表达式树变换,它适用于所有明确列出的(n元)运算符集合的全部;让其他人保持不变?

某些代码可能有用。我想要下面的C++ 11代码(自动)输出0,而不是2; 没有明确处理&或任何其他操作员/语句。

#include <iostream> 
#include <boost/phoenix.hpp> 
#include <boost/proto/proto.hpp> 

using namespace boost; 
using namespace proto; 
using namespace phoenix; 
using namespace arg_names; 

struct invrt { 
    template <typename Rule> struct when : proto::_ {}; 
}; 

template <> 
struct invrt::when<rule::plus> 
    : proto::call< 
    proto::functional::make_expr<proto::tag::minus>(
     evaluator(_left, _context), evaluator(_right, _context) 
    ) 
    > 
{}; 

int main(int argc, char *argv[]) 
{ 
    auto f = phoenix::eval(_1+_1&_2 , make_context(make_env(), invrt())); 
    std::cout << f(1,2) << std::endl; // Alas 2 instead of 0 
    return 0; 
} 

回答

2

这是你如何用直原做:

#include <iostream> 
#include <boost/phoenix.hpp> 
#include <boost/proto/proto.hpp> 
namespace proto = boost::proto; 
using namespace boost::phoenix; 
using namespace arg_names; 

struct invrt: 
    proto::or_< 
    proto::when< 
     // Turn plus nodes into minus 
     proto::plus<proto::_, proto::_>, 
     proto::functional::make_expr<proto::tag::minus>(
     invrt(proto::_left), invrt(proto::_right) 
    ) 
    >, 
    proto::otherwise< 
     // This recurses on children, transforming them with invrt 
     proto::nary_expr<proto::_, proto::vararg<invrt> > 
    > 
    > 
{}; 

int main(int argc, char *argv[]) 
{ 
    auto f = invrt()(_1+_1&_2); 
    proto::display_expr(f); 
    std::cout << f(1,2) << std::endl; 
    return 0; 
} 

凤凰分层一堆东西对原之上。我不知道pheonix::eval的语义,或者为什么你尝试过没有工作。也许有人懂行的凤凰会附和。

==== ====编辑

我与凤凰例如想通了这个问题。它不会递归用于非加号的情况。您的代码应该如下:

#include <iostream> 
#include <boost/phoenix.hpp> 
#include <boost/proto/proto.hpp> 

using namespace boost; 
using namespace proto; 
using namespace phoenix; 
using namespace arg_names; 

struct invrt { 
    template <typename Rule> 
    struct when : 
    // NOTE!!! recursively transform children and reassemble 
    nary_expr<_, vararg<proto::when<_, evaluator(_, _context)> > > 
    {}; 
}; 

template <> 
struct invrt::when<rule::plus> : 
    proto::call< 
    proto::functional::make_expr<proto::tag::minus>(
     evaluator(_left, _context), evaluator(_right, _context) 
    ) 
    > 
{}; 

int main() 
{ 
    auto f = phoenix::eval(_1+_1&_2 , make_context(make_env(), invrt())); 
    display_expr(f); 
    std::cout << f(1,2) << std::endl; // Prints 0. Huzzah! 
} 

无论你认为比直原解决方案更简单或更复杂的是你来决定。

+0

谢谢@Eric Niebler,这真是太棒了 - 2种解决方案非常慷慨。我喜欢第一个使用proto,但第二个使用模板专业化使它很好地模块化;说如果我想再次添加rule :: divides的情况。 – user2023370 2013-02-28 17:04:01