2013-02-25 85 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