Hvordan kan jeg utvide en tuppel i variadic mal funksjonens argumenter du?

stemmer
119

Vurdere saken på en malbasert funksjon med variadic malparametere:

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

Nå har jeg en tuppel tav verdier. Hvordan jeg kaller ikke func()bruke de tuppel verdier som argumenter? Jeg har lest om den bind()funksjon objekt, med call()funksjon, og også apply()funksjon i forskjellige noen nå-foreldede dokumenter. GNU GCC 4.4 gjennomføringen synes å ha en call()funksjon i bind()klassen, men det er svært lite dokumentasjon om emnet.

Noen foreslår håndskrevne rekursive hacks, men den sanne verdien av variadic malparametere er å kunne bruke dem i tilfeller som ovenfor.

Er det noen som har en løsning på er, eller hint om hvor du skal lese om det?

Publisert på 26/03/2009 klokken 20:43
kilden bruker
På andre språk...                            


13 svar

stemmer
1

Nyheten ser ikke bra.

Etter å ha lest over nettopp utgitt utkast standard , jeg ser ikke en innebygd løsning på dette, noe som virker merkelig.

Det beste stedet å spørre om slike ting (hvis du ikke allerede har gjort) er comp.lang.c ++. Modereres, fordi noen folk som er involvert i å utforme standard innlegg der regelmessig.

Hvis du sjekker ut denne tråden , noen har det samme spørsmålet (kanskje det er deg, og da kommer du til å finne hele dette svaret litt frustrerende!), Og noen rumpe-ugly implementeringer er foreslått.

Jeg bare lurte på om det ville være enklere å gjøre funksjonen akseptere en tuple, som konvertering på den måten er enklere. Men dette innebærer at alle funksjoner skal akseptere tupler som argumenter, for maksimal fleksibilitet, og slik at bare demonstrerer strangeness av ikke å gi en innebygd utvidelse av tuppel å fungere argument pack.

Oppdatering: linken ovenfor ikke fungerer - prøv å lime inn dette:

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

Svarte 26/03/2009 kl. 21:56
kilden bruker

stemmer
43

Her er min kode hvis noen er interessert

I utgangspunktet ved kompilering kompilatoren vil rekursivt rulle alle argumenter i forskjellige inkluderende funksjonskall <N> -> kaller <N-1> -> kaller ... -> anrop <0> som er den siste og kompilatoren vil optimalisere vekk de forskjellige mellomliggende funksjonskall til bare å holde den siste som er det samme som funk (Arg1, arg2, arg3, ...)

Forutsatt 2 versjoner, en for en funksjon som kalles på en gjenstand, og den andre for en statisk funksjon.

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

  }
}
Svarte 10/10/2009 kl. 05:13
kilden bruker

stemmer
35

I C ++ er det mange måter å utvide / utpakking tuppel og anvende disse tuppel elementer til en variadic mal funksjon. Her er en liten hjelper klasse som skaper index array. Den brukes mye i mal metaprogramming:

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

Nå kode som gjør jobben er ikke så stor:

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

Testen er vist under:

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

Jeg er ikke stor ekspert på andre språk, men jeg antar at hvis disse språkene ikke har slik funksjonalitet i sin meny, er det ingen måte å gjøre det. Minst med C ++ du kan, og jeg tror det er ikke så mye komplisert ...

Svarte 23/06/2011 kl. 12:45
kilden bruker

stemmer
-3

Hvorfor ikke bare vikle variadic argumenter i en tuppel klasse og deretter bruke kompilering rekursjon (se lenke ) for å hente indeksen du er interessert i. Jeg finner at utpakking variadic maler i en beholder eller samling kan ikke type trygt wrt heterogene typer

template<typename... Args>
auto get_args_as_tuple(Args... args) -> std::tuple<Args...> 
{
    return std::make_tuple(args);
}
Svarte 04/02/2012 kl. 05:27
kilden bruker

stemmer
28

