Bir parçayı değişken şablon işlevinin bağımsız değişkenlerine nasıl genişletebilirim?


136

Variadic şablon bağımsız değişkenlerine sahip şablonlanmış bir işlevi düşünün:

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

Şimdi, bir tdeğerler dizim var . func()Tuple değerlerini bağımsız değişken olarak kullanarak nasıl arayabilirim ? bind()İşlev nesnesini, call()işlevli ve aynı zamanda apply()artık eski olan farklı belgelerdeki işlevi okudum . GNU GCC 4.4 uygulamasının sınıfta bir call()işlevi var gibi görünüyor bind(), ancak konuyla ilgili çok az belge var.

Bazı insanlar elle yazılmış özyinelemeli saldırılar önerir, ancak değişken şablon argümanlarının gerçek değeri bunları yukarıdaki gibi durumlarda kullanabilmektir.

Herkes bu konuda nerede bir çözüm var veya ipucu var mı?


5
C ++ 14 standardının bir çözümü vardır bkz; open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3658.html
Skeen

1
Fikir, kullanarak bir tek değişkenli patlamada tuple açmak için integer_sequencebkz. En.cppreference.com/w/cpp/utility/integer_sequence
Skeen

6
Bir sahip olmak integer_sequence S, sadece işlevinizi olarak adlandırın func(std::get<S>(tuple)...)ve derleyicinin geri kalanını halletmesine izin verin.
Skeen

1
C ++ 17 veya daha yenisini kullanıyorsanız, bu yanıtı göz ardı edin ve std :: Apply
lewis

Yanıtlar:


46

İşte ilgilenen varsa benim kod

Temel olarak derleme zamanında derleyici <N> -> çağrıları <N-1> -> çağrıları ... -> sonuncusu olan <0> çağrılarını derler ve derleyici optimize eder çeşitli ara işlev yalnızca func eşdeğeri olan sonuncuyu tutmaya çağırır (arg1, arg2, arg3, ...)

Biri bir nesne üzerinde çağrılan bir işlev için diğeri statik bir işlev için olmak üzere 2 sürüm sağlanmıştır.

#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
Bunu, söz konusu "işlev" in aslında bir kurucu olduğu bir durumda çalışmak üzere uyarlamak mümkün müdür?
HighCommander4

Ne yapmak istediğinize bir örnek verebilir misiniz ve oradan gidebiliriz.
David

Bu çözüm yalnızca derleme zamanı yükünü sağlar ve sonunda (pObj -> * f) (arg0, arg, 1, ... argN) için basitleştirilir; sağ?
Goofy

evet, derleyici, tüm bu meta programlama şeylerinin güzelliği olan kendiniz yazmışsınız gibi çoklu işlev çağrılarını son haline sıkıştırır.
David

tüm tr1şeyler şimdi c ++ 11
Ryan Haining

37

C ++ 17'de şunları yapabilirsiniz:

std::apply(the_function, the_tuple);

Bu zaten Clang ++ 3.9, std :: experimental :: uygulamak kullanarak çalışır.

Bunun the_functiongeçici olması durumunda işe yaramayacağını söyleyen yoruma yanıt olarak, aşağıdakiler bir çözümdür:

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

Bu geçici çözüm, bir işlevin bekleneceği aşırı yük kümelerini ve işlev şablonunu geçirme genel sorununa basitleştirilmiş bir çözümdür. Genel çözüm (mükemmel yönlendirme, sınırsızlık ve önemsizlikle ilgilenen çözüm) burada sunulmaktadır: https://blog.tartanllama.xyz/passing-overload-sets/ .


Örnek koda göre std :: uygulathe_function şablonu esas alınırsa işe yaramıyor gibi görünüyor .
Zitrax

1
@Zitrax Fonksiyonun şablon argümanlarını belirtebilirsiniz:std::apply(add_generic<float>, std::make_pair(2.0f, 3.0f));
Erbureth diyor Reinstate Monica

Bu en basit, en zarif çözüm. Ve harikalar yaratıyor. Çok teşekkürler, M. Alaggan !!!!!! +100 oy
Elliott

36

