Làm cách nào để tôi mở rộng một tuple thành các đối số của hàm mẫu matrixdic?


135

Hãy xem xét trường hợp của một hàm templated với các đối số khuôn mẫu:

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

Bây giờ, tôi có một bộ tgiá trị. Làm thế nào để tôi gọi func()bằng cách sử dụng các giá trị tuple làm đối số? Tôi đã đọc về bind()đối tượng hàm, với call()hàm và cả apply()hàm trong một số tài liệu đã lỗi thời. Việc triển khai GNU GCC 4.4 dường như có một call()chức năng trongbind() lớp, nhưng có rất ít tài liệu về chủ đề này.

Một số người đề xuất các bản hack đệ quy viết tay, nhưng giá trị thực sự của các đối số khuôn mẫu dao động là có thể sử dụng chúng trong các trường hợp như trên.

Có ai có một giải pháp cho, hoặc gợi ý về nơi để đọc về nó?


5
Tiêu chuẩn C ++ 14 có một giải pháp xem; open-std.org/jtc1/sc22/wg21/docs/ con / 2013 / n3658.html
Skeen

1
Ý tưởng là giải nén bộ dữ liệu trong một vụ nổ dao động duy nhất, bằng cách sử dụng integer_sequence, xem en.cppreference.com/w/cpp/utility/integer_ resultence
Skeen

6
Có một integer_sequence S, bạn chỉ cần gọi hàm của bạn là func(std::get<S>(tuple)...)và để trình biên dịch xử lý phần còn lại.
Skeen

1
Nếu sử dụng C ++ 17 trở lên, hãy bỏ qua câu trả lời này và xem câu trả lời bên dưới bằng std :: áp dụng
lewis

Câu trả lời:


46

Đây là mã của tôi nếu có ai quan tâm

Về cơ bản tại thời gian biên dịch, trình biên dịch sẽ đệ quy đệ quy tất cả các đối số trong các lệnh gọi hàm bao gồm khác nhau <N> -> cuộc gọi <N-1> -> cuộc gọi ... -> cuộc gọi <0> là cuộc gọi cuối cùng và trình biên dịch sẽ tối ưu hóa đi các hàm trung gian khác nhau gọi để chỉ giữ cái cuối cùng tương đương với func (arg1, arg2, arg3, ...)

Được cung cấp có 2 phiên bản, một phiên bản cho một chức năng được gọi trên một đối tượng và phiên bản còn lại cho chức năng tĩnh.

#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
Có thể điều chỉnh cái này để hoạt động trong trường hợp "hàm" trong câu hỏi thực sự là một hàm tạo không?
HighCommander4

Bạn có thể cung cấp một ví dụ về những gì bạn muốn làm và chúng ta có thể đi từ đó.
David

Giải pháp này chỉ chứng minh được chi phí thời gian biên dịch và cuối cùng, nó sẽ được đơn giản hóa thành (pObj -> * f) (arg0, arg, 1, ... argN); đúng?
Ngu ngốc

vâng, trình biên dịch sẽ nén nhiều lệnh gọi hàm vào hàm cuối cùng như thể bạn đã tự viết nó, đó là nét đẹp của tất cả các công cụ lập trình meta này.
David

tất cả mọi tr1thứ có thể được lấy ra ngay bây giờ với c ++ 11
Ryan Hained

37

Trong C ++ 17 bạn có thể làm điều này:

std::apply(the_function, the_tuple);

Điều này đã hoạt động trong Clang ++ 3.9, sử dụng std :: thử nghiệm :: áp dụng.

Trả lời các bình luận nói rằng điều này sẽ không hoạt động nếu the_functionđược tạo khuôn mẫu, sau đây là một cách giải quyết:

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

Công việc này là một giải pháp đơn giản hóa cho vấn đề chung là chuyển các tập hợp quá tải và mẫu hàm trong đó một hàm sẽ được mong đợi. Giải pháp chung (một giải pháp chăm sóc chuyển tiếp hoàn hảo, constexpr-ness và noexcept-ness) được trình bày ở đây: https://blog.tartanllama.xyz/passing-overload-sets/ .


Theo mã ví dụ tại std :: áp dụng, nó dường như không hoạt động nếu the_functionđược tạo khuôn mẫu.
Zitrax

1
@Zitrax Bạn có thể chỉ định các đối số mẫu của hàm:std::apply(add_generic<float>, std::make_pair(2.0f, 3.0f));
Erbureth nói Phục hồi lại

Đây là giải pháp đơn giản nhất, thanh lịch nhất. Và nó hoạt động kỳ diệu. Cảm ơn rất nhiều, M. Alaggan !!!!!! +100 phiếu bầu
Elliott

36

