From: Vadim Zeitlin Date: Fri, 6 Feb 2009 18:33:01 +0000 (+0000) Subject: allow handling events in a function taking the base class of the event class correspo... X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/a01ada0599acdc11497850c4c8e846d3f237bf91?ds=inline allow handling events in a function taking the base class of the event class corresponding to the event type and not exactly the event class itself: this is more flexible (while still being safe) and incidentally fixes compatibility issues with existing code using explicit casts to wxObjectEventFunction (see #10477) git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@58690 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/include/wx/event.h b/include/wx/event.h index 4733d9207c..5fee9d2141 100644 --- a/include/wx/event.h +++ b/include/wx/event.h @@ -321,11 +321,32 @@ private: Functor m_handler; }; -// helper class defining operations different for method functors using an -// object of wxEvtHandler-derived class as handler and the others namespace wxPrivate { +// helper template defining nested "type" typedef as the event class +// corresponding to the given event type +template struct EventClassOf; + +// the typed events provide the information about the class of the events they +// carry themselves: +template +struct EventClassOf< wxEventTypeTag > +{ + typedef typename wxEventTypeTag::EventClass type; +}; + +// for the old untyped events we don't have information about the exact event +// class carried by them +template <> +struct EventClassOf +{ + typedef wxEvent type; +}; + + +// helper class defining operations different for method functors using an +// object of wxEvtHandler-derived class as handler and the others template struct HandlerImpl; // specialization for handlers deriving from wxEvtHandler @@ -339,7 +360,8 @@ struct HandlerImpl static wxEvtHandler *ConvertToEvtHandler(T *p) { return p; } static wxEventFunction ConvertToEvtFunction(void (T::*f)(A&)) - { return reinterpret_cast(f); } + { return static_cast( + reinterpret_cast(f)); } }; // specialization for handlers not deriving from wxEvtHandler @@ -362,18 +384,29 @@ struct HandlerImpl // // notice that the object class may be different from the class in which the // method is defined but it must be convertible to this class -template +// +// also, the type of the handler parameter doesn't need to be exactly the same +// as EventTag::EventClass but it must be its base class -- this is explicitly +// allowed to handle different events in the same handler taking wxEvent&, for +// example +template + class wxEventFunctorMethod : public wxEventFunctor, private wxPrivate::HandlerImpl < Class, - typename EventTag::EventClass, + EventArg, wxConvertibleTo::value > { +private: + static void CheckHandlerArgument(EventArg *) { } + public: - typedef typename EventTag::EventClass EventArg; + // the event class associated with the given event tag + typedef typename wxPrivate::EventClassOf::type EventClass; + wxEventFunctorMethod(void (Class::*method)(EventArg&), ObjClass *handler) { @@ -381,6 +414,11 @@ public: "handlers defined in non-wxEvtHandler-derived classes " "must be connected with a valid sink object" ); + // if you get an error here it means that the signature of the handler + // you're trying to use is not compatible with (i.e. is not the same as + // or a base class of) the real event class used for this event type + CheckHandlerArgument(static_cast(NULL)); + m_handler = handler; m_method = method; } @@ -396,6 +434,9 @@ public: wxCHECK_RET( realHandler, "invalid event handler" ); } + // the real (run-time) type of event is EventClass and we checked in + // the ctor that EventClass can be converted to EventArg, so this cast + // is always valid (realHandler->*m_method)(static_cast(event)); } @@ -429,18 +470,6 @@ private: void (Class::*m_method)(EventArg&); }; -// partial specialization for legacy event types -template -class wxEventFunctorMethod - : public wxObjectEventFunctor -{ -public: - wxEventFunctorMethod(wxObjectEventFunction method, ObjClass *handler) - : wxObjectEventFunctor(method, handler) - { - } -}; - // // Create functors for the templatized events, either allocated on the heap for @@ -469,39 +498,43 @@ wxMakeEventFunctor(const EventTag&, Functor func) // Create functors for methods: template -inline wxEventFunctorMethod * +inline wxEventFunctorMethod * wxNewEventFunctor(const EventTag&, void (Class::*method)(EventArg&), ObjClass *handler) { - return new wxEventFunctorMethod(method, handler); + return new wxEventFunctorMethod( + method, handler); } template -inline wxEventFunctorMethod +inline wxEventFunctorMethod wxMakeEventFunctor(const EventTag&, void (Class::*method)(EventArg&), ObjClass *handler) { - return wxEventFunctorMethod(method, handler); + return wxEventFunctorMethod( + method, handler); } // Special case for the wxNewEventFunctor() calls used inside the event table // macros: they don't specify the handler so ObjClass can't be deduced template -inline wxEventFunctorMethod * +inline wxEventFunctorMethod * wxNewEventFunctor(const EventTag&, void (Class::*method)(EventArg&)) { - return new wxEventFunctorMethod(method, NULL); + return new wxEventFunctorMethod( + method, NULL); } template -inline wxEventFunctorMethod +inline wxEventFunctorMethod wxMakeEventFunctor(const EventTag&, void (Class::*method)(EventArg&)) { - return wxEventFunctorMethod(method, NULL); + return wxEventFunctorMethod( + method, NULL); } #endif // !wxEVENTS_COMPATIBILITY_2_8 diff --git a/tests/events/evthandler.cpp b/tests/events/evthandler.cpp index 137fe3e6e2..ea86660020 100644 --- a/tests/events/evthandler.cpp +++ b/tests/events/evthandler.cpp @@ -23,19 +23,21 @@ // test events and their handlers // ---------------------------------------------------------------------------- -const wxEventType EVT_LEGACY = wxNewEventType(); +const wxEventType LegacyEventType = wxNewEventType(); class MyEvent; -wxDEFINE_EVENT( EVT_MYEVENT, MyEvent ) +wxDEFINE_EVENT( MyEventType, MyEvent ) class MyEvent : public wxEvent { public: - MyEvent() : wxEvent(0, EVT_MYEVENT) { } + MyEvent() : wxEvent(0, MyEventType) { } virtual wxEvent *Clone() const { return new MyEvent; } }; +#define EVT_MYEVENT(func) wx__DECLARE_EVT0(MyEventType, &func) + class AnotherEvent : public wxEvent { }; @@ -100,6 +102,7 @@ public: struct MySink { void OnMyEvent(MyEvent&) { g_called.method = true; } + void OnEvent(wxEvent&) { g_called.method = true; } void OnIdle(wxIdleEvent&) { g_called.method = true; } }; @@ -119,7 +122,11 @@ private: BEGIN_EVENT_TABLE(MyClassWithEventTable, wxEvtHandler) EVT_IDLE(MyClassWithEventTable::OnIdle) + EVT_MYEVENT(MyClassWithEventTable::OnMyEvent) + EVT_MYEVENT(MyClassWithEventTable::OnEvent) + // this shouldn't compile: + //EVT_MYEVENT(MyClassWithEventTable::OnIdle) //EVT_IDLE(MyClassWithEventTable::OnAnotherEvent) END_EVENT_TABLE() @@ -144,6 +151,7 @@ private: CPPUNIT_TEST( ConnectStaticMethod ); CPPUNIT_TEST( ConnectFunctor ); CPPUNIT_TEST( ConnectMethod ); + CPPUNIT_TEST( ConnectMethodUsingBaseEvent ); CPPUNIT_TEST( ConnectMethodWithSink ); CPPUNIT_TEST( ConnectNonHandler ); CPPUNIT_TEST( StaticConnect ); @@ -158,6 +166,7 @@ private: void ConnectStaticMethod(); void ConnectFunctor(); void ConnectMethod(); + void ConnectMethodUsingBaseEvent(); void ConnectMethodWithSink(); void ConnectNonHandler(); void StaticConnect(); @@ -187,6 +196,11 @@ void EvtHandlerTestCase::BuiltinConnect() handler.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), NULL, &handler); handler.Disconnect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), NULL, &handler); + // using casts like this is even uglier than using wxIdleEventHandler but + // it should still continue to work for compatibility + handler.Connect(wxEVT_IDLE, (wxObjectEventFunction)(wxEventFunction)&MyHandler::OnIdle); + handler.Disconnect(wxEVT_IDLE, (wxObjectEventFunction)(wxEventFunction)&MyHandler::OnIdle); + #if !wxEVENTS_COMPATIBILITY_2_8 handler.Connect(wxEVT_IDLE, GlobalOnIdle); handler.Disconnect(wxEVT_IDLE, GlobalOnIdle); @@ -205,22 +219,22 @@ void EvtHandlerTestCase::BuiltinConnect() void EvtHandlerTestCase::LegacyConnect() { - handler.Connect( EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent ); - handler.Connect( 0, EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent ); - handler.Connect( 0, 0, EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent ); + handler.Connect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent ); + handler.Connect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent ); + handler.Connect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent ); - handler.Disconnect( EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent ); - handler.Disconnect( 0, EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent ); - handler.Disconnect( 0, 0, EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent ); + handler.Disconnect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent ); + handler.Disconnect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent ); + handler.Disconnect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent ); - handler.Connect( EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler ); - handler.Connect( 0, EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler ); - handler.Connect( 0, 0, EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler ); + handler.Connect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler ); + handler.Connect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler ); + handler.Connect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler ); - handler.Disconnect( EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler ); - handler.Disconnect( 0, EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler ); - handler.Disconnect( 0, 0, EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler ); + handler.Disconnect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler ); + handler.Disconnect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler ); + handler.Disconnect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler ); } #if !wxEVENTS_COMPATIBILITY_2_8 @@ -228,40 +242,40 @@ void EvtHandlerTestCase::LegacyConnect() void EvtHandlerTestCase::ConnectFunction() { // function tests - handler.Connect( EVT_MYEVENT, GlobalOnMyEvent ); + handler.Connect( MyEventType, GlobalOnMyEvent ); g_called.Reset(); handler.ProcessEvent(e); CPPUNIT_ASSERT( g_called.function ); - handler.Disconnect( EVT_MYEVENT, GlobalOnMyEvent ); + handler.Disconnect( MyEventType, GlobalOnMyEvent ); g_called.Reset(); handler.ProcessEvent(e); CPPUNIT_ASSERT( !g_called.function ); // check that it was disconnected - handler.Connect( 0, EVT_MYEVENT, GlobalOnMyEvent ); - handler.Disconnect( 0, EVT_MYEVENT, GlobalOnMyEvent ); + handler.Connect( 0, MyEventType, GlobalOnMyEvent ); + handler.Disconnect( 0, MyEventType, GlobalOnMyEvent ); - handler.Connect( 0, 0, EVT_MYEVENT, GlobalOnMyEvent ); - handler.Disconnect( 0, 0, EVT_MYEVENT, GlobalOnMyEvent ); + handler.Connect( 0, 0, MyEventType, GlobalOnMyEvent ); + handler.Disconnect( 0, 0, MyEventType, GlobalOnMyEvent ); } void EvtHandlerTestCase::ConnectStaticMethod() { // static method tests (this is same as functions but still test it just in // case we hit some strange compiler bugs) - handler.Connect( EVT_MYEVENT, &MyHandler::StaticOnMyEvent ); + handler.Connect( MyEventType, &MyHandler::StaticOnMyEvent ); g_called.Reset(); handler.ProcessEvent(e); CPPUNIT_ASSERT( g_called.smethod ); - handler.Disconnect( EVT_MYEVENT, &MyHandler::StaticOnMyEvent ); + handler.Disconnect( MyEventType, &MyHandler::StaticOnMyEvent ); g_called.Reset(); handler.ProcessEvent(e); CPPUNIT_ASSERT( !g_called.smethod ); - handler.Connect( 0, EVT_MYEVENT, &MyHandler::StaticOnMyEvent ); - handler.Disconnect( 0, EVT_MYEVENT, &MyHandler::StaticOnMyEvent ); + handler.Connect( 0, MyEventType, &MyHandler::StaticOnMyEvent ); + handler.Disconnect( 0, MyEventType, &MyHandler::StaticOnMyEvent ); - handler.Connect( 0, 0, EVT_MYEVENT, &MyHandler::StaticOnMyEvent ); - handler.Disconnect( 0, 0, EVT_MYEVENT, &MyHandler::StaticOnMyEvent ); + handler.Connect( 0, 0, MyEventType, &MyHandler::StaticOnMyEvent ); + handler.Disconnect( 0, 0, MyEventType, &MyHandler::StaticOnMyEvent ); } void EvtHandlerTestCase::ConnectFunctor() @@ -269,50 +283,71 @@ void EvtHandlerTestCase::ConnectFunctor() // generalized functor tests MyFunctor functor; - handler.Connect( EVT_MYEVENT, functor ); + handler.Connect( MyEventType, functor ); g_called.Reset(); handler.ProcessEvent(e); CPPUNIT_ASSERT( g_called.functor ); - handler.Disconnect( EVT_MYEVENT, functor ); + handler.Disconnect( MyEventType, functor ); g_called.Reset(); handler.ProcessEvent(e); CPPUNIT_ASSERT( !g_called.functor ); - handler.Connect( 0, EVT_MYEVENT, functor ); - handler.Disconnect( 0, EVT_MYEVENT, functor ); + handler.Connect( 0, MyEventType, functor ); + handler.Disconnect( 0, MyEventType, functor ); - handler.Connect( 0, 0, EVT_MYEVENT, functor ); - handler.Disconnect( 0, 0, EVT_MYEVENT, functor ); + handler.Connect( 0, 0, MyEventType, functor ); + handler.Disconnect( 0, 0, MyEventType, functor ); } void EvtHandlerTestCase::ConnectMethod() { // class method tests - handler.Connect( EVT_MYEVENT, &MyHandler::OnMyEvent ); + handler.Connect( MyEventType, &MyHandler::OnMyEvent ); + g_called.Reset(); + handler.ProcessEvent(e); + CPPUNIT_ASSERT( g_called.method ); + handler.Disconnect( MyEventType, &MyHandler::OnMyEvent ); + g_called.Reset(); + handler.ProcessEvent(e); + CPPUNIT_ASSERT( !g_called.method ); + + handler.Connect( 0, MyEventType, &MyHandler::OnMyEvent ); + handler.Disconnect( 0, MyEventType, &MyHandler::OnMyEvent ); + + handler.Connect( 0, 0, MyEventType, &MyHandler::OnMyEvent ); + handler.Disconnect( 0, 0, MyEventType, &MyHandler::OnMyEvent ); +} + +void EvtHandlerTestCase::ConnectMethodUsingBaseEvent() +{ + // test connecting a method taking just wxEvent and not MyEvent: this + // should work too if we don't need any MyEvent-specific information in the + // handler + handler.Connect( MyEventType, &MyHandler::OnEvent ); g_called.Reset(); handler.ProcessEvent(e); CPPUNIT_ASSERT( g_called.method ); - handler.Disconnect( EVT_MYEVENT, &MyHandler::OnMyEvent ); + handler.Disconnect( MyEventType, &MyHandler::OnEvent ); g_called.Reset(); handler.ProcessEvent(e); CPPUNIT_ASSERT( !g_called.method ); - handler.Connect( 0, EVT_MYEVENT, &MyHandler::OnMyEvent ); - handler.Disconnect( 0, EVT_MYEVENT, &MyHandler::OnMyEvent ); + handler.Connect( 0, MyEventType, &MyHandler::OnEvent ); + handler.Disconnect( 0, MyEventType, &MyHandler::OnEvent ); - handler.Connect( 0, 0, EVT_MYEVENT, &MyHandler::OnMyEvent ); - handler.Disconnect( 0, 0, EVT_MYEVENT, &MyHandler::OnMyEvent ); + handler.Connect( 0, 0, MyEventType, &MyHandler::OnEvent ); + handler.Disconnect( 0, 0, MyEventType, &MyHandler::OnEvent ); } void EvtHandlerTestCase::ConnectMethodWithSink() { - handler.Connect( EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler ); - handler.Connect( 0, EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler ); - handler.Connect( 0, 0, EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler ); + handler.Connect( MyEventType, &MyHandler::OnMyEvent, NULL, &handler ); + handler.Connect( 0, MyEventType, &MyHandler::OnMyEvent, NULL, &handler ); + handler.Connect( 0, 0, MyEventType, &MyHandler::OnMyEvent, NULL, &handler ); - handler.Disconnect( EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler ); - handler.Disconnect( 0, EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler ); - handler.Disconnect( 0, 0, EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler ); + handler.Disconnect( MyEventType, &MyHandler::OnMyEvent, NULL, &handler ); + handler.Disconnect( 0, MyEventType, &MyHandler::OnMyEvent, NULL, &handler ); + handler.Disconnect( 0, 0, MyEventType, &MyHandler::OnMyEvent, NULL, &handler ); } void EvtHandlerTestCase::ConnectNonHandler() @@ -320,11 +355,11 @@ void EvtHandlerTestCase::ConnectNonHandler() // class method tests for class not derived from wxEvtHandler MySink sink; - handler.Connect( EVT_MYEVENT, &MySink::OnMyEvent, NULL, &sink ); + handler.Connect( MyEventType, &MySink::OnMyEvent, NULL, &sink ); g_called.Reset(); handler.ProcessEvent(e); CPPUNIT_ASSERT( g_called.method ); - handler.Disconnect( EVT_MYEVENT, &MySink::OnMyEvent, NULL, &sink ); + handler.Disconnect( MyEventType, &MySink::OnMyEvent, NULL, &sink ); g_called.Reset(); handler.ProcessEvent(e); CPPUNIT_ASSERT( !g_called.method ); @@ -332,13 +367,13 @@ void EvtHandlerTestCase::ConnectNonHandler() void EvtHandlerTestCase::StaticConnect() { - wxEvtHandler::Connect( &handler, EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler ); - wxEvtHandler::Connect( &handler, 0, EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler ); - wxEvtHandler::Connect( &handler, 0, 0, EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler ); + wxEvtHandler::Connect( &handler, MyEventType, &MyHandler::OnMyEvent, NULL, &handler ); + wxEvtHandler::Connect( &handler, 0, MyEventType, &MyHandler::OnMyEvent, NULL, &handler ); + wxEvtHandler::Connect( &handler, 0, 0, MyEventType, &MyHandler::OnMyEvent, NULL, &handler ); - wxEvtHandler::Disconnect( &handler, EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler ); - wxEvtHandler::Disconnect( &handler, 0, EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler ); - wxEvtHandler::Disconnect( &handler, 0, 0, EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler ); + wxEvtHandler::Disconnect( &handler, MyEventType, &MyHandler::OnMyEvent, NULL, &handler ); + wxEvtHandler::Disconnect( &handler, 0, MyEventType, &MyHandler::OnMyEvent, NULL, &handler ); + wxEvtHandler::Disconnect( &handler, 0, 0, MyEventType, &MyHandler::OnMyEvent, NULL, &handler ); } void EvtHandlerTestCase::InvalidConnect() @@ -346,10 +381,10 @@ void EvtHandlerTestCase::InvalidConnect() // these calls shouldn't compile but we unfortunately can't check this // automatically, you need to uncomment them manually and test that // compilation does indeed fail - //handler.Connect(EVT_MYEVENT, GlobalOnAnotherEvent); - //IdleFunctor f; handler.Connect(EVT_MYEVENT, f); - //handler.Connect(EVT_MYEVENT, &MyHandler::StaticOnAnotherEvent); - //handler.Connect(EVT_MYEVENT, &MyHandler::OnAnotherEvent); + //handler.Connect(MyEventType, GlobalOnAnotherEvent); + //IdleFunctor f; handler.Connect(MyEventType, f); + //handler.Connect(MyEventType, &MyHandler::StaticOnAnotherEvent); + //handler.Connect(MyEventType, &MyHandler::OnAnotherEvent); } #endif // !wxEVENTS_COMPATIBILITY_2_8