paragui-dev
[Top][All Lists]
Advanced

[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.






reply via email to

[Prev in Thread] Current Thread [Next in Thread]