Вывод подписи вызова лямбды или произвольного вызова для "make_function"
В некоторых ситуациях желательно иметь возможность печатать вызываемое (например, функцию, указатель на функцию, экземпляр объекта с помощью operator()
лямбда, mem_fn
), например, в Использование адаптеров Boost с лямбдами C++11, где требуется тип, назначаемый для копирования и конструируемый по умолчанию.
std::function
было бы идеально, но, кажется, нет никакого способа автоматически определить, какую сигнатуру создать экземпляр шаблона класса std::function
с. Есть ли простой способ получить сигнатуру функции произвольного вызываемого и / или обернуть ее в соответствующий std::function
экземпляр экземпляра (т.е. make_function
шаблон функции)?
В частности, я ищу один или другой из
template<typename F> using get_signature = ...;
template<typename F> std::function<get_signature<F>> make_function(F &&f) { ... }
такой, что make_function([](int i) { return 0; })
возвращает std::function<int(int)>
, Очевидно, что это не будет работать, если экземпляр может вызываться с более чем одной подписью (например, объекты с более чем одной, шаблоном или параметром по умолчанию) operator()
с).
Boost - это хорошо, хотя предпочтительны решения, не являющиеся чрезмерно сложными.
Изменить: ответить на мой собственный вопрос.
3 ответа
Я придумал довольно неприятное небиблиотечное решение, используя тот факт, что лямбды 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)); }
Есть идеи, где это можно упростить или улучшить? Есть очевидные ошибки?
Невозможно. Вы можете взять адрес operator()
для некоторых типов, но не для произвольного вызова, потому что он вполне может иметь перегрузки или параметры шаблона. Будет ли это работать для лямбды, несомненно, не очень ясно, AFAIK.
Для невариантных неуниверсальных лямбда-функций без захвата, а также простых свободных функций можно использовать следующий подход:
#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;
}