Functor Delegates (with patch!)

Garthy

24-01-2011 10:36:04

One limitation that I've noticed with MyGUI is that there *seems* to be no way to easily and automatically properly clean up resources on destruction of a delegate using any of the three main delegate forms (global function, class static function, object and method) as they are all represented by pointers. Please correct me if I'm wrong.

If you need to attach data to a delegate, one means of solving this problem is to use widget user data and store any resources there, knowing they'll be cleaned up by MyGUI::Any's destructor and template magic, and then use a delegate with an object pointer to the resource, knowing that you won't lose it whilst the object lives. This is a nasty and messy solution, and I know, since it's what I did to solve the problem initially. ;)

Another way of solving the problem is to add another delegate type, namely one that supports functors. For those that don't know, a functor is an object that encapsulates any information you wish, and can be called like a function, usually through "operator()" in C++, along with associated cleanup of its resources on destruction. Such a type would let you carry any data of your choosing with the delegate, knowing that it will be safely destructed when it falls out of scope. A general functor delegate is quite nice as you could even give it boost::function objects, and also wrap arguments up with boost::bind, which is *incredibly* useful.

I've had a shot at implementing such a thing, and I've had a degree of success. However, the perfect solution requires some fairly hairy template magic in a file that is included multiple times to create multiple sets of templates (namely "MyGUI_Delegate.h" and "MyGUI_DelegateImplement.h"), and frankly this heavy template magic is a bit beyond me. However, I'm happy to share what I did come up with, with the hope it is useful or someone can improve on it. :)

Included is a small patch that adds functor delegates to MyGUI. It *seems* to work, but no promises. It's against a fairly recent version of MyGUI from subversion.

An example of usage with boost::bind and boost::function is below:


typedef boost::function<void (MyGUI::WidgetPtr _sender)> Delegate_W_Type;

class SomeClass;
typedef shared_ptr<SomeClass> SomeClassPtr;

static void Delegate_W(SomeClassPtr foo, MyGUI::WidgetPtr _sender)
{
foo->doSomething(_sender);
}

static void setup(MyGUI::Button *w, SomeClassPtr foo)
{
Delegate_W_Type f = boost::bind(Delegate_W, foo, _1);
w->eventMouseButtonClick = MyGUI::newFunctorDelegate<
Delegate_W_Type, MyGUI::WidgetPtr>(f);
}


Now, the ideal would be something like:


class SomeClass;
typedef shared_ptr<SomeClass> SomeClassPtr;

static void Delegate_W(SomeClassPtr foo, MyGUI::WidgetPtr _sender)
{
foo->doSomething(_sender);
}

static void setup(MyGUI::Button *w, SomeClassPtr foo)
{
w->eventMouseButtonClick = MyGUI::newDelegate(
boost::bind(Delegate_W, foo, _1));
}


But I was unsuccessful in coming up with anything that worked in this way.

There are other ways to solve this problem as well; this is just one of them. I like the functor approach personally as it can be easily expanded to cover a huge range of scenarios.

I'm wondering if functionality like this, or ideally something that would work a bit more like the second block of code above, would be a worthwhile addition to MyGUI?

Altren

24-01-2011 13:59:03

Your solution looks pretty good. I'll try to do more template magic to achieve better interface for functor delegates. I think this functionality is good addition to MyGUI delegates, but still not sure.

Garthy

24-01-2011 22:45:33

Excellent! :) I'm glad to hear you like it. Thanks also for offering to look at it to improve the interface a bit; I really wanted to get it to act like the second block of code above, but I just couldn't figure it out.

There are other ways to solve this problem too. For example, a simple one is an object and method combination like the existing one that deletes the object at the end. The functor approach could be changed to copy in only on initial allocation, hold it as a pointer, and delete it at the end. The functor could be expanded to also take a method argument (which makes it similar to the previous approach!). Multiple types of delegates could be provided by implementing a simple deleted object and method combination and then layering the different types of delegates on top of that. In fact, any approach that ensures resource cleanup can be used as a base with everything else layered on top. It can be as simple or as complex as desired. :)

Altren

24-01-2011 23:04:32

I tried to make as in yours second variant, but looks like there's no way to implicitly convert result of boost::bind into boost::function, so I guess there's no way to get such result. The only thing that I achieved is
Delegate_W_Type f = boost::bind(Delegate_W, classInstance, _1);
button4->eventMouseButtonClick += MyGUI::newDelegate(f);


See UnitTest_Delegates

I was trying to implement delegate with boost::function, so it is not functor delegate any more.

Garthy

25-01-2011 00:44:31

That's definitely much neater, very nicely done. :)

The downside of course would be the dependence on boost::function hence Boost, as opposed to a general functor approach, which can use it optionally. For example, with the functor approach, I could have just declared my own class with an "operator()", and not used Boost at all. Using Boost just made for a neater example, and more closely matched the code I am actually using. :) Although since I *am* using Boost myself, I have to say I prefer your variant somewhat. :)

I imagine if the boost::function approach was included in the main MyGUIEngine directory it would have to be wrapped in "#ifdef BOOST_PLATFORM" or similar, so that Boost remains an optional dependency? It doesn't impact me personally (I use Boost heavily) but some people might not want the additional dependency. What do you think?

Would including both a general functor approach and an optional boost::function component be feasible, or is that just getting too complicated?

mos

06-03-2011 03:29:49

Great, I really want this feature.

mos

06-03-2011 03:41:00

I also find the MyGUI's signals / slots is not so much graceful. and I prefer to fastsig in my work.
there is his's web site.
http://www.ndl.kiev.ua/content/fastsig

Garthy

06-03-2011 11:37:10

I imagine MyGUI delegates could potentially be written that held Boost or fastsig signals and slots as well, and then you'd have even more options for hooking event handlers into the MyGUI code. I won't put my hand up for that one personally though. ;)