71

I know that the topic of "C++ delegates" has been done to death, and both http://www.codeproject.com and http://stackoverflow.com deeply cover the question.

Generally, it seems that Don Clugston's fastest possible delegate is the first choice for many people. There are a few other popular ones.

However, I noticed that most of those articles are old (around 2005) and many design choices seem to have been made taking in account old compilers like VC7.

I'm in need of a very fast delegate implementation for an audio application.

I still need it to be portable (Windows, Mac, Linux) but I only use modern compilers (VC9, the one in VS2008 SP1 and GCC 4.5.x).

My main criteria are:

  • it must be fast!
  • it must be forward-compatible with newer versions of the compilers. I have some doubts about that with Don's implementation because he explicitly states it's not standard-compliant.
  • optionally, a KISS-syntax and ease-of-use is nice to have
  • multicast would be nice, although I'm convinced it's really easy to build it around any delegate library

Furthermore, I don't really need exotic features. I just need the good old pointer-to-method thing. No need to support static methods, free functions or things like that.

As of today, what is the recommended approach? Still use Don's version? Or is there a "community consensus" about another option?

I really don't want to use Boost.signal/signal2 because it's not acceptable in terms of performance. A dependency on QT is not acceptable as well.

Furthermore, I've seen some newer libraries while googling, like for example cpp-events but I couldn't find any feedback from users, including on SO.

Jeffrey Bosboom
  • 13,313
  • 16
  • 79
  • 92
Dinaiz
  • 2,213
  • 4
  • 22
  • 32
  • 4
    And, besides, more than one year later : does C++11 add something in regard to this problem ? It doesn't seem to but I might have missed something obvious :) – Dinaiz Jun 07 '12 at 00:51

2 Answers2

121

Update: An article with the complete source code and a more detailed discussion has been posted on The Code Project.

Well, the problem with pointers to methods is that they're not all the same size. So instead of storing pointers to methods directly, we need to "standardize" them so that they are of a constant size. This is what Don Clugston attempts to achieve in his Code Project article. He does so using intimate knowledge of the most popular compilers. I assert that it's possible to do it in "normal" C++ without requiring such knowledge.

Consider the following code:

void DoSomething(int)
{
}

void InvokeCallback(void (*callback)(int))
{
    callback(42);
}

int main()
{
    InvokeCallback(&DoSomething);
    return 0;
}

This is one way to implement a callback using a plain old function pointer. However, this doesn't work for methods in objects. Let's fix this:

class Foo
{
public:
    void DoSomething(int) {}

    static void DoSomethingWrapper(void* obj, int param)
    {
        static_cast<Foo*>(obj)->DoSomething(param);
    }
};

void InvokeCallback(void* instance, void (*callback)(void*, int))
{
    callback(instance, 42);
}

int main()
{
    Foo f;
    InvokeCallback(static_cast<void*>(&f), &Foo::DoSomethingWrapper);
    return 0;
}

Now, we have a system of callbacks that can work for both free and member functions. This, however, is clumsy and error-prone. However, there is a pattern - the use of a wrapper function to "forward" the static function call to a method call on the proper instance. We can use templates to help with this - let's try generalizing the wrapper function:

template<typename R, class T, typename A1, R (T::*Func)(A1)>
R Wrapper(void* o, A1 a1)
{
    return (static_cast<T*>(o)->*Func)(a1);

}

class Foo
{
public:
    void DoSomething(int) {}
};

void InvokeCallback(void* instance, void (*callback)(void*, int))
{
    callback(instance, 42);
}

int main()
{
    Foo f;
    InvokeCallback(static_cast<void*>(&f),
        &Wrapper<void, Foo, int, &Foo::DoSomething> );
    return 0;
}

This is still extremely clumsy, but at least now we don't have to write out a wrapper function every single time (at least for the 1 argument case). Another thing we can generalize is the fact that we're always passing a pointer to void*. Instead of passing it as two different values, why not put them together? And while we're doing that, why not generalize it as well? Hey, let's throw in an operator()() so we can call it like a function!

template<typename R, typename A1>
class Callback
{
public:
    typedef R (*FuncType)(void*, A1);

    Callback(void* o, FuncType f) : obj(o), func(f) {}
    R operator()(A1 a1) const
    {
        return (*func)(obj, a1);
    }

private:
    void* obj;
    FuncType func;
};

template<typename R, class T, typename A1, R (T::*Func)(A1)>
R Wrapper(void* o, A1 a1)
{
    return (static_cast<T*>(o)->*Func)(a1);

}

class Foo
{
public:
    void DoSomething(int) {}
};

void InvokeCallback(Callback<void, int> callback)
{
    callback(42);
}

int main()
{
    Foo f;
    Callback<void, int> cb(static_cast<void*>(&f),
        &Wrapper<void, Foo, int, &Foo::DoSomething>);
    InvokeCallback(cb);
    return 0;
}

We're making progress! But now our problem is the fact that the syntax is absolutely horrible. The syntax appears redundant; can't the compiler figure out the types from the pointer to method itself? Unfortunately no, but we can help it along. Remember that a compiler can deduce types via template argument deduction in a function call. So why don't we start with that?

template<typename R, class T, typename A1>
void DeduceMemCallback(R (T::*)(A1)) {}

Inside the function, we know what R, T and A1 is. So what if we can construct a struct that can "hold" these types and return them from the function?

template<typename R, class T, typename A1>
struct DeduceMemCallbackTag
{
};

template<typename R, class T, typename A1>
DeduceMemCallbackTag2<R, T, A1> DeduceMemCallback(R (T::*)(A1))
{
    return DeduceMemCallbackTag<R, T, A1>();
}

And since DeduceMemCallbackTag knows about the types, why not put our wrapper function as a static function in it? And since the wrapper function is in it, why not put the code to construct our Callback object in it?

template<typename R, typename A1>
class Callback
{
public:
    typedef R (*FuncType)(void*, A1);

    Callback(void* o, FuncType f) : obj(o), func(f) {}
    R operator()(A1 a1) const
    {
        return (*func)(obj, a1);
    }

private:
    void* obj;
    FuncType func;
};

template<typename R, class T, typename A1>
struct DeduceMemCallbackTag
{
    template<R (T::*Func)(A1)>
    static R Wrapper(void* o, A1 a1)
    {
        return (static_cast<T*>(o)->*Func)(a1);
    }

    template<R (T::*Func)(A1)>
    inline static Callback<R, A1> Bind(T* o)
    {
        return Callback<R, A1>(o, &DeduceMemCallbackTag::Wrapper<Func>);
    }
};

template<typename R, class T, typename A1>
DeduceMemCallbackTag<R, T, A1> DeduceMemCallback(R (T::*)(A1))
{
    return DeduceMemCallbackTag<R, T, A1>();
}

The C++ standard allows us to call static functions on instances (!):

class Foo
{
public:
    void DoSomething(int) {}
};

void InvokeCallback(Callback<void, int> callback)
{
    callback(42);
}

int main()
{
    Foo f;
    InvokeCallback(
        DeduceMemCallback(&Foo::DoSomething)
        .Bind<&Foo::DoSomething>(&f)
    );
    return 0;
}

Still, it's a lengthy expression, but we can put that into a simple macro (!):

template<typename R, typename A1>
class Callback
{
public:
    typedef R (*FuncType)(void*, A1);

    Callback(void* o, FuncType f) : obj(o), func(f) {}
    R operator()(A1 a1) const
    {
        return (*func)(obj, a1);
    }

private:
    void* obj;
    FuncType func;
};

template<typename R, class T, typename A1>
struct DeduceMemCallbackTag
{
    template<R (T::*Func)(A1)>
    static R Wrapper(void* o, A1 a1)
    {
        return (static_cast<T*>(o)->*Func)(a1);
    }

    template<R (T::*Func)(A1)>
    inline static Callback<R, A1> Bind(T* o)
    {
        return Callback<R, A1>(o, &DeduceMemCallbackTag::Wrapper<Func>);
    }
};

