2012-05-26 8 views
29

Powiel możliwe:
How do I expand a tuple into variadic template function's arguments?
“unpacking” a tuple to call a matching function pointerC++ 11: Mogę przejść z wielu argumentów do krotki, ale czy mogę przejść od krotki do wielu argumentów?

w C++ 11 szablonów, czy istnieje sposób na wykorzystanie krotki jako indywidualne args od A (ewentualnie szablonu) funkcji?

Przykład:
Powiedzmy mam tej funkcji:

void foo(int a, int b) 
{ 
} 

I mam krotki auto bar = std::make_tuple(1, 2).

Czy mogę użyć tego do zadzwonienia pod numer foo(1, 2) w sposób szablonowy?

Nie mam na myśli po prostu foo(std::get<0>(bar), std::get<1>(bar)), ponieważ chcę to zrobić w szablonie, który nie zna liczby argumentów.

Pełniejsze przykład:

template<typename Func, typename... Args> 
void caller(Func func, Args... args) 
{ 
    auto argtuple = std::make_tuple(args...); 
    do_stuff_with_tuple(argtuple); 
    func(insert_magic_here(argtuple)); // <-- this is the hard part 
} 

Należy zauważyć, że wolałbym, aby nie stworzyć jeden szablon, który pracuje dla jednego Arg, inny, który działa na dwa, etc ...

+3

Pewnie. Chcesz coś takiego jak 'template call (F f, Tuple const i t) {f (std :: get (t) ...); } '. Teraz po prostu wypełnij puste pola :-) –

+0

Masz na myśli pomijanie szablonów variadic i tworzenie wielu szablonów 'caller()' zamiast? – Thomas

+0

@Thomas: Będziesz musiał zrobić małą uprząż wysyłającą, która buduje pakiet liczb całkowitych 'N ...' i częściowo specjalizuje się, gdy 'N == std :: tuple_size :: value', chcesz wywołać oryginalna funkcja w sposób jaki zasugerowałem. –

Odpowiedz

55

Spróbuj czegoś tak:

// implementation details, users never invoke these directly 
namespace detail 
{ 
    template <typename F, typename Tuple, bool Done, int Total, int... N> 
    struct call_impl 
    { 
     static void call(F f, Tuple && t) 
     { 
      call_impl<F, Tuple, Total == 1 + sizeof...(N), Total, N..., sizeof...(N)>::call(f, std::forward<Tuple>(t)); 
     } 
    }; 

    template <typename F, typename Tuple, int Total, int... N> 
    struct call_impl<F, Tuple, true, Total, N...> 
    { 
     static void call(F f, Tuple && t) 
     { 
      f(std::get<N>(std::forward<Tuple>(t))...); 
     } 
    }; 
} 

// user invokes this 
template <typename F, typename Tuple> 
void call(F f, Tuple && t) 
{ 
    typedef typename std::decay<Tuple>::type ttype; 
    detail::call_impl<F, Tuple, 0 == std::tuple_size<ttype>::value, std::tuple_size<ttype>::value>::call(f, std::forward<Tuple>(t)); 
} 

przykład:

#include <cstdio> 
int main() 
{ 
    auto t = std::make_tuple("%d, %d, %d\n", 1,2,3); 
    call(std::printf, t); 
} 

Z dodatkową magią i użyciem std::result_of, prawdopodobnie możesz również spowodować, że cała rzecz zwróci poprawną wartość zwracaną.

+6

To jest w porządku jako ćwiczenie w szablonowej masturbacji, ale czy naprawdę chcesz napisać' call_impl :: wartość, std :: tuple_size :: wartość> :: wywołanie (f, std :: forward (t)) 'gdziekolwiek chcesz rozwinąć krotkę? –

+9

@ JonathanWakely: 'call_impl' jest szczegółem implementacji - użytkownicy nigdy nie nazywają tego bezpośrednio, globalne' call' jest wszystkim, z czym użytkownicy korzystają. Czy przykład jest niejasny? – ildjarn

+0

Ah nie, po prostu nie wyglądał prawidłowo, myślałem, że 'call' jest zamiennikiem dla OP 'caller', a więc nieporęczne użycie' call_impl' stało się w kodzie użytkownika. –

4

Utwórz „krotki Index” (krotki liczb całkowitych w czasie kompilacji), a następnie do przodu do innej funkcji, która wywodzi indeksy jako opakowanie parametrów i wykorzystuje je w dodatku zadzwonić std::get na krotki:

#include <redi/index_tuple.h> 

template<typename Func, typename Tuple, unsigned... I> 
    void caller_impl(Func func, Tuple&& t, redi::index_tuple<I...>) 
    { 
    func(std::get<I>(t)...); 
    } 

template<typename Func, typename... Args> 
    void caller(Func func, Args... args) 
    { 
    auto argtuple = std::make_tuple(args...); 
    do_stuff_with_tuple(argtuple); 
    typedef redi::to_index_tuple<Args...> indices; 
    caller_impl(func, argtuple, indices()); 
    } 

Moja implementacja index_tuple jest https://gitlab.com/redistd/redistd/blob/master/include/redi/index_tuple.h ale opiera się ona na aliasów szablonów, więc jeśli Twój kompilator nie obsługuje, że trzeba by zmodyfikować go używać C++ 03-style „typedefs szablon” i wymienić dwa ostatnie linie caller z

typedef typename redi::make_index_tuple<sizeof...(Args)>::type indices; 
    caller_impl(func, argtuple, indices()); 

Podobne narzędzie zostało znormalizowane jako std::index_sequence w C++ 14 (patrz index_seq.h dla samodzielnej implementacji C++ 11).

+0

czy możesz wyjaśnić składnię 'type :: template'? – ubik

+0

@ubik https://womble.decadent.org.uk/c++/template-faq.html#disambiguation –

+0

@ JonathanWakely Very nice. Może brakuje mi oczywistej sztuczki, ale czy jest jakikolwiek sposób na zastosowanie "krotki" w ten sposób, gdy dana funkcja jest konstruktorem ... lub czy to wymaga zbyt wiele magii? : D –

Powiązane problemy