Як розгорнути кортеж на аргументи варіативної функції шаблону?


135

Розглянемо випадок шаблонної функції з аргументами варіативного шаблону:

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

Тепер у мене є кортеж tцінностей. Як зателефонувати func()за допомогою значень кортежу як аргументів? Я читав про bind()об’єкт функції, з call()функцією, а також про apply()функцію в деяких інших застарілих документах. Здається, реалізація GNU GCC 4.4 має call()функцію в bind()класі, але документації з цього питання дуже мало.

Деякі люди пропонують рукописні рекурсивні хаки, але справжня цінність аргументів варіативного шаблону полягає в тому, щоб мати можливість використовувати їх у випадках, описаних вище.

Хтось має рішення, чи натякнути, де про це читати?


5
Стандарт C ++ 14 має рішення див. open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3658.html
Skeen

1
Ідея полягає в тому , щоб розпакувати кортеж в одному VARIADIC вибуху, використовуючи integer_sequenceсм en.cppreference.com/w/cpp/utility/integer_sequence
Skeen

6
Маючи integer_sequence S, ви просто називаєте свою функцію як func(std::get<S>(tuple)...)і дозволите компілятору обробляти решту.
Skeen

1
Якщо ви використовуєте C ++ 17 або
lewis

Відповіді:


46

Ось мій код, якщо хтось зацікавлений

В основному під час компіляції компілятор рекурсивно розгортає всі аргументи в різних викликах функцій <N> -> виклики <N-1> -> виклики ... -> виклики <0>, які є останніми, і компілятор буде оптимізуватись різні проміжні функції закликають зберігати лише останній, який є еквівалентом func (arg1, arg2, arg3, ...)

Надається дві версії, одна для функції, яка викликається на об'єкті, а друга для статичної функції.

#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 )
  {

  }
}

2
Чи можливо пристосувати це до роботи у випадку, коли розглядається "функція" насправді є конструктором?
HighCommander4

Чи можете ви надати приклад того, що ви хочете зробити, і ми можемо піти звідти.
Девід

Це рішення передбачає лише накладний час компіляції, і в кінці буде спрощено до (pObj -> * f) (arg0, arg, 1, ... argN); правильно?
Гуфі

так, компілятор буде стискати кілька викликів функцій у остаточний, як ніби ви його написали самі, що є красою всього цього матеріалу метапрограмування.
Девід

всі tr1речі можна вивезти зараз із c ++ 11
Ryan Haining

37

У C ++ 17 ви можете це зробити:

std::apply(the_function, the_tuple);

Це вже працює в Clang ++ 3.9, використовуючи std :: експериментальний :: застосовувати.

Відповідаючи на коментар, вказуючи, що це не вийде, якщо 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;
}

Ця робота навколо є спрощеним рішенням загальної проблеми передачі наборів перевантажень та шаблону функцій, де функцію можна було б очікувати. Загальне рішення (таке, яке піклується про ідеальне переадресацію, конспектр-ность та ноексепс-носс), представлено тут: https://blog.tartanllama.xyz/passing-overload-sets/ .


Згідно з прикладом коду в std :: apply , схоже, він не працює, якщо the_functionвін шаблонований.
Zitrax

1
@Zitrax Ви можете вказати аргументи шаблону функції:std::apply(add_generic<float>, std::make_pair(2.0f, 3.0f));
Ербурет каже

Це найпростіше, найелегантніше рішення. І творить чудеса. Велике спасибі, М. Алаган !!!!!! +100 голосів
Елліотт

36

У C ++ існує багато способів розширення / розпакування кортежу та застосування цих елементів кортежу до функції варіативного шаблону. Ось невеликий допоміжний клас, який створює масив індексів. Він використовується в метапрограмуванні шаблонів:

// ------------- 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...> 
{}; 

Тепер код, який виконує завдання, не такий великий:

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

Тест показаний нижче:

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

Я не є великим знавцем інших мов, але, мабуть, якщо ці мови не мають такої функціональності у своєму меню, то це зробити неможливо. Принаймні, з C ++ ви можете, і я думаю, це не так вже й складно ...


"... і застосувати ці кортежні елементи до функції варіативного шаблону" . Тестовий розділ містить лише варіанти, що не мають варіантів шаблону. Якщо я додаю подібну форму template<class ... T> void three(T...) {}та спробую застосувати, застосувати застосувати те, що він не компілюється.
Zitrax

32

Я вважаю це найелегантнішим рішенням (і воно оптимально пересилається):

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

Приклад використання:

void foo(int i, bool b);

std::tuple<int, bool> t = make_tuple(20, false);

void m()
{
    apply(&foo, t);
}

