2013-02-25 3 views
5

In dem Boost-Phoenix-Artikel "Transforming the Expression Tree" here, ein Satz von Spezialisierungen einer benutzerdefinierten invert_actions-Klasse, werden binäre arithmetische Ausdrücke invertiert. Zum Beispiel wird a-b; a*b wird a/b; und umgekehrt für beide.Transformieren eines Boost C++ - Phoenix-Ausdrucksbaums

Hierbei handelt es sich um eine rekursive Traversierung des Ausdrucksbaums. Diese Traversierung wird jedoch beendet, wenn ein Ausdruck gefunden wird, der einen Operator betrifft, der nicht explizit behandelt wird. Zum Beispiel wird _1+_2-_3_1-_2+_3, aber _1+_1&_2 bleibt so wie es ist (es gibt keinen Handler für &). let(_a = 1, _b = 2) [ _a+_b ] wird auch unverändert bleiben.

Ich hatte gedacht, dies war wie in dem Artikel vorgesehen, aber mit Blick auf die Tests am Ende aufgeführt, sehe ich, dass if_(_1 * _4)[_2 - _3] erwartet wird, zu ändern; mit dem Code geliefert (here), finde ich, dass es nicht.

Wie kann ich dann eine generische Boost Phoenix Expression Tree Transformation definieren, die auf einer Reihe von explizit aufgeführten (n -ary) Operatoren gilt; die anderen unverändert lassen?

Einige Codes können nützlich sein. Ich möchte den folgenden C++ 11-Code (automatisch) ausgeben 0, und nicht 2; ohne, das ausdrücklich die & oder irgendeinen anderen Operator/eine Anweisung behandelt.

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

Antwort

2

Dies ist, wie Sie es mit gerade Proto tun:

#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 ein paar Sachen auf der Proto schichtet hat. Ich kenne die Semantik von pheonix::eval nicht oder warum, was Sie versucht haben, hat nicht funktioniert. Vielleicht hat jemand kenntnisreich von Phoenix wird in läuten.

==== ==== EDIT

ich das Problem mit dem Beispiel Phoenix herausgefunden. Es wiederholt sich nicht für den Nicht-Plus-Fall. Der Code sollte wie folgt sein:

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

Ob man bedenkt, dass einfacher oder komplizierter als die gerade Proto-Lösung ist für Sie zu entscheiden.

+0

Dank @Eric Niebler, das ist wirklich fantastisch - 2 Lösungen sind sehr großzügig. Ich mag die erste Verwendung von Proto, aber die Verwendung von Template-Spezialisierungen durch die zweite macht es schön modular; sagen, wenn ich den Fall für Regel :: dividiert wieder hinzufügen wollte. – user2023370