una script posicional pasar parametros parametro opcionales lista funciones explicacion ejecutar diccionario con como argumentos argumento c++ c++11 arguments tuples

c++ - script - kwargs python explicacion



¿Cómo puedo expandir una tupla en los argumentos de la función de plantilla variadic? (13)

Considere el caso de una función de plantilla con argumentos de plantilla variadica:

template<typename Tret, typename... T> Tret func(const T&... t);

Ahora, tengo una tupla de valores. ¿Cómo func() usando los valores de tupla como argumentos? He leído sobre el objeto de función bind() , con la función call() y también la función apply() en diferentes documentos ahora obsoletos. La implementación GNU GCC 4.4 parece tener una función call() en la clase bind() , pero hay muy poca documentación sobre el tema.

Algunas personas sugieren hacks recursivos escritos a mano, pero el verdadero valor de los argumentos de plantillas variadic es poder usarlos en casos como el anterior.

¿Alguien tiene una solución para el is, o insinúa dónde leer sobre él?


1) si tiene una estructura de paquete de parámetros preparada como argumento de función, puede usar std :: tie como este:

template <class... Args> void tie_func(std::tuple<Args...> t, Args&... args) { std::tie<Args...>(args...) = t; } int main() { std::tuple<int, double, std::string> t(2, 3.3, "abc"); int i; double d; std::string s; tie_func(t, i, d, s); std::cout << i << " " << d << " " << s << std::endl; }

2) si no tienes un arpa parampack lista para usar, tendrás que desenrollar la tupla como esta

#include <tuple> #include <functional> #include <iostream> template<int N> struct apply_wrap { template<typename R, typename... TupleArgs, typename... UnpackedArgs> static R applyTuple( std::function<R(TupleArgs...)>& f, const std::tuple<TupleArgs...>& t, UnpackedArgs... args ) { return apply_wrap<N-1>::applyTuple( f, t, std::get<N-1>( t ), args... ); } }; template<> struct apply_wrap<0> { template<typename R, typename... TupleArgs, typename... UnpackedArgs> static R applyTuple( std::function<R(TupleArgs...)>& f, const std::tuple<TupleArgs...>&, UnpackedArgs... args ) { return f( args... ); } }; template<typename R, typename... TupleArgs> R applyTuple( std::function<R(TupleArgs...)>& f, std::tuple<TupleArgs...> const& t ) { return apply_wrap<sizeof...(TupleArgs)>::applyTuple( f, t ); } int fac(int n) { int r=1; for(int i=2; i<=n; ++i) r *= i; return r; } int main() { auto t = std::make_tuple(5); auto f = std::function<decltype(fac)>(&fac); cout << applyTuple(f, t); }


Ampliando la solución de @David, puede escribir una plantilla recursiva que

  1. No usa la semántica de integer_sequence enteros (demasiado verbosa, imo)
  2. No utiliza un parámetro de plantilla temporal adicional int N para contar iteraciones recursivas
  3. (Opcional para funtores estáticos / globales) utiliza el funtor como parámetro de plantilla para la optimización en tiempo de compilación

P.ej:

template <class F, F func> struct static_functor { template <class... T, class... Args_tmp> static inline auto apply(const std::tuple<T...>& t, Args_tmp... args) -> decltype(func(std::declval<T>()...)) { return static_functor<F,func>::apply(t, args..., std::get<sizeof...(Args_tmp)>(t)); } template <class... T> static inline auto apply(const std::tuple<T...>& t, T... args) -> decltype(func(args...)) { return func(args...); } }; static_functor<decltype(&myFunc), &myFunc>::apply(my_tuple);

Alternativamente, si su functor no está definido en tiempo de compilación (por ejemplo, una instancia de functor no constexpr o una expresión lambda), puede usarlo como un parámetro de función en lugar de un parámetro de plantilla de clase, y de hecho eliminar completamente la clase contenedora :

template <class F, class... T, class... Args_tmp> inline auto apply_functor(F&& func, const std::tuple<T...>& t, Args_tmp... args) -> decltype(func(std::declval<T>()...)) { return apply_functor(func, t, args..., std::get<sizeof...(Args_tmp)>(t)); } template <class F, class... T> inline auto apply_functor(F&& func, const std::tuple<T...>& t, T... args) -> decltype(func(args...)) { return func(args...); } apply_functor(&myFunc, my_tuple);