Jeg synes dette å være den mest elegante løsningen (og det er optimalt videre):

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

Eksempler på bruk:

void foo(int i, bool b);

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

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

Dessverre GCC (4,6 minst) unnlater å kompilere dette med "beklager, unimplemented: mangling load" (som ganske enkelt betyr at kompilatoren ikke ennå fullt ut implementere C ++ 11 spec), og siden den bruker variadic maler, wont arbeide i MSVC, slik at det er mer eller mindre ubrukelig. Men når det er en kompilator som støtter spesifikasjonen, vil det være den beste tilnærmingen IMHO. (Merk: Det er ikke så vanskelig å endre dette, slik at du kan omgå manglene i GCC, eller å gjennomføre det med Boost Preprocessor, men det ødelegger eleganse, så dette er den versjonen jeg legger.)

GCC 4.7 støtter nå denne koden helt fint.

Edit: Lagt frem rundt selve funksjonskall for å støtte rvalue referansen skjema * dette i tilfelle du bruker klang (eller hvis noen andre faktisk får rundt for å legge det).

Edit: Lagt mangler frem rundt funksjonen objekt i den ikke-medlem gjelder funksjon kropp. Takk til pheedbaq for å peke på at det var mangler.

Edit: Og her er C ++ 14-versjonen bare fordi det er så mye bedre (faktisk ikke kompilere ennå):

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

Her er en versjon for medlemsfunksjoner (ikke testet veldig mye!):

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);
}
Svarte 29/09/2012 kl. 05:04
kilden bruker

stemmer
0

Hva med dette:

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

Den run_tuplefunksjonsmal tar den gitte tuppel og passere elementene individuelt til den gitte funksjon. Det utfører sitt arbeid ved rekursivt å kalle sine hjelpefunksjons maler explode_tuple. Det er viktig at run_tuplepasserer tuppel størrelse til explode_tuple; dette tallet virker som en teller for hvor mange elementer som skal trekkes ut.

Dersom tuppel er tom, deretter run_tuplekaller den første versjonen av explode_tuplemed den eksterne funksjon som den eneste andre argumentet. Fjern funksjonen kalles uten argumenter og vi er ferdige. Dersom tuppel ikke er tom, blir et høyere tall overføres til det andre versjon av explode_tuple, og den eksterne funksjon. En rekursiv kall til explode_tupleer laget, med de samme argumentene, bortsett fra telleren tall minskes med en og (en referanse til) den siste tuppel elementet er slått på som et argument etter at den eksterne funksjon. I en rekursiv samtale, enten er telleren ikke null, og en annen samtale er laget med telleren redusert igjen og den neste-unreferenced elementet er ført inn i argumentlisten etter at den eksterne funksjon, men før de andre innsatte argumenter, eller telleren når null og fjern funksjonen kalles med alle argumentene akkumulert etter det.

Jeg er ikke sikker på at jeg har syntaksen til å tvinge en bestemt versjon av en funksjon mal rett. Jeg tror du kan bruke en peker til funksjon som en funksjon objekt; kompilatoren vil automatisk fikse det.

Svarte 29/04/2013 kl. 09:22
kilden bruker

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

Dette er innrettet til fra den C ++ 14 utkast via index_sequence. Jeg kan foreslå å ha gjelde i en fremtidig standard (TS).

Svarte 27/09/2013 kl. 21:13
kilden bruker

stemmer
0

Jeg vurderer MSVS 2013RC, og det klarte ikke å samle noen av de tidligere løsningene som foreslås her i noen tilfeller. For eksempel vil MSVS klarer å kompilere "auto" avkastning hvis det er for mange funksjonsparametre, på grunn av en navne imbrication grense (jeg sendte det info til Microsoft for å få det rettet). I andre tilfeller må vi tilgang til funksjonen avkastning, men som også kan gjøres med en lamda: De følgende to eksemplene gir samme resultat ..

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