На жаль, GCC (щонайменше 4.6) не в змозі компілювати це з "вибачте, не виконане: мангінг перевантаження" (що просто означає, що компілятор ще не повністю реалізує специфікацію C ++ 11), і оскільки він використовує різноманітні шаблони, він не буде робота в MSVC, тому вона є більш-менш марною. Однак, коли є компілятор, який підтримує специфікацію, це буде найкращим підходом до IMHO. (Примітка. Це не так важко змінити, щоб ви могли подолати недоліки в GCC або реалізувати його за допомогою Boost Preprocessor, але це руйнує елегантність, тому це версія, яку я публікую.)

GCC 4.7 тепер добре підтримує цей код.

Редагувати: Додано вперед навколо фактичного виклику функції для підтримки посилальної форми rvalue * це у випадку, якщо ви використовуєте кланг (або якщо хто-небудь ще насправді не додає його).

Редагувати: Додано пропущений вперед навколо об'єкта функції в тілі функції, що не належить до членства. Завдяки Федбаку за те, що він вказав, що він відсутній.

Редагувати: А ось версія C ++ 14, так як вона набагато приємніша (насправді ще не компілюється):

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

Ось версія для функцій учасників (не дуже перевірена!):

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

1
+1 із перерахованих відповідей, ваша була найближчою, яку я міг отримати роботу з аргументами, аргументи яких вектори ... ... але я все одно отримую помилки компіляції. ideone.com/xH5kBH Якщо скомпілювати це за допомогою -DDIRECT_CALL і запустити його, ви побачите, яким повинен бути вихід. Інакше я отримую помилку компіляції (я думаю, що decltype недостатньо розумний, щоб розібратися в моєму спеціальному випадку), з gcc 4.7.2.
kfmfe04

3
Версія gcc на ideaone є старою, щоб це пройшло, вона не підтримує перевантаженого перезавантаження типу decltype. Я перевірив цей код порівняно ретельно в gcc 4.7.2, і не зіткнувся з жодними проблемами. За допомогою gcc 4.8 ви можете використовувати нову функцію автоматичного повернення значення C ++ 17, щоб уникнути всіх неприємних типових повернень decltype.
DRayX

1
applyЧому з цікавості, у функції, яка fне є членом , чому її не завершують std::forwardдзвінком, як це є у зворотному типі? Це не потрібно?
Бретт Россьє

3
З цікавості, я спробував foo('x', true)скомпілювати це в GCC 4.8 і склав точно такий же код складання, що apply(foo, ::std::make_tuple('x', true))і для будь-якого рівня оптимізації, крім -00.
DRayX

2
З C ++ 14 integer_sequenceви навіть отримаєте майже коректну реалізацію apply()у своєму прикладі. дивіться мою відповідь нижче.
PeterSom

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

Це адаптується з чернетки C ++ 14, використовуючи index_sequence. Я можу запропонувати застосувати їх у майбутньому стандарті (TS).


1

Новини виглядають недобре.

Прочитавши щойно випущений проект стандарту , я не бачу вбудованого рішення для цього, що здається дивним.

Найкраще запитати про подібні речі (якщо ви ще цього не зробили) - comp.lang.c ++. Модерується, оскільки деякі люди, які беруть участь у складанні стандартного допису, регулярно там.

Якщо ви перевірите цю тему , у когось є те саме запитання (можливо, це ви, в такому випадку ви знайдете цю всю відповідь трохи засмучуючої!), І пропонуються кілька некрасивих реалізацій.

Я просто задумався, чи було б простіше змусити функцію приймати a tuple, оскільки перетворення таким чином простіше. Але це означає, що всі функції повинні сприймати кортежі як аргументи, для досягнення максимальної гнучкості, і це просто демонструє дивацтва не надання вбудованого розширення кортежу до пакету аргументів функції.

Оновлення: посилання вище не працює - спробуйте вставити це:

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


Цікаво, чому вони навіть турбуються, маючи окремі поняття кортежу та пакету аргументів функції. Можливо, у відповідному компіляторі вони взаємозамінні, але я ніде не помітив вказівки на те, що я читав про них.
Даніель Ервікер

2
Оскільки tuple <int, char, string> необхідний як окремий тип; як і можливість зробити функцію, яка не вимагає make_type посеред кожного дзвінка.
coppro

1
Крім того, найкраще місце не модерується comp.lang.c ++. Питання щодо C ++ 1x майже завжди краще направляти на comp.std.c ++.
coppro

1

Всі ці реалізації добре. Але через використання компілятора функції вказівника на учасника часто не вдається вбудувати виклик цільової функції (принаймні, gcc 4.8 не може, незалежно від того, чому gcc не може вбудувати вказівники функції, які можна визначити? )

Але все змінюється, якщо надіслати покажчик на функцію члена як аргументи шаблону, а не як параметри функції:

/// from https://stackoverflow.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);

І необхідність:

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

Доказ нерозбірливого http://goo.gl/5UqVnC


З невеликими змінами ми можемо "перевантажувати" 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....)

Плюс це єдине рішення, яке працює з шаблонними функціями.


1

1) якщо у вас є аргумент функції готової параметра_пак як аргумент функції, ви можете просто використовувати std :: tie так:

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) якщо у вас немає готового аргументу з парампаком, вам доведеться розкрутити такий кортеж

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

0

Як щодо цього:

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

run_tupleШаблон функції приймає даний кортеж і передати його елементи по окремо для даної функції. Він виконує свою роботу шляхом рекурсивного виклику шаблонів функцій помічників explode_tuple. Важливо те, що run_tupleрозмір кортежу передається explode_tuple; це число виступає лічильником того, скільки елементів потрібно витягти.

Якщо кортеж порожній, тоді run_tupleвикликає першу версію explode_tupleз віддаленою функцією як єдиний інший аргумент. Віддалена функція викликається без аргументів, і ми закінчили. Якщо кортеж не порожній, вищий номер передається другій версії explode_tupleразом з віддаленою функцією. Рекурсивний дзвінок наexplode_tupleробиться з тими ж аргументами, за винятком того, що кількість лічильника зменшується на один і (посилання на) останній елемент кортежу вводиться як аргумент після віддаленої функції. У рекурсивному виклику або лічильник не дорівнює нулю, і робиться інший виклик, коли лічильник знову зменшується, а наступний невпорядкований елемент вставляється в список аргументів після віддаленої функції, але перед тим, як інші вставлені аргументи, або лічильник досягає нуль, і віддалена функція викликається з усіма накопиченими після неї аргументами.

Я не впевнений, що я маю синтаксис примушування конкретної версії шаблону функції вправо. Я думаю, ви можете використовувати вказівник на функцію як об’єкт функції; компілятор автоматично його виправить.


0

Я оцінюю MSVS 2013RC, і не вдалося скомпілювати деякі попередні рішення, запропоновані тут у деяких випадках. Наприклад, MSVS не зможе зібрати "автоматичні" повернення, якщо занадто багато функціональних параметрів через обмеження в області простору імен (я надіслав цю інформацію Microsoft, щоб вона була виправлена). В інших випадках нам потрібен доступ до повернення функції, хоча це також можна зробити за допомогою lamda: наступні два приклади дають той же результат.

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

І ще раз дякую тим, хто тут розмістив відповіді перед мною, я б не обійшвся без цього ... так ось:

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

Чому ви робите об’єктний аргумент покажчиком const? Не посилання, не const посилання, не просто вказівник? Що робити, якщо функція дзвінка не буде const?
tower120

0

Розширюючи рішення @ David, ви можете написати рекурсивний шаблон, який

  1. Не використовує (надмірно багатослівну, іммо) integer_sequenceсемантику
  2. Не використовує додатковий тимчасовий параметр шаблону int Nдля підрахунку рекурсивних ітерацій
  3. (Необов’язково для статичних / глобальних функторів) використовує функтор як параметр шаблону для оптимізації часу компіляції

Наприклад:

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

Крім того, якщо ваш функтор не визначений під час компіляції (наприклад, constexprекземпляр, який не є функтором, або лямбда-вираз), ви можете використовувати його як функціональний параметр замість параметра шаблону класу, і фактично повністю видалити містить клас:

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

Для дзвінків на функції вказівника до учасника ви можете налаштувати будь-який із наведених вище фрагментів коду так само, як у відповіді @ David.

Пояснення

Що стосується другого фрагмента коду, є дві функції шаблону: перша приймає функтор func, кортеж tз типами T...та пакет параметрів argsтипів Args_tmp.... При виклику він рекурсивно додає об'єкти tв пакет параметрів один за одним, від початку ( 0) до кінця і викликає функцію знову за допомогою нового інкремованого пакета параметрів.

Підпис другої функції майже ідентичний першій, за винятком того, що вона використовує тип T...для пакета параметрів args. Таким чином, як argsтільки перша функція буде повністю заповнена значеннями з t, тип її буде T...(у psuedo-коді, typeid(T...) == typeid(Args_tmp...)), і таким чином компілятор замість цього викличе другу перевантажену функцію, яка в свою чергу викликає func(args...).

Код у прикладі статичного функтора працює однаково, замість цього функтор використовується як аргумент шаблону класу.


будь-які зауваження щодо оптимізації часу складання першого варіанту були б вдячні, тому я можу зробити свою відповідь більш повною (і, можливо, дізнаюся щось нове).
CrepeGoat

-3

Чому б просто не загорнути ваші різноманітні аргументи в клас кортежів, а потім використати рекурсію часу компіляції (див. Посилання ) для отримання індексу, який вас цікавить. Я вважаю, що розпакування різноманітних шаблонів у контейнер або колекцію може не вводити безпечні wrt неоднорідні типи

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

6
Питання було навпаки. Не Args...-> tuple, а tuple-> Args....
Xeo

-4

Це просте рішення працює для мене:

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
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.