Para callables de puntero a miembro, puede ajustar cualquiera de los fragmentos de código anteriores de forma similar a la respuesta de @David.

Explicación

En referencia a la segunda parte de código, hay dos funciones de plantilla: la primera toma el funcctor func , la tupla t con tipos T... , y un paquete de parámetros args de tipos Args_tmp... When called, it recursively adds the objects from t to the parameter pack one at a time, from beginning ( 0 ) to end, and calls the function again with the new incremented parameter pack.

The second function''s signature is almost identical to the first, except that it uses type T... for the parameter pack args . Thus, once args in the first function is completely filled with the values from t , it''s type will be T... (in psuedo-code, typeid(T...) == typeid(Args_tmp...) ), and thus the compiler will instead call the second overloaded function, which in turn calls func(args...) .

The code in the static functor example works identically, with the functor instead used as a class template argument.


Aquí está mi código si alguien está interesado

Básicamente en tiempo de compilación, el compilador desenrollará de forma recursiva todos los argumentos en varias llamadas a función inclusiva <N> -> llamadas <N-1> -> llamadas ... -> llamadas <0> que es la última y el compilador optimizará las diversas llamadas a funciones intermedias solo conservan la última, que es el equivalente de func (arg1, arg2, arg3, ...)

Se proporcionan 2 versiones, una para una función llamada en un objeto y otra para una función estática.

#include <tr1/tuple> /** * Object Function Tuple Argument Unpacking * * This recursive template unpacks the tuple parameters into * variadic template arguments until we reach the count of 0 where the function * is called with the correct parameters * * @tparam N Number of tuple arguments to unroll * * @ingroup g_util_tuple */ template < uint N > struct apply_obj_func { template < typename T, typename... ArgsF, typename... ArgsT, typename... Args > static void applyTuple( T* pObj, void (T::*f)( ArgsF... ), const std::tr1::tuple<ArgsT...>& t, Args... args ) { apply_obj_func<N-1>::applyTuple( pObj, f, t, std::tr1::get<N-1>( t ), args... ); } }; //----------------------------------------------------------------------------- /** * Object Function Tuple Argument Unpacking End Point * * This recursive template unpacks the tuple parameters into * variadic template arguments until we reach the count of 0 where the function * is called with the correct parameters * * @ingroup g_util_tuple */ template <> struct apply_obj_func<0> { template < typename T, typename... ArgsF, typename... ArgsT, typename... Args > static void applyTuple( T* pObj, void (T::*f)( ArgsF... ), const std::tr1::tuple<ArgsT...>& /* t */, Args... args ) { (pObj->*f)( args... ); } }; //----------------------------------------------------------------------------- /** * Object Function Call Forwarding Using Tuple Pack Parameters */ // Actual apply function template < typename T, typename... ArgsF, typename... ArgsT > void applyTuple( T* pObj, void (T::*f)( ArgsF... ), std::tr1::tuple<ArgsT...> const& t ) { apply_obj_func<sizeof...(ArgsT)>::applyTuple( pObj, f, t ); } //----------------------------------------------------------------------------- /** * Static Function Tuple Argument Unpacking * * This recursive template unpacks the tuple parameters into * variadic template arguments until we reach the count of 0 where the function * is called with the correct parameters * * @tparam N Number of tuple arguments to unroll * * @ingroup g_util_tuple */ template < uint N > struct apply_func { template < typename... ArgsF, typename... ArgsT, typename... Args > static void applyTuple( void (*f)( ArgsF... ), const std::tr1::tuple<ArgsT...>& t, Args... args ) { apply_func<N-1>::applyTuple( f, t, std::tr1::get<N-1>( t ), args... ); } }; //----------------------------------------------------------------------------- /** * Static Function Tuple Argument Unpacking End Point * * This recursive template unpacks the tuple parameters into * variadic template arguments until we reach the count of 0 where the function * is called with the correct parameters * * @ingroup g_util_tuple */ template <> struct apply_func<0> { template < typename... ArgsF, typename... ArgsT, typename... Args > static void applyTuple( void (*f)( ArgsF... ), const std::tr1::tuple<ArgsT...>& /* t */, Args... args ) { f( args... ); } }; //----------------------------------------------------------------------------- /** * Static Function Call Forwarding Using Tuple Pack Parameters */ // Actual apply function template < typename... ArgsF, typename... ArgsT > void applyTuple( void (*f)(ArgsF...), std::tr1::tuple<ArgsT...> const& t ) { apply_func<sizeof...(ArgsT)>::applyTuple( f, t ); } // *************************************** // Usage // *************************************** template < typename T, typename... Args > class Message : public IMessage { typedef void (T::*F)( Args... args ); public: Message( const std::string& name, T& obj, F pFunc, Args... args ); private: virtual void doDispatch( ); T* pObj_; F pFunc_; std::tr1::tuple<Args...> args_; }; //----------------------------------------------------------------------------- template < typename T, typename... Args > Message<T, Args...>::Message( const std::string& name, T& obj, F pFunc, Args... args ) : IMessage( name ), pObj_( &obj ), pFunc_( pFunc ), args_( std::forward<Args>(args)... ) { } //----------------------------------------------------------------------------- template < typename T, typename... Args > void Message<T, Args...>::doDispatch( ) { try { applyTuple( pObj_, pFunc_, args_ ); } catch ( std::exception& e ) { } }


