c++ - examples - Inferir la firma de llamada de una lambda o arbitraria invocable para "make_function"
lambda examples c++ (3)
En algunas situaciones es deseable poder escribir y borrar un llamable (por ejemplo, función, puntero de función, instancia de objeto con operator()
, lambda, mem_fn
), por ejemplo, en el uso de adaptadores Boost con C ++ 11 lambdas donde una copia asignable y se requiere un tipo construible por defecto.
std::function
sería ideal, pero parece que no hay manera de determinar automáticamente con qué firmas crear una instancia de la plantilla de clase std::function
. ¿Hay una manera fácil de obtener la firma de la función de un invocable arbitrario y / o envolverlo en una instancia de instanciación std::function
apropiada (es decir, una plantilla de función make_function
)?
Específicamente, estoy buscando uno u otro de
template<typename F> using get_signature = ...;
template<typename F> std::function<get_signature<F>> make_function(F &&f) { ... }
tal que make_function([](int i) { return 0; })
devuelve una std::function<int(int)>
. Obviamente, no se esperaría que esto funcione si una instancia es invocable con más de una firma (por ejemplo, objetos con más de una, plantilla o operator()
parámetros por defecto operator()
s).
Boost está bien, aunque se prefieren las soluciones que no son Boost que no son excesivamente complejas.
Editar: respondiendo mi propia pregunta.
He encontrado una solución bastante desagradable que no es de biblioteca, usando el hecho de que lambdas tiene operator()
:
template<typename T> struct remove_class { };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...)> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) const> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) volatile> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) const volatile> { using type = R(A...); };
template<typename T>
struct get_signature_impl { using type = typename remove_class<
decltype(&std::remove_reference<T>::type::operator())>::type; };
template<typename R, typename... A>
struct get_signature_impl<R(A...)> { using type = R(A...); };
template<typename R, typename... A>
struct get_signature_impl<R(&)(A...)> { using type = R(A...); };
template<typename R, typename... A>
struct get_signature_impl<R(*)(A...)> { using type = R(A...); };
template<typename T> using get_signature = typename get_signature_impl<T>::type;
template<typename F> using make_function_type = std::function<get_signature<F>>;
template<typename F> make_function_type<F> make_function(F &&f) {
return make_function_type<F>(std::forward<F>(f)); }
¿Alguna idea donde esto puede ser simplificado o mejorado? ¿Algún error obvio?
Imposible. Es posible que pueda tomar la dirección del operator()
para algunos tipos, pero no para un llamador arbitrario, porque puede tener sobrecargas o parámetros de plantilla. Ya sea que funcione o no para una lambda seguramente no está bien definido, AFAIK.
Para las funciones lambda sin captura no genéricas no variadas, así como las funciones libres simples, se puede usar el siguiente enfoque:
#include <iostream>
#include <cstdlib>
template< typename L, typename R, typename ...A >
constexpr
auto // std::function< R (A...) >
to_function_pointer(L l, R (L::*)(A...) const)
{
return static_cast< R (*)(A...) >(l);
}
template< typename L, typename R, typename ...A >
constexpr
auto // std::function< R (A...) >
to_function_pointer(L l, R (L::*)(A...)) // for mutable lambda
{
return static_cast< R (*)(A...) >(l);
}
template< typename L >
constexpr
auto
to_function_pointer(L l)
{
return to_function_pointer(l, &L::operator ());
}
template< typename R, typename ...A >
constexpr
auto // std::function< R (A...) >
to_function_pointer(R (* fp)(A...))
{
return fp;
}
namespace
{
void f() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
}
int
main()
{
to_function_pointer([] () { std::cout << __PRETTY_FUNCTION__ << std::endl; })();
//to_function_pointer([&] () { std::cout << __PRETTY_FUNCTION__ << std::endl; })(); // can''t cast from non-captureless lambda to function pointer
to_function_pointer([] () mutable { std::cout << __PRETTY_FUNCTION__ << std::endl; })();
to_function_pointer(f)();
to_function_pointer(&f)();
return EXIT_SUCCESS;
}