मैं वेरिएडिक टेम्प्लेट फ़ंक्शन के तर्कों में एक टपल का विस्तार कैसे करूं?


135

वैरिएबल टेम्प्लेट तर्कों के साथ एक अस्थायी फ़ंक्शन के मामले पर विचार करें:

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

अब, मेरे पास tमूल्यों का एक समूह है । मैं func()टपल मूल्यों का उपयोग तर्क के रूप में कैसे कर सकता हूं ? मैंने bind()फ़ंक्शन ऑब्जेक्ट के बारे में पढ़ा है, फ़ंक्शन के साथ call(), और apply()कुछ अलग-अलग अब-अप्रचलित दस्तावेजों में भी फ़ंक्शन। जीएनयू जीसीसी 4.4 कार्यान्वयन के call()लिए bind()कक्षा में एक फ़ंक्शन है , लेकिन विषय पर बहुत कम प्रलेखन है।

कुछ लोग हाथ से लिखे हुए पुनरावर्ती हैक का सुझाव देते हैं, लेकिन वेरिएडिक टेम्प्लेट तर्कों का सही मूल्य उपरोक्त मामलों में उनका उपयोग करने में सक्षम होना है।

क्या किसी के पास इसका समाधान है, या इसके बारे में पढ़ने के लिए संकेत देना चाहिए?


5
C ++ 14 मानक में एक समाधान दृश्य है; open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3658.html
Skeen

1
इस विचार का उपयोग करते हुए एक एकल वैरिएड विस्फोट में टपल को खोलना है integer_sequence, देखें en.cppreference.com/w/cpp/utility/integer_fterence
Skeen

6
ए होने के बाद integer_sequence S, आप बस अपने फ़ंक्शन को कॉल करते हैं func(std::get<S>(tuple)...), और कंपाइलर को बाकी को संभालने दें।
स्किनी

1
यदि C ++ 17 या बाद का उपयोग करते हैं, तो इस उत्तर को अनदेखा करें और std का उपयोग करके नीचे एक देखें :: लागू करें
lewis

जवाबों:


46

अगर किसी को दिलचस्पी है तो यहां मेरा कोड है

मूल रूप से कंपाइलर समय पर कंपाइलर विभिन्न समावेशी फंक्शन कॉल्स <n> -> कॉल <N-1> -> कॉल्स ... -> कॉल्स <0> में सभी तर्कों को अनियंत्रित रूप से अनियंत्रित करेगा जो अंतिम एक है और कंपाइलर ऑप्टिमाइज़ कर देगा विभिन्न मध्यवर्ती फ़ंक्शन कॉल केवल अंतिम वाले को रखने के लिए है जो कि func के बराबर है (arg1, arg2, arg3, ...)

बशर्ते 2 संस्करण हैं, एक फ़ंक्शन के लिए एक ऑब्जेक्ट पर और दूसरा एक स्थिर फ़ंक्शन के लिए।

#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
क्या ऐसे मामले में काम करने के लिए इसे अनुकूलित करना संभव है, जहां प्रश्न में "फ़ंक्शन" वास्तव में एक निर्माता है?
हाईकमान्डर

क्या आप एक उदाहरण दे सकते हैं कि आप क्या करना चाहते हैं और हम वहां से जा सकते हैं।
डेविड

यह समाधान केवल एक संकलन समय उपरि साबित करता है और अंत में इसे सरल किया जाएगा (pbbj -> * f) (arg0, arg, 1, ... argN); सही?
नासमझ

हां, कंपाइलर कई फ़ंक्शन कॉल को अंतिम रूप से संपीड़ित कर देगा जैसे कि आपने इसे स्वयं लिखा था जो इस सभी प्रोग्रामिंग प्रोग्रामिंग सामान की सुंदरता है।
डेविड

सभी tr1सामान अब c ++ 11
रयान हेनिंग

37

C ++ 17 में आप ऐसा कर सकते हैं:

std::apply(the_function, the_tuple);

यह पहले से ही Clang ++ 3.9 में काम करता है, std :: प्रयोगात्मक :: apply का उपयोग करता है।

टिप्पणी का जवाब देते हुए कहा कि यह काम नहीं किया जाएगा अगर 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 :: पर लागू करें यह लगता है कि काम नहीं करता है अगर the_functionअस्थायी है।
Zitrax

1
@Zitrax आप फ़ंक्शन के टेम्प्लेट तर्कों को निर्दिष्ट कर सकते हैं:std::apply(add_generic<float>, std::make_pair(2.0f, 3.0f));
Erbureth का कहना है कि मोनिका