En C ++ 17 puedes hacer esto:

std::apply(the_function, the_tuple);

Esto ya funciona en Clang ++ 3.9, usando std :: experimental :: apply.

Respondiendo al comentario que dice que esto no funcionará si la the_function es the_function , lo siguiente es una the_function :

#include <tuple> template <typename T, typename U> void my_func(T &&t, U &&u) {} int main(int argc, char *argv[argc]) { std::tuple<int, float> my_tuple; std::apply([](auto &&... args) { my_func(args...); }, my_tuple); return 0; }

Este trabajo alternativo es una solución simplificada al problema general de pasar los conjuntos de sobrecarga y la plantilla de función donde se esperaría una función. Aquí se presenta la solución general (una que se ocupa del reenvío perfecto, la consistencia y la no excepción): https://blog.tartanllama.xyz/passing-overload-sets/ .


En C ++ hay muchas formas de expandir / desempaquetar tuplas y aplicar esos elementos de tupla a una función de plantilla variadica. Aquí hay una pequeña clase de ayuda que crea una matriz de índices. Se usa mucho en la metaprogramación de plantillas:

// ------------- UTILITY--------------- template<int...> struct index_tuple{}; template<int I, typename IndexTuple, typename... Types> struct make_indexes_impl; template<int I, int... Indexes, typename T, typename ... Types> struct make_indexes_impl<I, index_tuple<Indexes...>, T, Types...> { typedef typename make_indexes_impl<I + 1, index_tuple<Indexes..., I>, Types...>::type type; }; template<int I, int... Indexes> struct make_indexes_impl<I, index_tuple<Indexes...> > { typedef index_tuple<Indexes...> type; }; template<typename ... Types> struct make_indexes : make_indexes_impl<0, index_tuple<>, Types...> {};

Ahora el código que hace el trabajo no es tan grande:

// ----------UNPACK TUPLE AND APPLY TO FUNCTION --------- #include <tuple> #include <iostream> using namespace std; template<class Ret, class... Args, int... Indexes > Ret apply_helper( Ret (*pf)(Args...), index_tuple< Indexes... >, tuple<Args...>&& tup) { return pf( forward<Args>( get<Indexes>(tup))... ); } template<class Ret, class ... Args> Ret apply(Ret (*pf)(Args...), const tuple<Args...>& tup) { return apply_helper(pf, typename make_indexes<Args...>::type(), tuple<Args...>(tup)); } template<class Ret, class ... Args> Ret apply(Ret (*pf)(Args...), tuple<Args...>&& tup) { return apply_helper(pf, typename make_indexes<Args...>::type(), forward<tuple<Args...>>(tup)); }

La prueba se muestra a continuación:

// --------------------- TEST ------------------ void one(int i, double d) { std::cout << "function one(" << i << ", " << d << ");/n"; } int two(int i) { std::cout << "function two(" << i << ");/n"; return i; } int main() { std::tuple<int, double> tup(23, 4.5); apply(one, tup); int d = apply(two, std::make_tuple(2)); return 0; }

