Domanda

How can I obtain the types of the local variables used within a scoped Boost Phoenix statement? Using Phoenix and Proto I can extract numerous aspects of a Phoenix expression. For example, the following code exposes the arity (3); tag type (lambda_actor); and child-2 tag type (shift_left) of a Phoenix lambda expression:

#include <boost/proto/proto.hpp>
#include <boost/phoenix.hpp>

namespace proto   = boost::proto;
namespace phoenix = boost::phoenix;
using namespace phoenix::local_names;

struct Foo { const char str[6] = " Ok.\n"; };

int main(int argc, char *argv[])
{
  auto f = phoenix::lambda(_a = 17, _b = Foo()) [
    std::cout << _a << phoenix::bind(&Foo::str,_b)
  ];

  typedef typename proto::tag_of<decltype(                  f )>::type tag;
  typedef typename proto::tag_of<decltype(proto::child_c<2>(f))>::type tagc;
  static_assert(proto::arity_of<decltype(f)>::value==3,"");
  static_assert(std::is_same<tag,  phoenix::tag::lambda_actor>::value,"");
  static_assert(std::is_same<tagc, proto::tag::shift_left>::value,"");

  return 0;
}

How can I obtain the types of the local variables; in this example: _a and _b?

È stato utile?

Soluzione

I'm assuming that the types you are interested in are int and Foo, if this is not what you are after please disregard this answer. Looking over the documentation I haven't been able to find an easy way to get these types. But if you look at the type of the proto expression stored in f you can see that int and Foo can be found in a vector of actors inside the first child. The steps you need to make to finally get to the interesting types can be seen in the output and following that you can easily create a metafunction that does what you want. In this simple case get_local_type uses an index to access the type in question. If you want to access it via name (using _a) you should be able to get the index associated with a name using the data in map_local_index_to_tuple in the second child of the lambda expression. Using phoenix::detail::get_index that is defined here implementing get_local_type_from_name is also pretty easy. This metafunction expects the map mentioned above as its first argument and the type of the placeholder you want information from (more specifically it needs phoenix::detail::local<phoenix::local_names::_a_key>, and you can get that using proto::result_of::value on the type of the placeholder) as its second.

#include <iostream>
#include <typeinfo>
#include <string>
#include <cxxabi.h>
#include <type_traits>

#include <boost/proto/proto.hpp>
#include <boost/phoenix.hpp>

namespace proto   = boost::proto;
namespace phoenix = boost::phoenix;
using namespace phoenix::local_names;

namespace fusion = boost::fusion;

struct Foo { const char str[6] = " Ok.\n"; };



std::string demangle(const char* mangledName) {
    int status;
    char* result = abi::__cxa_demangle(mangledName, nullptr, nullptr, &status);
    switch(status) {
    case -1:
        std::cerr << "Out of memory!" << std::endl;
        exit(1);
    case -2:
        return mangledName;
    case -3: // Should never happen, but just in case?
        return mangledName;
    }
    std::string name = result;
    free(result);
    return name;
}


template <typename Lambda, int N>
struct get_local_type
{
    typedef typename proto::result_of::value<typename proto::result_of::child_c<Lambda,0>::type >::type vector_of_locals_type;
    typedef typename proto::result_of::value<typename fusion::result_of::at_c<vector_of_locals_type,N>::type >::type ref_type;
    typedef typename std::remove_reference<ref_type>::type type;
};

template <typename Lambda, typename Arg>
struct get_local_type_from_name
{
    typedef typename proto::result_of::value<Arg>::type local_name;
    typedef typename proto::result_of::value<typename proto::result_of::child_c<Lambda,1>::type >::type map_type;
    typedef typename phoenix::detail::get_index<map_type,local_name>::type index;
    typedef typename get_local_type<Lambda,index::value>::type type;

};



int main(int argc, char *argv[])
{
  auto f = phoenix::lambda(_b = 17, _a = Foo()) [
    std::cout << _b << phoenix::bind(&Foo::str,_a)
  ];

  std::cout << std::endl << "This is the whole lambda expression:" << std::endl;
  std::cout << std::endl << demangle(typeid(f).name()) << std::endl;
  std::cout << std::endl << "Take the first child:" << std::endl;
  std::cout << std::endl << demangle(typeid(proto::child_c<0>(f)).name()) << std::endl;
  std::cout << std::endl << "Then its value (this is a vector that contains the types you want):" << std::endl;
  std::cout << std::endl << demangle(typeid(proto::value(proto::child_c<0>(f))).name()) << std::endl;
  std::cout << std::endl << "Take the first element of that vector:" << std::endl;
  std::cout << std::endl << demangle(typeid(fusion::at_c<0>(proto::value(proto::child_c<0>(f)))).name()) << std::endl;
  std::cout << std::endl << "Take the value of that element:" << std::endl;
  std::cout << std::endl << demangle(typeid(proto::value(fusion::at_c<0>(proto::value(proto::child_c<0>(f))))).name()) << std::endl;


  typedef typename proto::tag_of<decltype(                  f )>::type tag;
  typedef typename proto::tag_of<decltype(proto::child_c<2>(f))>::type tagc;
  static_assert(proto::arity_of<decltype(f)>::value==3,"");
  static_assert(std::is_same<tag,  phoenix::tag::lambda_actor>::value,"");
  static_assert(std::is_same<tagc, proto::tag::shift_left>::value,"");

  typedef typename get_local_type<decltype(f),0>::type type_of_1st;
  typedef typename get_local_type<decltype(f),1>::type type_of_2nd;
  typedef typename get_local_type_from_name<decltype(f),_a_type>::type type_of_a;
  typedef typename get_local_type_from_name<decltype(f),decltype(_b)>::type type_of_b;
  static_assert(std::is_same<type_of_1st,int>::value,"");
  static_assert(std::is_same<type_of_2nd,Foo>::value,"");
  static_assert(std::is_same<type_of_a,Foo>::value,"");
  static_assert(std::is_same<type_of_b,int>::value,"");


  return 0;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top