[wx-dev] RFC improved event handling (more minor changes)

Brian Vanderburg II BrianVanderburg2 at aim.com
Wed Jul 30 07:45:18 PDT 2008


I forgot the attachment
> Brian Vanderburg II wrote:
>> 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
>>
> I've made a few more changes.
>
> 1.  A small problem that may crop up in some compilers.  Using the =

> last version:
>
> window.Connect(wxEVT_PAINT, &MyHandler::OnPaint) would only call the =

> first member version since no handler object is passed.
>
> window.Connect(wxEVT_PAINT, &MyHandler::OnPaint, &derived) would only =

> call the second member version since the handler object is not of the =

> same type but derived from the type the member function is in
>
> window.Connect(wxEVT_PAINT, &MyHandler::OnPaint, &myHandler) would =

> possibly match either the first or the second version, which may =

> produce ambiguity errors on some compilers.
>
> Change:
>
> The first member version does not take any handler object.  This =

> version is used as when no handler object is specified and uses 'this' =

> as the handler.  The second member version is as is, taking Derived as =

> a handler object.  This may be of the same class or of a derived class =

> of the handler member function.  There would be no ambiguity for the =

> the third situation above.
>
> 2.  Simulate wxWidgets connect - I've changed the Connect functions to =

> have the same arguments that wxWidgets currently has.  For each of the =

> three main connects I've also created the shortcut versions that take =

> only one ID and no ID as well.
>
>
> If this change is incorporated into wxWidgets, the static event tables =

> could eventually be completely eliminated.  It would no longer be the =

> easy way:
>
> BEGIN_EVENT_TABLE(MyWindow, wxFrame)
>    EVT_MENU(ID_MENU1, MyWindow::OnMenu1)
>    EVT_MENU(ID_MENU2, MyWindow::OnMenu2)
>    EVT_MENU(ID_MENU3, MyWindow::OnMenu3)
> END_EVENT_TABLE()
>
> vs:
>
> Connect(ID_MENU1, wxEVT_MENU, &MyWindow::OnMenu1);
> Connect(ID_MENU2, wxEVT_MENU, &MyWindow::OnMenu2);
> Connect(ID_MENU3, wxEVT_MENU, &MyWindow::OnMenu3);
>
> The second is fewer lines of code, and no longer need to use the =

> conversion macros.  However it may still be needed for parent =

> propogation of events to work:
>
> If MyWindow uses only Connect and no BEGIN_EVENT_TABLE/etc exists any =

> more, events not handled in MyWindow will not get processed by the =

> event tables of wxFrame.
>
> One solution:  since there is only one event table for each object, =

> unlike the static class event tables, base classes could use dynamic =

> connections as well.  When the base is created, it would Connect =

> whatever it needed, then the derived class could Connect whatever it =

> needed.  For this to work, the dynamic event table must be processed =

> backwards, so event handlers set in MyWindow would handled first and =

> if not found those set in wxFrame.  Also, the event handler for this =