Trong C ++, có nhiều cách để mở rộng / giải nén tuple và áp dụng các phần tử tuple đó cho một hàm khuôn mẫu. Đây là một lớp trợ giúp nhỏ tạo mảng chỉ mục. Nó được sử dụng rất nhiều trong siêu lập trình mẫu:

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

Bây giờ mã mà công việc không phải là lớn:

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

Kiểm tra được hiển thị dưới đây:

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

Tôi không phải là chuyên gia lớn trong các ngôn ngữ khác, nhưng tôi đoán rằng nếu những ngôn ngữ này không có chức năng như vậy trong thực đơn của họ, thì không có cách nào để làm điều đó. Ít nhất là với C ++, bạn có thể, và tôi nghĩ nó không quá phức tạp ...


"... Và áp dụng các phần tử tuple đó cho một hàm khuôn mẫu" . Phần kiểm tra chỉ chứa các hàm matrixdic không phải mẫu. Nếu tôi thêm một lượt thích template<class ... T> void three(T...) {}và cố gắng sử dụng thì nó không được biên dịch.
Zitrax

32

Tôi thấy đây là giải pháp tao nhã nhất (và nó được chuyển tiếp tối ưu):

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

Ví dụ sử dụng:

void foo(int i, bool b);

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

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

Thật không may, GCC (ít nhất là 4.6) không biên dịch được điều này với "xin lỗi, chưa thực hiện: quá tải xáo trộn" (điều này có nghĩa đơn giản là trình biên dịch chưa thực hiện đầy đủ thông số kỹ thuật C ++ 11) và vì nó sử dụng các mẫu matrixdic, nên nó sẽ không làm việc trong MSVC, vì vậy nó ít nhiều vô dụng. Tuy nhiên, một khi có một trình biên dịch hỗ trợ thông số kỹ thuật, nó sẽ là cách tiếp cận tốt nhất IMHO. (Lưu ý: không khó để sửa đổi điều này để bạn có thể khắc phục các thiếu sót trong GCC hoặc để triển khai nó với Boost Pre xử lý, nhưng nó phá hỏng sự thanh lịch, vì vậy đây là phiên bản tôi đang đăng.)

GCC 4.7 hiện hỗ trợ mã này tốt.

Chỉnh sửa: Đã thêm chuyển tiếp xung quanh chức năng gọi thực tế để hỗ trợ biểu mẫu tham chiếu rvalue * trong trường hợp bạn đang sử dụng clang (hoặc nếu có ai thực sự có xung quanh để thêm nó).

Chỉnh sửa: Đã thêm thiếu chuyển tiếp xung quanh đối tượng chức năng trong cơ thể của hàm không áp dụng thành viên. Cảm ơn pheedbaq đã chỉ ra rằng nó đã bị mất.

Chỉnh sửa: Và đây là phiên bản C ++ 14 vì nó đẹp hơn rất nhiều (chưa thực sự biên dịch):

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

Đây là phiên bản cho các chức năng thành viên (không được thử nghiệm nhiều!):

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 trong số các câu trả lời được liệt kê, câu trả lời của bạn là cách gần nhất tôi có thể làm việc với các đối số có đối số là vectơ ... ... nhưng tôi vẫn nhận được lỗi biên dịch. ideone.com/xH5kBH Nếu bạn biên dịch cái này với -DDIRECT_CALL và chạy nó, bạn sẽ thấy đầu ra sẽ là gì. Tôi nhận được một lỗi biên dịch khác (tôi nghĩ rằng dectype không đủ thông minh để tìm ra trường hợp đặc biệt của tôi), với gcc 4.7.2.
kfmfe04

3
Phiên bản của gcc trên ideaone đã cũ để vượt qua, nó không hỗ trợ quá tải kiểu trả về kiểu khai báo bị xáo trộn. Tôi đã kiểm tra mã này tương đối kỹ lưỡng trong gcc 4.7.2 và tôi không gặp phải bất kỳ vấn đề nào. Với gcc 4.8, bạn có thể sử dụng tính năng giá trị trả về tự động C ++ 17 mới để tránh tất cả các loại trả về theo sau khó chịu.
DRayX

1
Vì tò mò, trong applychức năng không phải thành viên , tại sao fkhông được bao bọc bằng một std::forwardcuộc gọi, vì nó thuộc loại trả về? Có cần thiết không?
Brett Rossier

3
Vì tò mò, tôi đã thử biên dịch cái này trong GCC 4.8 và foo('x', true)biên dịch thành mã lắp ráp chính xác giống như apply(foo, ::std::make_tuple('x', true))với bất kỳ mức độ tối ưu hóa nào ngoài -O0.
DRayX