No soy un gran experto en otros idiomas, pero supongo que si estos idiomas no tienen esa funcionalidad en su menú, no hay forma de hacerlo. Al menos con C ++ puedes, y creo que no es tan complicado ...


Encuentro que esta es la solución más elegante (y está reenviada de manera óptima):

#include <cstddef> #include <tuple> #include <type_traits> #include <utility> template<size_t N> struct Apply { template<typename F, typename T, typename... A> static inline auto apply(F && f, T && t, A &&... a) -> decltype(Apply<N-1>::apply( ::std::forward<F>(f), ::std::forward<T>(t), ::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)... )) { return Apply<N-1>::apply(::std::forward<F>(f), ::std::forward<T>(t), ::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)... ); } }; template<> struct Apply<0> { template<typename F, typename T, typename... A> static inline auto apply(F && f, T &&, A &&... a) -> decltype(::std::forward<F>(f)(::std::forward<A>(a)...)) { return ::std::forward<F>(f)(::std::forward<A>(a)...); } }; template<typename F, typename T> inline auto apply(F && f, T && t) -> decltype(Apply< ::std::tuple_size< typename ::std::decay<T>::type >::value>::apply(::std::forward<F>(f), ::std::forward<T>(t))) { return Apply< ::std::tuple_size< typename ::std::decay<T>::type >::value>::apply(::std::forward<F>(f), ::std::forward<T>(t)); }

Ejemplo de uso:

void foo(int i, bool b); std::tuple<int, bool> t = make_tuple(20, false); void m() { apply(&foo, t); }

Desafortunadamente GCC (4.6 al menos) no puede compilar esto con "lo siento, no implementado: sobrecarga de destrucción" (lo que simplemente significa que el compilador aún no implementa completamente la especificación C ++ 11), y dado que usa plantillas variadas, no lo hará trabajar en MSVC, por lo que es más o menos inútil. Sin embargo, una vez que haya un compilador que admita la especificación, será el mejor enfoque en mi humilde opinión. (Nota: no es tan difícil modificar esto para que pueda evitar las deficiencias en GCC, o implementarlo con Boost Preprocessor, pero arruina la elegancia, así que esta es la versión que estoy publicando).

GCC 4.7 ahora es compatible con este código muy bien.

Editar: añadido hacia adelante en función de la llamada a la función real para admitir la forma de referencia de valor * esto en caso de que esté utilizando clang (o si alguien más realmente consigue agregarlo).

Editar: se agregó la pérdida de avance hacia adelante alrededor del objeto de función en el cuerpo de la función aplicar no miembro. Gracias a pheedbaq por señalar que faltaba.

Editar: Y aquí está la versión C ++ 14 ya que es mucho más agradable (no se compila todavía):

#include <cstddef> #include <tuple> #include <type_traits> #include <utility> template<size_t N> struct Apply { template<typename F, typename T, typename... A> static inline auto apply(F && f, T && t, A &&... a) { return Apply<N-1>::apply(::std::forward<F>(f), ::std::forward<T>(t), ::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)... ); } }; template<> struct Apply<0> { template<typename F, typename T, typename... A> static inline auto apply(F && f, T &&, A &&... a) { return ::std::forward<F>(f)(::std::forward<A>(a)...); } }; template<typename F, typename T> inline auto apply(F && f, T && t) { return Apply< ::std::tuple_size< ::std::decay_t<T> >::value>::apply(::std::forward<F>(f), ::std::forward<T>(t)); }

Aquí hay una versión para funciones de miembros (¡no probada mucho!):

