C + + implementation examples about delegating (Delegates)

  • 2020-04-02 03:10:17
  • OfStack

  introduce

It is useful in C++ to bind member functions to objects with a global function, and this feature exists in other languages, such as C# delegates. It is the equivalent of a pointer to a member function in C++, but does not provide the corresponding feature. In this article, I would like to propose a simple C++ delegate implementation, which USES C++ member function Pointers and C++11's variadic templates. At present, this implementation method only supports GNU C++ 4.7.0, and MinGW can be used under Windows.

background

The create_delegate function provided in my winning method can be called in the following two ways:


  create_delegate(&object, &member_function)
  create_delegate(&function)

The first method creates an object and provides an operator() member function, and the second method generates a function pointer, both of which are compatible with type function < . > .

The sample program

First, we define a class that contains multiple methods:
 


class A
{
  int i;
public:  
  A(int k):i(k) {}
 
  auto get()const ->int { return i;}  
  auto set(int v)->void { i = v;}
 
  auto inc(int g)->int& { i+=g; return i;}
  auto incp(int& g)->int& { g+=i; return g;}
 
  auto f5 (int a1, int a2, int a3, int a4, int a5)const ->int
  {
    return i+a1+a2+a3+a4+a5;
  }
 
  auto set_sum4(int &k, int a1, int a2, int a3, int a4)->void
  {
    i+=a1+a2+a3+a4;
    k = i;
  }
 
  auto f8 (int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) const ->int
  {
    return i+a1+a2+a3+a4+a5+a6+a7+a8;
  }  
 
  static auto sqr(double x)->double { return x*x; }
};

Note that you don't necessarily need to use C++ 's auto syntax, you can also use the traditional method, and we'll use the following method to create a class:

 


A a(11);

Next we create the delegate:
 


auto set1 = create_delegate(&a,&A::set);
auto inc = create_delegate(&a,&A::inc);
std::function<int(int&)> incp = create_delegate(&a,&A::incp);
auto af5 = create_delegate(&a,&A::f5);
auto set_sum4= create_delegate(&a,&A::set_sum4);
auto af8 = create_delegate(&a,&A::f8);
auto sqr = create_delegate(&A::sqr); // static function </int(int&)>

 
set1(25);
int x = 5;
int k = inc(x);
k = incp(x);
std::cout << "a.get():" << a.get() << std::endl;
std::cout << "k: " << k << std::endl;
std::cout << "x: " << x << std::endl;
std::cout << "af5(1,2,3,4,5): " << af5(1,2,3,4,5) << std::endl;
 
set_sum4(x,1,2,3,20);
std::cout << "after set_sum4(x,1,2,3,20)" << std::endl;
std::cout << "a.get(): " << a.get() << std::endl;
std::cout << "x: " << x << std::endl;
std::cout << "af8(1,2,3,4,5,6,7,8): " << af8(1,2,3,4,5,6,7,8) << std::endl;
std::cout << "sqr(2.1): " << sqr(2.1) << std::endl;

 
The printed result of executing the above procedure is as follows:
 


a.get():30
k: 35
x: 35
af5(1,2,3,4,5): 45
after set_sum4(x,1,2,3,20)
a.get(): 56
x: 56
af8(1,2,3,4,5,6,7,8): 92
sqr(2.1): 4.41

The key point
For a simple function that is not volatile and const, the implementation is very simple, we just need to create a class to hold two Pointers, one is an object, the other is a member function:
 


template <class T, class R, class ... P>
struct _mem_delegate
{
  T* m_t;
  R (T::*m_f)(P ...);
  _mem_delegate(T* t, R (T::*f)(P ...) ):m_t(t),m_f(f) {}
  R operator()(P ... p)
  {
      return (m_t->*m_f)(p ...);
  }
}; 

The variable template variadic template allows you to define the operator() function for any number and type parameters, while the create_function implementation simply returns the object of that class:

 


template <class T, class R, class ... P>
_mem_delegate<T,R,P ...> create_delegate(T* t, R (T::*f)(P ...))
{
  _mem_delegate<T,R,P ...> d(t,f);
  return d;
}

 
 

In practice, we need three more implementations to override the const, volatile, and const volatile member functions, which is why the traditional #define macro is so handy that you don't have to rewrite the code snippet. Here's the complete implementation:
 


template <class F>
F* create_delegate(F* f)
{
  return f;
}
#define _MEM_DELEGATES(_Q,_NAME)
template <class T, class R, class ... P>
struct _mem_delegate ## _NAME
{
  T* m_t;
  R (T::*m_f)(P ...) _Q;
  _mem_delegate ## _NAME(T* t, R (T::*f)(P ...) _Q):m_t(t),m_f(f) {}
  R operator()(P ... p) _Q
  {
    return (m_t->*m_f)(p ...);
  }
};

template <class T, class R, class ... P>
  _mem_delegate ## _NAME<T,R,P ...> create_delegate(T* t, R (T::*f)(P ...) _Q)
{
  _mem_delegate ##_NAME<T,R,P ...> d(t,f);
  return d;
}
 
_MEM_DELEGATES(,Z)
_MEM_DELEGATES(const,X)
_MEM_DELEGATES(volatile,Y)
_MEM_DELEGATES(const volatile,W)


Related articles: