3

Hi I am trying to write a delegate class that can take a template argument similar to a standard function signature and create a delegate for a member function pointer as shown below in the main. The code may be oversimplified but that is what I was looking for was a simple and fast solution to this problem with as little overhead as possible. I think this implementation is pretty close to achieving what I want if I can get the type T in the class without runtime polymorphism etc.

template<class T>
struct FastDelegate {};

template<class R, class... Args>
struct FastDelegate<R (Args...)> 
{
    template <typename T>
    FastDelegate(T* t, R (T::*f)(Args...)) : m_t(t), m_f(f) {} 

    R operator()(Args... p)
    {
        return (m_t->*m_f)(std::forward<Args>(p)...);
    }

    T* m_t;  // How can I capture T as a type in this partial specialization?
    R  (T::*m_f)(Args...);
};

struct Test
{
 int add ( int x, int y ) { return x+y; }
};

int main ()
{
 int x = 5;
 int y = 4;

 Tester t;
 FastDelegate<int (int,int)> d (&t, &Test::calc );
 int z = d(x,y);
}
bjackfly
  • 3,236
  • 2
  • 25
  • 38

2 Answers2

4

You could capture the objects as a void*, store the member function in random member function type, and have a function restore the necessary types. This approach avoid allocating any memory on the heap. The problematic step is the conversion from a member function of some type to another member function. However, according to 5.2.10 [expr.reinterpret.cast] paragraph 10 this approach can be used safely as long at the member function is cast back to its original type before being used:

[...] The result of this conversion is unspecified, except in the following cases:

  • converting a prvalue of type “pointer to member function” to a different pointer to member function type and back to its original type yields the original pointer to member value.

Below is an example which implements this approach. Note, however, that it is probably easier to use std::function<R(Args...)> with a suitable lambda as the standard library is likely to implementation an approach like that in the first place.

#include <iostream>
#include <utility>

template<class T>
struct FastDelegate {};

template<class R, class... Args>
struct FastDelegate<R (Args...)> 
{
    struct dummy {};
    template <typename T>
    FastDelegate(T* t, R (T::*f)(Args...))
        : m_t(t)
        , m_f(reinterpret_cast<void (dummy::*)()>(f))
        , m_call([](void(dummy::*d)(), void* v, Args... a){
                typedef R (T::*mem)(Args...);
                T* t = static_cast<T*>(v);
                mem f = reinterpret_cast<mem>(d);
                return (t->*f)(std::forward<Args>(a)...);
            }) {
    }

    R operator()(Args... p) {
        return (this->m_call)(this->m_f, this->m_t, std::forward<Args>(p)...);
    }

    void* m_t;
    void  (dummy::*m_f)();
    R     (*m_call)(void (dummy::*)(), void*, Args...);
};

struct Tester
{
 int add ( int x, int y ) {
     std::cout << "add(" << x << ", " << y << ")\n";
     return x+y;
 }
};

int main ()
{
 int x = 5;
 int y = 4;

 Tester t;
 FastDelegate<int (int,int)> d (&t, &Tester::add);
 int z = d(x,y);
}
Community
  • 1
  • 1
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • Awesome! Never know you can play lambda like this! – Xin Dec 21 '13 at 02:14
  • This looks great I will accept this as the solution. Quick follow up question though, is this class assignable or safely copied? – bjackfly Dec 24 '13 at 13:44
  • @bjackfly: the class doesn't store anything interesting: just a couple of pointers. There is nothing which makes the class non-copyable. It doesn't copy the object, though (could be done, too, of course). – Dietmar Kühl Dec 24 '13 at 15:05
  • Is there a way to check 2 instance are equal? I don't see a way to do this since the types are hidden inside the lamda – bjackfly Jan 27 '14 at 17:52
3

You don't need to capture type T, because you can use t->*m_f as a function<R(Args...)>

template<class R, class... Args>
struct FastDelegate<R (Args...)> 
{
    template <typename T>
    FastDelegate(T* t, R (T::*f)(Args...)) 
        : m_f([=](Args... v){ return (t->*f)(std::forward(v)...); }) {} 

    R operator()(Args... p)
    {
        return m_f(std::forward<Args>(p)...);
    }

    std::function<R(Args...)> m_f;
};

If you want to compare 2 FastDelegate instance by comparing t and f, there is still no need for type info, just take raw pointer.

Xin
  • 1,300
  • 2
  • 14
  • 27
  • In other words, what he's asking is impossible. You just hid the polymorphism under `std::function`. Which is fine, except you should mention that it explicitly contradicts what he was looking for. – user541686 Dec 21 '13 at 09:39