C ++ 'da, grubu genişletmenin / paketini açmanın ve bu grup öğelerini varyasyonlu bir şablon işlevine uygulamanın birçok yolu vardır. İşte dizin dizisi oluşturan küçük bir yardımcı sınıf. Şablon meta programlamasında çok kullanılır:

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

Şimdi işi yapan kod o kadar büyük değil:

 // ----------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 aşağıda gösterilmiştir:

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

Diğer dillerde büyük uzman değilim, ancak bu diller kendi menüsünde böyle bir işleve sahip değilse, bunu yapmanın bir yolu olmadığını düşünüyorum. En azından C ++ ile yapabilirsiniz ve bence o kadar karmaşık değil ...


"... ve bu tuple elemanlarını varyasyonlu bir şablon fonksiyonuna uygula" . Sınama bölümü yalnızca şablon olmayan değişken işlevler içerir. Eğer bir tane eklerse template<class ... T> void three(T...) {}ve kullanmaya çalışırsanız derlemediğini uygulayın.
Zitrax

32

Bunu en zarif çözüm olarak görüyorum (ve en iyi şekilde yönlendiriliyor):

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

Örnek kullanım:

void foo(int i, bool b);

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

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

Ne yazık ki GCC (en az 4.6) bunu "üzgün, uygulanmamış: aşırı yükleme" ile derleyemedi (bu sadece derleyicinin henüz C ++ 11 spesifikasyonunu tam olarak uygulamadığı anlamına gelir) ve varyasyon şablonları kullandığından MSVC'de çalışır, bu yüzden az çok işe yaramaz. Ancak, spesifikasyonu destekleyen bir derleyici varsa, en iyi yaklaşım IMHO olacaktır. (Not: GCC'deki eksiklikler üzerinde çalışabilmeniz veya Boost Preprocessor ile uygulayabilmeniz için bunu değiştirmek zor değil, ancak zarafeti bozuyor, bu yüzden yayınladığım sürüm bu.)

GCC 4.7 artık bu kodu gayet iyi destekliyor.

Düzenleme: Clang kullanıyor olmanız durumunda (veya başka herhangi bir kişi gerçekten eklemek için etrafta dolanırsa), rvalue referans formunu * desteklemek için gerçek işlev çağrısı etrafında ileri eklendi.

Düzenleme: Üye olmayan uygula işlevinin gövdesindeki işlev nesnesi etrafında eksik ileri eklendi. Eksik olduğuna işaret ettiği için pheedbaq'a teşekkürler.

Düzenleme: Ve işte C ++ 14 sürümü sadece çok daha güzel olduğu için (aslında derlemiyor):

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

Üye işlevleri için bir sürüm (çok fazla test edilmedi!):

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
Listelenen cevapların + 1'i, sizinki, argümanları vektör olan argümanlarla çalışmaya en yakınımdı ... ... ama hala derleme hataları alıyorum. ideone.com/xH5kBH -DDIRECT_CALL ile derlerseniz ve çalıştırırsanız, çıktının ne olması gerektiğini göreceksiniz. Aksi takdirde derleme hatası alıyorum (decltype özel durumumu anlamaya yetecek kadar akıllı değil), gcc 4.7.2 ile.
kfmfe04

3
İdeaone üzerindeki gcc sürümü eskidir, karıştırılmış decltype dönüş tipi aşırı yüklemeyi desteklemez. Bu kodu göreli olarak gcc 4.7.2 test ettik ve herhangi bir sorunla karşılaşmadım. Gcc 4.8 ile, tüm kötü decltype sondaki dönüş türlerinden kaçınmak için yeni C ++ 17 otomatik dönüş değeri özelliğini kullanabilirsiniz.
DRayX

1
Meraktan, üye olmayan applyişlevde, dönüş türünde olduğu gibi neden fbir std::forwardçağrı ile sarılmıyor ? Gerekli değil mi?
Brett Rossier

3
Meraktan, bunu GCC 4.8'de derlemeyi denedim ve -O0 dışında herhangi bir optimizasyon seviyesiyle foo('x', true)aynı derleme kodunu apply(foo, ::std::make_tuple('x', true))derledim.
DRayX

2
C ++ 14 ile, örneğinde integer_sequenceneredeyse doğru bir uygulama elde edersiniz apply(). aşağıdaki cevabımı gör.
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());
}

Bu, index_sequence kullanılarak C ++ 14 taslağından uyarlanır. Gelecek bir standartta (TS) başvurmayı önerebilirim.


1

Haberler iyi görünmüyor.

Yeni yayınlanan taslak standardını okuduktan sonra , bunun tuhaf görünen yerleşik bir çözümü görmüyorum.

Bu tür şeyleri sormak için en iyi yer (eğer henüz yapmadıysanız) comp.lang.c ++. Yönetilir, çünkü bazı insanlar düzenli olarak standart yazıyı hazırlamaya dahil olurlar.

Bu konuyu kontrol ederseniz , birisinin aynı sorusu vardır (belki sizsiniz, bu durumda bu cevabı biraz sinir bozucu bulacaksınız!) Ve birkaç popo çirkin uygulama önerilmektedir.

Ben sadece fonksiyonu kabul etmenin daha basit olup olmayacağını merak ettim. tupleDönüşümün bu şekilde daha kolay . Ancak bu, tüm işlevlerin, maksimum esneklik için tuples'i argüman olarak kabul etmesi gerektiği anlamına gelir ve böylece işlev argüman paketine yerleşik bir demet genişletme sağlama tuhaflığını gösterir.

Güncelleme: yukarıdaki bağlantı çalışmıyor - şunu yapıştırmayı deneyin:

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


Neden ayrı ayrı tuple ve fonksiyon argüman paketi kavramlarını bile rahatsız ettiklerini merak ediyorum. Belki uygun bir derleyicide değiştirilebilirler, ancak onlar hakkında okuduğum herhangi bir yerde bir gösterge görmedim.
Daniel Earwicker

2
Çünkü tuple <int, char, string> ayrı bir tür olarak gereklidir; her çağrının ortasında make_type gerektirmeyen bir işlev yapabilme yeteneğidir.
coppro

1
Ayrıca, en iyi yer comp.lang.c ++. Yönetilmez. C ++ 1x hakkındaki sorular neredeyse her zaman daha iyi comp.std.c ++ 'a yönlendirilir.
coppro

1

Tüm bu uygulamalar iyi. Ancak işaretçinin üye işlev derleyicisine kullanımı nedeniyle genellikle hedef işlev çağrısını satır içine alamaz (en azından gcc 4.8 yapamaz, ne olursa olsun neden gcc belirlenebilir işlev işaretçileri satır içi yapamaz? )

Ancak üye işlevine işaretçiyi işlev parametreleri olarak değil, şablon bağımsız değişkenleri olarak gönderirseniz işler değişir:

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

Ve kullanım:

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

Inlinable kanıtı http://goo.gl/5UqVnC


Küçük değişikliklerle "aşırı yüklenebilir" 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....)

Ayrıca bu, temperlenmiş işlevlerle çalışan tek çözümdür.


1

1) işlev bağımsız değişkeni olarak hazır bir parametre_paketi yapınız varsa, std :: tie komutunu şu şekilde kullanabilirsiniz:

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) hazır bir parampack argümanı yoksa, tupleı bu şekilde çözmeniz gerekir.

#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

Buna ne dersin:

// 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İşlevi şablon verilen tuple alır ve belirli bir işleve ayrı ayrı elemanlarını geçmektedir. Yardımcı işlev şablonlarını özyinelemeli olarak çağırarak çalışmalarını yürütür explode_tuple. run_tupleTupleın büyüklüğünün geçmesi önemlidir explode_tuple; bu sayı, kaç elemanın çıkarılacağı için bir sayaç görevi görür.

Grup boşsa, diğer bağımsız değişken olarak uzak işlevli run_tupleilk sürümünü çağırır explode_tuple. Uzak işlev bağımsız değişken olmadan çağrılır ve işimiz biter. Grup boş değilse explode_tuple, uzak işleviyle birlikte ikinci sürümüne daha yüksek bir sayı iletilir . İçin özyinelemeli çağrıexplode_tuple , sayaç numarası bir azaltılır ve son tuple elemanına bir argüman olarak bağlanırsa, aynı argümanlarla uzaktan kumanda işlevi. Özyinelemeli bir çağrıda, sayaç sıfır değildir ve sayaç tekrar azaltıldığında başka bir çağrı yapılır ve sonraki işlevsiz öğe, uzak işlevden sonra ancak eklenen diğer bağımsız değişkenlerden önce bağımsız değişken listesine eklenir veya sayaç ulaşır sıfır ve uzaktan işlev hepsi ile çağrılır ondan sonra biriken argümanlar.

Bir işlev şablonunun belirli bir sürümünü zorlama sözdizimine sahip olduğundan emin değilim. Ben bir işlev nesnesi olarak bir işaretçi-işlev kullanabilirsiniz düşünüyorum; derleyici bunu otomatik olarak düzeltir.


0

MSVS 2013RC'yi değerlendiriyorum ve bazı durumlarda burada önerilen önceki çözümlerden bazılarını derleyemedi. Örneğin, bir ad alanı imbrication sınırı nedeniyle MSVS "auto" döndürmelerini derleyemez (düzeltilmesi için bu bilgileri Microsoft'a gönderdim). Diğer durumlarda, bir lamda ile de yapılabilmesine rağmen, işlevin geri dönüşüne erişmemiz gerekir: aşağıdaki iki örnek aynı sonucu verir.

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

Ve benden önce buraya cevap gönderenlere tekrar teşekkürler, onsuz buna ulaşamazdım ... işte burada:

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

Nesne argümanını neden sabit gösterici yapıyorsunuz? Referans değil, const referans değil, sadece işaretçi değil mi? Çağrılabilir işlev olmazsa ne olur const?
kule120

0

@ David'in çözümünü genişleterek, yinelemeli bir şablon yazabilirsiniz.

  1. (Aşırı ayrıntılı, imo) integer_sequencesemantiği kullanmaz
  2. int NYinelemeli yinelemeleri saymak için fazladan geçici bir şablon parametresi kullanmaz
  3. (Statik / genel işlevler için isteğe bağlı), derleyiciyi derleme zamanı optimizasyonu için şablon parametresi olarak kullanır

Örneğin:

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

Alternatif olarak, işlevciniz derleme zamanında tanımlanmamışsa (ör.constexpr işlevsiz bir örnek veya lambda ifadesi), bunu sınıf şablonu parametresi yerine bir işlev parametresi olarak kullanabilir ve aslında içeren sınıfı tamamen kaldırabilirsiniz:

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

İşaretçi-üye-işlev callables için, yukarıdaki kod parçalarından birini @ David yanıtında olduğu gibi ayarlayabilirsiniz.

açıklama

İkinci kod parçasına referans olarak, iki şablon fonksiyonu vardır: birincisi functor func, ttipleri içeren T...bir grup ve bir parametre argstipi paketi alır Args_tmp.... Çağrıldığında, nesnelerini özyinelemeli olarakt başlangıçtan ( 0) sonuna kadar birer birer parametre paketine ve yeni artan parametre paketiyle işlevi yeniden çağırır.

İkinci işlevin imzası, T...parametre paketi için tür kullanması dışında, ilkiyle hemen hemen aynıdır args. Böylece, argsilk işlevde bir kez değerleri ile tamamen dolduğunda t, türü T...(psuedo-code,typeid(T...) == typeid(Args_tmp...) ) olur ve böylece derleyici bunun yerine ikinci aşırı yüklenmiş fonksiyonu çağırır, bu da çağrıları çağırır func(args...).

Statik işleç örneğindeki kod, sınıf şablonu bağımsız değişkeni olarak kullanılan işlevle aynı şekilde çalışır.


ilk seçeneğin derleme zamanı optimizasyonu ile ilgili herhangi bir yorum takdir edilecektir, bu yüzden cevabımı daha eksiksiz hale getirebilirim (ve belki de yeni bir şeyler öğrenebilirim).
CrepeGoat

-3

Neden sadece değişken bir argümanları bir tuple sınıfına sarmak ve sonra ilgilendiğiniz dizini almak için derleme zamanı özyineleme ( bağlantıya bakın ) kullanın. Heterojen türler bir konteyner veya koleksiyon içine paket açma tür güvenli olmayabilir

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

6
Soru tam tersiydi. Args...-> değil tuple, tuple-> Args....
Xeo

-4

Bu basit çözüm benim için çalışıyor:

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
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.