Yet Another C#-style Delegate Class in Standard C++






4.89/5 (41 votes)
Aug 28, 2005
2 min read

215531

1198
This article describes a C#-style delegate class completely written in standard C++.
Introduction
Delegates and events are definitely cool features of .NET/C# and there are already many attempts to simulate them in standard C++. The Delegate
class described in this article tries to provide a complete and easy to use solution for using delegates in standard C++. Your feedback is welcome.
Design Goals
There are already several good delegate implementations on CodeProject. However, there is still no solution good enough for broad developers. For example, some implementations do not support multicast delegates (which is a must for events), some implementations do not support function objects (functors), some implementations are hard to use (syntax). So the design goals of my Deletgate
class are:
- Support all C++ callable entities, including static functions, member functions and functors.
- Support single-cast and multicast in one
Delegate
class, so users don't have to implement multicast functionalities themselves. - Easy to understand and use.
Examples
1. Using delegates
#include "AcfDelegate.h" using namespace Acf; static void H() { ... } class Foo { public: void G() { ... } }; class Foo2 { public: void operator()(int n) { ... } }; // Create delegate a which attaches to a static function Delegate<void ()> a(&H); assert(a == &H); // Create delegate b which attaches // to an instance and a member function Foo foo; Delegate<void ()> b(&foo, &Foo::G); assert(b == std::make_pair(&foo, &Foo::G); // Create delegate c from a Delegate<void ()> c = a; assert(c == &H); // Create delegate d which attaches to a function object Delegate<void (int)> d(Foo2()); // Call delegates a(); d(100); // Combine/remove delegates d += &H; d += std::make_pair(&foo, &Foo::G); d -= std::make_pair(&foo, &Foo::G); d -= &H;
2. Using events
class Button { public: Delegate<void ()> Click; }; Button btn; btn.Click += &F; btn.Click += std::make_pair(&o, &MyObj::G); btn.Click(); btn.Click -= std::make_pair(&o, &MyObj::G);
3. Using events (advanced)
You may want more control on how event handlers are managed and fired, for example, you care about thread safety.
class Button { private: Delegate<void ()> click; Mutex mutex; public: template <class T> void add_Click(const T& h) { ScopedLock lock(this->mutex); this->click += h; } template <class T> void remove_Click(const T& h) { ScopedLock lock(this->mutex); this->click -= h; } protected: void OnClick() { if (this->click) this->click(); } }; btn.add_Click(&F); btn.add_Click(std::make_pair(&o, &MyObj::G));
Delegate Class
namespace Acf { template <class TSignature> class Delegate; // no body template <class R, class T1, class T2, ..., class TN> class Delegate<R (T1, T2, ..., TN)> { // Constructor/Destructor public: Delegate(); template <class TFunctor> Delegate(const TFunctor& f); template <class TPtr, class TFunctionPtr> Delegate(const TPtr& obj, const TFunctionPtr& mfp); Delegate(const Delegate& d); ~Delegate(); // Properties public: bool IsEmpty() const; bool IsMulticast() const; // Methods public: template <class TFunctor> void Add(const TFunctor& f); template <class TPtr, class TFunctionPtr> void Add(const TPtr& obj, const TFunctionPtr& mfp); template <class TFunctor> bool Remove(const TFunctor& f); template <class TPtr, class TFunctionPtr> bool Remove(const TPtr& obj, const TFunctionPtr& mfp); void Clear(); // Operators public: operator bool() const; bool operator!() const; template <class TFunctor> Delegate& operator=(const TFunctor& f); Delegate& operator=(const Delegate& d); template <class TFunctor> Delegate& operator+=(const TFunctor& f); template <class TFunctor> friend Delegate operator+(const Delegate& d, const TFunctor& f); template <class TFunctor> friend Delegate operator+(const TFunctor& f, const Delegate& d); template <class TFunctor> Delegate& operator-=(const TFunctor& f); template <class TFunctor> Delegate operator-(const TFunctor& f) const; template <class TFunctor> friend bool operator==(const Delegate& d, const TFunctor& f); template <class TFunctor> friend bool operator==(const TFunctor& f, const Delegate& d); template <class TFunctor> friend bool operator!=(const Delegate& d, const TFunctor& f); template <class TFunctor> friend bool operator!=(const TFunctor& f, const Delegate& d); R operator()(T1, T2, ..., TN) const; }; } // namespace Acf
The TFunctor
template parameter in the Delegate
class supports static functions, member functions (via std::pair
class that wraps an object pointer and a member function pointer) and functors. The TPtr
template parameter for member functions support plain pointers (e.g. Foo*
) and smart pointers (e.g. boost::shared_ptr<Foo>
).
Notes
- You must enable runtime type information (RTTI) in your project in order to use the
Delegate
class. - The current implementation supports up to six function parameters, which should be enough for most applications (and in general it's a bad practice to have more than six parameters).
- There are no operators
==
and!=
for comparing delegates, because they are impossible to implement correctly for functors (seeboost.function
). - The current implementation is not optimized for performance. However, it should be OK for most applications.
History
- 12/24/2005: changed the exception behavior when an empty delegate is called - if the delegate return type is
void
, then no exception will be thrown, otherwise anInvalidCallException
will be thrown. - 8/28/2005: initial release.