Development: Event Handling Improvements

From WxWiki
Revision as of 19:36, 19 October 2018 by Tierra (talk | contribs) (Text replacement - "<source>" to "<syntaxhighlight lang="cpp">")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Introduction

This page describes what changes have to be made to the current event handling system to get it type safe. It currently serves as a kind of blackboard, so that participating developer don't have to wade through the complete discussion to see what has already been solved/decided.

Some issues I omitted from this code, because I think they are quite easily solvable or are already solved in the original code:

  • Disconnecting an event handler.

Because this code serves as a kind of prototype, unnecessary parameters have been omitted to keep the 'noise' down and the signatures short.

Desired use cases

// A new/typesafe wxWidgets event:

class wxTimerEvent : public wxEvent
{
    public:
        wxTimerEvent( wxEventType eventType )
            : wxEvent( eventType )
        { }
};

// The timer event handler without any casts:

#define wxTimerEventHandler( func ) ( &func )

extern const wxTypedEventType< wxTimerEvent > wxEVT_TIMER;

const wxTypedEventType< wxTimerEvent > wxEVT_TIMER( wxNewEventType() );

// An old/user defined event type:
    
class wxColorEvent : public wxEvent
{
    public:
        wxColorEvent( wxEventType eventType )
            : wxEvent( eventType )
        { }
};

// Two possible ways how the event can be declared:

extern const wxEventType wxEVT_BLUE;
DECLARE_EVENT_TYPE( wxEVT_BLUE, 0 )
DEFINE_EVENT_TYPE( wxEVT_BLUE )

// The color event handler with a lot of casts:

typedef void (wxEvtHandler::*wxColorEventFunction)(wxColorEvent&);
typedef void (wxEvtHandler::*wxEventFunction)(wxEvent&);
typedef wxEventFunction wxObjectEventFunction;
#define wxColorEventHandler(func) \
    (wxObjectEventFunction)(wxEventFunction)static_cast< wxColorEventFunction >( &func )

// Some functions as event handlers:

void handleTimerEventFunction( wxTimerEvent &event )
{
    printf( "::handleTimerEventFunction( wxTimerEvent & )\n" );
}

void handleColorEventFunction( wxColorEvent &event )
{
    printf( "::handleColorEventFunction( wxColorEvent & )\n" );
}

// An event handler which uses it's own methods as handlers:

class MyEventHandler : public wxEvtHandler
{
    public:
        MyEventHandler()
        {
            Connect( wxEVT_TIMER, wxTimerEventHandler( MyEventHandler::handleTimerEventMethod ));
            Connect( wxEVT_BLUE, wxColorEventHandler( MyEventHandler::handleColorEventMethod ));
        }

        void handleTimerEventMethod( wxTimerEvent &event )
        {
            printf( "MyEventHandler::handleTimerEventMethod( wxTimerEvent & )\n" );
        }
       
        void handleColorEventMethod( wxColorEvent &event )
        {
            printf( "MyEventHandler::handleColorEventMethod( wxColorEvent & )\n" );
        }

};

// Another event handler which uses the base class methods as handlers:

class MyDerivedEventHandler : public MyEventHandler
{
    public:
        MyDerivedEventHandler()
        {
            Connect( wxEVT_TIMER, wxTimerEventHandler( MyDerivedEventHandler::handleTimerEventMethod ));
            Connect( wxEVT_BLUE, wxColorEventHandler( MyDerivedEventHandler::handleColorEventMethod ));
        }
};



wxEventTableEntry sm_staticEventHandlers[] =
{
    DECLARE_EVENT_TABLE_ENTRY( wxEVT_TIMER, &handleTimerEventFunction ),
    DECLARE_EVENT_TABLE_ENTRY( wxEVT_TIMER, wxTimerEventHandler( MyDerivedEventHandler::handleTimerEventMethod )),

    DECLARE_EVENT_TABLE_ENTRY( wxEVT_BLUE, wxColorEventHandler( MyDerivedEventHandler::handleColorEventMethod )),

    // Must not compile
    // DECLARE_EVENT_TABLE_ENTRY( wxEVT_BLUE, wxTimerEventHandler( MyDerivedEventHandler::handleTimerEventMethod )),

    // Mark the end of the table ie: END_EVENT_TABLE()
    wxEventTableEntry( wxEVT_NULL, NULL )
};


