2013-03-27 4 views
5

Posso convertire un'espressione Boost Phoenix in una stringa rappresentativa di C++? Potrei avere:Posso stringificare un'espressione Boost Phoenix?

stringify(_1<_2); 

che potrebbe quindi produrre una stringa contenente qualcosa come:

template <class T1, class T2> 
struct foo { 
    auto operator()(T1 x1, T2 x2) 
    -> decltype(x1 < x2) 
    { return x1 < x2; } 
}; 

Apprezzo questo esempio ha alcune asperità, ma mi chiedo se qualcosa in questo senso è stato tentato?

+1

Perché vuoi farlo? –

+0

Ingannevole. 'stringify (_1 <2);' è _almost_ lo stesso ma ha un'espansione abbastanza diversa. (funzione unaria) – MSalters

+1

Non capisco la domanda. '_1 <_2' definisce una funzione che accetta due argomenti e restituisce un' bool'. Il tuo 'foo' definisce una funzione che prende due argomenti e restituisce' x' o 'y'. Qual è la relazione tra i due? –

risposta

3

Utilizzando la trasformazione eval che è possibile trovare here come "ispirazione".

Live example.

#include <iostream> 
#include <string> 
#include <sstream> 

#include <boost/phoenix.hpp> 
#include <boost/phoenix/core/arity.hpp> 
#include <boost/lexical_cast.hpp> 

namespace phx=boost::phoenix; 
namespace proto=boost::proto; 

struct do_print : proto::callable 
{ 
    typedef std::string result_type; 

    template <typename NotArgument> 
    std::string operator()(NotArgument n) 
    { 
     return boost::lexical_cast<std::string>(n); 
    } 

    template <int I> 
    std::string operator()(phx::argument<I>) 
    { 
     return std::string("x")+boost::lexical_cast<std::string>(I-1); 
    } 

#define UNARY_OP(TAG, OP)              \ 
    template<typename Arg>              \ 
    std::string operator()(proto::tag::TAG, Arg arg) const       \ 
    {                   \ 
     return std::string("(") + OP + arg + ")";               \ 
    }                   \ 
    /**/ 

#define BINARY_OP(TAG, OP)              \ 
    template<typename Left, typename Right>          \ 
    std::string operator()(proto::tag::TAG, Left left, Right right) const   \ 
    {                   \ 
     return std::string("(") + left + OP + right + ")";             \ 
    }                   \ 
    /**/ 

    UNARY_OP(negate, "-") 
    BINARY_OP(plus, "+") 
    BINARY_OP(minus, "-") 
    BINARY_OP(multiplies, "*") 
    BINARY_OP(divides, "/") 
    BINARY_OP(less, "<") 
    BINARY_OP(greater, ">") 
    /*... others ...*/ 
}; 

struct print_expression 
    : proto::or_< 
     proto::when<proto::terminal<proto::_>, do_print(proto::_value)> 
     , proto::otherwise<do_print(proto::tag_of<proto::_>(), print_expression(proto::pack(proto::_))...)> 
    > 
{}; 

struct do_get_arity : proto::callable 
{ 
    typedef int result_type; 

    template <typename NotArgument> 
    int operator()(NotArgument) 
    { 
     return 0; 
    } 

    template <int I> 
    int operator()(phx::argument<I>) 
    { 
     return I; 
    } 


    template<typename Tag, typename Arg>              
    int operator()(Tag, Arg arg) const       
    {                   
     return arg;               
    }                   
    /**/ 

    template<typename Tag, typename Left, typename Right>          
    int operator()(Tag, Left left, Right right) const   
    {                   
     return std::max(left,right);             \ 
    }                   

}; 

struct get_arity 
    : proto::or_< 
     proto::when<proto::terminal<proto::_>, do_get_arity(proto::_value)> 
     , proto::otherwise<do_get_arity(proto::tag_of<proto::_>(),get_arity(proto::pack(proto::_))...)> 
    > 
{}; 




template <typename Expr> 
std::string stringify(const Expr& expr, const std::string& name="foo") 
{ 
    std::stringstream result; 
    int current_arg; 
    int arity= get_arity()(expr); 

    result << "template <"; 

    for(current_arg=0;current_arg<arity-1; ++current_arg) 
     result << " typename T" << current_arg << ","; 
    result << " typename T" << current_arg; 

    result << " >\n"; 
    result << "struct " << name << " {\n\t"; 
    result << "auto operator()("; 

    for(current_arg=0;current_arg<arity-1; ++current_arg) 
     result << " T" << current_arg << " x" << current_arg << ","; 
    result << " T" << current_arg << " x" << current_arg; 
    result << ")\n\t\t-> typename std::remove_reference< decltype(" << print_expression()(expr) << ") >::type\n"; 
    result << "\t{ return " << print_expression()(expr) << "; }\n"; 
    result << "};\n"; 

    return result.str(); 
} 

int main() 
{ 
    using phx::placeholders::_1; 
    using phx::placeholders::_2; 
    using phx::placeholders::_3; 
    std::cout << stringify(-_1) << std::endl; 
    std::cout << stringify(_1+_2) << std::endl; 
    std::cout << stringify(_1+_2*_3) << std::endl; 

    std::cout << stringify((_1+_2)*_3) << std::endl; 
    std::cout << stringify(_1>2) << std::endl; 
    std::cout << stringify(_1*(-_2)) << std::endl; 
    return 0; 
} 
+0

Sembra interessante, ci investirò più tardi. – user2023370

+0

Molto ben fatto. – user2023370

-2

La stringificazione è una funzione di preprocessore: è possibile solo stringificare i token disponibili per il preprocessore. La gestione dei tipi (inclusa l'espansione di tipo ed espressione) viene eseguita dal compilatore che esegue dopo il il preprocessore, quindi non è possibile eseguire la stringa che si desidera.

+1

Penso che stai prendendo "stringify" troppo alla lettera. Non vedo l'ipotesi che ciò dovrebbe essere fatto dal preprocessore. Non è come se stesse chiedendo un 'STRINGIFY (_1 <_2)' dopo tutto - il minuscolo e il traling '; – MSalters

+0

Non c'è altro modo per stringere il codice C++ rispetto al preprocessore :) –

+1

Vero per codice C++ casuale, non per tutti i sottogruppi. Ad esempio, è banale stringificare il sottoinsieme di tutte le espressioni decimali. È ancora più semplice se si considera equivalente il codice equivalente, cioè se 'stringify (0x10)' può produrre '16'. – MSalters