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.

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 (see boost.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 an InvalidCallException will be thrown.

8/28/2005: initial release.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Share

About the Author

Yingle Jia is a software engineer located in Beijing, China. He currently works at IBM CSDL (China Software Development Lab). His interests include C++/COM/C#/.NET/XML, etc. He likes coding and writing.

After this patch the Delegate.cpp can compiled with g++ 4.2.1 and have the same output as with MSVC.

For more recent g++ compiler version (ex: 4.6.x), it may complains you about "Explicit Specialization cannot have a storage class" when it sees the declarations of _HandleInvalidCall() (both primary and specialized) in AcfDelegate.h having a 'static' modifier. Just remove the modifier and things will be fine.

I tried to compile the source in linux using g++(4.0.2), but got some error,
[zzq01@zhangzq71 Delegate]$ make
g++ -c -o Delegate.o Delegate.cpp
In file included from AcfDelegate.h:81,
from Delegate.cpp:12:
AcfDelegateTemplate.h:37:1: error: pasting "," and "typename" does not give a valid preprocessing token
AcfDelegateTemplate.h:38:1: error: pasting "," and "T" does not give a valid preprocessing token
AcfDelegateTemplate.h:66:1: error: pasting "," and "a" does not give a valid preprocessing token
AcfDelegateTemplate.h:75:1: error: pasting "," and "a" does not give a valid preprocessing token
AcfDelegateTemplate.h:103:1: error: pasting "," and "a" does not give a valid preprocessing token
AcfDelegateTemplate.h:201:1: error: pasting "," and "a" does not give a valid preprocessing token
AcfDelegateTemplate.h:202:1: error: pasting "," and "a" does not give a valid preprocessing token
AcfDelegateTemplate.h:366:1: error: pasting "," and "a" does not give a valid preprocessing token
AcfDelegateTemplate.h:367:1: error: pasting "," and "a" does not give a valid preprocessing token
In file included from AcfDelegate.h:85,
from Delegate.cpp:12:

it seems the preprocessor error, I tried another way,
g++ -E Delegate.cpp > output.cpp
g++ -o output output.cpp
it can successfully create the executable file, and the result of the program is correct. Do you know what is the error when compiling with gcc?

I would find it very handy if the delegate had at least an option to not throw an exception when a delegate that has never been assigned is nonetheless invoked. This would avoid having to check .IsEmpty() each time one wants to signal an event and doesn't know if anyone is subscribed. One could add a dummy subscriber to each delegate but not being able to add a default value in the delegate declaration (as is possible in C#) you would have to add it in the containing class constructor and that would complicate matters unnecessarily.
I've modified operator() for my personal use in the way listed below.
Thanks for a great library.

1. Your implementation is probably appropriate for most applications (especially GUI) although I doubt that class templates that use function signatures as template arguments can be 'Easy to understand and use'.

2. compiling your code with g++ 3.4 gives the following errors:
Compiling source file(s)...
Delegate.cpp
In file included from AcfDelegate.h:74,
from Delegate.cpp:12:
AcfDelegateTemplate.h:37:1: pasting "," and "class" does not give a valid preprocessing token
AcfDelegateTemplate.h:38:1: pasting "," and "T" does not give a valid preprocessing token
AcfDelegateTemplate.h:66:1: pasting "," and "a" does not give a valid preprocessing token
AcfDelegateTemplate.h:75:1: pasting "," and "a" does not give a valid preprocessing token
AcfDelegateTemplate.h:103:1: pasting "," and "a" does not give a valid preprocessing token
AcfDelegateTemplate.h:201:1: pasting "," and "a" does not give a valid preprocessing token
AcfDelegateTemplate.h:202:1: pasting "," and "a" does not give a valid preprocessing token
AcfDelegateTemplate.h:366:1: pasting "," and "a" does not give a valid preprocessing token
AcfDelegateTemplate.h:367:1: pasting "," and "a" does not give a valid preprocessing token

... and many more

Your macros don't work as expected for g++ (don't know whether it's a g++ bug). The macro cascades can easily be avoided by using a simple Code Generator that does what the current macros are supposed to do.

It would be nice if you compared your implementation with other implementations (advantages/disadvantages, performance, ease of use, ...).

1. It's impossible to have a better syntax in current C++. The original syntax is Delegate0<R>, Delegate1<R, T1>, Delegate2<R, T1, T2>, .... However many people do not like it. The function signature is probably the best we can do (at least it has a clear separation of return value and parameters).

2. I did not try g++ 3.4, but on g++ 3.2.3 they are just warnings. Maybe you can avoid this by some compiler switch (not sure). I will look at this when I have time. (If you have better solution, don't hestitate to tell me)

3. People really asked about comparsion to other delegate implementations (boost.signal, or others delegate classes on CodeProject). It's a good topic, I will try when I update this article later.

Yingle Jia wrote:2. I did not try g++ 3.4, but on g++ 3.2.3 they are just warnings. Maybe you can avoid this by some compiler switch (not sure). I will look at this when I have time. (If you have better solution, don't hestitate to tell me)

I don't know if it's a better solution but you could preprocess only the file with VC++, e.g.

cl /EP Delegate.cpp > out.txt

and then exchange the contents of AcfDelegate.h with the relevant portion of the preprocessor output (replace the macros with source code).

1. It's impossible to have a better syntax in current C++. The original syntax is Delegate0, Delegate1, Delegate2, .... However many people do not like it. The function signature is probably the best we can do (at least it has a clear separation of return value and parameters).

Just realize that some compilers don't support this syntax (this is why Boost and other implementations for TR1 of STL offer 2 versions of the fuction template).

Yingle Jia wrote:

3. People really asked about comparsion to other delegate implementations (boost.signal, or others delegate classes on CodeProject). It's a good topic, I will try when I update this article later.

Boost is a good one to compare your implementation to since there is a strong liklihood that much of the work done by Boost will end up in the next version of the Standard C++ Libraries. If your implementation is more efficient and/or robust, you may want to tweak it a bit (to meet their coding standards) and submit it for review.

If you decide to become a software engineer, you are signing up to have a 1/2" piece of silicon tell you exactly how stupid you really are for 8 hours a day, 5 days a week

Thanks for your comments. Boost does have good signal and function libraries, and I learned something from them (e.g. the function signature syntax). However, my Delegate class has different design goals, e.g. I like it to be like C#/.NET and I don't like to mass the design (syntax) for old and silly compilers (like VC6). People can have their own choices.

I wouldn't call VC6 "old and silly". There are still plenty of companies that use it as their standard development environment (the company I work for is one of them ... and quite frankly, management has no immediate plans to upgrade ... unfortunately).

My points with regard to boost is simple: 1) many of the libraries in TR1 are boost derivitives and signals could very well make that list and 2) it is designed to be portable. If you know that there is a strong chance something will become a standard, it is worth your time to at least learn how to use it. If portability is not a concern (that is, you know for sure that you won't be switching compilers, ever), then you have nothing to worry about. I don't know many companies that can guarantee that, though. Just some things to keep in mind.

As a side note, creating libraries like this are excellent academic exercises and can only enhance your skills (and you did a pretty good job -- the exception-safe syncronization is great).

If you decide to become a software engineer, you are signing up to have a 1/2" piece of silicon tell you exactly how stupid you really are for 8 hours a day, 5 days a week

I quite understand your points. Yes, VC6 is still widely used (some people don't want to upgrade to VC7/7.1 because UI changed a lot, some people don't want to upgrade because they mistakenly think VC7/7.1 can only be used to write .NET programs). Actually when I first wrote the class, it supported the syntax Delegate0, Delegate1, Delegate2 ... like boost, which is portable and supports VC6. However, when I talked to people, many of them (I guess they are using VC7/7.1 or GCC like me ^_^) thought that syntax is ugly, and a big concern is that people will use that syntax everywhere in order to make their code "portable" (I see many people using boost.function are using function0, function1 everywhere instead of the prefered syntax). That's why I did not put VC6 in my must-support list. Of course this can change according to user feedback.

I'm VERY interested in the way this class makes use of variable template paramters. I have never before seen this (T1, T2, ..., TN) syntax. It's only available in VC7.1+, and very few UNIX C++ compilers. I notice BOOST makes use of it as well, but also has the old-fashioned Functor1 Functor2 Functor3 style syntax for backwards compilers.

Does anyone have a link that describes this syntax in detail? I've been unlucky so far with Google.

-- modified at 12:46 Thursday 1st September, 2005

One more question - Why does the article say it only supports functions with up to 6 parameters? Does that ...TN syntax only support 6 paramters?

You see there is a compiler-time limit on the numbers of template parameters. Boost actually supports generating arbitary numbers using script language, but I think it's a good practice to keep function parameters no more than 6 - that's the reason.

Currently there is no direct variable template parameters support in standard C++ (though there is a proposal for C++0x). One has to use template (partial) specialization to achieve it. i.e.

...

You see there is a compiler-time limit on the numbers of template parameters. Boost actually supports generating arbitary numbers using script language, but I think it's a good practice to keep function parameters no more than 6 - that's the reason.

This is partially correct. Standard C++ currently does support variable template parameters, but there are several compilers that do not fully support it. Additionally, even those that do, some of them do not support the function-style syntax (even though it is technically standard as well). The C++0x Standard is suppose to more formally define this area, but you can get compilers that are fully compliant in this area.

armentage wrote:

What I don't understand is how those (T1,T2,T3) work. Was that just pseudo code in your example? I've never seen any C++ like that other than your Delegate and the Boost signals library.

It is real code, however, it is not supported by all compilers. If you take away the (), it is more portable at the moment. Boost has several libraries that use this feature (the function template is another example). You should note that currently, the Boost libraries offer 2 versions to support non-compliant compilers. I imagine that will change when C++0x is finalized.

If you decide to become a software engineer, you are signing up to have a 1/2" piece of silicon tell you exactly how stupid you really are for 8 hours a day, 5 days a week

Hi Yingle, got my 5, i'm really impressed by your ACF.
actually, boost::signal dose almost the same thing as you described here.
IMHO, boost is designed by advanced c++ developers for all standard c++ users.