int main( void )
{
    //
    // should compile:
    //
    wxEvtHandler eventProvider;
    eventProvider.Connect( wxEVT_TIMER, wxTimerEventHandler( handleTimerEventFunction ));

    MyEventHandler eventHandler;
    eventProvider.Connect( wxEVT_TIMER, wxTimerEventHandler( MyEventHandler::handleTimerEventMethod ));
    eventProvider.Connect( wxEVT_TIMER, wxTimerEventHandler( MyEventHandler::handleTimerEventMethod ), &eventHandler);
    eventProvider.Connect( wxEVT_BLUE, wxColorEventHandler( MyEventHandler::handleColorEventMethod ));
    eventProvider.Connect( wxEVT_BLUE, wxColorEventHandler( MyEventHandler::handleColorEventMethod ), &eventHandler );


    MyDerivedEventHandler derivedEventHandler;
    eventProvider.Connect( wxEVT_TIMER, wxTimerEventHandler( MyDerivedEventHandler::handleTimerEventMethod ));
    eventProvider.Connect( wxEVT_TIMER, wxTimerEventHandler( MyDerivedEventHandler::handleTimerEventMethod ), &derivedEventHandler );
    eventProvider.Connect( wxEVT_BLUE, wxColorEventHandler( MyDerivedEventHandler::handleColorEventMethod ));
    eventProvider.Connect( wxEVT_BLUE, wxColorEventHandler( MyDerivedEventHandler::handleColorEventMethod ), &derivedEventHandler );

    // Must not compile:
    // eventProvider.Connect( wxEVT_BLUE, &handleTimerEventFunction );
    // eventProvider.Connect( wxEVT_BLUE, wxColorEventHandler( MyEventHandler::handleTimerEventMethod ));

    // Send an event:
    wxTimerEvent timerEvent( wxEVT_TIMER );
    eventProvider.ProcessEvent( timerEvent );

    // Send an illegal event (leads to an ASSERT in wxEvtFunction<Event>::Relay):
    wxColorEvent colorEvent( wxEVT_TIMER );
    eventProvider.ProcessEvent( colorEvent );

    return ( 0 );
}

Provide a template which associates the event type with the correct event

template < typename Event >
    class wxTypedEventType
    {
        public:
            typedef Event AssociatedEvent;

            wxTypedEventType( wxEventType type )
            {
                m_type = type;
            }

            // Will be needed for the static event tables:

            operator const wxEventType &() const
            {
                return ( m_type );
            }

        private:
            wxEventType m_type;
    };

Modify all event handler macros and remove the (now unnecessary) castings to wxEvtHandler

Old definition:

#define wxTimerEventHandler(func) \
    (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(wxTimerEventFunction, &func)

New definition:

#define wxTimerEventHandler( func ) \
    ( &func )

Modify all event type definitions/declarations to make them type safe

Old definiton:

const wxEventType wxEVT_TIMER = wxNewEventType();

New definition:

const wxTypedEventType< wxTimerEvent > wxEVT_TIMER( wxNewEventType() );

Provide wxEvtFunctor, wxEvtMethod and wxEvtFunction to serve as a kind of command pattern to be stored in the dynamic and static event tables

class wxEvtHandler;

// The functor which will be stored in the static/dynamic tables:

class wxEvtFunctor
{
    public:
        virtual ~wxEvtFunctor()
        { }

        virtual void Relay( wxEvtHandler *handler, wxEvent & ) = 0;
};

template < typename Event >
    class wxEvtFunction : public wxEvtFunctor
    {
        public:
            wxEvtFunction( void ( *handler )( Event & ))
            {
                m_handler = handler;
            }

            virtual void Relay( wxEvtHandler *, wxEvent &event )
            {
                // Protect against wrong event i.e. wxMouseEvent evt(wxEVT_PAINT):

                wxASSERT( dynamic_cast< Event * >( &event ) != NULL );

                // Will throw a std::bad_cast exception in release build:

                ( *m_handler )( dynamic_cast< Event & >( event ));
            }

        private:
            void ( *m_handler )( Event & );
    };

            

template < typename Event, typename Class >
    class wxEvtMethod : public wxEvtFunctor
    {
        public:
            wxEvtMethod( void ( Class::*method )( Event & ), wxEvtHandler *handler )
            {
                m_handler = handler;
                m_method = static_cast< void ( wxEvtHandler::* )( Event & ) >( method );
            }

            virtual void Relay( wxEvtHandler *handler, wxEvent &event )
            {
                if ( m_handler != NULL )
                    handler = m_handler;

                // Protect against wrong event i.e. wxMouseEvent evt(wxEVT_PAINT):

                wxASSERT( dynamic_cast< Event * >( &event ) != NULL );

                // Will throw a std::bad_cast exception in release build:

                ( handler->*m_method )( dynamic_cast< Event & >( event ));
            }

        private:
            wxEvtHandler *m_handler;
            void ( wxEvtHandler::*m_method )( Event & );
    };




// Wrap and unify the creation of the correct functor mainly for the DECLARE_EVENT_TABLE_ENTRY
// but we also can reuse it for the Connect-methods from wxEvtHandler.
// NB: As long as typeof() is not available, we need the unused EventType parameter to deduce
// the correct type and access the associated event):


// Create a functor for the legacy events:

wxEvtFunctor *wxNewEvtFunctor( wxEventType, void ( *function )( wxEvent & ))
{
    return ( new wxEvtFunction< wxEvent >( function ));
}

wxEvtFunctor *wxNewEvtFunctor( wxEventType, void ( wxEvtHandler::*method )( wxEvent & ), wxEvtHandler *handler = NULL )
{
    return ( new wxEvtMethod< wxEvent, wxEvtHandler >( method, handler ));
}

// Create a functor for the correctly typed events:

template < typename EventType >
    wxEvtFunctor *wxNewEvtFunctor( const EventType &, void ( *function )( typename EventType::AssociatedEvent & ))
    {
        return ( new wxEvtFunction< typename EventType::AssociatedEvent >( function ));
    }

template < typename EventType, typename Class >
    wxEvtFunctor *wxNewEvtFunctor( const EventType &, void ( Class::*method )( typename EventType::AssociatedEvent & ), wxEvtHandler *handler = NULL )
    {
        return ( new wxEvtMethod< typename EventType::AssociatedEvent, Class >( method, handler ));
    }

Change the wxEventTableEntry to accept a functor as the event handler

If a memory tracer is reporting memory leaks 'before' the destructors for static instances have been executed, then the m_functor will be reported as a memory leak.

struct wxEventTableEntry
{
    wxEventTableEntry( const wxEventType &eventType, wxEvtFunctor *functor )
        : m_eventType( eventType ), m_functor( functor )
    {
    }

    ~wxEventTableEntry()
    {
        delete m_functor; // Or use auto_ptr<>
    }


    void Relay( wxEvtHandler *handler, wxEvent &event )
    {
        m_functor->Relay( handler, event );
    }

    const wxEventType &m_eventType;
    wxEvtFunctor *m_functor;
};

#define DECLARE_EVENT_TABLE_ENTRY(type, fn) \
    wxEventTableEntry(type, wxNewEvtFunctor( type, fn ))

Provide new Connect-methods in wxEvtHandler to allow connecting function/methods as event handlers

class wxEvtHandler
{
    public:
        virtual ~wxEvtHandler()
        {
        }

        // Connect methods to connect functions/methods to an event (if the overhead for the unused eventType is to be
        // avoided, then we have to instantiate the wEvtFunction/wxEvtMethod directly):

        // Connect legacy event handlers:

        void Connect( wxEventType eventType, void ( *function )( wxEvent & ))
        {
            wxEvtFunctor *functor = wxNewEvtFunctor( eventType, function );
            m_dynamicEventHandlers.push_back( make_pair( eventType, functor ));
        }

        void Connect( wxEventType eventType, void ( wxEvtHandler::*method )( wxEvent & ), wxEvtHandler *handler = NULL )
        {
            if ( handler == NULL )
                handler = this;

            wxEvtFunctor *functor = wxNewEvtFunctor( eventType, method, handler );
            m_dynamicEventHandlers.push_back( make_pair( eventType, functor ));
        }

        // Connect correctly typed event handlers:

        template < typename EventType >
            void Connect( const EventType &eventType, void ( *function )( typename EventType::AssociatedEvent & ))
            {
                wxEvtFunctor *functor = wxNewEvtFunctor( eventType, function );
                m_dynamicEventHandlers.push_back( make_pair( eventType, functor ));
            }

        template < typename EventType, typename Class >
            void Connect( const EventType &eventType, void ( Class::*method )( typename EventType::AssociatedEvent & ), wxEvtHandler *handler = NULL )
            {
                if ( handler == NULL )
                    handler = this;

                wxEvtFunctor *functor = wxNewEvtFunctor( eventType, method, handler );
                m_dynamicEventHandlers.push_back( make_pair( eventType, functor ));
            }




        bool ProcessEvent( wxEvent& event )
        {
            // Relay the event to the 'dynamic' handlers:

            for ( Handlers::iterator handler = m_dynamicEventHandlers.begin(); handler != m_dynamicEventHandlers.end(); ++handler ) {
                // Is this a handler for the event (first=wxEventType, second=wxEvtFunction)
                if ( event == handler->first )
                    handler->second->Relay( this, event );
            }

            // Relay the event to the 'static' handlers:

            extern wxEventTableEntry sm_staticEventHandlers[];
            for ( size_t i = 0; sm_staticEventHandlers[ i ].m_functor != NULL; ++i )
            {
                if ( event == sm_staticEventHandlers[ i ].m_eventType )
                    sm_staticEventHandlers[ i ].Relay( this, event );
            }
            return ( true );
        }

    private:
        typedef list< pair< wxEventType, wxEvtFunctor * > > Handlers;
        Handlers m_dynamicEventHandlers;
};

Question/Comments/Suggestions

Please feel free to comment and/or ask questions in this section!

  1. Missing: Allow boost::function< void (wxTimerEvent & ) > as event handlers.
  2. <Question>

Complete (GCC-4.2.3 compilable) source code

// Self contained prototype for new/improved event handling
// (c) by Peter Most
//
#include <list>
#include <utility>
#include <cassert>

using namespace std;

#define wxASSERT assert

// The 'legacy' event type:

typedef int wxEventType;

// This is basically the same function as the one in src/event.cpp:

wxEventType wxNewEventType()
{
    static int s_lastUsedEventType = 10000;

    return ( wxEventType( s_lastUsedEventType++ ));
}

// Template which associates the event type with the correct event:

template < typename Event >
    class wxTypedEventType
    {
        public:
            typedef Event AssociatedEvent;

            wxTypedEventType( wxEventType type )
            {
                m_type = type;
            }

            // Will be needed for the static event tables:

            operator const wxEventType &() const
            {
                return ( m_type );
            }

        private:
            wxEventType m_type;
    };



// Legacy event declaration/definition from include/wx/event.h:

#define DECLARE_EVENT_TYPE( name, value ) \
    extern const wxEventType name;

// src/event.cpp:

#define DEFINE_EVENT_TYPE( name ) \
    const wxEventType name = wxNewEventType();


// Base class for all events from include/wx/event.h:
        
class wxEvent
{
    public:
        virtual ~wxEvent()
        {
        }

        wxEvent( wxEventType type )
        {
            m_type = type;
        }

        bool operator == ( const wxEvent &other ) const
        {
            return ( m_type == other.m_type );
        }

    private:
        wxEventType m_type;
};




class wxEvtHandler;

// The functor which will be stored in the static/dynamic tables:

class wxEvtFunctor
{
    public:
        virtual ~wxEvtFunctor()
        { }

        virtual void Relay( wxEvtHandler *handler, wxEvent & ) = 0;
};

template < typename Event >
    class wxEvtFunction : public wxEvtFunctor
    {
        public:
            wxEvtFunction( void ( *handler )( Event & ))
            {
                m_handler = handler;
            }

            virtual void Relay( wxEvtHandler *, wxEvent &event )
            {
                // Protect against wrong event i.e. wxMouseEvent evt(wxEVT_PAINT):

                wxASSERT( dynamic_cast< Event * >( &event ) != NULL );

                // Will throw a std::bad_cast exception in release build:

                ( *m_handler )( dynamic_cast< Event & >( event ));
            }

        private:
            void ( *m_handler )( Event & );
    };

            

template < typename Event, typename Class >
    class wxEvtMethod : public wxEvtFunctor
    {
        public:
            wxEvtMethod( void ( Class::*method )( Event & ), wxEvtHandler *handler )
            {
                m_handler = handler;
                m_method = static_cast< void ( wxEvtHandler::* )( Event & ) >( method );
            }

            virtual void Relay( wxEvtHandler *handler, wxEvent &event )
            {
                if ( m_handler != NULL )
                    handler = m_handler;

                // Protect against wrong event i.e. wxMouseEvent evt(wxEVT_PAINT):

                wxASSERT( dynamic_cast< Event * >( &event ) != NULL );

                // Will throw a std::bad_cast exception in release build:

                ( handler->*m_method )( dynamic_cast< Event & >( event ));
            }

        private:
            wxEvtHandler *m_handler;
            void ( wxEvtHandler::*m_method )( Event & );
    };




// Wrap and unify the creation of the correct functor mainly for the DECLARE_EVENT_TABLE_ENTRY
// but we also can reuse it for the Connect-methods from wxEvtHandler.
// NB: As long as typeof() is not available, we need the unused EventType parameter to deduce
// the correct type and access the associated event):


// Create a functor for the legacy events:

wxEvtFunctor *wxNewEvtFunctor( wxEventType, void ( *function )( wxEvent & ))
{
    return ( new wxEvtFunction< wxEvent >( function ));
}

wxEvtFunctor *wxNewEvtFunctor( wxEventType, void ( wxEvtHandler::*method )( wxEvent & ), wxEvtHandler *handler = NULL )
{
    return ( new wxEvtMethod< wxEvent, wxEvtHandler >( method, handler ));
}

// Create a functor for the correctly typed events:

template < typename EventType >
    wxEvtFunctor *wxNewEvtFunctor( const EventType &, void ( *function )( typename EventType::AssociatedEvent & ))
    {
        return ( new wxEvtFunction< typename EventType::AssociatedEvent >( function ));
    }

template < typename EventType, typename Class >
    wxEvtFunctor *wxNewEvtFunctor( const EventType &, void ( Class::*method )( typename EventType::AssociatedEvent & ), wxEvtHandler *handler = NULL )
    {
        return ( new wxEvtMethod< typename EventType::AssociatedEvent, Class >( method, handler ));
    }


struct wxEventTableEntry
{
    wxEventTableEntry( const wxEventType &eventType, wxEvtFunctor *functor )
        : m_eventType( eventType ), m_functor( functor )
    {
    }

    void Relay( wxEvtHandler *handler, wxEvent &event )
    {
        m_functor->Relay( handler, event );
    }

    const wxEventType &m_eventType;
    wxEvtFunctor *m_functor;
};

#define DECLARE_EVENT_TABLE_ENTRY(type, fn) \
    wxEventTableEntry(type, wxNewEvtFunctor( type, fn ))

class wxEvtHandler
{
    public:
        virtual ~wxEvtHandler()
        {
        }

        // Connect methods to connect functions/methods to an event (if the overhead for the unused eventType is to be
        // avoided, then we have to instantiate the wEvtFunction/wxEvtMethod directly):

        // Connect legacy event handlers:

        void Connect( wxEventType eventType, void ( *function )( wxEvent & ))
        {
            wxEvtFunctor *functor = wxNewEvtFunctor( eventType, function );
            m_dynamicEventHandlers.push_back( make_pair( eventType, functor ));
        }

        void Connect( wxEventType eventType, void ( wxEvtHandler::*method )( wxEvent & ), wxEvtHandler *handler = NULL )
        {
            if ( handler == NULL )
                handler = this;

            wxEvtFunctor *functor = wxNewEvtFunctor( eventType, method, handler );
            m_dynamicEventHandlers.push_back( make_pair( eventType, functor ));
        }

        // Connect correctly typed event handlers:

        template < typename EventType >
            void Connect( const EventType &eventType, void ( *function )( typename EventType::AssociatedEvent & ))
            {
                wxEvtFunctor *functor = wxNewEvtFunctor( eventType, function );
                m_dynamicEventHandlers.push_back( make_pair( eventType, functor ));
            }

        template < typename EventType, typename Class >
            void Connect( const EventType &eventType, void ( Class::*method )( typename EventType::AssociatedEvent & ), wxEvtHandler *handler = NULL )
            {
                if ( handler == NULL )
                    handler = this;

                wxEvtFunctor *functor = wxNewEvtFunctor( eventType, method, handler );
                m_dynamicEventHandlers.push_back( make_pair( eventType, functor ));
            }




        bool ProcessEvent( wxEvent& event )
        {
            // Relay the event to the 'dynamic' handlers:

            for ( Handlers::iterator handler = m_dynamicEventHandlers.begin(); handler != m_dynamicEventHandlers.end(); ++handler ) {
                // Is this a handler for the event (first=wxEventType, second=wxEvtFunction)
                if ( event == handler->first )
                    handler->second->Relay( this, event );
            }

            // Relay the event to the 'static' handlers:

            extern wxEventTableEntry sm_staticEventHandlers[];
            for ( size_t i = 0; sm_staticEventHandlers[ i ].m_functor != NULL; ++i )
            {
                if ( event == sm_staticEventHandlers[ i ].m_eventType )
                    sm_staticEventHandlers[ i ].Relay( this, event );
            }
            return ( true );
        }

    private:
        typedef list< pair< wxEventType, wxEvtFunctor * > > Handlers;
        Handlers m_dynamicEventHandlers;
};

extern const wxTypedEventType< wxEvent > wxEVT_NULL;

const wxTypedEventType< wxEvent > wxEVT_NULL( 0 );


// A new/typesafe wxWidgets event:

class wxTimerEvent : public wxEvent
{
    public:
        wxTimerEvent( wxEventType eventType )
            : wxEvent( eventType )
        { }
};

// The timer event handler without any casts:

#define wxTimerEventHandler( func ) ( &func )

extern const wxTypedEventType< wxTimerEvent > wxEVT_TIMER;

const wxTypedEventType< wxTimerEvent > wxEVT_TIMER( wxNewEventType() );

// An old/user defined event type:
    
class wxColorEvent : public wxEvent
{
    public:
        wxColorEvent( wxEventType eventType )
            : wxEvent( eventType )
        { }
};

// Two possible ways how the event can be declared:

extern const wxEventType wxEVT_BLUE;
DECLARE_EVENT_TYPE( wxEVT_BLUE, 0 )
DEFINE_EVENT_TYPE( wxEVT_BLUE )

// The color event handler with a lot of casts:

typedef void (wxEvtHandler::*wxColorEventFunction)(wxColorEvent&);
typedef void (wxEvtHandler::*wxEventFunction)(wxEvent&);
typedef wxEventFunction wxObjectEventFunction;
#define wxColorEventHandler(func) \
    (wxObjectEventFunction)(wxEventFunction)static_cast< wxColorEventFunction >( &func )

// Some functions as event handlers:

void handleTimerEventFunction( wxTimerEvent &event )
{
    printf( "::handleTimerEventFunction( wxTimerEvent & )\n" );
}

void handleColorEventFunction( wxColorEvent &event )
{
    printf( "::handleColorEventFunction( wxColorEvent & )\n" );
}

// An event handler which uses it's own methods as handlers:

class MyEventHandler : public wxEvtHandler
{
    public:
        MyEventHandler()
        {
            Connect( wxEVT_TIMER, wxTimerEventHandler( MyEventHandler::handleTimerEventMethod ));
            Connect( wxEVT_BLUE, wxColorEventHandler( MyEventHandler::handleColorEventMethod ));
        }

        void handleTimerEventMethod( wxTimerEvent &event )
        {
            printf( "MyEventHandler::handleTimerEventMethod( wxTimerEvent & )\n" );
        }
       
        void handleColorEventMethod( wxColorEvent &event )
        {
            printf( "MyEventHandler::handleColorEventMethod( wxColorEvent & )\n" );
        }

};

// Another event handler which uses the base class methods as handlers:

class MyDerivedEventHandler : public MyEventHandler
{
    public:
        MyDerivedEventHandler()
        {
            Connect( wxEVT_TIMER, wxTimerEventHandler( MyDerivedEventHandler::handleTimerEventMethod ));
            Connect( wxEVT_BLUE, wxColorEventHandler( MyDerivedEventHandler::handleColorEventMethod ));
        }
};



wxEventTableEntry sm_staticEventHandlers[] =
{
    DECLARE_EVENT_TABLE_ENTRY( wxEVT_TIMER, &handleTimerEventFunction ),
    DECLARE_EVENT_TABLE_ENTRY( wxEVT_TIMER, wxTimerEventHandler( MyDerivedEventHandler::handleTimerEventMethod )),

    DECLARE_EVENT_TABLE_ENTRY( wxEVT_BLUE, wxColorEventHandler( MyDerivedEventHandler::handleColorEventMethod )),

    // Must not compile
    // DECLARE_EVENT_TABLE_ENTRY( wxEVT_BLUE, wxTimerEventHandler( MyDerivedEventHandler::handleTimerEventMethod )),

    // Mark the end of the table ie: END_EVENT_TABLE()
    wxEventTableEntry( wxEVT_NULL, NULL )
};


int main( void )
{
    //
    // should compile:
    //
    wxEvtHandler eventProvider;
    eventProvider.Connect( wxEVT_TIMER, wxTimerEventHandler( handleTimerEventFunction ));

    MyEventHandler eventHandler;
    eventProvider.Connect( wxEVT_TIMER, wxTimerEventHandler( MyEventHandler::handleTimerEventMethod ));
    eventProvider.Connect( wxEVT_TIMER, wxTimerEventHandler( MyEventHandler::handleTimerEventMethod ), &eventHandler);
    eventProvider.Connect( wxEVT_BLUE, wxColorEventHandler( MyEventHandler::handleColorEventMethod ));
    eventProvider.Connect( wxEVT_BLUE, wxColorEventHandler( MyEventHandler::handleColorEventMethod ), &eventHandler );


    MyDerivedEventHandler derivedEventHandler;
    eventProvider.Connect( wxEVT_TIMER, wxTimerEventHandler( MyDerivedEventHandler::handleTimerEventMethod ));
    eventProvider.Connect( wxEVT_TIMER, wxTimerEventHandler( MyDerivedEventHandler::handleTimerEventMethod ), &derivedEventHandler );
    eventProvider.Connect( wxEVT_BLUE, wxColorEventHandler( MyDerivedEventHandler::handleColorEventMethod ));
    eventProvider.Connect( wxEVT_BLUE, wxColorEventHandler( MyDerivedEventHandler::handleColorEventMethod ), &derivedEventHandler );

    // Must not compile:
    // eventProvider.Connect( wxEVT_BLUE, &handleTimerEventFunction );
    // eventProvider.Connect( wxEVT_BLUE, wxColorEventHandler( MyEventHandler::handleTimerEventMethod ));

    // Send an event:
    wxTimerEvent timerEvent( wxEVT_TIMER );
    eventProvider.ProcessEvent( timerEvent );

    // Send an illegal event (leads to an ASSERT in wxEvtFunction<Event>::Relay):
    wxColorEvent colorEvent( wxEVT_TIMER );
    eventProvider.ProcessEvent( colorEvent );

    return ( 0 );
}