2011-01-22 8 views
11

Estoy tratando de escribir una metafunción llamada signature_of que, dado el tipo de una función (puntero), functor o lambda, devuelve su firma.Cómo obtener la firma de una expresión de enlace C++ (...)

Esto es lo que tengo hasta ahora:

#include <boost/mpl/pop_front.hpp> 
#include <boost/mpl/push_front.hpp> 
#include <boost/function_types/is_member_function_pointer.hpp> 
#include <boost/function_types/function_type.hpp> 
#include <boost/function_types/result_type.hpp> 
#include <boost/function_types/parameter_types.hpp> 

#include <type_traits> 

template <typename F> 
struct signature_of_member 
{ 
    typedef typename boost::function_types::result_type<F>::type result_type; 
    typedef typename boost::function_types::parameter_types<F>::type parameter_types; 
    typedef typename boost::mpl::pop_front<parameter_types>::type base; 
    typedef typename boost::mpl::push_front<base, result_type>::type L; 
    typedef typename boost::function_types::function_type<L>::type type; 
}; 

template <typename F, bool is_class> 
struct signature_of_impl 
{ 
    typedef typename boost::function_types::function_type<F>::type type; 
}; 

template <typename F> 
struct signature_of_impl<F, true> 
{ 
    typedef typename signature_of_member<decltype(&F::operator())>::type type; 
}; 

template <typename F> 
struct signature_of 
{ 
    typedef typename signature_of_impl<F, std::is_class<F>::value>::type type; 
}; 

Es bastante sencillo, con la mayor parte del trabajo real que está realizando la biblioteca de impulso :: function_types. La idea general es:

  • uso std :: is_class para discriminar entre funciones integradas (incluyendo lambdas) y funtores
  • para este tipo de funciones integradas, el uso impulso :: :: function_types tipo_función para obtener su firma
  • de funtores, obtener el tipo de su operador(), obtener su firma, y ​​el médico para eliminar el "este" parámetro

esto funciona para las funciones incorporadas:

int f(int); 
typedef signature_of<decltype(f)>::type Sig; // Sig is int(int) 

de lambdas:

auto f = [](int) { return 0; } 
typedef signature_of<decltype(f)>::type Sig; // Sig is int(int) 

y de funtores:

struct A 
{ 
    int operator()(int); 
}; 
typedef signature_of<A>::type Sig; // Sig is int(int) 

Sin embargo, no funciona para bind() expresiones (que son un caso especial de funtores). Si intento esto:

#include <functional> 
int g(int); 
typedef signature_of<decltype(std::bind(g, 0))>::type Sig; 

me sale un error de compilación:

In file included from test.cpp:3:0: 
signature_of.hpp: In instantiation of 'signature_of_impl< 
     _Bind<int (*(int))(int)>, true 
    >': 
signature_of.hpp:45:74: instantiated from 'signature_of< 
     _Bind<int (*(int))(int)> 
    >' 
test.cpp:21:52: instantiated from here 
signature_of.hpp:39:74: error: type of '& _Bind< 
     int (*)(int)({int} ...) 
    >::operator()' is unknown 

El problema es que el operador() del funtor devuelto por bind() es una plantilla, por lo que su tipo no pueden ser determinado.

¿Es posible obtener la firma de una expresión bind() de otra manera?

+0

VC10 no puede compilar esto, los errores son estúpidos como habitualmente ('error C2039: 'tipo': no ​​es miembro de 'boost :: function_types :: result_type ' '), no le gusta esta especialización:' typedef typename signature_of_member :: type type; ' –

+0

@Andy T: cómo se convierte a std :: function sin mencionar el parámetro template de std :: function, que * es * la firma ? – HighCommander4

+0

Sí, es mi culpa, borrar ... –

Respuesta

11

Tiene más problemas que el hecho de que el operador de un encuadernador() está modelado, también tiene un recuento de parámetros arbitrario. Recuerde que se supone que puede invocar el resultado de vincular con cualquier cantidad de argumentos adicionales. Por ejemplo:

int f(int); 

auto bound = boost::bind(f, _2); 

bound ahora pueden ser llamados con cualquier número de argumentos de 2 o más, sólo el segundo es en realidad reenviado a la función.

Así que, básicamente, como dice otra respuesta, este objeto no tiene firma. Su firma se define solo por cómo se usa.

+0

Buen punto, en realidad no sabía que podrías usar bind() de esta manera. – HighCommander4

4

Si el operador tiene plantilla, entonces no tiene una firma.

+0

O mirándolo de manera ligeramente diferente, no tiene una firma única. 'operator()' se puede instanciar con diferentes argumentos de plantilla, y participan en la firma de esa instanciación de 'operator()'. –

+2

Sí, pero si g tiene signature int (int), conceptualmente, bind (g, 0) debe tener signature int (void). El hecho de que la implementación std :: bind use un operador con plantilla() es solo un detalle de implementación. – HighCommander4

+0

@ HighCommander4: ¿Conceptualmente? ¿A quien le importa? El hecho es que no va a suceder. Si 'bind' produce un functor con un operador de plantilla(), estás jodido, y puedes diseñar lo que quieras, y eso no va a cambiar eso. – Puppy

Cuestiones relacionadas