programar parametros funciones ejemplos con como comandos codigos basicos c++ c++11 metaprogramming

parametros - Meta función de C++ para determinar si un tipo es llamable.



funciones en c++ ejemplos (3)

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

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

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

Que podemos probar así:

template<class F, class...Args, typename std::enable_if<is_callable<F, Args&&...>::value>::type* = nullptr> void test_call(F, Args&&...args) { std::cout << "callable" << std::endl; } template<class F, class...Args, typename std::enable_if<not is_callable<F, Args&&...>::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<void(int, const std::string&)> 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

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

Por tipo llamable me refiero a un tipo de función, tipo de puntero de función, tipo de referencia de función (estos 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).

EDITAR : 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 <typename Predicate1, typename Predicate2> struct and_predicate { template <typename ArgT> bool operator()(const ArgT& arg) { return predicate1(arg) && predicate2(arg); } Predicate1 predicate1; Predicate2 predicate2; }; template <typename Predicate1, typename Predicate2> enable_if<ice_and<is_callable<Predicate1>::value, is_callable<Predicate2>::value>::value, and_predicate<Predicate1, Predicate2>>::type operator&&(Predicate1 predicate1, Predicate2 predicate2) { return and_predicate<Predicate1, Predicate2>{predicate1, predicate2}; }

is_callable es lo que me gustaría implementar.


Esta es una pregunta muy interesante. Me ha intrigado mucho por ello.

Creo que logré realizar una variación en el código de Crazy Eddie que permitirá cualquier número de parámetros, sin embargo, usa plantillas variables y requiere especificar los parámetros con los que se espera que se llame el objeto "callable". En pocas palabras, lo hice funcionar y funcionar como se esperaba en gcc 4.6.0:

EDITAR: También se puede usar la utilidad std :: result_of, sin embargo, no funciona porque requiere un typename para desambiguar el tipo std::result_of<..>::type que rompe el Sfinae.

#include <iostream> #include <type_traits> 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<decltype(std::declval<CheckType>()(std::declval<Args>()...))> *); template < typename CheckType> static no check(...); enum { value = sizeof(check<PotentiallyCallable>(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 <typename T> void operator()(T) { }; }; int main() { if( is_callable<f1_type,int,double>::value ) std::cout << "f1 is callable!" << std::endl; if( is_callable<Foo>::value ) std::cout << "Foo is callable!" << std::endl; if( is_callable<Bar,int>::value ) std::cout << "Bar is callable with int!" << std::endl; if( is_callable<Bar,double>::value ) std::cout << "Bar is callable with double!" << std::endl; };

Espero que esto sea lo que está 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 <typename Predicate1, typename Predicate2> struct and_predicate { template <typename ArgT> enable_if<ice_and<is_callable<Predicate1,ArgT>::value, is_callable<Predicate2,ArgT>::value>::value, bool>::type operator()(const ArgT& arg) { return predicate1(arg) && predicate2(arg); } Predicate1 predicate1; Predicate2 predicate2; }; template <typename Predicate1, typename Predicate2> enable_if<ice_and<is_callable< Predicate1, boost::any >::value, is_callable< Predicate2, boost::any >::value>::value, and_predicate<Predicate1, Predicate2>>::type operator&&(Predicate1 predicate1, Predicate2 predicate2) { return and_predicate<Predicate1, Predicate2>{predicate1, predicate2}; }


La presencia de un operador T :: sin plantilla para un tipo T dado se puede detectar mediante:

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

La presencia de un operador con plantilla puede ser detectada por:

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

Sin embargo, estos dos no juegan muy 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 la secuencia de comprobaciones contra un operador con plantilla primero, y buscar un operador regular () si y solo si no se puede encontrar uno con plantilla. Esto se hace especializando el cheque sin plantilla a un no-op si se encuentra uno con plantilla.

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

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