> window must be installed after creating the parent window (after =

> calling Parent::Create()
>
> // For single-step construction
> class  MyFrame : public wxFrame
> {
> public
>    MyFrame()
>    {
>       wxFrame::Create(...) // Could also do MyFrame() : wxFrame(...)
>       // Connect event handlers after Parent::Create()
>    }
> }
>
> // For two-step construction
> class MyFrame : public wxFrame
> {
> public:
>    MyFrame()
>    {
>       // no connections
>    }
>
>    MyFrame(...)
>    {
>       Create(...)
>    }
>
>    bool Create(...)
>    {
>       wxFrame::Create(...)
>       // Connect here after Parent::Create
>    }
> }
>
> Another issue would be how to handle Disconnect?
>
> Brian Vanderburg II
> _______________________________________________
> wx-dev mailing list
> wx-dev at lists.wxwidgets.org
> http://lists.wxwidgets.org/mailman/listinfo/wx-dev

-------------- 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 wxObject;
#define wxID_ANY -1
class wxEvtHandler {
    public:
        // Connect method for connecting normal functions to an event:

        template < typename EventType >
            void Connect( int winid,
                          int lastid,
                          const EventType &event,
                          void ( *handler )( typename EventType::Associated=
Event & ),
                          wxObject* userData =3D 0)
            {
                wxEvtFunctor *functor =3D new wxEvtFunction< EventType >( h=
andler );
                m_handlers.push_back( make_pair( event, functor ));
            }

        template < typename EventType >
            void Connect( int winid,
                          const EventType &event,
                          void ( *handler )( typename EventType::Associated=
Event & ),
                          wxObject* userData =3D 0)
            {
                Connect( winid, wxID_ANY, event, handler, userData );
            }
            =

        template < typename EventType >
            void Connect( const EventType &event,
                          void ( *handler )( typename EventType::Associated=
Event & ),
                          wxObject* userData =3D 0)
            {
                Connect( wxID_ANY, wxID_ANY, event, handler, userData );
            }

        // Connect methods for connecting methods to an event:
        // this method is used if no handler is passed
        =

        template < typename EventType, typename Class >
            void Connect( int winid,
                          int lastid,
                          const EventType &event,
                          void ( Class::*method )( typename EventType::Asso=
ciatedEvent & ),
                          wxObject* userData =3D 0 )
            {
                wxEvtFunctor *functor =3D new wxEvtMethod< EventType, Class=
 >( static_cast< Class * >( this ), method );
                m_handlers.push_back( make_pair( event, functor ));
            }
            =

        template < typename EventType, typename Class >
            void Connect( int winid,
                          const EventType &event,
                          void ( Class::*method )( typename EventType::Asso=
ciatedEvent & ),
                          wxObject* userData =3D 0 )
            {
                Connect( winid, wxID_ANY, event, method, userData );
            }
            =

        template < typename EventType, typename Class >
            void Connect( const EventType &event,
                          void ( Class::*method )( typename EventType::Asso=
ciatedEvent & ),
                          wxObject* userData =3D 0 )
            {
                Connect( wxID_ANY, wxID_ANY, event, method, userData );
            }

        // this method is used if the passed handler is of type Class or de=
rived from Class
        template < typename EventType, typename Class, typename Derived >
            void Connect( int winid,
                          int lastid,
                          const EventType &event,
                          void ( Class::*method )( typename EventType::Asso=
ciatedEvent & ),
                          wxObject* userData,
                          Derived *handler )
            {
                wxEvtFunctor *functor =3D new wxEvtMethod< EventType, Class=
 >( handler, method );
                m_handlers.push_back( make_pair( event, functor ));
            }
            =

        template < typename EventType, typename Class, typename Derived >
            void Connect( int winid,
                          const EventType &event,
                          void ( Class::*method )( typename EventType::Asso=
ciatedEvent & ),
                          wxObject* userData,
                          Derived *handler )
            {
                Connect( winid, wxID_ANY, event, method, userData, handler =
);
            }
            =

        template < typename EventType, typename Class, typename Derived >
            void Connect( const EventType &event,
                          void ( Class::*method )( typename EventType::Asso=
ciatedEvent & ),
                          wxObject* userData,
                          Derived *handler )
            {
                Connect( wxID_ANY, wxID_ANY, event, method, userData, handl=
er );
            }

        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, 0, &rec=
eiver );
    window.Connect( wxEVT_WHITE, &MyEventHandler::handleColorEvent, 0, &rec=
eiver );

    window.Connect( wxEVT_TIMER, reinterpret_cast< void ( MyEventHandler::*=
 )( wxEvent & ) >( &MyEventHandler::handleTimerEvent ), 0, &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 ), 0, &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, 0, &rece=
iver2 );


    // other tests
    window.Connect( wxEVT_BLUE, &DerivedEventHandler::handleRealColorEvent,=
 0, &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,=
 0, &receiver2 );
    receiver.Connect( wxEVT_BLUE, &DerivedEventHandler::handleRealColorEven=
t, 0, &receiver2 );


}




More information about the wx-dev mailing list