2012-05-26 17 views
29

Duplicar posible:
How do I expand a tuple into variadic template function's arguments?
“unpacking” a tuple to call a matching function pointerC++ 11: Puedo pasar de múltiples args a tupla, pero ¿puedo pasar de tuple a multiple args?

En 11 plantillas de C++, hay una manera de usar una tupla como los args individuales de una función (posiblemente plantilla)?

Ejemplo:
Digamos que tengo esta función:

void foo(int a, int b) 
{ 
} 

y tengo la tupla auto bar = std::make_tuple(1, 2).

¿Puedo usar eso para llamar al foo(1, 2) de una manera templaty?

No me refiero simplemente a foo(std::get<0>(bar), std::get<1>(bar)) ya que quiero hacer esto en una plantilla que no sabe el número de args.

ejemplo más completo:

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 
} 

Debo señalar que yo preferiría para no crear una plantilla que funciona para una arg, otro que funciona para dos, etc ...

+3

Sure. Desea algo como 'template call (F f, Tuple const & t) {f (std :: get (t) ...); } ' Ahora solo complete los espacios en blanco :-) –

+0

¿Quiere decir omitir plantillas variadic y crear múltiples plantillas 'calller()' en su lugar? – Thomas

+0

@Thomas: Tendrás que hacer un pequeño arnés de envío que construya el paquete entero 'N ...', y se especialice parcialmente cuando 'N == std :: tuple_size :: value', quieras llamar al función original en la forma que sugerí. –

Respuesta

55

Prueba algo como esta:

// 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)); 
} 

Ejemplo:

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

Con algo de magia extra y usando std::result_of, probablemente también pueda hacer que todo devuelva el valor de retorno correcto.

+6

Esto está bien como ejercicio en la masturbación de plantillas, pero ¿realmente quieres tener que escribir' call_impl :: value, std :: tuple_size :: value> :: call (f, std :: forward (t)) 'en cualquier lugar que desee expandir una tupla? –

+9

@JonathanWakely: 'call_impl' es un detalle de implementación: los usuarios nunca lo llaman directamente, la' llamada' global es con la que todos los usuarios interactúan. ¿Es el ejemplo poco claro? – ildjarn

+0

Ah, no, simplemente no había mirado correctamente, pensé que 'call' era un reemplazo para 'la llamada' del OP y por lo tanto el uso difícil de' call_impl' ocurrió en el código del usuario. –

4

Crear una "tupla de índice" (una tupla de enteros en tiempo de compilación) y luego hacia adelante a otra función que deduce los índices como un paquete de parámetros y los utiliza en un pack de expansión para llamar std::get en la tupla:

#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()); 
    } 

Mi implementación de index_tuple está en https://gitlab.com/redistd/redistd/blob/master/include/redi/index_tuple.h pero depende de alias plantilla así que si su compilador no admite que se necesitaría para modificarlo para utilizar C++ 03-estilo "typedefs plantilla" y vuelva a colocar los dos últimos líneas de caller con

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

Una utilidad similar se estandarizó como std::index_sequence en C++ 14 (consulte index_seq.h para una implementación independiente de C++ 11).

+0

, ¿puede explicar la sintaxis 'type :: template'? – ubik

+0

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

+0

@JonathanWakely Muy bonito. Tal vez me estoy perdiendo un truco obvio, pero ¿hay alguna forma de aplicar una 'tupla' como esta cuando la función en cuestión es un constructor ... o es eso pedir un poco demasiada magia? : D –

Cuestiones relacionadas