Changes to template Connect() to make it compile with MSVC7 and possible other

not quite up-to-date compilers, to reduce repetitions and to allow using
methods of non-wxEvtHandler-derived classes as event callbacks:

1. Don't rely on compiler ability to deduce template parameter from the type
   of a parameter of a function used as another template parameter, at least
   MSVC7 can't do this and it's probably not the only one.
2. Do rely on compiler support for partial specialization to make
   wxEventFunctorMethod compile for non-wxEvtHandler-derived handlers while
   still keeping the old functionality for the wxEvtHandler-derived ones.
3. Don't make any difference between functions and functors, both are callable
   objects so use them as such, this allows to fold code for both cases.
4. Avoid the use of dynamic_cast<>.
5. Several naming changes:
 a) wxTypedEventType -> wxEventTypeTag (because this is what it is)
 b) Subscribe/Unsubscribe -> DoConnect/Disconnect (to follow the usual
    convention of public Foo calling private DoFoo and to avoid using up
    another name)
 c) Derived -> ObjClass (because it's not clear what does Derived mean)
6. Extend the unit test to cover more cases.


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@58625 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2009-02-02 20:38:56 +00:00
parent 4c509aecb6
commit f3ff831f3b
4 changed files with 686 additions and 568 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -406,7 +406,7 @@
Windows.
*/
#if !wxEVENTS_COMPATIBILITY_2_8
# if !wxCHECK_VISUALC_VERSION(8)
# if !wxCHECK_VISUALC_VERSION(7)
# undef wxEVENTS_COMPATIBILITY_2_8
# define wxEVENTS_COMPATIBILITY_2_8 1
# endif

View File

@@ -1065,7 +1065,7 @@ wxEvtHandler::~wxEvtHandler()
// Remove ourselves from sink destructor notifications
// (this has usually been done, in wxTrackable destructor)
wxEvtHandler *eventSink = entry->m_fn->GetHandler();
wxEvtHandler *eventSink = entry->m_fn->GetEvtHandler();
if ( eventSink )
{
wxEventConnectionRef * const
@@ -1408,10 +1408,11 @@ bool wxEvtHandler::SearchEventTable(wxEventTable& table, wxEvent& event)
return false;
}
void wxEvtHandler::Subscribe( int id, int lastId,
void wxEvtHandler::DoConnect(int id,
int lastId,
wxEventType eventType,
wxEventFunctor *func,
wxObject *userData )
wxObject *userData)
{
wxDynamicEventTableEntry *entry =
new wxDynamicEventTableEntry(eventType, id, lastId, func, userData);
@@ -1423,7 +1424,7 @@ void wxEvtHandler::Subscribe( int id, int lastId,
m_dynamicEvents->Insert( (wxObject*) entry );
// Make sure we get to know when a sink is destroyed
wxEvtHandler *eventSink = func->GetHandler();
wxEvtHandler *eventSink = func->GetEvtHandler();
if ( eventSink && eventSink != this )
{
wxEventConnectionRef *evtConnRef = FindRefInTrackerList(eventSink);
@@ -1435,7 +1436,7 @@ void wxEvtHandler::Subscribe( int id, int lastId,
}
bool
wxEvtHandler::Unsubscribe(int id,
wxEvtHandler::DoDisconnect(int id,
int lastId,
wxEventType eventType,
const wxEventFunctor& func,
@@ -1445,7 +1446,7 @@ wxEvtHandler::Unsubscribe(int id,
return false;
// Remove connection from tracker node (wxEventConnectionRef)
wxEvtHandler *eventSink = func.GetHandler();
wxEvtHandler *eventSink = func.GetEvtHandler();
if ( eventSink && eventSink != this )
{
wxEventConnectionRef *evtConnRef = FindRefInTrackerList(eventSink);
@@ -1490,7 +1491,7 @@ bool wxEvtHandler::SearchDynamicEventTable( wxEvent& event )
if ( event.GetEventType() == entry->m_eventType )
{
wxEvtHandler *handler = entry->m_fn->GetHandler();
wxEvtHandler *handler = entry->m_fn->GetEvtHandler();
if ( !handler )
handler = this;
if ( ProcessEventIfMatchesId(*entry, handler, event) )
@@ -1571,7 +1572,7 @@ void wxEvtHandler::OnSinkDestroyed( wxEvtHandler *sink )
wxDynamicEventTableEntry *entry = (wxDynamicEventTableEntry*)node->GetData();
node_nxt = node->GetNext();
if ( entry->m_fn->GetHandler() == sink )
if ( entry->m_fn->GetEvtHandler() == sink )
{
delete entry->m_callbackUserData;
m_dynamicEvents->Erase( node );

View File

@@ -17,38 +17,11 @@
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#endif // WX_PRECOMP
#include "wx/event.h"
// --------------------------------------------------------------------------
// test class
// --------------------------------------------------------------------------
class EvtHandlerTestCase : public CppUnit::TestCase
{
public:
EvtHandlerTestCase() {}
private:
CPPUNIT_TEST_SUITE( EvtHandlerTestCase );
CPPUNIT_TEST( TestConnectCompilation );
CPPUNIT_TEST( TestEventFunctorCompare );
CPPUNIT_TEST_SUITE_END();
void TestConnectCompilation();
void TestEventFunctorCompare();
DECLARE_NO_COPY_CLASS(EvtHandlerTestCase)
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( EvtHandlerTestCase );
// also include in it's own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( EvtHandlerTestCase, "EvtHandlerTestCase" );
// ----------------------------------------------------------------------------
// test events and their handlers
// ----------------------------------------------------------------------------
const wxEventType EVT_LEGACY = wxNewEventType();
@@ -93,44 +66,145 @@ void GlobalOnMyEvent(MyEvent&)
g_called.function = true;
}
class MyFunctor
{
public:
void operator()(MyEvent &) { g_called.functor = true; }
void GlobalOnAnotherEvent(AnotherEvent&);
bool operator==(const MyFunctor &) const { return true; }
void GlobalOnIdle(wxIdleEvent&)
{
g_called.function = true;
}
struct MyFunctor
{
void operator()(MyEvent &) { g_called.functor = true; }
};
struct IdleFunctor
{
void operator()(wxIdleEvent &) { g_called.functor = true; }
};
class MyHandler : public wxEvtHandler
{
public:
void OnMyEvent(MyEvent&) { g_called.method = true; }
static void StaticOnMyEvent(MyEvent &) { g_called.smethod = true; }
static void StaticOnAnotherEvent(AnotherEvent &);
static void StaticOnIdle(wxIdleEvent&) { g_called.smethod = true; }
void OnEvent(wxEvent &) { }
void OnAnotherEvent(AnotherEvent&) { }
void OnMyEvent(MyEvent&) { g_called.method = true; }
void OnEvent(wxEvent&) { g_called.method = true; }
void OnAnotherEvent(AnotherEvent&);
void OnIdle(wxIdleEvent&) { g_called.method = true; }
};
// we can also handle events in classes not deriving from wxEvtHandler
struct MySink
{
void OnMyEvent(MyEvent&) { g_called.method = true; }
void OnIdle(wxIdleEvent&) { g_called.method = true; }
};
// also test event table compilation
class MyClassWithEventTable : public wxEvtHandler
{
public:
void OnMyEvent(MyEvent&) { g_called.method = true; }
void OnEvent(wxEvent&) { g_called.method = true; }
void OnAnotherEvent(AnotherEvent&);
void OnIdle(wxIdleEvent&) { g_called.method = true; }
private:
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(MyClassWithEventTable, wxEvtHandler)
EVT_IDLE(MyClassWithEventTable::OnIdle)
// this shouldn't compile:
//EVT_IDLE(MyClassWithEventTable::OnAnotherEvent)
END_EVENT_TABLE()
} // anonymous namespace
void EvtHandlerTestCase::TestConnectCompilation()
{
// Test that connecting the 'legacy' events still compiles:
// --------------------------------------------------------------------------
// test class
// --------------------------------------------------------------------------
class EvtHandlerTestCase : public CppUnit::TestCase
{
public:
EvtHandlerTestCase() {}
private:
CPPUNIT_TEST_SUITE( EvtHandlerTestCase );
CPPUNIT_TEST( BuiltinConnect );
CPPUNIT_TEST( LegacyConnect );
#if !wxEVENTS_COMPATIBILITY_2_8
CPPUNIT_TEST( ConnectFunction );
CPPUNIT_TEST( ConnectStaticMethod );
CPPUNIT_TEST( ConnectFunctor );
CPPUNIT_TEST( ConnectMethod );
CPPUNIT_TEST( ConnectMethodWithSink );
CPPUNIT_TEST( ConnectNonHandler );
CPPUNIT_TEST( StaticConnect );
CPPUNIT_TEST( InvalidConnect );
#endif // !wxEVENTS_COMPATIBILITY_2_8
CPPUNIT_TEST_SUITE_END();
void BuiltinConnect();
void LegacyConnect();
#if !wxEVENTS_COMPATIBILITY_2_8
void ConnectFunction();
void ConnectStaticMethod();
void ConnectFunctor();
void ConnectMethod();
void ConnectMethodWithSink();
void ConnectNonHandler();
void StaticConnect();
void InvalidConnect();
#endif // !wxEVENTS_COMPATIBILITY_2_8
// these member variables exceptionally don't use "m_" prefix because
// they're used so many times
MyHandler handler;
MyEvent e;
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 );
DECLARE_NO_COPY_CLASS(EvtHandlerTestCase)
};
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 );
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( EvtHandlerTestCase );
// also include in it's own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( EvtHandlerTestCase, "EvtHandlerTestCase" );
void EvtHandlerTestCase::BuiltinConnect()
{
handler.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle));
handler.Disconnect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle));
handler.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), NULL, &handler);
handler.Disconnect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), NULL, &handler);
#if !wxEVENTS_COMPATIBILITY_2_8
handler.Connect(wxEVT_IDLE, GlobalOnIdle);
handler.Disconnect(wxEVT_IDLE, GlobalOnIdle);
IdleFunctor f;
handler.Connect(wxEVT_IDLE, f);
handler.Disconnect(wxEVT_IDLE, f);
handler.Connect(wxEVT_IDLE, &MyHandler::OnIdle);
handler.Disconnect(wxEVT_IDLE, &MyHandler::OnIdle);
handler.Connect(wxEVT_IDLE, &MyHandler::StaticOnIdle);
handler.Disconnect(wxEVT_IDLE, &MyHandler::StaticOnIdle);
#endif // !wxEVENTS_COMPATIBILITY_2_8
}
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 );
@@ -139,38 +213,99 @@ void EvtHandlerTestCase::TestConnectCompilation()
handler.Disconnect( 0, EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent );
handler.Disconnect( 0, 0, EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent );
// Call (and therefore instantiate) all Connect() variants to detect template
// errors:
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.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 );
}
#if !wxEVENTS_COMPATIBILITY_2_8
void EvtHandlerTestCase::ConnectFunction()
{
// function tests
handler.Connect( EVT_MYEVENT, GlobalOnMyEvent );
g_called.Reset();
handler.ProcessEvent(e);
CPPUNIT_ASSERT( g_called.function );
handler.Disconnect( EVT_MYEVENT, 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, 0, EVT_MYEVENT, GlobalOnMyEvent );
handler.Disconnect( 0, 0, EVT_MYEVENT, 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 );
g_called.Reset();
handler.ProcessEvent(e);
CPPUNIT_ASSERT( g_called.smethod );
handler.Disconnect( EVT_MYEVENT, &MyHandler::StaticOnMyEvent );
g_called.Reset();
handler.ProcessEvent(e);
CPPUNIT_ASSERT( !g_called.smethod ); // check that it was disconnected
CPPUNIT_ASSERT( !g_called.smethod );
handler.Connect( 0, EVT_MYEVENT, &MyHandler::StaticOnMyEvent );
handler.Disconnect( 0, EVT_MYEVENT, &MyHandler::StaticOnMyEvent );
handler.Connect( 0, 0, EVT_MYEVENT, &MyHandler::StaticOnMyEvent );
handler.Disconnect( 0, 0, EVT_MYEVENT, &MyHandler::StaticOnMyEvent );
}
void EvtHandlerTestCase::ConnectFunctor()
{
// generalized functor tests
MyFunctor functor;
handler.Connect( EVT_MYEVENT, functor );
g_called.Reset();
handler.ProcessEvent(e);
CPPUNIT_ASSERT( g_called.functor );
handler.Disconnect( EVT_MYEVENT, 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, 0, EVT_MYEVENT, functor );
handler.Disconnect( 0, 0, EVT_MYEVENT, functor );
}
void EvtHandlerTestCase::ConnectMethod()
{
// class method tests
handler.Connect( EVT_MYEVENT, &MyHandler::OnMyEvent );
handler.Connect( 0, EVT_MYEVENT, &MyHandler::OnMyEvent );
handler.Connect( 0, 0, EVT_MYEVENT, &MyHandler::OnMyEvent );
g_called.Reset();
handler.ProcessEvent(e);
CPPUNIT_ASSERT( g_called.method );
handler.Disconnect( EVT_MYEVENT, &MyHandler::OnMyEvent );
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, 0, EVT_MYEVENT, &MyHandler::OnMyEvent );
handler.Disconnect( 0, 0, EVT_MYEVENT, &MyHandler::OnMyEvent );
}
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 );
@@ -178,8 +313,25 @@ void EvtHandlerTestCase::TestConnectCompilation()
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 );
}
void EvtHandlerTestCase::ConnectNonHandler()
{
// class method tests for class not derived from wxEvtHandler
MySink sink;
handler.Connect( EVT_MYEVENT, &MySink::OnMyEvent, NULL, &sink );
g_called.Reset();
handler.ProcessEvent(e);
CPPUNIT_ASSERT( g_called.method );
handler.Disconnect( EVT_MYEVENT, &MySink::OnMyEvent, NULL, &sink );
g_called.Reset();
handler.ProcessEvent(e);
CPPUNIT_ASSERT( !g_called.method );
}
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 );
@@ -187,43 +339,17 @@ void EvtHandlerTestCase::TestConnectCompilation()
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 );
}
MyFunctor functor;
handler.Connect( EVT_MYEVENT, functor );
handler.Connect( 0, EVT_MYEVENT, functor );
handler.Connect( 0, 0, EVT_MYEVENT, functor );
handler.Disconnect( EVT_MYEVENT, functor );
handler.Disconnect( 0, EVT_MYEVENT, functor );
handler.Disconnect( 0, 0, EVT_MYEVENT, functor );
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, &MyHandler::OnAnotherEvent, NULL, &handler);
//handler.Connect(EVT_MYEVENT, GlobalOnAnotherEvent);
//IdleFunctor f; handler.Connect(EVT_MYEVENT, f);
//handler.Connect(EVT_MYEVENT, &MyHandler::StaticOnAnotherEvent);
//handler.Connect(EVT_MYEVENT, &MyHandler::OnAnotherEvent);
}
#endif // !wxEVENTS_COMPATIBILITY_2_8
}
void EvtHandlerTestCase::TestEventFunctorCompare()
{
//#if !wxEVENTS_COMPATIBILITY_2_8
// MyHandler handler1;
// wxEventFunctor *connectFunctor = wxNewEventFunctor( EVT_MYEVENT, &MyHandler::OnMyEvent, &handler1 );
// wxEventFunctor *disconnectFunctor = wxNewEventFunctor( EVT_MYEVENT, &MyHandler::OnMyEvent, &handler1 );
// wxEventFunctor *nullFunctor = wxNewEventFunctor( EVT_MYEVENT, &MyHandler::OnMyEvent );
//
// CPPUNIT_ASSERT( connectFunctor->Matches( *disconnectFunctor ));
// CPPUNIT_ASSERT( disconnectFunctor->Matches( *connectFunctor ));
//
// CPPUNIT_ASSERT( connectFunctor->Matches( *nullFunctor ));
// CPPUNIT_ASSERT( nullFunctor->Matches( *connectFunctor ));
//
// CPPUNIT_ASSERT( disconnectFunctor->Matches( *nullFunctor ));
// CPPUNIT_ASSERT( nullFunctor->Matches( *disconnectFunctor ));
//#endif
}