简体   繁体   中英

member function pointer with variadic templates

I am trying to write a class that "manages" delegates in c++. I already have the delegate class implemented for me. I want this delegate manager class to have two functions:

  • One would take a pointer to instance of a delegate of a certain type with a given input-argument/return type, and cache it.

  • The other function would take a member function of the right type in order to bind the cached delegate instance to it.

Currently, I have:

template<typename... Args>
struct FunctionParamsPack { };

This is the container for the types of the parameters this function takes. ie for foo(int i, double d) that would be int and double . I am following the advice from here .

then I have the DelegateInfoPack class:

template<typename FuncRetType,typename... FuncParams>
struct DelegateInfoPack{
    //for look-up by components in the program
    typedef typename DelegateClass<FuncRetType, FuncParams...>          _Delegate;
    //for the delegate manager
    typedef typename FuncRetType                                        _FuncRetType;
    typedef typename FunctionParamsPack<FuncParams...>                  _FuncParams;
};

This struct is included by that the components in the program and it typedefs three typenames, two of which are to be used in the DelegateManger class:

template<typename DelegateInfoPack>
class DelegateManager
{

typedef typename    DelegateInfoPack::_Delegate         _Delegate;  

typedef typename    DelegateInfoPack::_FuncRetType      _FuncRetType;
typedef typename    DelegateInfoPack::_FuncParams       _FuncParams;


void CacheDelegate(_Delegate* del,...) {}

template<typename UserClass>
void BindDelegate(..., _FuncRetType(UserClass::*fp)( _FuncParams())) {} //Doesn't work!

}

My problem is with the BindDelegate() function. I am not able to create the correct signature for the member function of the type with a given return type and input parameter types.

basically, I need to know the way to have the right function pointer type with a given return type and argument type so my BindDelegate takes it as an argument.

One approach is to use partial specialization:

template<typename> class DelegateManager;

template<typename FuncRetType,typename... FuncParams>
class DelegateManager<DelegateInfoPack<FuncRetType,FuncParams...>>
{
    template<typename UserClass>
    void BindDelegate(_FuncRetType(UserClass::*fp)(FuncParams...))
    {
    }
};

Another approach is to have a class which generates the appropriate function type

template <typename FuncRetType,typename FuncParams>
struct FunctionPointer;

template <typename FuncRetType,typename...ARGS>
struct FunctionPointer<FuncRetType,FunctionParamsPack<ARGS...>> {
    typedef FuncRetType (Type)(ARGS...);
};

Then use that in your BindDelegate member function:

template<typename UserClass>
void
  BindDelegate(
    typename FunctionPointer<_FuncRetType,_FuncParams>::Type UserClass::*fp
  )
{ ... }

Or maybe even put this into your DelegateInfoPack class:

template<typename FuncRetType,typename... FuncParams>
struct DelegateInfoPack {
    .
    .
    .
    typedef FuncRetType (_FuncType)(FuncParams...);
};

and use that in your DelegateManager

template<typename DelegateInfoPack>
struct DelegateManager
{
    .
    .
    .

    typedef typename DelegateInfoPack::_FuncType _FuncType;

    template<typename UserClass>
    void BindDelegate(_FuncType UserClass::*fp)
    {
    }
};

As an additional way to solve your task - C++11 introduces new language features that can make your code more flexible with using standard elements

#include <iostream>
#include <functional>
#include <tuple>
#include <iostream>

using std::cout;
using std::endl;
using namespace std::placeholders;

// helpers for tuple unrolling
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; };

// simple function
double foo_fn(int x, float y, double z)
{
  return x + y + z;
}

// structure with memner function to call
struct foo_struct
{
    // member function to be used as a delegate
    double foo_fn(int x, float y, double z)
    {
        return x + y + z;
    }
    // this member function has different signature - but it can be used too
    // please note that argument order is changed too
    double foo_fn_4(int x, double z, float y, long xx)
    {
        return x + y + z + xx;
    }
};

// delegate class that holds as delegate as its params for future call
template <typename Ret, typename ...Args>
struct delayed_call
{
  // tuple can be used as FunctionParamsPack type
  typedef std::tuple<Args...> params_type;
  // std::function as delegate type
  typedef std::function<Ret(Args...)> function_type;

  // stored parameters
  params_type params;
  // stored delegate
  function_type func;

  // invocation
  Ret operator()()
  {
    return callFunc(typename gens<sizeof...(Args)>::type());
  }
  // direct invocation
  Ret operator()(Args... args)
  {
    return func(args...);
  }

  // internal invocation with tuple unrolling
  template<int ...S>
  double callFunc(seq<S...>)
  {
    return func(std::get<S>(params) ...);
  }
};

int main(void)
{
  // arguments
  std::tuple<int, float, double> t = std::make_tuple(1, 5, 10);
  // var #1 - you can use simple function as delegate
  delayed_call<double, int,float, double> saved_foo_fn{t, foo_fn};
  foo_struct fs;
  // var #2 - you can use member function as delegate
  delayed_call<double, int,float, double> saved_foo_fn_struct{t, std::bind(&foo_struct::foo_fn, fs, _1, _2, _3)};
  // var #3 - you can use member function with different signature as delegate. 
  // bind 0 to xx and change argument order
  delayed_call<double, int,float, double> saved_foo_fn_struct_4{t, std::bind(&foo_struct::foo_fn_4, fs, _1, _3, _2, 0l)};
  // var #4 - you can use lambda function as delegate
  delayed_call<double, int,float, double> saved_lambda{t, [](int x, float y, double z)
    {
        return x + y + z;
    }
  };
  cout << "saved_foo_fn: " << saved_foo_fn() << endl;
  cout << "saved_foo_fn_struct: " << saved_foo_fn_struct() << endl;
  cout << "saved_foo_fn_struct_4: " << saved_foo_fn_struct_4() << endl;
  cout << "saved_lambda: " << saved_lambda() << endl;
  cout << "direct call with (1,2,3) to a member: " << saved_foo_fn_struct(1, 2, 3) << endl;
}

Output:

saved_foo_fn: 16
saved_foo_fn_struct: 16
saved_foo_fn_struct_4: 16
saved_lambda: 16
direct call with (1,2,3) to a member: 6

Live demo

So you are not limited with only member functions, but also can use any callable type with even different signature

If placeholders::_1... look ugly for you - there is a solution

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM