Introduction

It is an answer to the article "Member Function Pointers and the Fastest Possible C++ Delegates by Don Clugston". Don proposed an approach to delegates (further called FastDelegate) which requires the same invocation code as is produced for invocation by a pointer to member function in the simplest case (he described why some compilers produce more complex code for polymorphic classes and classes with virtual inheritance). He described why many other popular approaches are inefficient. Unfortunately his approach is based on 'a horrible hack' (as he said). It works on many popular compilers, but is incompatible with the C++ Standard.

It seems to be true that the FastDelegate is the fastest possible way. But I suppose that such a claim needs a proof because modern C++ optimizing compilers make incredible things. I believe that boost::function and other dynamic memory allocation based delegates are slow, but who said there are no other good approaches?

I'm going to propose another approach, which:

is fast,

doesn't use dynamic allocated memory,

is completely compatible with the C++ Standard.

Yet another approach to delegates

Let's consider a delegate which receives one argument and returns no value. It may be defined in the following way using the preferred syntax (as the boost::function and the FastDelegate, my library supports preferred and compatibility syntaxes; see documentation for details):

delegate<void (int)>

I've simplified its code to help you understand how it works. The following code has been derived by removing unnecessary lines under and above the considered code and by replacing template parameters with concrete types.

So, a delegate consists of an untyped pointer to data (because a delegate mustn't depend on the type of receiver) and a pointer to a function. This function receives the pointer to data as an extra parameter. It converts the data pointer to object pointer ('void*', unlike member pointers can be safely converted back to object pointers: [expr.static.cast], item 10) and calls the required member function.

When you create a nonempty delegate, you implicitly instantiate a stub function by getting its address (see line #1 above). It is possible because the C++ Standard allows using a pointer to member or a pointer to function as a template parameter ([temp.params], item 4):

Now 'd' is containing a pointer to stub function bound to 'someMethod' at compile time. Although a member pointer was specified, invocation at line #2 is as fast as direct method invocation (because its value is known at compile time).

As usual, the delegate may be invoked by an inline function call operator which redirects the call to the target method through the stub function:

d(10); // invocation of SomeObject::someMethod
// for obj and passing them 10 as a parameter

Of course, it assumes an additional function call, but the overhead essentially depends on the optimizer. Actually, there may be no overhead at all.

Performance measurement

I've measured performance of the delegate invocation with various combinations of virtual/non-virtual methods, various numbers of arguments and with various types of inheritance. Also I've measured performance of delegates bound to a function and a static method. I've compared performance of FastDelegate with my approach using MS Visual C++ 7.1 and Intel C++ 8.0 compilers on a P4 Celeron processor.

In tangled cases, using a stub function may be a cause of a noticeable overhead (up to 5.5 times on MSVC and up to 2.4 times on Intel). But sometimes The Fastest Possible Delegates are slower (up to 15% on Intel and a little bit on MSVC). They are always slower on static members and on free functions. How could it be?

During disassembled code analysis, I've found an interesting fact. In the worst case, the compiler copies all parameters of the stub function and passes them into a target method. In some cases (if target method has no arguments or conversion is trivial), the optimizer reduces the stub function to a single jump instruction. And when a target method is inlinable, optimizer puts its code into the stub function. In this case there is no overhead at all.

The Fastest Possible Delegates are forced to use 'thiscall' calling convention. My delegates are free to use any calling convention (except 'thiscall') including '__fastcall'. It allows passing up to two int-size arguments through registers ('thiscall' passes only 'this' pointer through ECX).

Actually there exists a simple way to make your delegate based code extremely fast (if you really need it):

don't use a virtual method as a target for delegates (because usually it may not be inlined),

put a target method implementation and delegate creation code into the same compilation unit.

You can try to use my benchmark code to measure performance on your platform and your compiler.

Copying and comparing delegates

Performance of copying constructor is not the matter for both types of delegates (in contrast to delegates based on dynamic memory allocation, such as boost::function). Nevertheless my delegates can be copied a bit faster because they tend to occupy less space.

My delegates cannot be compared. Comparison operators are not defined because a delegate doesn't contain a pointer to method. Pointer to a stub function can be different in various compilation units. Actually this feature cut was the main reason why Don Clugston was not satisfied with my approach.

However, I suppose that a possibility of comparing pointer-to-methods is dangerous. It may work well until once you make some class inlinable.

I know only one reason why you may need to compare delegates. It is event syntax such as that of C#. It looks nice, but it can't be implemented without dynamic memory allocation. Moreover in C++ it may not work well in some cases. I would like to suggest another event propagation mechanism, more suitable for C++ in my opinion.

Portability

Although this approach is compatible with the C++ Standard, unfortunately it doesn't work on some compilers. I haven't managed to compile a test code on Borland C++. The preferred syntax doesn't work on MSVC 7.1 although it successfully compiles boost::function in the same syntax.

I think it is because of the rarely used language features.

Event library

I'm proposing an event library to demonstrate that delegates don't really need comparison operations. Actually this event library isn't tight with my delegates. It can work with many kind of delegates including boost::function. Also it can work with callback interfaces (like those of Java).

My event library provides a fast method to subscribe and unsubscribe to an event producer (even during event emitting) and doesn't use dynamically allocated memory as well (it must be important to you if you are interested in fast delegates).

This library provides two entities: event_source (it is a simplified analogue of boost::signal) and event_binder (an analogue of boost::signals::scoped_connection). Usually an event producer keeps event_source and an event consumer keeps event_binder. A connection between a producer and a consumer exists while both event_source and event_binder exist.

You can't use an anonymous connection. Actually in Boost you can use it in two ways:

you are absolutely sure that the event consumer exists longer than the event producer and

you should use boost::signals::trackable as the base class of an event consumer (it is possible to implement the analogue in my library, but I'm not sure it is a good idea).

You could use it in C#-style multicast delegates, but there is another problem: you must maintain pairs of actions (subscription and unsubscription), but their correctness can't be checked at compile time.

For more details, see documentation.

Conclusion

May be some details of C++ design are not ideal, but I don't see any reason to break the C++ Standard. Moreover, sometimes hacking doesn't allow optimizers to present all of their abilities.

Share

About the Author

I'm MS in Math (with honor). I've been working as a software engineer in the Vessel Traffic System department since the year 2002.

I like the C++, riddle solving, travelling and sports. My daughter was born when I was participating in a rock climbing competition (4 Jul 2004). Also I took up kayaking and skydiving, but unfortunately now I have no time for it.

I have about 4 years experience in ACM ICPC (ICP contests). The best achievement is 18-th place in World Finals (2002).

It is a class template, that copies all provided arguments and then later on applies them on a provided functor. This functionality is used in the event library, where the stored arguments are applied on a provided delegate.

Compiling the delegate_demo.cpp on said compiler ("g++ ./delegate_demo.cpp -I. -Ipath_to_SRDelegates_dir/include -o delegate_demo") with SRUTIL_DELEGATE_PREFERRED_SYNTAX enabled produces the following error: delegate_demo.cpp:34: error: variable 'srutil::delegate_invoker<void(*)(int, int)> inv' has initializer but incomplete type. The error suggests that compiler has no access to full body of the given class, although this knowledge helps me little as to understanding this particular case.

"Pointer to a stub function can be different in various compilation units."

AFAIK, this is not true. Compilers are required to re-use template functions generated in different compilation units (this I am sure of - but I think Borland once violated this rule). I think it is because classes (ones not in 'nameless' namespaces) use external linkage and the way you use the stub functions will always prevent them from being inlined (although this shouldn't be an issue either as taking the address of the function will force a non-inline version to be generated and 'external linkage' performed by the linker will eliminate all but one similarly named function (they are assumed and required to be identical by the standard))...

If you define a template function one translation unit (cpp file) and then define the same function differently in another translation unit, only one of the two versions will make it into the final executable. (This actually violates the "One Definition Rule", but works on GCC, at least... not sure about MSVC.) The point is: the address [of the stub] will be the same in different units.

I would urge you to update the article (including comparison capability) if you find this to be true for MSVC - if MSVC is standards conformant, in this regard.

You have some very nice code here, and I'm beating myself over the head for not thinking of it before. (I have seen Don Clugston's code, and this is very elegant in comparison.)

I were searching for way of implenting a fast dataflow framework (the boost.dataflow proposal is nice but has too much overhaed for my needs) when I stumbled over this article.
The idea is nice but I were missing the possibility to compare two delegates (to remove connections between processing blocks).
Wouldn't it be possible to store an additional binary image of the encapsulated function?

I included your headers directly in my source code and tried using it. It compiled perfectly (with the alternate, non-preferred syntax) under CodeWarrior with no modifications required. In my opinion, the code is tight and simple and easy to use. Thanks for sharing! 5 stars.

Portability
Although this approach is compatible with the C++ Standard, unfortunately it doesn't work
on some compilers. I haven't managed to compile a test code on Borland C++.
The preferred syntax doesn't work on MSVC 7.1 although it successfully compiles
boost::function in the same syntax.

By default you can define delegate with accepts up 10 arguments. Also you can enlarge this number (you should change value of $max_param_count in make_delegates.pl and regenerate file delegate_list.hpp).

You might want to look at the boost preprocessor library to avoid having to run a Perl script to reformat your libraries. It allows you to expand code at preprocessor time in this exact way. I use it all the time with configuration defines and it works great!

Although this approach is compatible with the C++ Standard, unfortunately it doesn't work on some compilers.

This is the paradox I mentioned in my article! I think people often confuse 'Standard' and 'Portable'. In my view, 'Standard' is desirable, but I'd chose 'Portable' over 'Standard' any day. (Note that if the Standards commitee chose to define the result of invoking a cast member function pointer, my code would become standard retrospectively! (Excluding the horrible hacks for MSVC, of course))

Note that the primary design goal for my fast delegates was to make the most common case (calling a member function of a class) blindingly fast, and with the nicest possible syntax. (The code actually started life as an emulation of Borland's __closure pointers). I have only added extra functionality if it didn't interfere with that goal. The support for static functions was viewed as secondary. I make no guarantee that it is as fast as possible (after all, if it is time critical, maybe you should be using an ordinary function pointer instead).

I know only one reason why you may need to compare delegates. It is event syntax such as that of C#. It looks nice, but it can't be implemented without dynamic memory allocation. Moreover in C++ it may not work well in some cases.

More generally, comparison operators allow delegates to be stored in STL containers. Events are the most common use of this, but other applications are possible. Note that although this will probably involve dynamic memory allocation, it might only happen once in the life of the program! (eg, if you use std::vector, with an initial size of 10, you can add and remove events for years with calling 'new' as long as the number of events stays between 0 and 10 -- this is very different to a memory allocation whenever an event is created).

Certainly you can implement event systems without comparison operators -- you just combine them with some tag which can be compared -- but they are much cleaner if you have them (you have to store the tag).

Thanks for posting the article. We both agree that dynamic memory allocation in something as simple as a delegate is unacceptable.

If you rate speed and standards compliance highly, yours is the clear winner.
If you rate speed, portability and syntax highly, but don't care about standards compliance, use my scheme.
And if you don't care about speed or syntax, but want extreme flexibility, portability, and standards compliance, chose boost::function.

It would be nice to achieve all the goals at once, wouldn't it! But I suspect it is impossible without compiler support.

Don Clugston wrote:More generally, comparison operators allow delegates to be stored in STL containers. Events are the most common use of this, but other applications are possible. Note that although this will probably involve dynamic memory allocation, it might only happen once in the life of the program! (eg, if you use std::vector, with an initial size of 10, you can add and remove events for years with calling 'new' as long as the number of events stays between 0 and 10 -- this is very different to a memory allocation whenever an event is created).

The comparation is necessary only to use a type as a key in associative containers (set, map etc). You can make vector, deque, stack of objects without a comparation. Also you can use such types as a mapped_type in a map. Of course, you can't use a find operation, but it looks like events (you find an element to remove it), doesn't it? Could you provide an example of using a comparation operation except an event implementation? Have you ever used it?

Don Clugston wrote:
Certainly you can implement event systems without comparison operators -- you just combine them with some tag which can be compared -- but they are much cleaner if you have them (you have to store the tag).

Yes I can. I've already done it and it mentioned in my article You can see it in attached files. You don't suppuse this approach enough clear? It's allied to boost::signals (in my opinion).

I would say 3-5.75/5 And you able to achieve a rate 5/5 or more in a specific case (if your profiler recomended you to do it).

Portability: 2/5

It's just the matter of time. Compiler producers fix their bugs.

Don Clugston wrote:If you rate speed and standards compliance highly, yours is the clear winner.
If you rate speed, portability and syntax highly, but don't care about standards compliance, use my scheme.
And if you don't care about speed or syntax, but want extreme flexibility, portability, and standards compliance, chose boost::function.

It would be nice to achieve all the goals at once, wouldn't it! But I suspect it is impossible without compiler support.

What about callback interfaces (i.e. using pure absatract base classes for an event notification)? It is fast, extremely portable, absolutelly compatible with the Standard (and have some other advantages).

Use of member function also promote code readability. The template class is interesting but limited to the set of parameter(s), this is even true when binding the instantiated template object to proxy class (since the template act as a stub). Where else member function is bind during automatically, like MFC use of macro.