Development: Event Handling Improvements

From WxWiki
Jump to: navigation, 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 );
}