template<typename R, class T, typename A1>
DeduceMemCallbackTag<R, T, A1> DeduceMemCallback(R (T::*)(A1))
{
    return DeduceMemCallbackTag<R, T, A1>();
}

#define BIND_MEM_CB(memFuncPtr, instancePtr) \
    (DeduceMemCallback(memFuncPtr).Bind<(memFuncPtr)>(instancePtr))

class Foo
{
public:
    void DoSomething(int) {}
};

void InvokeCallback(Callback<void, int> callback)
{
    callback(42);
}

int main()
{
    Foo f;
    InvokeCallback(BIND_MEM_CB(&Foo::DoSomething, &f));
    return 0;
}

We can enhance the Callback object by adding a safe bool. It's also a good idea to disable the equality operators since it's not possible to compare two Callback objects. Even better, is to use partial specialization to allow for a "preferred syntax". This gives us:

template<typename FuncSignature>
class Callback;

template<typename R, typename A1>
class Callback<R (A1)>
{
public:
    typedef R (*FuncType)(void*, A1);

    Callback() : obj(0), func(0) {}
    Callback(void* o, FuncType f) : obj(o), func(f) {}

    R operator()(A1 a1) const
    {
        return (*func)(obj, a1);
    }

    typedef void* Callback::*SafeBoolType;
    operator SafeBoolType() const
    {
        return func != 0? &Callback::obj : 0;
    }

    bool operator!() const
    {
        return func == 0;
    }

private:
    void* obj;
    FuncType func;
};

template<typename R, typename A1> // Undefined on purpose
void operator==(const Callback<R (A1)>&, const Callback<R (A1)>&);
template<typename R, typename A1>
void operator!=(const Callback<R (A1)>&, const Callback<R (A1)>&);

template<typename R, class T, typename A1>
struct DeduceMemCallbackTag
{
    template<R (T::*Func)(A1)>
    static R Wrapper(void* o, A1 a1)
    {
        return (static_cast<T*>(o)->*Func)(a1);
    }

    template<R (T::*Func)(A1)>
    inline static Callback<R (A1)> Bind(T* o)
    {
        return Callback<R (A1)>(o, &DeduceMemCallbackTag::Wrapper<Func>);
    }
};

template<typename R, class T, typename A1>
DeduceMemCallbackTag<R, T, A1> DeduceMemCallback(R (T::*)(A1))
{
    return DeduceMemCallbackTag<R, T, A1>();
}

#define BIND_MEM_CB(memFuncPtr, instancePtr) \
    (DeduceMemCallback(memFuncPtr).Bind<(memFuncPtr)>(instancePtr))

Usage example:

class Foo
{
public:
    float DoSomething(int n) { return n / 100.0f; }
};

float InvokeCallback(int n, Callback<float (int)> callback)
{
    if(callback) { return callback(n); }
    return 0.0f;
}

int main()
{
    Foo f;
    float result = InvokeCallback(97, BIND_MEM_CB(&Foo::DoSomething, &f));
    // result == 0.97
    return 0;
}

I have tested this on the Visual C++ compiler (version 15.00.30729.01, the one that comes with VS 2008), and you do need a rather recent compiler to use the code. By inspection of the disassembly, the compiler was able to optimize away the wrapper function and the DeduceMemCallback call, reducing down to simple pointer assignments.

It's simple to use for both sides of the callback, and uses only (what I believe to be) standard C++. The code I've shown above works for member functions with 1 argument, but can be generalized to more arguments. It can also be further generalized by allowing support for static functions.

Note that the Callback object requires no heap allocation - they are of a constant size thanks to this "standardization" procedure. Because of this, it's possible to have a Callback object be a member of larger class, since it has a default constructor. It is also assignable (the compiler generated copy assignment functions are sufficient). It is also typesafe, thanks to the templates.