Og takk igjen til dem som har lagt svar her før meg, jeg ville ikke ha fått til dette uten at det ... så her er det:

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));
}
Svarte 15/10/2013 kl. 18:45
kilden bruker

stemmer
-4

Denne enkle løsningen fungerer for meg:

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
}
Svarte 12/06/2014 kl. 15:06
kilden bruker

stemmer
1

Alt dette implementeringer er gode. Men på grunn av bruk av pekeren til medlem funksjon kompilatoren ofte kan ikke inline målet funksjonskall (minst gcc 4.8 kan ikke, uansett hva Hvorfor gcc kan ikke integrerte funksjonspekere som kan bestemmes? )

Men ting endres hvis send pekeren til medlem fungere som mal argumenter, ikke som funksjon params:

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

Og ussage:

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

Bevis på inlinable http://goo.gl/5UqVnC


Med små endringer, kan vi "overload" 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....)

Pluss dette er den eneste løsningen som fungerer med malbasert funksjoner.

Svarte 17/07/2014 kl. 13:01
kilden bruker

stemmer
1

1) Hvis du har et ferdighus parameter_pack struktur som funksjonsargument, kan du bare bruke std :: slips som dette:

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) Hvis du ikke har en ferdighus parampack arg, må du koble den tuppel som dette

#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);
}
Svarte 14/05/2015 kl. 14:07
kilden bruker

stemmer
18

I C ++ 17 kan du gjøre dette:

std::apply(the_function, the_tuple);

Dette allerede fungerer i klang ++ 3.9, bruke std :: eksperimentell :: gjelder.

Svare på kommentaren si at dette ikke vil fungere hvis the_functioner malbasert, er følgende en work-around:

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

Dette arbeidet rundt er en forenklet løsning for det generelle problemet med å passere overbelastnings sett og funksjon templat hvor en funksjon ville forventes. Den generelle løsningen (en som tar seg av perfekt videresending, constexpr-ness, og noexcept-ness) er presentert her: https://blog.tartanllama.xyz/passing-overload-sets/ .

Svarte 08/05/2016 kl. 13:59
kilden bruker

stemmer
0

Utvide på @ Davids løsning, kan du skrive en rekursiv mal som

  1. Bruker ikke de (altfor ordrik, IMO) integer_sequencesemantikk
  2. Bruker ikke en ekstra midlertidig malparameter int Nå telle rekursive gjentakelser
  3. (Valgfritt for statisk / globale funktorer) benytter funktor som et templat parameter for kompilering tid optimizaion

Eg:

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

Alternativt hvis funktor ikke er definert ved kompilering-tid (for eksempel en ikke- constexprfunktor eksempel, eller en lambda uttrykk), kan man bruke den som en funksjon parameter i stedet for en klasse malparameter, og faktisk fjerne inneholdende klasse helt:

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

For pekeren-til-medlem-funksjon callables, kan du justere noen av de ovennevnte kode stykker på samme måte som i @ Davids svar.

Forklaring

Under henvisning til den andre del av koden, er det to malen funksjoner: den første man tar funktor func, tuppel tmed typene T..., og en parameter pakke argsav typer Args_tmp.... Når kalt, det rekursivt legger gjenstandene fra tparameter pakke en om gangen, fra begynnelsen ( 0) til slutt, og kaller funksjonen på nytt med det nye inkrementeres parameter pakke.

Den andre funksjonen signatur er nesten identisk med den første, bortsett fra at den bruker skriver T...for parameteren pakken args. Således, en gang argsi den første funksjon er fullstendig fylt med verdiene fra t, vil det være type T...(i kvasikode typeid(T...) == typeid(Args_tmp...)), og således vil kompilatoren i stedet kalle den andre belastet funksjon, som i sin tur anrop func(args...).

Koden i den statiske funktor eksempel fungerer likt med det funktor i stedet benyttet som en klasse malparametere.

Svarte 31/10/2017 kl. 19:33
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more