Functional
Sometimes I find that I need to use delegates in C++, in the past I almost always used the fast delegate library available on codeproject, but over the last year or so I’ve been using std::tr1::functional often enough instead–functional is somewhat more powerful as you can
bind functors containing state.
I was curious if the std::tr1::functional that ships with VS2010 has any sort of SBO.
(small buffer optimization- this allows it to avoid any dynamic memory allocation if the bound object is small enough).
Turns out it does, although it isn’t terribly large, in the header xxxfunction I found this union…
union _Space_union
{ // storage for small wrappers
_Pfnty _Pfn[3];
void *_Pobj[3];
long double _Ldbl; // for maximum alignment
char _Alias[3 * sizeof (void *)]; // to permit aliasing
} _Space;
Only 12 bytes for storage(in 32 bit mode).
//member fxn takes up 16 bytes and thus causes dynamic allocation
auto memfxn = std::tr1::bind(&RenderModule::UpdateDisplay, this);
//free fxn is only 4 bytes
auto freeFxn = std::tr1::bind(Hello);
//lambda capturing this, is only 4 bytes also
auto lambda = [this](){this->UpdateDisplay();};
Functional also seems to tack on an extra 4 bytes when you pass the bind to the functional,
so the mem fxn ends up requiring a 20 byte SBO to avoid dynamic allocation.
So to avoid dynamic allocation on member functions you can either go into xxxfunction and
simply change the size of the SBO(changing the 3 to a 5 works for mem fxn),
or use a lambda which in turn calls the desired mem fxn, the disadvantage being it involves an extra function call.
The fast delegate library uses less static memory(sizeof shows only 8 bytes)
and does not dynamically allocate, also as the name implies– it is quite fast.
So I’ll continue using fast delegate wherever speed is paramount.
June 10th, 2010 at 7:42 am
Well, did you do speed measurements? I’ve been into programming my own delegates for some time now, and found that with GCC 4.4.1 and Win7 there is no (notable) speed difference between my implementation and FastDelegate. This is probably very compiler / platform specific, and my naive implementation might be slower on other platform of course, but it follows the standard, is only a fraction of the code FastDelegate has and is really simple.
An example for a one parameter member function might be:
template
struct NormalDelegate
{
NormalDelegate(T *i_object, R (T::*i_member_function)(P)) :
object(i_object),
member_function(i_member_function)
{
}
T *object;
R (T::*member_function)(P);
void emit(P p)
{
(object->*member_function)(p);
}
};
It’s really straightforward, you can extend this to 9 parameters with some macro hacking and add regular funtions and const member functions. But I was astonished FastDelegate and my naive implementation resulted in equal performances (no significant differences). I guess this is because GCC improved over the last couple of years with respect to these kind of optimizations.
July 22nd, 2010 at 9:55 pm
Hey Daevius, sorry I took so long to respond..
No I didn’t do any speed measurements, but you are right that I should
The code you posted appears to be missing a part at the begging that I assume
looks something like this?
template< class T, class R, class P>(edit) It appears that if you don’t surround the template def with code markings it deletes the template params, doh
This seems to be to bind the the delegate to that specific class type, whereas
fastdelegate supports delegates that can be bound to any class(only the return type,
and parameter types must match). I actually wouldn’t be surprised if your code was faster
given that it isn’t quite as generic.
I don’t use GCC, one of my friends does though– gotta admit I’m abit envious of how quickly
some of the new C++/0x features are making it into the latest builds.
December 14th, 2010 at 12:34 pm
Hey Foxtox, sorry it took me even longer to respond.
Yes, I forgot the template parameters.
But you can bind the implementation I any class (hence the class T template parameter) with any number of parameters (up to 9) and any parameter types as well as any return type.
Luckily with C++0x we can leave out the macros, that saves us a lot!
So I’d argue it’s just as generic as it can bind any function. I have some small limitations or “features”, whatever you like to call them, so it fits with the big library I’m writing.
Good luck with it, if you’re still working on it