Maxim Yegorushkin
10/24/2008 9:20:00 AM
On Oct 24, 8:51 am, James Kanze <james.ka...@gmail.com> wrote:
> On Oct 24, 3:39 am, Victor Bazarov <v.Abaza...@comAcast.net> wrote:
>
> > ghula...@gmail.com wrote:
> > > On Oct 23, 4:15 pm, "ghula...@gmail.com" <ghula...@gmail.com> wrote:
> > >> I am having trouble implementing some function pointer
> > >> stuff in c++
> > >> An object can register itself for many events
> > >> void addEventListener(CFObject *target, CFEventHandler
> > >> callback, uint8_t event);
> > >> so I declared a function pointer like
> > >> typedef void (CFObject::*CFEventHandler)(CFEvent *theEvent);
> > >> So when I register a handler
> > >> plane->addEventListener((CFObject *)gun, &MachineGun::handleEvent, 0);
>
> Just curious, but: what is the type of gun, and why did you cast
> it?
>
> > >> MachineGun's class contains
> > >> void handleEvent(CFEvent *theEvent);
> > >> I am getting the following error:
> > >> error: no matching function for call to
> > >> 'Airplane::addEventListener(CFObject*, void (MachineGun::*)(CFEvent*),
> > >> uint8_t&)'/Users/ghulands/Desktop/arduino-0012/hardware/libraries/
> > >> CoreFoundation/CFApplication.h:42: note: candidates are: void
> > >> CFApplication::addEventListener(CFObject*, void (CFObject::*)
> > >> (CFEvent*), uint8_t)
> > >> MachineGun is a subclass (not a direct one) of CFObject.
> > >> If I put an event handler on CFObject it compiles fine. I
> > >> don't want to have to put it in there as a virtual method
> > >> as it will break the design.
> > >> Is there a way for the function pointer definition to be
> > >> defined in that it can also accept subclasses of the type?
> > > Found the solution: static_cast<>
> > > plane->addEventListener(gun,
> > > static_cast<CFEventHandler>(&MachineGun::handleEvent), 0);
> > You should be aware that this is dangerous. You're basically
> > telling the compiler to shut up, claiming that you know what
> > you're doing. In fact converting (and using) the pointer to
> > member of derived when a pointer to member of base is expected
> > is wrought with peril. What if the object for which you're
> > going to use your handler is not of the type 'MachineGun'?
> > And inside the handler you will pretend that the 'this'
> > pointer is a pointer to 'MachineGun', which is not right.
> > Undefined behaviour ensues.
> > Virtual functions are there for a reason, you know...
>
> I agree. It's an example of very poor design. On the other
> hand, it seems to be an established idiom in some GUI circles;
> I've seen it required by more than one framework.
wxWidgets is one example.
> On the whole: a much better design would be to define an
> abstract base class for the EventHandler, with a pure virtual
> function for the notification, and pass a pointer to it. In
> many cases, his actual implementation class can just derive from
> this abstract base class (in addition to any other classes it
> may derive from), and implement the function directly.
They argue that if there are hundreds of events there should be
hundreds of virtual functions. Which leads to relatively large virtual
tables for every derived class.
> Otherwise, it's not too hard to define a forwarding class---you
> could even create a template class which you just have to
> instantiate---and use it.
>
> If you really insist on using the above pattern, it should be
> done by means of a template member function, e.g.:
>
> template< typename T >
> void MyClass::addEventHandler(
> T* obj,
> void (T:* pmf)( CFEvent* ),
> uint8_t ) ;
>
> (You can safely do the static_cast<> bit inside this function,
> and even call a private member function, virtual or not, with
> the results of the cast. But you've ensured type safety at the
> user interface level, at least.)
IMHO, using member function pointers for callbacks is always a design
mistake because it requires casting member function pointers, which is
not portable. And looks wrong because there are easier ways to achieve
the desired effect of calling back a member function of an object.
It is trivial to make it right in a 100% portable way using C-style
callbacks, i.e. function pointer + void* pointer. Such callbacks can
be bound to regular functions as well as to member functions. The only
cast required is absolutely safe static_cast<T*>(void*):
#include <stdio.h>
struct Callback
{
void(*fun)(void* arg);
void* arg;
};
void invoke(Callback c)
{
c.fun(c.arg);
}
// a member-function to Callback function adapter
template<class T, void(T::*mem_fun)()>
Callback makeCallback(T* obj)
{
struct local
{
static void call(void* p)
{
(static_cast<T*>(p)->*mem_fun)();
}
};
Callback c = { local::call, obj };
return c;
}
struct X
{
void foo() { printf("foo\n"); }
void bar() { printf("bar\n"); }
// bar to callback adapter
static void bar_cb(void* p) { static_cast<X*>(p)->bar(); }
};
int main()
{
X x;
// autogenerate member function adapter
invoke(makeCallback<X, &X::foo>(&x));
// or use an existing adapter
Callback c = { X::bar_cb, &x };
invoke(c);
}
--
Max