2

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.

Community
  • 1
  • 1
Maths noob
  • 1,684
  • 20
  • 42

2 Answers2

2

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)
    {
    }
};
Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
  • I prefer to keep my current design. Really want to know what's wrong with my method of storing parameter types and the way I "recall" them in the signature for the member function pointer type. – Maths noob Oct 15 '16 at 19:19
  • @user4376555: in order to expand a parameter pack, the pack must be in scope, so something will have to change. There are various ways to do it. Can you be more specific about what aspects of your design you want to preserve? – Vaughn Cato Oct 15 '16 at 19:26
  • oh I see. Currently, DelegateInfoPack serves two purposes, it typedefs the delegate type so that by header-including the DelegateInfoPacks, the components would be able to have DELEGATE_TYPE del; and they would later be able to pass it on the delegate manager. secondly, its just a tidy way of wrapping all the info together and making the DelegateManager class clean. It sounds like I can't keep it "clean" the way I was hoping. I tried playing around with "friend" but that didn't work either. I guess I will have to make DelegateInfoPack thinner and take FuncParams out of it. – Maths noob Oct 15 '16 at 19:58
  • Btw, I do in fact have a container class for storing the templatized versions of these delegate Mangers. so my DelegateManager in fact has the form: `template class DelegateManager Contract : public DelegateManagerContainer{...}` would you be able to combine your `template class DelegateManager;` with such a container class? – Maths noob Oct 15 '16 at 23:21
  • @user4376555: Yes, you can have the template specialization derive from another class. – Vaughn Cato Oct 16 '16 at 01:50
  • @user4376555: I've added a couple other options to my answer. – Vaughn Cato Oct 16 '16 at 02:08
  • This is perfect! I did in fact try to do exactly what you are doing in your last option but got the syntax wrong for this line: `typedef FuncRetType (_FuncType)(FuncParams...);` so it wasn't working. Thanks. – Maths noob Oct 16 '16 at 12:00
1

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

Community
  • 1
  • 1
Evgeniy
  • 2,481
  • 14
  • 24