[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[paragui-dev] replacing callbacks with boost::function
From: |
Ulrich Eckhardt |
Subject: |
[paragui-dev] replacing callbacks with boost::function |
Date: |
Wed, 28 Aug 2002 17:29:11 +0200 |
(note: please keep me in the CCs!)
Hi Folks!
I have been hacking paragui-1.0.2 a bit (mostly in order to teach myself
boost's function) and found that replacing callbacks was pretty easy. I got
paragui and the tests to compile and run using GCC 3.2 and boost 1.28. A
tarball with the changed sources is available at
http://home.knuut.de/Doomster/paragui-1.0.2boost.tar.bz2, all changes are
#ifdef PARAGUI_USE_BOOST_CALLBACKS (which is currently defined in
paraconfig.h but could as well be moved into a configure-option).
I know that the modified version is stable and that such changes will never
be made to a stable version. If there is interest, I would take a shot at the
current cvs.
btw: is there an IRC-channel where you meet ? I usually hang around on
freenode (former openprojects.net), nickname doomster.
cheers
Uli
What's the difference ?
-----------------------
Boost.Function (http://boost.org/libs/function/doc/tutorial.html) is a
generic wrapper around function-like objects. It comes with a set of helpers
that make things like the void* that is passed with current callbacks
unnecessary. If you need the pointer, you can simply bind one arg of your
function to the value you want.
What's the difference to SigC++ ?
---------------------------------
SigC++ is more intrusive but on the other side possibly more secure. SigC++
requires you to derive every object you want to connect to (if it is not a
static/global object or plain function) from SigC::Object.
It automatically removes all existing callbacks that still point to the
object in the dtor, which makes it a bit more save. Furthermore, you can
connect several recipients(slots) to one sender(signal), making it a bit more
flexible (although I don't see any immediate advantage in that feature for
paragui).
What has changed ?
------------------
The typedef for MSG_CALLBACK has changed:
typedef bool (*MSG_CALLBACK)
(int id, PG_Widget* widget, unsigned long data, void *clientdata);
typedef bool (PG_EventObject::*MSG_CALLBACK_OBJ)
(int id, PG_Widget* widget, unsigned long data, void* clientdata);
have both been replaced by
typedef boost::function<bool, int, PG_Widget*, unsigned long>
MSG_CALLBACK;
(read this like 'function returning a bool and taking an int, a PG_Widget*
and an unsigned long'). As you see the 'void* clientdata' was removed, I'll
explaing later how to get the same effect.
Accordingly, PG_MessageObject's two methods to register a callback,
SetEventCallback and SetEventObject have been merged. Other widgets
(popupmenu) had to be modified in the same way.
Also note that event-recipients need not be derived from a common baseclass
for this to work.
How do I get my userdata passed with the callback ?
---------------------------------------------------
assuming the callback is a member function:
bool MyClass::callback
(int id, PG_Widget* widget, unsigned long data, void* clientdata)
when assigning this to a boost::function, this becomes a
boost::function<bool,MyClass*,int,PG_Widget*,unsigned long,void*>
In order to fit this into a generic callback, we have to remove the
'MyClass*' and the 'void*' parameter:
boost::bind(&MyClass::callback, myclass_ptr, _1, _2, _3, userdata)
The first parameter to bind() is the original function, followed by the
parameters. The funny '_1' etc are placeholders (template-voodoo) to mark the
position where the args of the resulting functor are passed to the original
function. Note here that there is no more reason that userdata has to be
void*, you can use the whole C++ typechecking!
Example:
#ifdef PARAGUI_USE_BOOST_CALLBACKS
MSG_CALLBACK cb =
boost::bind(&PG_DropDown::select_handler, this, _1, _2, _3, (void*)0);
my_DropList->SetEventCallback(MSG_SELECTITEM, cb);
#else
my_DropList->SetEventObject(MSG_SELECTITEM, this,
(MSG_CALLBACK_OBJ)&PG_DropDown::select_handler);
#endif
Of course, this could be written in a single line. Also note that the (void*)
is necessary lest the compiler assume the zero was an integer. OTOH it could
be removed if select_handler wouldn't take a 'void*' it doesn't really use...
What are the drawbacks ?
------------------------
Very little imho. One thing of course is the dependancy on another library,
another is that the compiler-errors that improper use of Boost.Function
creates are very hard to parse (due to heavy template usage).
The biggest lack currently is that PG_UnregisterEventTarget cannot be
implemented in that scheme, it simply doesn't fit into the design. Some
tests-apps call this function for cleanup. This of course bites programs that
dynamically modify their GUI. Those programs would have to be modified so
that instead of unregistering an object they unregister every callback
connected to that object, which implies they have to keep track of
connections themselves.
- [paragui-dev] replacing callbacks with boost::function,
Ulrich Eckhardt <=