5

Can I convert a Boost Phoenix expression into a representative string of C++? I could have:

stringify(_1<_2);

which might then produce a string containing something like:

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

I appreciate this example has some rough edges, but I wonder if anything along these lines has been attempted?

user2023370
  • 10,488
  • 6
  • 50
  • 83
  • 1
    Why would you want to do this? – Tom Knapen Mar 27 '13 at 22:17
  • Tricky. `stringify(_1<2);` is _almost_ the same but has quite a different expansion. (unary function) – MSalters Mar 28 '13 at 12:27
  • 1
    I don't understand the question. `_1<_2` defines a function that takes two args and returns a `bool`. Your `foo` defines a function that takes two args and returns either `x` or `y`. What's the relation between the two? – Eric Niebler Mar 28 '13 at 16:14
  • Oops, thanks Eric. I'll update the "string". – user2023370 Mar 28 '13 at 23:33
  • The example, as it now stands, assumes that `operator<(T1, T2)` returns `bool` (modulo conversion sequences). That's fairly reasonable, but not certain, and you probably can't generalize that. E.g. what's the type of `operator/(T1,T2)` ? – MSalters Mar 29 '13 at 10:18

2 Answers2

3

Using the transform eval that you can find here as "inspiration".

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

Stringification is a preprocessor feature - you can only stringify tokens that are available to the preprocessor. Type handling (including type and expression expansion) is done by the compiler which runs after the preprocessor thus there's no way you could perform the stringification you want.

Karel Petranek
  • 15,005
  • 4
  • 44
  • 68
  • 1
    I think you're taking "stringify" too literally. I don't see an assumption that this should be done by the preprocessor. It's not like he's asking for a `STRINGIFY(_1<_2)` after all - the lowercase and traling `;` are telling. – MSalters Mar 28 '13 at 12:25
  • There's no other way to stringify C++ code than using the preprocessor :) – Karel Petranek Mar 28 '13 at 21:39
  • 1
    True for random C++ code, not for all subsets. For instance, it's trivial to stringify the subset of all decimal number expressions. It's even easier if you consider equivalent code equal, i.e. if `stringify(0x10)` may produce `16`. – MSalters Mar 29 '13 at 07:38
  • True, but the OP wants to get the type of the expression. Unless you create a template specialization for every possible type, there's no way to create a portable stringify function. – Karel Petranek Mar 29 '13 at 09:49