2013-02-25 12 views
5

Nell'articolo Boost Phoenix, "Trasformare l'albero delle espressioni", here, una serie di specializzazioni di una classe personalizzata invert_actions, vengono utilizzate per invertire le espressioni aritmetiche binarie. Ad esempio a+b diventa a-b; a*b diventa a/b; e viceversa per entrambi.Trasformazione di un Boost C++ Phoenix Expression Tree

Ciò comporta un attraversamento ricorsivo dell'albero delle espressioni, tuttavia questa interruzione si interrompe quando si incontra un'espressione che coinvolge un operatore non gestito in modo esplicito. Ad esempio, _1+_2-_3 diventerà _1-_2+_3, ma _1+_1&_2 rimarrà invariato (non è disponibile il gestore per &). let(_a = 1, _b = 2) [ _a+_b ] rimarrà invariato.

Avevo pensato che questo fosse come previsto dall'articolo, ma guardando i test elencati alla fine, vedo che if_(_1 * _4)[_2 - _3] si prevede che cambi; con il codice fornito (here), trovo che non lo sia.

Come è possibile definire una trasformata generica dell'albero di espressioni Boost Phoenix che si applica a a tutti di un insieme di operatori esplicitamente elencati (n-ari); lasciando gli altri immutati?

Alcuni codici potrebbero essere utili. Mi piacerebbe che il seguente codice C++ 11 (auto) emettesse 0, e non 2; senza che gestisce esplicitamente lo & o qualsiasi altro operatore/istruzione.

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

risposta

2

Questo è come si fa con Proto dritto:

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

Phoenix ha stratificato un mucchio di roba in cima Proto. Non conosco la semantica di pheonix::eval o perché quello che hai provato non ha funzionato. Forse qualcuno informato di Phoenix sarà carillon.

==== ==== EDIT

ho capito il problema con l'esempio Phoenix. Non ricorre per il caso non-plus. Il codice dovrebbe essere la seguente:

#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! 
} 

Se si considera che più semplice o più complicata la soluzione Proto dritto è per voi a decidere.

+0

Grazie a @Eric Niebler, è davvero fantastico - 2 soluzioni sono molto generose. Mi piace il primo uso del proto, ma il secondo uso delle specializzazioni dei template lo rende piacevolmente modulare; dì se volevo aggiungere il caso per la regola :: divide di nuovo. – user2023370

Problemi correlati