In silico
  • 51,091
  • 10
  • 150
  • 143
  • 1
    I tried doing this several years back, and using pointer-to-member-functions as template arguments, while allowed by the standard, caused internal compiler errors in Visual C++ (I think it was version 2005 SP1). So with the caveat that this really does require using recent compiler versions, it's a very good approach. – Ben Voigt Nov 29 '10 at 03:32
  • @Ben Voigt: Right. This method does require a rather recent compiler (that's why I mentioned the compiler I used - the one on VS2008). Obviously this just won't work on something like VC6. Of course this can be improved and generalized for free and member functions accepting up to `n` arguments. It unfortunately requires a macro to make this usable by humans, but I think for this one case it's justified. – In silico Nov 29 '10 at 03:34
  • Later, when I have time, I'm going to have to download the Visual C++ 2010 compiler as well as a GCC compiler (perhaps version 4.5) to test this out. – In silico Nov 29 '10 at 05:12
  • This is impressive ! Is your real name "Herb sutter" or "Andrei Alexandrescu" ??? :-)))) Ah no it's not (note that I checked !!! :-) ). If you have time, this code should be grouped into a mini-delegate library (along with multicast and a support for several arguments), I'm pretty sure it would be very successfull ! – Dinaiz Nov 29 '10 at 06:28
  • @Dinaiz: I appreciate the sentiment, although I didn't think it was *that* impressive. :-) I can certainly (and already have) created a version that can handle 0 to 5 arguments and can bind to global, static and member functions. Although I haven't tried it out, one can probably build some kind of multicast system out of this. I'll see if I can do more tests/develop it more then perhaps write up an article/library of some sort. Remember that this only works with recent compilers, but you have mentioned in your question already that this is not an issue. – In silico Nov 29 '10 at 09:20
  • Haha, well reading your answer reminded me a lot ofr "Modern C++ Design" by Alexandrescu : basically, using the C++ templates to do something that probably never crossed the minds of the guys who designed the templates :-)). Yeah old compilers aren't really a problem. Would you mind sharing your 5 arguments version as well ? Regards – Dinaiz Nov 29 '10 at 09:54
  • 2
    @Dinaiz: The 0-5 argument version is really almost the same thing as above, but with things like `DeduceMemCallbackTag5` with parameters like `A1`, `A2`, `A3`, etc. The partial template specialization business makes it transparent. It does blow up into hundreds of lines of code, which makes it a good candidate for a library in its own right. I'm a bit busy at the moment, but later I'll write an article with the complete source code. This Stack Overflow answer is long enough as it is. – In silico Nov 29 '10 at 10:11
  • Awesome answer, definitely learn something new every day here. – wkl Nov 29 '10 at 15:21
  • Nice work! But I'm wondering: why is `Bind()` a function template? It's already inside a class template that knows the relevant parameters (`R` and `A1`) so it can just be a plain static method, and `BIND_MEM_CB` doesn't actually need to specify `memFuncPtr` as its explicit template parameter. In fact I'm pretty sure that macro can be completely replaced a function template that infers all relevant types and returns a `Callback` object of appropriate type. – j_random_hacker Nov 30 '10 at 18:12
  • 2
    @j_random_hacker: Good question. The `Wrapper<>()` function needs the pointer to the method **at compile time**, so the user code has to provide a method pointer via a **non-type** template parameter, i.e. the only non-type template parameter of `Bind<>()`. The only purpose of `DeduceMemCallback()` is to deduce the types making up the method pointer, returning a dummy object that "knows" the deduced types in the process. This allows `Bind<>()` to accept a method pointer as a non-type argument without having to explicitly accept the types making up the method pointer as well. – In silico Nov 30 '10 at 23:39
  • @In silico: Thanks I see it now! For some reason I was seeing an imaginary `typename` in front of the `R (T::*Func)(A1)` for the `Bind` and `Wrapper` declarations. I see now why you need the 2-step approach -- if you tried to do it in a single function template, it would need to have template arguments ``, with earlier type arguments being deduced from the last non-type one which is not allowed by 14.8.2.4/12: "A template type argument cannot be deduced from the type of a non-type template-argument." – j_random_hacker Dec 01 '10 at 04:07
  • @j_random_hacker: Exactly. Without this compile-time mechanism, the user code would have to explicitly provide all the types before providing the method pointer, leading to an unusable, clumsy syntax. C++ standard 9.4/2 explicitly allows static functions to be called on objects, which links the two different steps. This allows for a simple macro to be used, giving a natural syntax. The compiler was able to optimize away the deduction and wrapper functions, probably because this all happens at compile time. – In silico Dec 01 '10 at 06:14
  • @j_random_hacker: I actually tried eliminating the macro, but I think this is about as good as it gets. As a library, all the classes and functions would be in their own namespace and the original macro will be given an extra long name to avoid naming conflict. I would then define an additional "shortcut" macro that expands to the extra long name, so users can easily `#undef` it and possibly give it another name, if need be. – In silico Dec 01 '10 at 06:30
  • Ok your code works, but I'm trying to really understand how it does :-). Your explanations are very clear but there's one point I don't understand : "return DeduceMemCallbackTag();" -> what does this statement do ? I mean I had no idea this was valid C++ ! It looks like a constructor call in a return statement ?! :-O Or maybe am I overlooking something obvious ? – Dinaiz Dec 03 '10 at 09:14
  • @Dinaiz: No worries, the mechanism is definitely not obvious. The statement creates a temporary `DeduceMemCallbackTag` object which is then returned from the function. Then, `Bind()` is called on the returned temporary object to create a `Callback` object. In reality, compilers nowadays will see that we're returning empty objects and calling only static functions on the object, so the compiler may eliminate the call, resulting in code that involves only pointer assignments. – In silico Dec 04 '10 at 00:17
  • @Insilico this seems very neat but I find the comparison limitation a problem. Another library I've seen lets you do `notifier.onClick += newDelegate(&observer,&Observer::onClick);` but _also_ allows `notifier.onClick -= newDelegate(&observer,&Observer::onClick);` Any thoughts on this? – Mr. Boy Jan 16 '12 at 11:58
  • 3
    @John: Actually there is a way to make these delegates comparable by [taking advantage of the one definition rule](http://stackoverflow.com/questions/7670000/addresses-of-identical-function-template-instantiations-across-compilation-units). I wasn't aware that ODR applies to taking addresses of function template when I wrote this up (note that the linked question was asked later than mine). I've done some tests and it seems to work. All you need to do is to compare both the function pointer and the object pointer. I may update this answer and the article in the future when I have time. – In silico Jan 16 '12 at 20:40
  • @Insilico - do you know how boost::bind/std::bind is able to accomplish what you do without using macros and without necessitating specifying the function pointer as a template argument? – Steve Lorimer May 10 '12 at 05:11
  • @lori: They use template argument deduction on the function pointer that is passed at runtime. However, because the size of the function pointer is not known until runtime (and sizes of function pointers vary!), it may involve allocating from the free store. The answer I present is guaranteed to use only two pointers at runtime (thanks to compile-time mechanisms that eliminate the free store requirement). – In silico May 10 '12 at 05:26
  • @Insilico, thanks - that's great. You mention that you have extended this to work with free functions / static functions too. Do you have to use a separate macro (eg: `BIND_FREE_CB`) or were you able to have a macro and/or template functions which can correctly deduce which callback types you are using? Do you have sample code by any chance? – Steve Lorimer Jun 12 '12 at 04:11
  • @lori: I used two separate macros (i.e. `BIND_FREE_CB` and `BIND_MEM_CB`), but there is a way to require only one macro using variadic macros. (e.g. probably `#define BIND_CB(x, ...) (GetCallbackFactory(x).Bind(__VA_ARGS__))`) Some sample code is available in the article linked in my answer. – In silico Jun 12 '12 at 18:33
  • @Insilico - I wish you'd find some time to update both this answer and your article (and I believe the code as well) to reflect the comparison ability. This stuff is important and to the best of my knowledge Don Clugston solution is the only one offering comparable delegates (albeit, it is not standards-compliant). – Izhaki Nov 13 '12 at 00:15
  • 2
    This is the most epic answer I've ever seen on S/O. Bravo! – Reuben Scratton Jul 27 '13 at 20:23
  • @Insilico Nice, the deduction mechanism solves the ugly syntax I had for functor creation. However, do you have a solution which works with cv-qualified functions? – JarkkoL Jun 21 '14 at 01:27
10

I wanted to follow off of @Insilico's answer with a bit of my own stuff.

Before I had stumbled upon this answer, I was trying to figure out fast callbacks as well that incurred no overhead and were uniquely comparable / identified by function signature only. What I ended up creating - with some serious help from Klingons Who Happened To Be at a BBQ - works for all function types (except Lambdas, unless you store the Lambda, but don't try it because it's really difficult and hard to do and may result in a robot proving to you how difficult it is and making you eat the shit for it). Thanks to @sehe, @nixeagle, @StackedCrooked, @CatPlusPlus, @Xeo, @DeadMG and of course @Insilico for the help in creating the event system. Feel free to use as you desire.

Anyway, an example is up on ideone, but the source code is also here for your use (because, since Liveworkspace went down, I don't trust them shady compiling services. Who knows when ideone will go down?!). I hope this is useful for somebody who's not busy Lambda/Function-objecting the world to pieces:

IMPORTANT NOTE: As of right now (28/11/2012, 9:35 PM) This variadic version will not work with the Microsoft VC++ 2012 November CTP (Milan). If you want to use it with that, you will have to get rid of all the variadic stuff and explicitly enumerate the number of arguments (and possibly template-specialize the 1-argument type for Event for void) to make it work. It's a pain, and I could only manage to write it out for 4 arguments before I got tired (and decided that passing more than 4 arguments was a bit of a stretch).

Source Example

Source:

#include <iostream>
#include <vector>
#include <utility>
#include <algorithm>
 
template<typename TFuncSignature>
class Callback;
 
template<typename R, typename... Args>
class Callback<R(Args...)> {
public:
        typedef R(*TFunc)(void*, Args...);
 
        Callback() : obj(0), func(0) {}
        Callback(void* o, TFunc f) : obj(o), func(f) {}
        
        R operator()(Args... a) const {
                return (*func)(obj, std::forward<Args>(a)...);
        }
        typedef void* Callback::*SafeBoolType;
        operator SafeBoolType() const {
                return func? &Callback::obj : 0;
        }
        bool operator!() const {
                return func == 0;
        }
        bool operator== (const Callback<R (Args...)>& right) const {
                return obj == right.obj && func == right.func;
        }
        bool operator!= (const Callback<R (Args...)>& right) const {
                return obj != right.obj || func != right.func;
        }
private:
        void* obj;
        TFunc func;
};
 
namespace detail {
        template<typename R, class T, typename... Args>
        struct DeduceConstMemCallback { 
                template<R(T::*Func)(Args...) const> inline static Callback<R(Args...)> Bind(T* o) {
                        struct _ { static R wrapper(void* o, Args... a) { return (static_cast<T*>(o)->*Func)(std::forward<Args>(a)...); } };
                        return Callback<R(Args...)>(o, (R(*)(void*, Args...)) _::wrapper);
                }
        };
 
        template<typename R, class T, typename... Args>
    struct DeduceMemCallback { 
                template<R(T::*Func)(Args...)> inline static Callback<R(Args...)> Bind(T* o) {
                        struct _ { static R wrapper(void* o, Args... a) { return (static_cast<T*>(o)->*Func)(std::forward<Args>(a)...); } };
                        return Callback<R(Args...)>(o, (R(*)(void*, Args...)) _::wrapper);
                }
        };
 
        template<typename R, typename... Args>
        struct DeduceStaticCallback { 
                template<R(*Func)(Args...)> inline static Callback<R(Args...)> Bind() { 
                        struct _ { static R wrapper(void*, Args... a) { return (*Func)(std::forward<Args>(a)...); } };
                        return Callback<R(Args...)>(0, (R(*)(void*, Args...)) _::wrapper); 
                }
        };
}
 
template<typename R, class T, typename... Args>
detail::DeduceConstMemCallback<R, T, Args...> DeduceCallback(R(T::*)(Args...) const) {
    return detail::DeduceConstMemCallback<R, T, Args...>();
}
 
template<typename R, class T, typename... Args>
detail::DeduceMemCallback<R, T, Args...> DeduceCallback(R(T::*)(Args...)) {
        return detail::DeduceMemCallback<R, T, Args...>();
}
 
template<typename R, typename... Args>
detail::DeduceStaticCallback<R, Args...> DeduceCallback(R(*)(Args...)) {
        return detail::DeduceStaticCallback<R, Args...>();
}
 
template <typename... T1> class Event {
public:
        typedef void(*TSignature)(T1...);
        typedef Callback<void(T1...)> TCallback;
        typedef std::vector<TCallback> InvocationTable;
 
protected:
        InvocationTable invocations;
 
public:
        const static int ExpectedFunctorCount = 2;
 
        Event() : invocations() {
                invocations.reserve(ExpectedFunctorCount);
        }
 
        template <void (* TFunc)(T1...)> void Add() {
                TCallback c = DeduceCallback(TFunc).template Bind<TFunc>();
                invocations.push_back(c);
        }
 
        template <typename T, void (T::* TFunc)(T1...)> void Add(T& object) {
                Add<T, TFunc>(&object);
        }
 
        template <typename T, void (T::* TFunc)(T1...)> void Add(T* object) {
                TCallback c = DeduceCallback(TFunc).template Bind<TFunc>(object);
                invocations.push_back(c);
        }
 
        template <typename T, void (T::* TFunc)(T1...) const> void Add(T& object) {
                Add<T, TFunc>(&object);
        }
 
        template <typename T, void (T::* TFunc)(T1...) const> void Add(T* object) {
                TCallback c = DeduceCallback(TFunc).template Bind<TFunc>(object);
                invocations.push_back(c);
        }
 
        void Invoke(T1... t1) {
                for(size_t i = 0; i < invocations.size() ; ++i) invocations[i](std::forward<T1>(t1)...); 
        }
 
        void operator()(T1... t1) {
                Invoke(std::forward<T1>(t1)...);
        }
 
        size_t InvocationCount() { return invocations.size(); }
 
        template <void (* TFunc)(T1...)> bool Remove ()          
        { return Remove (DeduceCallback(TFunc).template Bind<TFunc>()); } 
        template <typename T, void (T::* TFunc)(T1...)> bool Remove (T& object) 
        { return Remove <T, TFunc>(&object); } 
        template <typename T, void (T::* TFunc)(T1...)> bool Remove (T* object) 
        { return Remove (DeduceCallback(TFunc).template Bind<TFunc>(object)); } 
        template <typename T, void (T::* TFunc)(T1...) const> bool Remove (T& object) 
        { return Remove <T, TFunc>(&object); } 
        template <typename T, void (T::* TFunc)(T1...) const> bool Remove (T* object) 
        { return Remove (DeduceCallback(TFunc).template Bind<TFunc>(object)); } 
 
protected:
        bool Remove( TCallback const& target ) {
                auto it = std::find(invocations.begin(), invocations.end(), target);
                if (it == invocations.end()) 
                        return false;
                invocations.erase(it);
                return true;
        }
};
Community
  • 1
  • 1
  • Would you be willing to share the non-variadic, up-to-4-arg version, for those of us with older compilers? Thx! – Dave Feb 26 '13 at 21:25
  • @supertwang Uh. Not really, because then I'd have to stop using all the fancy defines I made and clean up the code to rely on only the standard library, and that's a pain. You can grab a 2-argument version at: http://stackoverflow.com/questions/15032594/adding-a-callback-to-a-variadic-template-class-impossible/15033103#15033103 –  Feb 27 '13 at 04:18