2
Với C ++ 14, integer_sequencebạn thậm chí có được một triển khai gần như đúng apply()trong ví dụ của nó. xem câu trả lời của tôi dưới đây
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());
}

Điều này được điều chỉnh từ bản nháp C ++ 14 bằng cách sử dụng index_ resultence. Tôi có thể đề xuất áp dụng trong một tiêu chuẩn trong tương lai (TS).


1

Tin tức có vẻ không tốt.

Đã đọc qua tiêu chuẩn dự thảo vừa được phát hành , tôi không thấy một giải pháp tích hợp cho vấn đề này, điều này có vẻ kỳ lạ.

Nơi tốt nhất để hỏi về những điều như vậy (nếu bạn chưa có) là comp.lang.c ++. Được kiểm duyệt, bởi vì một số người liên quan đến việc soạn thảo bài đăng tiêu chuẩn ở đó thường xuyên.

Nếu bạn kiểm tra chủ đề này , ai đó có cùng một câu hỏi (có thể là bạn, trong trường hợp đó bạn sẽ tìm thấy toàn bộ câu trả lời này một chút bực bội!), Và một vài cách triển khai xấu xí được đề xuất.

Tôi chỉ tự hỏi nếu nó sẽ đơn giản hơn để làm cho chức năng chấp nhận một tuple , vì việc chuyển đổi theo cách đó dễ dàng hơn. Nhưng điều này ngụ ý rằng tất cả các hàm nên chấp nhận bộ dữ liệu làm đối số, để linh hoạt tối đa và do đó chỉ thể hiện sự kỳ lạ của việc không cung cấp mở rộng tích hợp của bộ dữ liệu cho gói đối số chức năng.

Cập nhật: liên kết ở trên không hoạt động - hãy thử dán này:

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


Tôi tự hỏi tại sao họ thậm chí bận tâm có các khái niệm riêng biệt của gói đối số và hàm đối số. Có thể trong một trình biên dịch phù hợp, chúng có thể hoán đổi cho nhau nhưng tôi không phát hiện ra dấu hiệu nào cho thấy bất cứ nơi nào tôi đọc về chúng.
Daniel Earwicker

2
Bởi vì tuple <int, char, chuỗi> là cần thiết như một loại riêng biệt; như khả năng thực hiện một chức năng không yêu cầu make_type ở giữa mỗi cuộc gọi.
coppro

1
Ngoài ra, nơi tốt nhất không phải là comp.lang.c ++. Được kiểm duyệt. Các câu hỏi về C ++ 1x hầu như luôn được chuyển hướng tốt hơn đến comp.std.c ++.
coppro

1

Tất cả các thực hiện này là tốt. Nhưng do sử dụng con trỏ để trình biên dịch hàm thành viên thường không thể thực hiện cuộc gọi hàm mục tiêu (ít nhất là gcc 4.8 không thể, bất kể tại sao gcc không thể xác định được con trỏ hàm nội tuyến? )

Nhưng mọi thứ thay đổi nếu gửi con trỏ đến hàm thành viên dưới dạng đối số mẫu, không phải là tham số hàm:

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

Và sử dụng:

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

Bằng chứng về inlinable http://goo.gl/5UqVnC


Với những thay đổi nhỏ, chúng ta có thể "quá tải" 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....)

Thêm vào đó, đây là giải pháp duy nhất hoạt động với các chức năng templated.


1

1) nếu bạn có cấu trúc read_ade readymade làm đối số hàm, bạn chỉ có thể sử dụng std :: tie như thế này:

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) nếu bạn không có parampack argymade arg, bạn sẽ phải thư giãn tuple như thế này

#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

Còn cái này thì sao:

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

Mẫu run_tuplehàm lấy bộ dữ liệu đã cho và chuyển từng phần tử của nó cho hàm đã cho. Nó thực hiện công việc của mình bằng cách gọi đệ quy các mẫu hàm trợ giúp của nó explode_tuple. Điều quan trọng là run_tuplevượt qua kích thước của bộ dữ liệu đến explode_tuple; con số đó đóng vai trò là bộ đếm cho bao nhiêu phần tử cần trích xuất.

Nếu bộ dữ liệu trống, sau đó run_tuplegọi phiên bản đầu tiên explode_tuplevới chức năng từ xa là đối số duy nhất khác. Hàm từ xa được gọi không có đối số và chúng ta đã hoàn thành. Nếu bộ dữ liệu không trống, một số cao hơn được chuyển sang phiên bản thứ hai explode_tuple, cùng với chức năng từ xa. Một cuộc gọi đệ quy đếnexplode_tuple được tạo ra, với cùng một đối số, ngoại trừ số bộ đếm bị giảm đi một và (một tham chiếu đến) phần tử bộ cuối cùng được xử lý như một đối số sau chức năng từ xa. Trong một cuộc gọi đệ quy, bộ đếm không bằng 0 và một cuộc gọi khác được thực hiện với bộ đếm giảm lần nữa và phần tử không được ước tính tiếp theo được chèn vào danh sách đối số sau hàm từ xa nhưng trước khi các đối số được chèn khác hoặc bộ đếm đạt tới không và hàm từ xa được gọi với tất cả các đối số tích lũy sau nó.

