Metafunción C ++ para determinar si un tipo es invocable

¿Es posible escribir una metafunción C ++ (0x) que determine si un tipo es invocable?

Por tipo invocable me refiero a un tipo de función, tipo de puntero a función, tipo de referencia de función (éstos son detectados por boost::function_types::is_callable_builtin ), tipos lambda y cualquier clase con un operator() sobrecargado operator() (y tal vez cualquier clase con un implícito operador de conversión a uno de estos, pero eso no es absolutamente necesario).

EDIT : la metafunción debe detectar la presencia de un operator() con cualquier firma, incluido un operator() plantilla operator() . Creo que esta es la parte difícil.

EDITAR : Aquí hay un caso de uso:

 template  struct and_predicate { template  bool operator()(const ArgT& arg) { return predicate1(arg) && predicate2(arg); } Predicate1 predicate1; Predicate2 predicate2; }; template  enable_if<ice_and<is_callable::value, is_callable::value>::value, and_predicate>::type operator&&(Predicate1 predicate1, Predicate2 predicate2) { return and_predicate{predicate1, predicate2}; } 

is_callable es lo que me gustaría implementar.

La presencia de un operador T :: no templado () para un tipo dado T puede detectarse mediante:

 template // detect regular operator() static char test(decltype(&C::operator())); template // worst match static char (&test(...))[2]; static const bool value = (sizeof( test(0) ) 

La presencia de un operador con plantilla se puede detectar mediante:

 template // detect 1-arg operator() static char test(int, decltype( (*(F*)0)( (*(A*)0) ) ) = 0); template // detect 2-arg operator() static char test(int, decltype( (*(F*)0)( (*(A*)0), (*(B*)0) ) ) = 0); // ... detect N-arg operator() template // worst match static char (&test(...))[2]; static const bool value = (sizeof( test(0) ) == 1) || (sizeof( test(0) ) == 1); // etc... 

Sin embargo, estos dos no funcionan bien juntos, ya que decltype (& C :: operator ()) producirá un error si C tiene un operador de llamada de función con plantilla. La solución es ejecutar primero la secuencia de verificaciones contra un operador con plantilla y verificar si hay un operador normal () solo si no se puede encontrar uno con plantilla. Esto se hace al especializar el cheque sin plantilla a un no-operativo si se encuentra uno con plantilla.

 template struct has_regular_call_operator { template // detect regular operator() static char test(decltype(&C::operator())); template // worst match static char (&test(...))[2]; static const bool value = (sizeof( test(0) ) == 1); }; template struct has_regular_call_operator { static const bool value = true; }; template struct has_call_operator { template // detect 1-arg operator() static char test(int, decltype( (*(F*)0)( (*(A*)0) ) ) = 0); template // detect 2-arg operator() static char test(int, decltype( (*(F*)0)( (*(A*)0), (*(B*)0) ) ) = 0); template // detect 3-arg operator() static char test(int, decltype( (*(F*)0)( (*(A*)0), (*(B*)0), (*(C*)0) ) ) = 0); template // worst match static char (&test(...))[2]; static const bool OneArg = (sizeof( test(0) ) == 1); static const bool TwoArg = (sizeof( test(0) ) == 1); static const bool ThreeArg = (sizeof( test(0) ) == 1); static const bool HasTemplatedOperator = OneArg || TwoArg || ThreeArg; static const bool value = has_regular_call_operator::value; }; 

Si la arity es siempre una, como se discutió anteriormente, entonces la verificación debería ser más simple. No veo la necesidad de ningún rasgo de tipo adicional o instalaciones de biblioteca para que esto funcione.

Con el advenimiento de nuestra experiencia colectiva en c ++ 11 (y más allá) es probable que sea hora de volver a esta pregunta.

Este pequeño rasgo de tipo parece funcionar para mí:

 #include  #include  template struct is_callable { template static auto test(U* p) -> decltype((*p)(std::declval()...), void(), std::true_type()); template static auto test(...) -> decltype(std::false_type()); static constexpr bool value = decltype(test(0))::value; }; 

Que podemos probar así:

 template::value>::type* = nullptr> void test_call(F, Args&&...args) { std::cout << "callable" << std::endl; } template::value>::type* = nullptr> void test_call(F, Args&&...args) { std::cout << "not callable" << std::endl; } extern void f3(int, const std::string&) { } int main() { auto f1 = [](int, std::string) {}; test_call(f1, 0, "hello"); test_call(f1, "bad", "hello"); std::function f2; test_call(f2, 0, "hello"); test_call(f2, "bad", "hello"); test_call(f3, 0, "hello"); test_call(f3, "bad", "hello"); } 

Rendimiento esperado:

 callable not callable callable not callable callable not callable 

Esta es una pregunta realmente interesante. Me ha intrigado mucho.

Creo que logré hacer una variación del código de Crazy Eddie que permitirá cualquier número de parámetros, sin embargo, usa plantillas variadic y requiere especificar los parámetros con los que se espera que se llame el objeto “invocable”. Para resumir, lo tengo funcionando y trabajando como esperaba en gcc 4.6.0:

EDITAR: También se podría usar la utilidad std :: result_of, pero no funciona porque requiere un typename para desambiguar el std::result_of<..>::type que rompe el Sfinae.

 #include  #include  template < typename PotentiallyCallable, typename... Args> struct is_callable { typedef char (&no) [1]; typedef char (&yes) [2]; template < typename T > struct dummy; template < typename CheckType> static yes check(dummy()(std::declval()...))> *); template < typename CheckType> static no check(...); enum { value = sizeof(check(0)) == sizeof(yes) }; }; int f1(int,double) { return 0; }; typedef int(*f1_type)(int,double) ; //this is just to have a type to feed the template. struct Foo { }; struct Bar { template  void operator()(T) { }; }; int main() { if( is_callable::value ) std::cout << "f1 is callable!" << std::endl; if( is_callable::value ) std::cout << "Foo is callable!" << std::endl; if( is_callable::value ) std::cout << "Bar is callable with int!" << std::endl; if( is_callable::value ) std::cout << "Bar is callable with double!" << std::endl; }; 

Espero que esto sea lo que estás buscando porque no creo que sea posible hacer mucho más.

EDITAR: para su caso de uso, es una solución parcial, pero podría ayudar:

 template  struct and_predicate { template  enable_if::value, is_callable::value>::value, bool>::type operator()(const ArgT& arg) { return predicate1(arg) && predicate2(arg); } Predicate1 predicate1; Predicate2 predicate2; }; template  enable_if::value, is_callable< Predicate2, boost::any >::value>::value, and_predicate>::type operator&&(Predicate1 predicate1, Predicate2 predicate2) { return and_predicate{predicate1, predicate2}; }