using std::forward; // You can change this if you like unreadable code or care hugely about namespace pollution. template<size_t N> struct ApplyMember { template<typename C, typename F, typename T, typename... A> static inline auto apply(C&& c, F&& f, T&& t, A&&... a) -> decltype(ApplyMember<N-1>::apply(forward<C>(c), forward<F>(f), forward<T>(t), std::get<N-1>(forward<T>(t)), forward<A>(a)...)) { return ApplyMember<N-1>::apply(forward<C>(c), forward<F>(f), forward<T>(t), std::get<N-1>(forward<T>(t)), forward<A>(a)...); } }; template<> struct ApplyMember<0> { template<typename C, typename F, typename T, typename... A> static inline auto apply(C&& c, F&& f, T&&, A&&... a) -> decltype((forward<C>(c)->*forward<F>(f))(forward<A>(a)...)) { return (forward<C>(c)->*forward<F>(f))(forward<A>(a)...); } }; // C is the class, F is the member function, T is the tuple. template<typename C, typename F, typename T> inline auto apply(C&& c, F&& f, T&& t) -> decltype(ApplyMember<std::tuple_size<typename std::decay<T>::type>::value>::apply(forward<C>(c), forward<F>(f), forward<T>(t))) { return ApplyMember<std::tuple_size<typename std::decay<T>::type>::value>::apply(forward<C>(c), forward<F>(f), forward<T>(t)); }

// Example: class MyClass { public: void foo(int i, bool b); }; MyClass mc; std::tuple<int, bool> t = make_tuple(20, false); void m() { apply(&mc, &MyClass::foo, t); }


Estoy evaluando MSVS 2013RC, y no compiló algunas de las soluciones anteriores propuestas aquí en algunos casos. Por ejemplo, MSVS no compilará retornos "automáticos" si hay demasiados parámetros de función, debido a un límite de imbricación del espacio de nombres (envié esa información a Microsoft para que se corrigiera). En otros casos, necesitamos acceso a la devolución de la función, aunque eso también se puede hacer con un lamda: los dos ejemplos siguientes dan el mismo resultado.

apply_tuple([&ret1](double a){ret1 = cos(a); }, std::make_tuple<double>(.2)); ret2 = apply_tuple((double(*)(double))cos, std::make_tuple<double>(.2));

Y gracias de nuevo a aquellos que publicaron respuestas aquí antes que yo, no habría llegado a esto sin ella ... así que aquí está:

template<size_t N> struct apply_impl { template<typename F, typename T, typename... A> static inline auto apply_tuple(F&& f, T&& t, A&&... a) -> decltype(apply_impl<N-1>::apply_tuple(std::forward<F>(f), std::forward<T>(t), std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...)) { return apply_impl<N-1>::apply_tuple(std::forward<F>(f), std::forward<T>(t), std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...); } template<typename C, typename F, typename T, typename... A> static inline auto apply_tuple(C*const o, F&& f, T&& t, A&&... a) -> decltype(apply_impl<N-1>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t), std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...)) { return apply_impl<N-1>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t), std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...); } }; // This is a work-around for MSVS 2013RC that is required in some cases #if _MSC_VER <= 1800 /* update this when bug is corrected */ template<> struct apply_impl<6> { template<typename F, typename T, typename... A> static inline auto apply_tuple(F&& f, T&& t, A&&... a) -> decltype(std::forward<F>(f)(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)), std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...)) { return std::forward<F>(f)(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)), std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...); } template<typename C, typename F, typename T, typename... A> static inline auto apply_tuple(C*const o, F&& f, T&& t, A&&... a) -> decltype((o->*std::forward<F>(f))(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)), std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...)) { return (o->*std::forward<F>(f))(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)), std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...); } }; #endif template<> struct apply_impl<0> { template<typename F, typename T, typename... A> static inline auto apply_tuple(F&& f, T&&, A&&... a) -> decltype(std::forward<F>(f)(std::forward<A>(a)...)) { return std::forward<F>(f)(std::forward<A>(a)...); } template<typename C, typename F, typename T, typename... A> static inline auto apply_tuple(C*const o, F&& f, T&&, A&&... a) -> decltype((o->*std::forward<F>(f))(std::forward<A>(a)...)) { return (o->*std::forward<F>(f))(std::forward<A>(a)...); } }; // Apply tuple parameters on a non-member or static-member function by perfect forwarding template<typename F, typename T> inline auto apply_tuple(F&& f, T&& t) -> decltype(apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(std::forward<F>(f), std::forward<T>(t))) { return apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(std::forward<F>(f), std::forward<T>(t)); } // Apply tuple parameters on a member function template<typename C, typename F, typename T> inline auto apply_tuple(C*const o, F&& f, T&& t) -> decltype(apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t))) { return apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t)); }


Las noticias no se ven bien.

Después de leer el borrador del estándar recién publicado , no veo una solución incorporada a esto, que parece extraño.

