2012-08-09 5 views
25

W niektórych sytuacjach pożądane jest, aby można było wpisać-wymazać wywoływalny (np. Funkcja, wskaźnik funkcji, instancja obiektu z operator(), lambda, mem_fn), na przykład w Using Boost adaptors with C++11 lambdas, gdzie wymagany jest typ możliwy do przypisania i domyślny.Wywołanie sygnatury wywołania lambda lub arbitralnie wywoływanej dla "make_funkcji"

std::function byłby idealny, ale wydaje się, że nie da się automatycznie ustalić, co podpis do instancji klasy szablon std::function z. Czy istnieje prosty sposób na uzyskanie sygnatury funkcji dowolnego wywołania i/lub zawinięcie jej w odpowiedniej instancji instancji std::function (tj. Szablonu funkcji make_function)?

Konkretnie szukam jednej lub drugiej

template<typename F> using get_signature = ...; 
template<typename F> std::function<get_signature<F>> make_function(F &&f) { ... } 

takie, że make_function([](int i) { return 0; }) Zwraca std::function<int(int)>. Oczywiście nie oczekuje się, że zadziała, jeśli wystąpienie będzie można wywołać z więcej niż jednym sygnaturą (np. Obiekty z więcej niż jednym szablonem lub domyślnym parametrem operator() s).

Zwiększenie jest w porządku, chociaż preferowane są rozwiązania nieobciążające, które nie są nadmiernie skomplikowane.


Edytuj: odpowiadając na własne pytanie.

+1

Jeśli tylko dbać o lambdas wtedy tak, można zmusić go do pracy. Odradzam to - podejrzewam, że introspekcyjne funktory są w pewien sposób antyplemne w C++ 11. Upewnij się również, że naprawdę potrzebujesz 'std :: function'. –

Odpowiedz

27

mam wymyślić dość bolesnego rozwiązania niż biblioteki, wykorzystując fakt, że lambdas mieć 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)); } 

Jakieś pomysły gdzie to można uprościć lub poprawić? Jakieś oczywiste błędy?

+0

+1. Czym jest parametr szablonu "get_signature_impl' bool"? –

+0

@LeonidVolnitsky jest całkowicie niepotrzebny; Usunąłem to. Myślę, że umieściłem to tam, kiedy pisałem 'get_signature' jako szablon rekursywny, ale teraz nie jest to potrzebne. – ecatmur

+0

dlaczego put & in decltype (& std :: remove_reference :: type :: operator())> :: type? – Guillaume07

2

Niemożliwe. Być może będziesz w stanie podać adres operator() dla typów niektórych typów, ale nie dla dowolnego wywoływanego, ponieważ może on mieć przeciążenia lub parametry szablonu. To, czy zadziała w przypadku lambda, z pewnością nie jest dobrze określone, AFAIK.

+0

Dobra uwaga, ale przede wszystkim interesuję się lambdami, gdzie istnieje dokładnie jeden 'operator()'. – ecatmur

+0

Cóż, na pewno nie jest to dobrze zdefiniowane, i nie jestem pewien, czy jest nawet zdefiniowany, że taki istnieje. – Puppy

+5

5.1.2: 5 * Typ zamknięcia dla wyrażenia lambda ma publiczny operator połączeń inline [...] * – ecatmur

0

Dla non-zmiennej liczbie argumentów nierodzajową funkcji captureless lambda, a także prostych bezpłatnych funkcji można stosować następujące podejście:

#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; 
} 
Powiązane problemy