[wx-dev] RFC improved event handling (minor changes)
Brian Vanderburg II
BrianVanderburg2 at aim.com
Wed Jul 30 05:11:25 PDT 2008
I've made some minor changes to your code to get a few cases working:
1. Two different connects for members. The first one expects the =
handler object to be the same class as the handler function. The second =
allows the handler object to be derived from the class containing the =
handler function.
2. Type safety is only guaranteed if a handler object is passed. If =
you do window.Connect(wxEVT_BLUE, &MyEventHandler::onHandleColor ), type =
casting is done converting 'this' to MyEventHandler but window is only =
wxEvtHandler, so MyEventHandler::onHandleColor will get called with a =
'this' pointer only pointing to wxEvtHandler, not MyEventHandler.
I've attached the changes to this file, with some comments where I've =
made changes.
Brian Vanderburg II
-------------- next part --------------
// Self contained prototype for new/improved event handling
// (c) by Peter Most
//
#include <list>
#include <utility>
using namespace std;
// This is the same function as the one in src/event.cpp:
int wxNewEventType()
{
static int s_lastUsedEventType =3D 10000;
return s_lastUsedEventType++;
}
class wxEventType {
public:
wxEventType()
{
m_value =3D wxNewEventType();
}
bool operator =3D=3D ( const wxEventType &other ) const
{
return ( m_value =3D=3D other.m_value );
}
private:
int m_value;
};
// The templatized event type:
template < typename Event >
class wxEventTypeTemplate : public wxEventType {
public:
typedef Event AssociatedEvent;
};
// The new macro which associates the event type with the event it delivers:
#define DEFINE_NEW_EVENT_TYPE( name, event ) \
const wxEventTypeTemplate< event > name;
// The 'old' macro for backward compatibility:
#define DEFINE_EVENT_TYPE( name ) \
DEFINE_NEW_EVENT_TYPE( name, wxEvent )
// Base class for all events from include/wx/event.h:
=
class wxEvent {
public:
wxEvent( wxEventType type )
{
m_type =3D type;
}
bool operator =3D=3D ( const wxEvent &other ) const
{
return ( m_type =3D=3D other.m_type );
}
private:
wxEventType m_type;
};
// A legacy event from include/wx/timer.h:
class wxTimerEvent : public wxEvent {
public:
wxTimerEvent( wxEventType eventType )
: wxEvent( eventType )
{ }
};
DEFINE_EVENT_TYPE( wxEVT_TIMER )
// If the folling line is activated and the line above deactivated, then the
// 'old' way of defining event handler doesn't compile anymore, which IMO i=
s a
// good thing.
//
// DEFINE_NEW_EVENT_TYPE( wxEVT_TIMER, wxTimerEvent )
// A 'new' type event:
=
class wxColorEvent : public wxEvent {
public:
wxColorEvent( wxEventType eventType )
: wxEvent( eventType )
{ }
};
DEFINE_NEW_EVENT_TYPE( wxEVT_BLUE, wxColorEvent )
DEFINE_NEW_EVENT_TYPE( wxEVT_WHITE, wxColorEvent )
class wxEvtFunctor {
public:
virtual ~wxEvtFunctor()
{ }
virtual void operator()( wxEvent & ) =3D 0;
};
template < typename EventType >
class wxEvtFunction : public wxEvtFunctor {
public:
wxEvtFunction( void ( *handler )( typename EventType::Associate=
dEvent & ))
{
m_handler =3D handler;
}
virtual void operator()( wxEvent &event )
{
( *m_handler )( static_cast< typename EventType::Associated=
Event & >( event ));
}
private:
void ( *m_handler )( typename EventType::AssociatedEvent & );
};
=
template < typename EventType, typename Class >
class wxEvtMethod : public wxEvtFunctor {
public:
wxEvtMethod( Class *handler, void ( Class::*method )( typename =
EventType::AssociatedEvent & ))
{
m_handler =3D handler;
m_method =3D method;
}
virtual void operator()( wxEvent &event )
{
( m_handler->*m_method )( static_cast< typename EventType::=
AssociatedEvent & >( event ));
}
private:
Class *m_handler;
void ( Class::*m_method )( typename EventType::AssociatedEvent =
& );
};
class wxEvtHandler {
public:
// Connect method for connecting normal functions to an event:
template < typename EventType >
void Connect( const EventType &event, void ( *handler )( typena=
me EventType::AssociatedEvent & ))
{
wxEvtFunctor *functor =3D new wxEvtFunction< EventType >( h=
andler );
m_handlers.push_back( make_pair( event, functor ));
}
// Connect methods for connecting methods to an event:
// this method is used if no handler is passed or if the passed han=
dler is of the same type as Class
template < typename EventType, typename Class >
void Connect( const EventType &event, void ( Class::*method )( =
typename EventType::AssociatedEvent & ), Class *handler =3D 0 )
{
if ( handler =3D=3D 0 )
handler =3D static_cast< Class * >( this );
wxEvtFunctor *functor =3D new wxEvtMethod< EventType, Class=
>( handler, method );
m_handlers.push_back( make_pair( event, functor ));
}
// this method is used if the passed handler is derived from Class
template < typename EventType, typename Class, typename Derived >
void Connect( const EventType &event, void ( Class::*method )( =
typename EventType::AssociatedEvent & ), Derived *handler )
{
wxEvtFunctor *functor =3D new wxEvtMethod< EventType, Class=
>( handler, method );
m_handlers.push_back( make_pair( event, functor ));
}
bool ProcessEvent( wxEvent& event )
{
for ( Handlers::iterator handler =3D m_handlers.begin(); handle=
r !=3D m_handlers.end(); ++handler ) {
// Is this a handler for the event (first=3DwxEventType, se=
cond=3DwxEvtFunctor)
if ( event =3D=3D handler->first )
( *handler->second )( event );
}
}
private:
typedef list< pair< wxEventType, wxEvtFunctor * > > Handlers;
Handlers m_handlers;
};
// Duplicated typedefs from include/wx/event.h and include/wx/timer.h
typedef void (wxEvtHandler::*wxEventFunction)(wxEvent&);
typedef wxEventFunction wxObjectEventFunction;
typedef void (wxEvtHandler::*wxTimerEventFunction)(wxTimerEvent&);
#define wxTimerEventHandler(func) \
(wxObjectEventFunction)(wxEventFunction)static_cast< wxTimerEventFuncti=
on >( &func )
void handleColorEvent( wxColorEvent &event )
{
}
void handleTimerEvent( wxTimerEvent &event )
{
}
class MyEventHandler : public wxEvtHandler {
public:
MyEventHandler()
{
Connect( wxEVT_BLUE, &MyEventHandler::handleColorEvent );
Connect( wxEVT_WHITE, &MyEventHandler::handleColorEvent );
Connect( wxEVT_TIMER, wxTimerEventHandler( MyEventHandler::hand=
leTimerEvent ));
// If wxEVT_TIMER becomes a 'new' event, then this line has to =
be used:
// Connect( wxEVT_TIMER, &MyEventHandler::handleTimerEvent );
}
void handleColorEvent( wxColorEvent &event )
{
}
void handleTimerEvent( wxTimerEvent &event )
{
}
};
class DerivedEventHandler : public MyEventHandler
{
public:
void handleRealColorEvent( wxColorEvent& evt )
{
}
};
int main( void )
{
//
// should compile:
//
wxEvtHandler window;
window.Connect( wxEVT_WHITE, &handleColorEvent );
window.Connect( wxEVT_BLUE, &handleColorEvent );
window.Connect( wxEVT_TIMER, reinterpret_cast< void ( * )( wxEvent & ) =
>( &handleTimerEvent ));
//
// Would be nice if these would compile, but don't see how, without sac=
rificing type safety:
//
// window.Connect( wxEVT_TIMER, &handleTimerEvent );
// window.Connect( wxEVT_TIMER, &MyEventHandler::handleTimerEvent, &rec=
eiver );
MyEventHandler receiver;
window.Connect( wxEVT_BLUE, &MyEventHandler::handleColorEvent, &receiv=
er );
window.Connect( wxEVT_WHITE, &MyEventHandler::handleColorEvent, &receiv=
er );
window.Connect( wxEVT_TIMER, reinterpret_cast< void ( MyEventHandler::*=
)( wxEvent & ) >( &MyEventHandler::handleTimerEvent ), &receiver );
// This line doesn't compile, probably because it tries to cast to wxEv=
tHandler
// It doesn't compile because no template matches. It expects Class to=
be wxEvtHandler, but
// receiver is MyEventHandler so the first Connect for members does not=
match.
// Change: This line now compiles because of the second Connect for mem=
bers
window.Connect( wxEVT_TIMER, wxTimerEventHandler( MyEventHandler::handl=
eTimerEvent ), &receiver );
//
// must not compile:
//
=
// window.Connect( wxEVT_TIMER, &handleColorEvent );
// window.Connect( wxEVT_TIMER, &MyEventHandler::handleColorEvent, &rec=
eiver );
//
// Send an event:
//
wxColorEvent colorEvent( wxEVT_BLUE );
window.ProcessEvent( colorEvent );
// This originally will not compile without the second Connect for memb=
ers
DerivedEventHandler receiver2;
window.Connect( wxEVT_BLUE, &MyEventHandler::handleColorEvent, &receive=
r2 );
// other tests
window.Connect( wxEVT_BLUE, &DerivedEventHandler::handleRealColorEvent,=
&receiver2 );
receiver2.Connect( wxEVT_BLUE, &DerivedEventHandler::handleRealColorEve=
nt );
receiver2.Connect( wxEVT_BLUE, &MyEventHandler::handleColorEvent );
// type safety test
// Problem: casting wxEvtHandler to DerivedEventHandler
// this is pointer to wxEvtHandler but cast converts to DerivedEventHan=
dler so
// DerivedEventHandler::handleRealColorEvent will get a 'this' pointer =
to
// a wxEvtHandler object
window.Connect( wxEVT_BLUE, &DerivedEventHandler::handleRealColorEvent =
);
receiver.Connect( wxEVT_BLUE, &DerivedEventHandler::handleRealColorEven=
t );
// Should be written as
window.Connect( wxEVT_BLUE, &DerivedEventHandler::handleRealColorEvent,=
&receiver2 );
receiver.Connect( wxEVT_BLUE, &DerivedEventHandler::handleRealColorEven=
t, &receiver2 ); =
}
More information about the wx-dev
mailing list