Tôi không chắc mình có cú pháp buộc phải có một phiên bản cụ thể của mẫu hàm. Tôi nghĩ rằng bạn có thể sử dụng một con trỏ đến chức năng như một đối tượng chức năng; trình biên dịch sẽ tự động sửa nó.


0

Tôi đang đánh giá MSVS 2013RC và không thể biên dịch một số giải pháp trước đây được đề xuất ở đây trong một số trường hợp. Ví dụ: MSVS sẽ không biên dịch trả về "tự động" nếu có quá nhiều tham số chức năng, do giới hạn định mức không gian tên (tôi đã gửi thông tin đó cho Microsoft để sửa lỗi). Trong các trường hợp khác, chúng ta cần quyền truy cập vào trả về của hàm, mặc dù điều đó cũng có thể được thực hiện với một lamda: hai ví dụ sau đây cho kết quả tương tự ..

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

Và một lần nữa cảm ơn những người đã đăng câu trả lời ở đây trước tôi, tôi sẽ không nhận được điều này nếu không có nó ... vì vậy đây là:

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

Tại sao bạn làm cho đối số một con trỏ const? Không tham chiếu, không tham chiếu const, không chỉ con trỏ? Điều gì nếu chức năng có thể gọi được sẽ không const?
tháp120

0

Mở rộng giải pháp của @ David, bạn có thể viết một mẫu đệ quy

  1. Không sử dụng integer_sequencengữ nghĩa (quá dài dòng, imo)
  2. Không sử dụng tham số mẫu tạm thời bổ sung int Nđể đếm số lần lặp đệ quy
  3. (Tùy chọn cho functor tĩnh / toàn cầu) sử dụng functor làm tham số mẫu để tối ưu hóa thời gian biên dịch

Ví dụ:

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

Ngoài ra, nếu functor của bạn không được xác định tại thời gian biên dịch (ví dụ: một đối tượng không phải là constexprfunctor hoặc biểu thức lambda), bạn có thể sử dụng nó làm tham số hàm thay vì tham số mẫu lớp và trên thực tế loại bỏ hoàn toàn lớp chứa:

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

Đối với các hàm gọi con trỏ đến hàm thành viên, bạn có thể điều chỉnh một trong các đoạn mã trên tương tự như trong câu trả lời của @ David.

Giải trình

Để tham chiếu đến đoạn mã thứ hai, có hai hàm mẫu: hàm thứ nhất lấy functor func, tuple tvới các kiểu T...và một gói tham số argscác kiểu Args_tmp.... Khi được gọi, nó đệ quy thêm các đối tượng từ tgói tham số một lần, từ đầu ( 0) đến hết và gọi lại hàm với gói tham số tăng mới.

Chữ ký của hàm thứ hai gần giống với chữ ký thứ nhất, ngoại trừ việc nó sử dụng kiểu T...cho gói tham số args. Do đó, một khi argstrong hàm đầu tiên được điền đầy đủ các giá trị từ tđó, kiểu của nó sẽ là T...(theo mã psuedo typeid(T...) == typeid(Args_tmp...)), và do đó trình biên dịch sẽ gọi hàm quá tải thứ hai, lần lượt gọi func(args...).

Mã trong ví dụ functor tĩnh hoạt động giống hệt nhau, với functor thay vào đó được sử dụng làm đối số khuôn mẫu lớp.


bất kỳ nhận xét nào về tối ưu hóa thời gian biên dịch của tùy chọn đầu tiên sẽ được đánh giá cao, vì vậy tôi có thể làm cho câu trả lời của mình đầy đủ hơn (và có thể học một cái gì đó mới).
CrepeGoat

-3

Tại sao không chỉ bọc các đối số biến đổi của bạn vào một lớp tuple và sau đó sử dụng đệ quy thời gian biên dịch (xem liên kết ) để truy xuất chỉ mục mà bạn quan tâm. Tôi thấy rằng việc giải nén các mẫu biến đổi vào một thùng chứa hoặc bộ sưu tập có thể không phải là loại không đồng nhất.

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

6
Câu hỏi là cách khác. Không phải Args...-> tuple, mà là tuple-> Args....
Xèo

-4

Giải pháp đơn giản này hiệu quả với tôi:

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
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.