यह सबसे सरल, सबसे सुरुचिपूर्ण समाधान है। और यह अद्भुत काम करता है। बहुत बहुत धन्यवाद, एम। अलागन !!!!!! +100 वोट
इलियट

36

C ++ में tuple के विस्तार / अनपैकिंग के कई तरीके हैं और उन tuple तत्वों को एक varadic टेम्पलेट फ़ंक्शन पर लागू करते हैं। यहाँ एक छोटा सहायक वर्ग है जो सूचकांक सरणी बनाता है। इसका उपयोग खाका मेटाप्रोग्रामिंग में बहुत किया जाता है:

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

दुर्भाग्य से जीसीसी (4.6 कम से कम) इसे "क्षमा करें, अकल्पित: प्रबंधनीय अधिभार" के साथ संकलित करने में विफल रहता है (जिसका सीधा अर्थ है कि कंपाइलर अभी तक पूरी तरह से सी ++ 11 कल्पना को लागू नहीं करता है), और चूंकि यह वैरेडिक टेम्प्लेट का उपयोग करता है, इसलिए यह अभ्यस्त नहीं है MSVC में काम करते हैं, इसलिए यह कमोबेश बेकार है। हालांकि, एक बार एक कंपाइलर है जो कल्पना का समर्थन करता है, यह सबसे अच्छा दृष्टिकोण आईएमएचओ होगा। (नोट: इसे संशोधित करना इतना कठिन नहीं है कि आप जीसीसी में कमियों के आसपास काम कर सकते हैं, या इसे बूस्ट प्रीप्रोसेसर के साथ लागू कर सकते हैं, लेकिन यह लालित्य को बर्बाद कर देता है, इसलिए यह वह संस्करण है जिसे मैं पोस्ट कर रहा हूं।)

GCC 4.7 अब इस कोड का समर्थन करता है।

संपादित करें: प्रतिद्वंद्विता संदर्भ फ़ॉर्म का समर्थन करने के लिए वास्तविक फ़ंक्शन कॉल के आसपास जोड़ा गया है * यह इस मामले में है कि आप क्लैंग का उपयोग कर रहे हैं (या यदि कोई और वास्तव में इसे जोड़ने के लिए चारों ओर हो जाता है)।

संपादित करें: गैर-सदस्य अनुप्रयोग फ़ंक्शन के मुख्य भाग में फ़ंक्शन ऑब्जेक्ट के आसपास गुम हुआ जोड़ा गया। यह याद करने के लिए कि वह गायब था, इसके लिए फेदबाक का शुक्रिया।

संपादित करें: और यहाँ 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 के साथ संकलित करते हैं और इसे चलाते हैं, तो आप देखेंगे कि आउटपुट क्या होना चाहिए। मुझे एक संकलन त्रुटि मिलती है अन्यथा (मुझे लगता है कि घोषणापत्र मेरे विशेष मामले का पता लगाने के लिए पर्याप्त स्मार्ट नहीं है), gcc 4.7.2 के साथ।
kfmfe04

3
विचार के लिए gcc का संस्करण इसको पास करने के लिए पुराना है, यह mangled घोषणापत्र वापसी प्रकार ओवरलोडिंग का समर्थन नहीं करता है। मैंने इस कोड का अपेक्षाकृत अच्छी तरह से gcc 4.7.2 में परीक्षण किया है, और मैंने किसी भी समस्या में भाग नहीं लिया है। 4.8 gcc के साथ, आप सभी गंदे घोषणापत्र अनुगामी रिटर्न प्रकारों से बचने के लिए नए C ++ 17 स्वचालित वापसी मान सुविधा का उपयोग कर सकते हैं।
20

1
गैर-सदस्यीय applyफ़ंक्शन में, जिज्ञासा से, कॉल के fसाथ क्यों नहीं लपेटा जाता है std::forward, क्योंकि यह रिटर्न प्रकार में है? क्या इसकी जरूरत नहीं है?
ब्रेट रॉसिएर

3
जिज्ञासा से बाहर, मैंने इसे जीसीसी 4.8 में foo('x', true)संकलित करने की कोशिश की, और ठीक उसी असेंबली कोड के apply(foo, ::std::make_tuple('x', true))साथ-साथ अनुकूलन के किसी भी स्तर के साथ -0 पर संकलित किया।
डीआरएएएक्स

2
C ++ 14 के साथ integer_sequenceआपको apply()इसके उदाहरण में लगभग सही कार्यान्वयन भी मिलता है । नीचे मेरा जवाब देखें।
पीटरसम सिप

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 ड्राफ्ट से अनुकूलित है। मैं भविष्य के मानक (टीएस) में आवेदन करने का प्रस्ताव कर सकता हूं।


1

खबर अच्छी नहीं लगती।

अभी-अभी जारी किए गए ड्राफ्ट मानक पर पढ़ने के बाद , मुझे इसका कोई अंतर्निहित समाधान नहीं दिखाई दे रहा है, जो कि अजीब लगता है।

ऐसी चीजों के बारे में पूछने के लिए सबसे अच्छी जगह (यदि आप पहले से नहीं हैं) comp.lang.c ++ है। मॉडरेट किया गया है, क्योंकि कुछ लोग नियमित रूप से मानक पोस्ट को प्रारूपित करने में शामिल हैं।

यदि आप इस धागे की जांच करते हैं , तो किसी के पास एक ही सवाल है (शायद यह आप है, जिस स्थिति में आप इस पूरे उत्तर को थोड़ा निराश करने वाले हैं!), और कुछ बट-बदसूरत कार्यान्वयन सुझाए गए हैं।

मैं सिर्फ यह सोचता हूं कि क्या फ़ंक्शन को स्वीकार करना सरल होगा 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 की आवश्यकता नहीं है बनाने की क्षमता है।
कोप्रो

1
इसके अलावा, सबसे अच्छी जगह comp.lang.c ++ नहीं है। C ++ 1x के बारे में प्रश्न लगभग हमेशा comp.std.c ++ से बेहतर निर्देशित होते हैं।
2

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 :: टाई का उपयोग कर सकते हैं:

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 को भेज दी है कि इसे ठीक किया गया है)। अन्य मामलों में, हमें फ़ंक्शन की वापसी तक पहुंच की आवश्यकता है, हालांकि यह एक लामडा के साथ भी किया जा सकता है: निम्नलिखित दो उदाहरण एक ही परिणाम देते हैं ...

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?
टॉवर १०

0

@ डेविड के समाधान पर विस्तार से, आप एक पुनरावर्ती टेम्पलेट लिख सकते हैं

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

पॉइंटर-टू-मेंबर-फंक्शन कॉलिबल्स के लिए, आप उपरोक्त कोड के किसी भी टुकड़े को उसी तरह से एडजस्ट कर सकते हैं, जैसे @ डेविड के जवाब में।

व्याख्या

कोड के दूसरे टुकड़े के संदर्भ में, दो टेम्प्लेट फ़ंक्शंस हैं: पहला फ़नकार लेता है func, tप्रकारों के साथ ट्यूपल T..., और एक argsप्रकार का पैरामीटर पैक Args_tmp...। जब बुलाया जाता है, तो यह पुन t: प्रारंभ ( 0) से अंत तक, एक बार में पैरामीटर पैक एक से वस्तुओं को जोड़ता है , और नए बढ़े हुए पैरामीटर पैक के साथ फ़ंक्शन को फिर से कॉल करता है।

दूसरे फ़ंक्शन का हस्ताक्षर लगभग पहले के समान है, सिवाय इसके कि यह T...पैरामीटर पैक के लिए प्रकार का उपयोग करता है args। इस प्रकार, argsपहले फ़ंक्शन के पूरी तरह से मानों से भरे जाने के बाद t, यह टाइप T...(psuedo-code typeid(T...) == typeid(Args_tmp...)) में होगा, और इस प्रकार संकलक बदले में दूसरे अतिभारित फ़ंक्शन को कॉल करेगा, जो बदले में कॉल करता है func(args...)

स्टैटिक फ़ंक्टर उदाहरण में कोड, फ़ंक्शनल के साथ एक क्लास टेम्प्लेट तर्क के रूप में उपयोग किए जाने के साथ, समान रूप से काम करता है।


पहले विकल्प के संकलन-समय अनुकूलन पर किसी भी टिप्पणी की सराहना की जाएगी, इसलिए मैं अपने उत्तर को और अधिक पूर्ण कर सकता हूं (और शायद कुछ नया सीख सकता हूं)।
क्रेपगेट

-3

सिर्फ अपने वैरिएड के तर्कों को एक टुपल क्लास में क्यों न लपेटें और फिर जिस इंडेक्स में आपकी रुचि हो उसे पुनः प्राप्त करने के लिए संकलन समय पुनरावर्तन ( लिंक देखें ) का उपयोग करें । मुझे लगता है कि कंटेनर या संग्रह में अनपैकिंग वैरिएड टेम्पलेट सुरक्षित नहीं है।

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

6
सवाल दूसरे तरीके का था। नहीं Args...-> tuple, लेकिन tuple-> Args...
XIO

-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 Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.