El mejor lugar para preguntar acerca de tales cosas (si no lo has hecho) es comp.lang.c ++ moderado, porque algunas personas involucradas en el borrador de la publicación estándar allí regularmente.

Si revisa este hilo , alguien tiene la misma pregunta (¡tal vez sea usted, en cuyo caso encontrará frustrante toda esta respuesta!), Y se sugieren algunas implementaciones desagradables.

Me preguntaba si sería más fácil hacer que la función acepte una tuple , ya que la conversión de esa manera es más fácil. Pero esto implica que todas las funciones deben aceptar tuplas como argumentos, para una flexibilidad máxima, y ​​eso solo demuestra la rareza de no proporcionar una expansión incorporada de tupla para funcionar con el paquete de argumentos.

Actualización: el enlace de arriba no funciona, intente pegar esto:

http://groups.google.com/group/comp.lang.c++moderated/browse_thread/thread/750fa3815cdaac45/d8dc09e34bbb9661?lnk=gst&q=tuple+variadic#d8dc09e34bbb9661


Qué tal esto:

// Warning: NOT tested! #include <cstddef> #include <tuple> #include <type_traits> #include <utility> using std::declval; using std::forward; using std::get; using std::integral_constant; using std::size_t; using std::tuple; namespace detail { template < typename Func, typename ...T, typename ...Args > auto explode_tuple( integral_constant<size_t, 0u>, tuple<T...> const &t, Func &&f, Args &&...a ) -> decltype( forward<Func>(f)(declval<T const>()...) ) { return forward<Func>( f )( forward<Args>(a)... ); } template < size_t Index, typename Func, typename ...T, typename ...Args > auto explode_tuple( integral_constant<size_t, Index>, tuple<T...> const&t, Func &&f, Args &&...a ) -> decltype( forward<Func>(f)(declval<T const>()...) ) { return explode_tuple( integral_constant<size_t, Index - 1u>{}, t, forward<Func>(f), get<Index - 1u>(t), forward<Args>(a)... ); } } template < typename Func, typename ...T > auto run_tuple( Func &&f, tuple<T...> const &t ) -> decltype( forward<Func>(f)(declval<T const>()...) ) { return detail::explode_tuple( integral_constant<size_t, sizeof...(T)>{}, t, forward<Func>(f) ); } template < typename Tret, typename ...T > Tret func_T( tuple<T...> const &t ) { return run_tuple( &func<Tret, T...>, t ); }

La plantilla de función run_tuple toma la tupla dada y pasa sus elementos individualmente a la función dada. Lleva a cabo su trabajo llamando recursivamente a sus plantillas de funciones auxiliares explode_tuple . Es importante que run_tuple pase el tamaño de la tupla a explode_tuple ; ese número actúa como un contador de cuántos elementos extraer.

Si la tupla está vacía, run_tuple llama a la primera versión de explode_tuple con la función remota como único argumento. La función remota se llama sin argumentos y terminamos. Si la tupla no está vacía, se pasa un número mayor a la segunda versión de explode_tuple , junto con la función remota. Se realiza una llamada recursiva a explode_tuple , con los mismos argumentos, excepto que el número de contador disminuye en uno y (una referencia a) el último elemento de tupla se agrega como argumento después de la función remota. En una llamada recursiva, el contador no es cero y se realiza otra llamada con el contador disminuido nuevamente y el siguiente elemento sin referencia se inserta en la lista de argumentos después de la función remota pero antes de los otros argumentos insertados, o el contador alcanza cero y se llama a la función remota con todos los argumentos acumulados después de ella.

No estoy seguro de tener la sintaxis de forzar una versión particular de una plantilla de función correcta. Creo que puede usar un puntero a función como un objeto de función; el compilador lo arreglará automáticamente.


Todas estas implementaciones son buenas. Pero debido al uso del puntero al compilador de la función miembro a menudo no se puede alinear la llamada a la función objetivo (al menos no puede gcc 4.8, no importa por qué gcc no puede punteros de función en línea que se pueden determinar? )

Pero las cosas cambian si se envía el puntero a la función de miembro como argumentos de plantilla, no como parámetros de función:

/// from https://.com/a/9288547/1559666 template<int ...> struct seq {}; template<int N, int ...S> struct gens : gens<N-1, N-1, S...> {}; template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; }; template<typename TT> using makeSeq = typename gens< std::tuple_size< typename std::decay<TT>::type >::value >::type; // deduce function return type template<class ...Args> struct fn_type; template<class ...Args> struct fn_type< std::tuple<Args...> >{ // will not be called template<class Self, class Fn> static auto type_helper(Self &self, Fn f) -> decltype((self.*f)(declval<Args>()...)){ //return (self.*f)(Args()...); return NULL; } }; template<class Self, class ...Args> struct APPLY_TUPLE{}; template<class Self, class ...Args> struct APPLY_TUPLE<Self, std::tuple<Args...>>{ Self &self; APPLY_TUPLE(Self &self): self(self){} template<class T, T (Self::* f)(Args...), class Tuple> void delayed_call(Tuple &&list){ caller<T, f, Tuple >(forward<Tuple>(list), makeSeq<Tuple>() ); } template<class T, T (Self::* f)(Args...), class Tuple, int ...S> void caller(Tuple &&list, const seq<S...>){ (self.*f)( std::get<S>(forward<Tuple>(list))... ); } }; #define type_of(val) typename decay<decltype(val)>::type #define apply_tuple(obj, fname, tuple) / APPLY_TUPLE<typename decay<decltype(obj)>::type, typename decay<decltype(tuple)>::type >(obj).delayed_call< / decltype( fn_type< type_of(tuple) >::type_helper(obj, &decay<decltype(obj)>::type::fname) ), / &decay<decltype(obj)>::type::fname / > / (tuple);

Y uso:

struct DelayedCall { void call_me(int a, int b, int c){ std::cout << a+b+c; } void fire(){ tuple<int,int,int> list = make_tuple(1,2,3); apply_tuple(*this, call_me, list); // even simpler than previous implementations } };

Prueba de http://goo.gl/5UqVnC inlinable

Con pequeños cambios, podemos "sobrecargar" apply_tuple :

#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N #define VA_NARGS(...) VA_NARGS_IMPL(X,##__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0) #define VARARG_IMPL_(base, count, ...) base##count(__VA_ARGS__) #define VARARG_IMPL(base, count, ...) VARARG_IMPL_(base, count, __VA_ARGS__) #define VARARG(base, ...) VARARG_IMPL(base, VA_NARGS(__VA_ARGS__), __VA_ARGS__) #define apply_tuple2(fname, tuple) apply_tuple3(*this, fname, tuple) #define apply_tuple3(obj, fname, tuple) / APPLY_TUPLE<typename decay<decltype(obj)>::type, typename decay<decltype(tuple)>::type >(obj).delayed_call< / decltype( fn_type< type_of(tuple) >::type_helper(obj, &decay<decltype(obj)>::type::fname) ), / &decay<decltype(obj)>::type::fname / /* ,decltype(tuple) */> / (tuple); #define apply_tuple(...) VARARG(apply_tuple, __VA_ARGS__) ... apply_tuple(obj, call_me, list); apply_tuple(call_me, list); // call this->call_me(list....)

Además, esta es la única solución que funciona con funciones de plantilla.


This simple solution works for me:

template<typename... T> void unwrap_tuple(std::tuple<T...>* tp) { std::cout << "And here I have the tuple types, all " << sizeof...(T) << " of them" << std::endl; } int main() { using TupleType = std::tuple<int, float, std::string, void*>; unwrap_tuple((TupleType*)nullptr); // trick compiler into using template param deduction }


Why not just wrap your variadic arguments into a tuple class and then use compile time recursion (see link ) to retrieve the index you are interested in. I find that unpacking variadic templates into a container or collection may not be type safe wrt heterogeneous types

template<typename... Args> auto get_args_as_tuple(Args... args) -> std::tuple<Args...> { return std::make_tuple(args); }


template<typename F, typename Tuple, std::size_t ... I> auto apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>) { return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...); } template<typename F, typename Tuple> auto apply(F&& f, Tuple&& t) { using Indices = std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>; return apply_impl(std::forward<F>(f), std::forward<Tuple>(t), Indices()); }

Esto se ha adaptado del borrador de C ++ 14 usando index_sequence. Podría proponerme aplicar en un futuro estándar (TS).