From cc6ceca789f584cf3fc3fc17f6da4b7493ca69bd Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 13 Jan 2008 01:12:13 +0000 Subject: [PATCH] improvements to wxWeakRef and related classes git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@51187 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- docs/latex/wx/classes.tex | 1 + docs/latex/wx/trackable.tex | 16 +- docs/latex/wx/weakref.tex | 29 ++- docs/latex/wx/weakrefdynamic.tex | 20 ++ include/wx/defs.h | 15 ++ include/wx/event.h | 87 +++++---- include/wx/tracker.h | 140 ++++--------- include/wx/weakref.h | 324 +++++++++++++++++++++++++------ src/common/event.cpp | 116 ++++------- tests/test.bkl | 2 + tests/weakref/evtconnection.cpp | 247 +++++++++++++++++++++++ tests/weakref/weakref.cpp | 281 +++++++++++++++++++++++++++ 12 files changed, 976 insertions(+), 302 deletions(-) create mode 100644 docs/latex/wx/weakrefdynamic.tex create mode 100644 tests/weakref/evtconnection.cpp create mode 100644 tests/weakref/weakref.cpp diff --git a/docs/latex/wx/classes.tex b/docs/latex/wx/classes.tex index e270a4397b..11c7f6d8cd 100644 --- a/docs/latex/wx/classes.tex +++ b/docs/latex/wx/classes.tex @@ -467,6 +467,7 @@ \input windowdc.tex \input destroyevt.tex \input weakref.tex +\input weakrefdynamic.tex \input wnddisbl.tex \input wizard.tex \input wizevt.tex diff --git a/docs/latex/wx/trackable.tex b/docs/latex/wx/trackable.tex index 274a1e8002..a78f1a65cc 100644 --- a/docs/latex/wx/trackable.tex +++ b/docs/latex/wx/trackable.tex @@ -1,5 +1,4 @@ - -\section{\class{wxTrackableBase}}\label{wxtrackablebase} +\section{\class{wxTrackable}}\label{wxtrackable} Add-on base class for a trackable object. This class maintains an internal linked list of classes of type wxTrackerNode and @@ -10,7 +9,7 @@ API. Its only use is by deriving another class from it to make it trackable. \begin{verbatim} -class MyClass: public Foo, public TrackableBase +class MyClass: public Foo, public wxTrackable { // whatever } @@ -31,18 +30,7 @@ No base class \section{\class{wxTrackable}}\label{wxtrackable} -The only difference to \helpref{wxTrackableBase}{wxtrackablebase} is -that this class adds a virtual table to enable dynamic\_cast query for -wxTrackable. - -\wxheading{Derived from} - -\helpref{wxTrackableBase}{wxtrackablebase} - \wxheading{Include files} -\wxheading{Data structures} - - diff --git a/docs/latex/wx/weakref.tex b/docs/latex/wx/weakref.tex index de989942c5..5464e81f6d 100644 --- a/docs/latex/wx/weakref.tex +++ b/docs/latex/wx/weakref.tex @@ -6,11 +6,11 @@ such as \helpref{wxEvtHandler}{wxevthandler}, \helpref{wxWindow}{wxwindow} and pointer, but when the object pointed is destroyed, the weak reference is automatically reset to a NULL pointer. -wxWeakref can be used whenever one must keep a pointer to an object -that does not directly own, and that may be destroyed before the object +wxWeakRef can be used whenever one must keep a pointer to an object +that one does not directly own, and that may be destroyed before the object holding the reference. -wxWeakref is a small object and the mechanism behind it is fast +wxWeakRef is a small object and the mechanism behind it is fast (\textbf{O(1)}). So the overall cost of using it is small. @@ -31,9 +31,8 @@ wxWeakref is a small object and the mechanism behind it is fast wxASSERT( wr==NULL ); \end{verbatim} -wxWeakref works for any objects that are derived from -\helpref{wxTrackableBase}{wxtrackablebase} or \helpref{wxTrackable}{wxtrackable}. -By default, wxEvtHandler and wxWindow derive from wxTrackableBase. However, +wxWeakRef works for any objects that are derived from \helpref{wxTrackable}{wxtrackable}. +By default, wxEvtHandler and wxWindow derive from wxTrackable. However, wxObject does not, so types like \helpref{wxFont}{wxfont} and \helpref{wxColour}{wxcolour} are not trackable. The example below shows how to create a wxObject derived class that is trackable: @@ -44,18 +43,11 @@ create a wxObject derived class that is trackable: }; \end{verbatim} -\textbf{Note:} Custom trackable objects should derive from wxTrackable -if one wants to reference them from a \texttt{wxWeakRef}. The -difference between the two base classes is that wxTrackableBase -has no virtual member functions (no VTable), and thus cannot be detected -through \texttt{dynamic\_cast<>}. - \wxheading{Predefined types} The following types of weak references are predefined: \begin{verbatim} -typedef wxWeakRef wxObjectRef; typedef wxWeakRef wxEvtHandlerRef; typedef wxWeakRef wxWindowRef; \end{verbatim} @@ -134,7 +126,7 @@ method will cause an assert in debug mode. \membersection{wxWeakRef::operator=}\label{wxweakrefoperatorassign} -\func{T* operator}{operator=}{\param{T* }{pobj}} +\func{T*}{operator=}{\param{T* }{pobj}} Releases the currently tracked object and starts tracking {\it pobj}. A weak reference may be reset by passing {\it NULL} as {\it pobj}. @@ -142,12 +134,19 @@ A weak reference may be reset by passing {\it NULL} as {\it pobj}. \membersection{wxWeakRef::operator =}\label{wxweakrefoperatorassign2} -\func{T* operator}{operator =}{\param{wxWeakRef\& }{wr}} +\func{T*}{operator =}{\param{wxWeakRef\& }{wr}} Release currently tracked object and start tracking the same object as the wxWeakRef {\it wr}. +\membersection{wxWeakRef::Release}\label{wxweakrefrelease} + +\func{void}{Release}{\void} + +Release currently tracked object and rests object reference. + + \membersection{wxWeakRef::OnObjectDestroy}\label{wxweakrefonobjectdestroy} \func{virtual void}{OnObjectDestroy}{\void} diff --git a/docs/latex/wx/weakrefdynamic.tex b/docs/latex/wx/weakrefdynamic.tex new file mode 100644 index 0000000000..cc7522fda7 --- /dev/null +++ b/docs/latex/wx/weakrefdynamic.tex @@ -0,0 +1,20 @@ +\section{\class{wxWeakRefDynamic}}\label{wxweakrefdynamic} + +wxWeakRefDynamic is a template class for weak references that is used in +the same way as wxWeakRef. The only difference is that wxWeakRefDynamic +defaults to using \texttt{dynamic\_cast<>} for establishing the object +reference (while wxWeakRef defaults to \texttt{static\_cast<>}). + +So, wxWeakRef will detect a type mismatch during compile time and will +have a little better run-time performance. The role of wxWeakRefDynamic +is to handle objects which derived type one does not know. + +\textbf{Note:} wxWeakRef selects an implementation based on the static type +of T. If T does not have wxTrackable statically, it defaults to to a mixed- +mode operation, where it uses \texttt{dynamic\_cast<>} as the last measure (if +available from the compiler and enabled when building wxWidgets). + +For general cases, wxWeakRef is the better choice. + +For API documentation, see: \helpref{wxWeakRef}{wxweakref} + diff --git a/include/wx/defs.h b/include/wx/defs.h index 44704739c5..6acc275f86 100644 --- a/include/wx/defs.h +++ b/include/wx/defs.h @@ -444,6 +444,21 @@ typedef short int WXTYPE; #endif #endif /* HAVE_WOSTREAM */ +// ---------------------------------------------------------------------------- +// other C++ features +// ---------------------------------------------------------------------------- + +#ifndef HAVE_PARTIAL_SPECIALIZATION + // be optimistic by default + #define HAVE_PARTIAL_SPECIALIZATION +#endif + +#ifdef __VISUALC__ + #if __VISUALC__ < 1310 + #undef HAVE_PARTIAL_SPECIALIZATION + #endif +#endif // __VISUALC__ + /* ---------------------------------------------------------------------------- */ /* portable calling conventions macros */ /* ---------------------------------------------------------------------------- */ diff --git a/include/wx/event.h b/include/wx/event.h index 3e5bc79873..55e8cf5f31 100644 --- a/include/wx/event.h +++ b/include/wx/event.h @@ -39,6 +39,7 @@ class WXDLLIMPEXP_FWD_BASE wxList; #endif // wxUSE_GUI class WXDLLIMPEXP_FWD_BASE wxEvtHandler; +class wxEventConnectionRef; // ---------------------------------------------------------------------------- // Event types @@ -2258,44 +2259,11 @@ protected: DECLARE_NO_COPY_CLASS(wxEventHashTable) }; -// ---------------------------------------------------------------------------- -// wxEventConnectionRef: A class that represents all connections between two event -// handlers and enables automatic disconnect when an event handler sink goes -// out of scope. Each connection/disconnect increases/decreases ref count, and -// when zero the node goes out of scope. -// ---------------------------------------------------------------------------- - -struct wxEventConnectionRef : public wxTrackerNode { - - wxEventConnectionRef() : m_src(0), m_sink(0), m_refCount(0) { } - wxEventConnectionRef( wxEvtHandler *src, wxEvtHandler *sink ); - virtual ~wxEventConnectionRef(); - - // The sink is being destroyed - virtual void OnObjectDestroy( ); - virtual wxTrackerNodeType GetType( ){ return EventConnectionRef; } - - void IncRef( ) { m_refCount++; } - void DecRef( ); - -protected: - wxEvtHandler *m_src, *m_sink; - int m_refCount; - - friend class wxEvtHandler; - -private: - // It makes no sense to copy objects of this class - wxEventConnectionRef& operator = (const wxEventConnectionRef& WXUNUSED(other)) { wxFAIL; return *this; } -}; - - - // ---------------------------------------------------------------------------- // wxEvtHandler: the base class for all objects handling wxWidgets events // ---------------------------------------------------------------------------- -class WXDLLIMPEXP_BASE wxEvtHandler : public wxObject, public wxTrackableBase +class WXDLLIMPEXP_BASE wxEvtHandler : public wxObject, public wxTrackable { public: wxEvtHandler(); @@ -2463,12 +2431,61 @@ protected: virtual void *DoGetClientData() const; // Search tracker objects for event connection with this sink - wxEventConnectionRef *FindRefInTrackerList( wxEvtHandler *eventSink ); + wxEventConnectionRef *FindRefInTrackerList(wxEvtHandler *eventSink); private: DECLARE_DYNAMIC_CLASS_NO_COPY(wxEvtHandler) }; +// ---------------------------------------------------------------------------- +// wxEventConnectionRef represents all connections between two event handlers +// and enables automatic disconnect when an event handler sink goes out of +// scope. Each connection/disconnect increases/decreases ref count, and +// when it reaches zero the node goes out of scope. +// ---------------------------------------------------------------------------- + +class wxEventConnectionRef : public wxTrackerNode +{ +public: + wxEventConnectionRef() : m_src(NULL), m_sink(NULL), m_refCount(0) { } + wxEventConnectionRef(wxEvtHandler *src, wxEvtHandler *sink) + : m_src(src), m_sink(sink), m_refCount(1) + { + m_sink->AddNode(this); + } + + // The sink is being destroyed + virtual void OnObjectDestroy( ) + { + if ( m_src ) + m_src->OnSinkDestroyed( m_sink ); + delete this; + } + + virtual wxEventConnectionRef *ToEventConnection() { return this; } + + void IncRef() { m_refCount++; } + void DecRef() + { + if ( !--m_refCount ) + { + // The sink holds the only external pointer to this object + if ( m_sink ) + m_sink->RemoveNode(this); + delete this; + } + } + +private: + wxEvtHandler *m_src, + *m_sink; + int m_refCount; + + friend class wxEvtHandler; + + DECLARE_NO_ASSIGN_CLASS(wxEventConnectionRef) +}; + // Post a message to the given eventhandler which will be processed during the // next event loop iteration inline void wxPostEvent(wxEvtHandler *dest, const wxEvent& event) diff --git a/include/wx/tracker.h b/include/wx/tracker.h index ef1ab84388..0ffbc1df99 100644 --- a/include/wx/tracker.h +++ b/include/wx/tracker.h @@ -2,7 +2,7 @@ // Name: wx/tracker.h // Purpose: Support class for object lifetime tracking (wxWeakRef) // Author: Arne Steinarson -// Created: 2007-12-28 +// Created: 28 Dec 07 // RCS-ID: $Id$ // Copyright: (c) 2007 Arne Steinarson // Licence: wxWindows licence @@ -11,133 +11,73 @@ #ifndef _WX_TRACKER_H_ #define _WX_TRACKER_H_ +#include "wx/defs.h" -// This structure represents an object tracker and is stored in a linked list +class wxEventConnectionRef; + +// This class represents an object tracker and is stored in a linked list // in the tracked object. It is only used in one of its derived forms. -struct wxTrackerNode { - wxTrackerNode( ) : m_nxt(0) { } +class WXDLLIMPEXP_BASE wxTrackerNode +{ +public: + wxTrackerNode() : m_nxt(NULL) { } virtual ~wxTrackerNode() { } - - virtual void OnObjectDestroy( ) = 0; - - // This is to tell the difference between different tracker node types. - // It's a replacement of dynamic_cast<> for this class since dynamic_cast - // may be disabled (and we don't have wxDynamicCast since wxTrackerNode - // is not derived wxObject). - enum wxTrackerNodeType { WeakRef, EventConnectionRef }; - - virtual wxTrackerNodeType GetType() = 0; - -protected: + + virtual void OnObjectDestroy() = 0; + + virtual wxEventConnectionRef *ToEventConnection() { return NULL; } + +private: wxTrackerNode *m_nxt; - friend class wxTrackableBase; // For list access + + friend class wxTrackable; // For list access friend class wxEvtHandler; // For list access }; - // Add-on base class for a trackable object. -struct wxTrackableBase { - wxTrackableBase() : m_first(0) { } - ~wxTrackableBase() - { +class wxTrackable +{ +public: + wxTrackable() : m_first(NULL) { } + + ~wxTrackable() + { // Notify all registered refs - - wxTrackerNode *first; - while( m_first ) + while ( m_first ) { - first = m_first; - first->OnObjectDestroy( ); - RemoveNode(first); + wxTrackerNode * const first = m_first; + m_first = first->m_nxt; + first->OnObjectDestroy(); } } - - void AddNode( wxTrackerNode *prn ) + + void AddNode(wxTrackerNode *prn) { prn->m_nxt = m_first; m_first = prn; } - void RemoveNode( wxTrackerNode *prn ) + + void RemoveNode(wxTrackerNode *prn) { - for( wxTrackerNode **pprn=&m_first; *pprn; pprn=&(*pprn)->m_nxt ) + for ( wxTrackerNode **pprn = &m_first; *pprn; pprn = &(*pprn)->m_nxt ) { - if( *pprn==prn ) + if ( *pprn == prn ) { *pprn = prn->m_nxt; return; } } - // Not found, an error. - wxASSERT( false ); - } - - wxTrackerNode* GetFirst( ){ return m_first; } - // If trying to copy this object, then do not copy its ref list. - wxTrackableBase& operator = (const wxTrackableBase& WXUNUSED(other)) { return *this; } - -protected: - wxTrackerNode *m_first; -}; + wxFAIL_MSG( "removing invalid tracker node" ); + } + wxTrackerNode *GetFirst() const { return m_first; } -// The difference to wxTrackableBase is that this class adds -// a VTable to enable dynamic_cast query for wxTrackable. -struct wxTrackable : public wxTrackableBase -{ - virtual ~wxTrackable(){ } -}; - +protected: + wxTrackerNode *m_first; -// Helper to decide if an object has a base class or not -// (strictly speaking, this test succeeds if a type is convertible -// to another type in some way.) -template -struct wxHasBase{ - static char Match( B* pb ); - static int Match( ... ); - enum { value = (sizeof(Match((T*)NULL))==sizeof(char)) }; + DECLARE_NO_COPY_CLASS(wxTrackable) }; -// VC++ before 7.1 does not have partial template specialization -#ifdef __VISUALC__ - #if __VISUALC__ < 1310 - #define HAVE_NO_PARTIAL_SPECIALIZATION - #endif -#endif - -#if defined(HAVE_DYNAMIC_CAST) && !defined(HAVE_NO_PARTIAL_SPECIALIZATION) - // A structure to cast to wxTrackableBase, using either static_cast<> or dynamic_cast<>. - template - struct wxTrackableCaster; - - template - struct wxTrackableCaster { - static wxTrackableBase* Cast(T* pt){ return static_cast(pt); } - }; - - template - struct wxTrackableCaster { - static wxTrackableBase* Cast(T* pt){ return dynamic_cast(pt); } - }; -#else - #if defined(HAVE_DYNAMIC_CAST) - // If we have dynamic_cast, default to that. For gcc, dynamic_cast<> does the job - // of both the dynamic and the static case. It could be that all compilers do it - // that way, rendering the specialization code above rednundant. - template - struct wxTrackableCaster { - static wxTrackableBase* Cast(T* pt){ return dynamic_cast(pt); } - }; - #else - // No dynamic_cast<> is available. - // We use static_cast<>, that gives support for wxEvtHandler and wxWindow references. - // We don't get weak refs to other wxObject derived types. - template - struct wxTrackableCaster { - static wxTrackableBase* Cast(T* pt){ return static_cast(pt); } - }; - #endif -#endif - #endif // _WX_TRACKER_H_ diff --git a/include/wx/weakref.h b/include/wx/weakref.h index 3d4c445774..99621fca49 100644 --- a/include/wx/weakref.h +++ b/include/wx/weakref.h @@ -2,7 +2,7 @@ // Name: wx/weakref.h // Purpose: wxWeakRef - Generic weak references for wxWidgets // Author: Arne Steinarson -// Created: 2007-12-27 +// Created: 27 Dec 07 // RCS-ID: $Id$ // Copyright: (c) 2007 Arne Steinarson // Licence: wxWindows licence @@ -11,114 +11,314 @@ #ifndef _WX_WEAKREF_H_ #define _WX_WEAKREF_H_ -#include +#include "wx/tracker.h" -// A weak reference to an object of type T, where T has type wxTrackable -// as one of its base classes (in a static or dynamic sense). +#include "wx/meta/convertible.h" +#include "wx/meta/int2type.h" + +template +struct wxIsStaticTrackable +{ + enum { value = wxConvertibleTo::value }; +}; + +// Weak ref implementation when T has wxTrackable as a known base class +template +class wxWeakRefStatic : public wxTrackerNode +{ +public: + wxWeakRefStatic() : m_pobj(NULL) { } + + void Release() + { + // Release old object if any + if ( m_pobj ) + { + // Remove ourselves from object tracker list + wxTrackable *pt = static_cast(m_pobj); + pt->RemoveNode(this); + m_pobj = NULL; + } + } + +protected: + void Assign(T* pobj) + { + if ( m_pobj == pobj ) + return; + + Release(); + + // Now set new trackable object + if ( pobj ) + { + // Add ourselves to object tracker list + wxTrackable *pt = static_cast(pobj); + pt->AddNode(this); + m_pobj = pobj; + } + } + + void AssignCopy( const wxWeakRefStatic& wr ) + { + Assign( wr.m_pobj ); + } + + virtual void OnObjectDestroy() + { + // Tracked object itself removes us from list of trackers + wxASSERT( m_pobj!=NULL ); + m_pobj = NULL; + } + + T *m_pobj; +}; + +#ifdef HAVE_PARTIAL_SPECIALIZATION + +template +struct wxWeakRefImpl; + +// Intermediate class, to select the static case above. +template +struct wxWeakRefImpl : public wxWeakRefStatic +{ + enum { value = 1 }; +}; + +// Weak ref implementation when T does not have wxTrackable as known base class template -class wxWeakRef : public wxTrackerNode +struct wxWeakRefImpl : public wxTrackerNode +{ + void Release() + { + // Release old object if any + if ( m_pobj ) + { + // Remove ourselves from object tracker list + m_ptbase->RemoveNode(this); + m_pobj = NULL; + m_ptbase = NULL; + } + } + +protected: + wxWeakRefImpl() : m_pobj(NULL), m_ptbase(NULL) { } + + // Assign receives most derived class here and can use that + template + void Assign( TDerived* pobj ) + { + AssignHelper( pobj, wxInt2Type::value>() ); + } + + template + void AssignHelper(TDerived* pobj, wxInt2Type) + { + wxTrackable *ptbase = static_cast(pobj); + DoAssign( pobj, ptbase ); + } + +#ifdef HAVE_DYNAMIC_CAST + void AssignHelper(T* pobj, wxInt2Type) + { + // A last way to get a trackable pointer + wxTrackable *ptbase = dynamic_cast(pobj); + if ( ptbase ) + { + DoAssign( pobj, ptbase ); + } + else + { + wxFAIL_MSG( "Tracked class should inherit from wxTrackable" ); + + Release(); + } + } +#endif // HAVE_DYNAMIC_CAST + + void AssignCopy(const wxWeakRefImpl& wr) + { + DoAssign( wr.m_pobj, wr.m_ptbase ); + } + + void DoAssign( T* pobj, wxTrackable *ptbase ) { + if( m_pobj==pobj ) return; + Release(); + + // Now set new trackable object + if( pobj ) + { + // Add ourselves to object tracker list + wxASSERT( ptbase ); + ptbase->AddNode( this ); + m_pobj = pobj; + m_ptbase = ptbase; + } + } + + virtual void OnObjectDestroy() + { + // Tracked object itself removes us from list of trackers + wxASSERT( m_pobj!=NULL ); + m_pobj = NULL; + m_ptbase = NULL; + } + + T *m_pobj; + wxTrackable *m_ptbase; +}; + +#endif // HAVE_PARTIAL_SPECIALIZATION + + +// A weak reference to an object of type T, where T has type wxTrackable +// (usually statically but if not dynamic_cast<> is tried). +template +class wxWeakRef : public +#ifdef HAVE_PARTIAL_SPECIALIZATION + wxWeakRefImpl::value> +#else + wxWeakRefStatic +#endif { public: - typedef T element_type; - - wxWeakRef(T *pobj = NULL) : m_pobj(NULL) { Assign(pobj); } + // Default ctor + wxWeakRef() { } + + // When we have the full type here, static_cast<> will always work + // (or give a straight compiler error). + template + wxWeakRef(TDerived* pobj) + { + Assign(pobj); + } + + template + wxWeakRef& operator=(TDerived* pobj) + { + Assign(pobj); + return *this; + } + + wxWeakRef& operator=(const wxWeakRef& wr) + { + AssignCopy(wr); + return *this; + } - virtual ~wxWeakRef() { Assign(NULL); } + virtual ~wxWeakRef() { Release(); } + + // Smart pointer functions + T& operator*() const { return *m_pobj; } + T* operator->() const { return m_pobj; } + + T* get() const { return m_pobj; } // test for pointer validity: defining conversion to unspecified_bool_type // and not more obvious bool to avoid implicit conversions to integer types typedef T *(wxWeakRef::*unspecified_bool_type)() const; operator unspecified_bool_type() const { - return m_pobj ? &wxWeakRef::get : NULL; + return this->m_pobj ? &wxWeakRef::get : NULL; } +}; + + +// Weak ref implementation assign objects are queried for wxTrackable +// using dynamic_cast<> +template +class wxWeakRefDynamic : public wxTrackerNode +{ +public: + wxWeakRefDynamic() : m_pobj(NULL) { } - T * get() const - { - return m_pobj; - } - - T* operator->() - { - wxASSERT(m_pobj != NULL); - return m_pobj; - } - - T& operator*() const - { - wxASSERT(m_pobj != NULL); - return *m_pobj; - } - - T* operator=(T *pobj) + wxWeakRefDynamic(T* pobj) : m_pobj(pobj) { Assign(pobj); - return m_pobj; } - // Assign from another weak ref, point to same object - T* operator = (const wxWeakRef &wr) - { - Assign(wr); - return m_pobj; - } - - virtual void OnObjectDestroy() + virtual ~wxWeakRefDynamic() { Release(); } + + // Smart pointer functions + T& operator * (){ wxASSERT(this->m_pobj); return *m_pobj; } + T* operator -> (){ wxASSERT(this->m_pobj); return m_pobj; } + T* operator = (T* pobj) { Assign(pobj); return m_pobj; } + + T* get(){ return this->m_pobj; } + + // operator T* (){ return this->m_pobj; } + + // test for pointer validity: defining conversion to unspecified_bool_type + // and not more obvious bool to avoid implicit conversions to integer types + typedef T *(wxWeakRef::*unspecified_bool_type)() const; + operator unspecified_bool_type() const { - // Tracked object itself removes us from list of trackers - wxASSERT( m_pobj ); - m_pobj = NULL; + return m_pobj ? &wxWeakRef::get : NULL; } -protected: - friend class wxTrackableBase; - friend class wxEvtHandler; + // Assign from another weak ref, point to same object + T* operator = (const wxWeakRef &wr) { Assign( wr.get() ); return this->m_pobj; } - virtual wxTrackerNodeType GetType() { return WeakRef; } - - void Assign(T* pobj) + void Release() { - if ( m_pobj == pobj ) - return; - - // First release old object if any - if ( m_pobj ) + // Release old object if any + if( m_pobj ) { // Remove ourselves from object tracker list - // This does static_cast if available, otherwise it tries dynamic cast - wxTrackableBase *pt = wxTrackableCaster::value >::Cast(m_pobj); + wxTrackable *pt = dynamic_cast(m_pobj); wxASSERT(pt); pt->RemoveNode(this); m_pobj = NULL; } + } + +protected: + void Assign(T *pobj) + { + if ( m_pobj == pobj ) + return; + + Release(); // Now set new trackable object if ( pobj ) { - wxTrackableBase *pt = wxTrackableCaster::value >::Cast(pobj); - if( pt ) + // Add ourselves to object tracker list + wxTrackable *pt = dynamic_cast(pobj); + if ( pt ) { - pt->AddNode( this ); + pt->AddNode(this); m_pobj = pobj; } else { - // If the tracked we want to track does not support wxTackableBase, then - // log a message and keep the NULL object pointer. - wxLogWarning( _T("wxWeakRef::Assign - Type does not provide wxTrackableBase - resetting tracked object") ); + // If the object we want to track does not support wxTackable, then + // log a message and keep the NULL object pointer. + wxFAIL_MSG( "Tracked class should inherit from wxTrackable" ); } } } + void AssignCopy(const wxWeakRefDynamic& wr) + { + Assign(wr.m_pobj); + } + + virtual void OnObjectDestroy() + { + wxASSERT_MSG( m_pobj, "tracked object should have removed us itself" ); + + m_pobj = NULL; + } + T *m_pobj; }; // Provide some basic types of weak references -class WXDLLIMPEXP_FWD_BASE wxObject; class WXDLLIMPEXP_FWD_BASE wxEvtHandler; -class WXDLLIMPEXP_FWD_CORE wxWindow; +class WXDLLIMPEXP_FWD_BASE wxWindow; -typedef wxWeakRef wxObjectRef; typedef wxWeakRef wxEvtHandlerRef; typedef wxWeakRef wxWindowRef; diff --git a/src/common/event.cpp b/src/common/event.cpp index 4ca845660e..a03b9fe1b9 100644 --- a/src/common/event.cpp +++ b/src/common/event.cpp @@ -406,13 +406,15 @@ wxCommandEvent::wxCommandEvent(wxEventType commandType, int theId) wxString wxCommandEvent::GetString() const { - if(m_eventType != wxEVT_COMMAND_TEXT_UPDATED || !m_eventObject) + if (m_eventType != wxEVT_COMMAND_TEXT_UPDATED || !m_eventObject) + { return m_cmdString; + } else { #if wxUSE_TEXTCTRL wxTextCtrl *txt = wxDynamicCast(m_eventObject, wxTextCtrl); - if(txt) + if ( txt ) return txt->GetValue(); else #endif // wxUSE_TEXTCTRL @@ -1012,44 +1014,6 @@ void wxEventHashTable::GrowEventTypeTable() delete[] oldEventTypeTable; } - -// ---------------------------------------------------------------------------- -// wxEventConnectionRef -// ---------------------------------------------------------------------------- - -// Below functions are mostly short but kept in cpp file to simplify setting -// breakpoints (GDB) -wxEventConnectionRef::wxEventConnectionRef(wxEvtHandler *src, wxEvtHandler *sink) - : m_src(src), m_sink(sink), m_refCount(1) -{ - wxASSERT( m_sink ); - m_sink->AddNode( this ); -} - -wxEventConnectionRef::~wxEventConnectionRef() -{ -} - -void wxEventConnectionRef::OnObjectDestroy( ) -{ - if( m_src ) - m_src->OnSinkDestroyed( m_sink ); - // It is safe to delete this tracker object here - delete this; -} - -void wxEventConnectionRef::DecRef( ) -{ - if( !--m_refCount ) - { - // The sink holds the only external pointer to this object - if( m_sink ) - m_sink->RemoveNode(this); - delete this; - } -} - - // ---------------------------------------------------------------------------- // wxEvtHandler // ---------------------------------------------------------------------------- @@ -1089,19 +1053,20 @@ wxEvtHandler::~wxEvtHandler() { wxDynamicEventTableEntry *entry = (wxDynamicEventTableEntry*)*it; - // Remove ourselves from sink destructor notifications + // Remove ourselves from sink destructor notifications // (this has usually been been done, in wxTrackable destructor) wxEvtHandler *eventSink = entry->m_eventSink; - if( eventSink ) + if ( eventSink ) { - wxEventConnectionRef *pecr = FindRefInTrackerList( eventSink ); - if( pecr ) - { - eventSink->RemoveNode( pecr ); - delete pecr; - } + wxEventConnectionRef * const + evtConnRef = FindRefInTrackerList(eventSink); + if ( evtConnRef ) + { + eventSink->RemoveNode(evtConnRef); + delete evtConnRef; + } } - + if (entry->m_callbackUserData) delete entry->m_callbackUserData; delete entry; @@ -1117,7 +1082,7 @@ wxEvtHandler::~wxEvtHandler() if ( wxPendingEvents ) { #if wxUSE_THREADS - if(wxPendingEventsLocker) + if (wxPendingEventsLocker) wxENTER_CRIT_SECT(*wxPendingEventsLocker); #endif @@ -1130,7 +1095,7 @@ wxEvtHandler::~wxEvtHandler() //else: we weren't in this list at all, it's ok #if wxUSE_THREADS - if(wxPendingEventsLocker) + if (wxPendingEventsLocker) wxLEAVE_CRIT_SECT(*wxPendingEventsLocker); #endif } @@ -1401,15 +1366,15 @@ void wxEvtHandler::Connect( int id, int lastId, // Insert at the front of the list so most recent additions are found first m_dynamicEvents->Insert( (wxObject*) entry ); - - // Make sure we get to know when a sink is destroyed - if( eventSink ) + + // Make sure we get to know when a sink is destroyed + if ( eventSink ) { - wxEventConnectionRef *pecr = FindRefInTrackerList( eventSink ); - if( pecr ) - pecr->IncRef( ); + wxEventConnectionRef *evtConnRef = FindRefInTrackerList(eventSink); + if ( evtConnRef ) + evtConnRef->IncRef( ); else - pecr = new wxEventConnectionRef(this,eventSink); + evtConnRef = new wxEventConnectionRef(this, eventSink); } } @@ -1422,11 +1387,11 @@ bool wxEvtHandler::Disconnect( int id, int lastId, wxEventType eventType, return false; // Remove connection from tracker node (wxEventConnectionRef) - if( eventSink ) + if ( eventSink ) { - wxEventConnectionRef *pecr = FindRefInTrackerList( eventSink ); - if( pecr ) - pecr->DecRef( ); + wxEventConnectionRef *evtConnRef = FindRefInTrackerList(eventSink); + if ( evtConnRef ) + evtConnRef->DecRef(); } wxList::compatibility_iterator node = m_dynamicEvents->GetFirst(); @@ -1519,37 +1484,36 @@ void *wxEvtHandler::DoGetClientData() const return m_clientData; } -// A helper func to find an wxEventConnectionRef object -wxEventConnectionRef* wxEvtHandler::FindRefInTrackerList( wxEvtHandler *eventSink ) +// A helper to find an wxEventConnectionRef object +wxEventConnectionRef * +wxEvtHandler::FindRefInTrackerList(wxEvtHandler *eventSink) { - wxASSERT(eventSink); - for( wxTrackerNode *ptn=eventSink->GetFirst(); ptn; ptn=ptn->m_nxt ) + for ( wxTrackerNode *node = eventSink->GetFirst(); node; node = node->m_nxt ) { - // Only want wxEventConnectionRef nodes here - if( ptn->GetType()!=wxTrackerNode::EventConnectionRef ) - continue; - wxEventConnectionRef *pecr = static_cast(ptn); - if( pecr && pecr->m_src==this ) + // we only want wxEventConnectionRef nodes here + wxEventConnectionRef *evtConnRef = node->ToEventConnection(); + if ( evtConnRef && evtConnRef->m_src == this ) { - wxASSERT( pecr->m_sink==eventSink ); - return pecr; + wxASSERT( evtConnRef->m_sink==eventSink ); + return evtConnRef; } } + return NULL; } void wxEvtHandler::OnSinkDestroyed( wxEvtHandler *sink ) { wxASSERT(m_dynamicEvents); - + // remove all connections with this sink wxList::compatibility_iterator node = m_dynamicEvents->GetFirst(), node_nxt; while (node) { wxDynamicEventTableEntry *entry = (wxDynamicEventTableEntry*)node->GetData(); node_nxt = node->GetNext(); - - if( entry->m_eventSink==sink ) + + if ( entry->m_eventSink==sink ) { if (entry->m_callbackUserData) delete entry->m_callbackUserData; diff --git a/tests/test.bkl b/tests/test.bkl index 7718e6c2e0..68132e086d 100644 --- a/tests/test.bkl +++ b/tests/test.bkl @@ -70,6 +70,8 @@ thread/queue.cpp uris/uris.cpp vectors/vectors.cpp + weakref/evtconnection.cpp + weakref/weakref.cpp net base diff --git a/tests/weakref/evtconnection.cpp b/tests/weakref/evtconnection.cpp new file mode 100644 index 0000000000..3d9db0421b --- /dev/null +++ b/tests/weakref/evtconnection.cpp @@ -0,0 +1,247 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: tests/weakref/evtconnection.cpp +// Purpose: wxWeakRef unit test +// Author: Arne Steinarson +// Created: 2008-01-10 +// RCS-ID: $Id$ +// Copyright: (c) 2007 Arne Steinarson +/////////////////////////////////////////////////////////////////////////////// + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +#include "testprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif // WX_PRECOMP + +#include "wx/event.h" +#include "wx/weakref.h" + +static int gs_value = 0; // Increased by 1 by first object and 0x10000 by 2nd object + +static wxObject *gs_psrc1; +static wxObject *gs_psrc2; + +// We need some event types +const wxEventType wxEVT_TEST = wxNewEventType(), + wxEVT_TEST1 = wxNewEventType(), + wxEVT_TEST2 = wxNewEventType(); + +class wxTestEvent : public wxEvent +{ +public: + wxTestEvent(wxEventType type = wxEVT_TEST) : wxEvent(0, type) { } + virtual wxEvent *Clone() const { return new wxTestEvent(GetEventType()); } +}; + +class wxTestSink : public wxEvtHandler +{ +public: + void OnTestEvent(wxEvent& evt) + { + if ( evt.GetEventObject() == gs_psrc1 ) + gs_value += 1; + else if ( evt.GetEventObject() == gs_psrc2 ) + gs_value += 0x10000; + } + + void OnTestEvent1(wxEvent& ) + { + gs_value += 0x00100; + } + + void OnTestEvent2(wxEvent&) + { + gs_value += 0x01000000; + } +}; + + + +// -------------------------------------------------------------------------- +// test class +// -------------------------------------------------------------------------- + +class EvtConnectionTestCase : public CppUnit::TestCase +{ +public: + EvtConnectionTestCase() {} + +private: + CPPUNIT_TEST_SUITE( EvtConnectionTestCase ); + CPPUNIT_TEST( SinkTest ); + CPPUNIT_TEST( SourceDestroyTest ); + CPPUNIT_TEST( MultiConnectionTest ); + CPPUNIT_TEST_SUITE_END(); + + void SinkTest(); + void SourceDestroyTest(); + void MultiConnectionTest(); + + DECLARE_NO_COPY_CLASS(EvtConnectionTestCase) +}; + +// register in the unnamed registry so that these tests are run by default +CPPUNIT_TEST_SUITE_REGISTRATION( EvtConnectionTestCase ); + +// also include in it's own registry so that these tests can be run alone +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( EvtConnectionTestCase, "EvtConnectionTestCase" ); + + +// Helpers +void DoConnect( wxEvtHandler& eh1, wxEvtHandler& eh2, wxTestSink& ts ){ + eh1.Connect(wxEVT_TEST, (wxObjectEventFunction)&wxTestSink::OnTestEvent, + NULL, &ts); + eh2.Connect(wxEVT_TEST, (wxObjectEventFunction) &wxTestSink::OnTestEvent, + NULL, &ts); +} + +void DoDisconnect( wxEvtHandler& eh1, wxEvtHandler& eh2, wxTestSink& ts ){ + eh1.Disconnect(wxEVT_TEST, (wxObjectEventFunction) &wxTestSink::OnTestEvent, + NULL, &ts); + eh2.Disconnect(wxEVT_TEST, (wxObjectEventFunction) &wxTestSink::OnTestEvent, + NULL, &ts); +} + + +void EvtConnectionTestCase::SinkTest() +{ + // Let the sink be destroyed before the sources + + // An event used below + wxTestEvent evt; + + // Connect two event handlers to one sink + wxEvtHandler eh1, eh2; + gs_psrc1 = &eh1; + gs_psrc2 = &eh2; + + { + wxTestSink ts; + CPPUNIT_ASSERT( !ts.GetFirst() ); + DoConnect(eh1, eh2, ts); + + DoDisconnect(eh1, eh2, ts); + + DoConnect(eh1, eh2, ts); + + // Fire events + evt.SetEventObject(&eh1); + eh1.ProcessEvent(evt); + evt.SetEventObject(&eh2); + eh2.ProcessEvent(evt); + + // Make sure they were processed correctly + CPPUNIT_ASSERT_EQUAL( 0x00010001, gs_value ); + } + + // Fire events again, should be no sink connected now + gs_value = 0; + evt.SetEventObject(&eh1); + eh1.ProcessEvent( evt ); + evt.SetEventObject(&eh2); + eh2.ProcessEvent( evt ); + + // Make sure no processing happened + CPPUNIT_ASSERT_EQUAL( 0, gs_value ); +} + +void EvtConnectionTestCase::SourceDestroyTest() +{ + // Let the sources be destroyed before the sink + wxTestSink ts; + wxTestEvent evt; + { + wxEvtHandler eh1; + { + CPPUNIT_ASSERT( !ts.GetFirst() ); + + // Connect two event handlers to one sink + wxEvtHandler eh2; + gs_psrc1 = &eh1; + gs_psrc2 = &eh2; + DoConnect( eh1, eh2, ts ); + + // Fire events + gs_value = 0; + evt.SetEventObject(&eh1); + eh1.ProcessEvent( evt ); + evt.SetEventObject(&eh2); + eh2.ProcessEvent( evt ); + + // Make sure they were processed correctly + CPPUNIT_ASSERT_EQUAL( 0x00010001, gs_value ); + } + + gs_value = 0; + evt.SetEventObject(&eh1); + eh1.ProcessEvent( evt ); + + // Make sure still connected + CPPUNIT_ASSERT_EQUAL( 0x00000001, gs_value ); + } + CPPUNIT_ASSERT( !ts.GetFirst() ); +} + +void EvtConnectionTestCase::MultiConnectionTest() +{ + // events used below + wxTestEvent evt; + wxTestEvent evt1(wxEVT_TEST1); + wxTestEvent evt2(wxEVT_TEST2); + + // One source + wxEvtHandler eh1; + evt.SetEventObject(&eh1); + gs_psrc1 = NULL; + gs_psrc2 = &eh1; + + { + // ...and one sink + wxTestSink ts; + + eh1.Connect(wxEVT_TEST, (wxObjectEventFunction)&wxTestSink::OnTestEvent, + NULL, &ts); + eh1.Connect(wxEVT_TEST1, (wxObjectEventFunction)&wxTestSink::OnTestEvent1, + NULL, &ts); + eh1.Connect(wxEVT_TEST2, (wxObjectEventFunction)&wxTestSink::OnTestEvent2, + NULL, &ts); + + // Generate events + gs_value = 0; + eh1.ProcessEvent(evt); + eh1.ProcessEvent(evt1); + eh1.ProcessEvent(evt2); + CPPUNIT_ASSERT( gs_value==0x01010100 ); + + { + // Declare weak references to the objects (using same list) + wxEvtHandlerRef re(&eh1), rs(&ts); + } + // And now destroyed + + eh1.Disconnect(wxEVT_TEST, (wxObjectEventFunction)&wxTestSink::OnTestEvent, + NULL, &ts); + eh1.ProcessEvent(evt); + eh1.ProcessEvent(evt1); + eh1.ProcessEvent(evt2); + CPPUNIT_ASSERT_EQUAL( 0x02010200, gs_value ); + } + + // No connection should be left now + gs_value = 0; + eh1.ProcessEvent(evt); + eh1.ProcessEvent(evt1); + eh1.ProcessEvent(evt2); + + // Nothing should have been done + CPPUNIT_ASSERT_EQUAL( 0, gs_value ); +} + diff --git a/tests/weakref/weakref.cpp b/tests/weakref/weakref.cpp new file mode 100644 index 0000000000..f2a9c79d04 --- /dev/null +++ b/tests/weakref/weakref.cpp @@ -0,0 +1,281 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: tests/weakref/weakref.cpp +// Purpose: wxWeakRef unit test +// Author: Arne Steinarson +// Created: 2008-01-10 +// RCS-ID: $Id$ +// Copyright: (c) 2007 Arne Steinarson +/////////////////////////////////////////////////////////////////////////////// + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +#include "testprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif // WX_PRECOMP + +#include "wx/event.h" +#include "wx/weakref.h" + +// A statically trackable derived wxObject +class wxObjectTrackable : public wxObject, public wxTrackable +{ +public: + // Test member access + void TestFunc(){ } + + // Make sure this does not clash with wxTrackableBase method + int GetFirst() { return 0; } +}; + +// -------------------------------------------------------------------------- +// test class +// -------------------------------------------------------------------------- + +class WeakRefTestCase : public CppUnit::TestCase +{ +public: + WeakRefTestCase() {} + +private: + CPPUNIT_TEST_SUITE( WeakRefTestCase ); + CPPUNIT_TEST( DeclareTest ); + CPPUNIT_TEST( AssignTest ); + CPPUNIT_TEST( AssignWeakRefTest ); + CPPUNIT_TEST( MultiAssignTest ); + CPPUNIT_TEST( CleanupTest ); + CPPUNIT_TEST( DeleteTest ); +#ifdef HAVE_DYNAMIC_CAST + CPPUNIT_TEST( DynamicRefTest ); +#endif + CPPUNIT_TEST_SUITE_END(); + + void DeclareTest(); + void AssignTest(); + void AssignWeakRefTest(); + void MultiAssignTest(); + void CleanupTest(); + void DeleteTest(); +#ifdef HAVE_DYNAMIC_CAST + void DynamicRefTest(); +#endif + + DECLARE_NO_COPY_CLASS(WeakRefTestCase) +}; + +// register in the unnamed registry so that these tests are run by default +CPPUNIT_TEST_SUITE_REGISTRATION( WeakRefTestCase ); + +// also include in it's own registry so that these tests can be run alone +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( WeakRefTestCase, "WeakRefTestCase" ); + +void WeakRefTestCase::DeclareTest() +{ + { + wxObject o; // Should not work + wxEvtHandler eh; + wxObjectTrackable ot; + + // Test declare when T is wxObject + // wxWeakRef wro1(&o); // Gives compile time failure + wxWeakRef wro2(&eh); + wxWeakRef wro3(&ot); + + CPPUNIT_ASSERT( wro2.get() == &eh ); + CPPUNIT_ASSERT( wro3.get() == &ot ); + + // Test accessing wxObject members + CPPUNIT_ASSERT( !wro2->GetRefData() ); + CPPUNIT_ASSERT( !wro3->GetRefData() ); + + + wxWeakRef wreh(&eh); + wxWeakRef wrot(&ot); + + CPPUNIT_ASSERT( wreh.get() == &eh ); + CPPUNIT_ASSERT( wrot.get() == &ot ); + } +} + +void WeakRefTestCase::AssignTest() +{ + wxWeakRef wro1; + wxWeakRef wro2; + + { // Scope for object destruction + wxEvtHandler eh; + wxObjectTrackable ot; + + wro1 = &eh; + wro2 = &ot; + + CPPUNIT_ASSERT( wro1.get() == &eh ); + CPPUNIT_ASSERT( wro2.get() == &ot ); + } + + // Should be reset now + CPPUNIT_ASSERT( !wro1 ); + CPPUNIT_ASSERT( !wro2 ); +} + +void WeakRefTestCase::AssignWeakRefTest() +{ + // Test declare when T is wxObject + wxWeakRef wro1; + wxWeakRef wro2; + + { // Scope for object destruction + wxEvtHandler eh; + wxObjectTrackable ot; + wxWeakRef wro3; + wxWeakRef wro4; + + wro1 = &eh; + wro2 = &ot; + wro3 = wro1; + wro4 = wro2; + + CPPUNIT_ASSERT( wro1.get() == &eh ); + CPPUNIT_ASSERT( wro2.get() == &ot ); + CPPUNIT_ASSERT( wro3.get() == &eh ); + CPPUNIT_ASSERT( wro4.get() == &ot ); + + wro4.Release(); + CPPUNIT_ASSERT( !wro4.get() ); + } + + // Should be reset now + CPPUNIT_ASSERT( !wro1 ); + CPPUNIT_ASSERT( !wro2 ); +} + +void WeakRefTestCase::MultiAssignTest() +{ + // Object is tracked by several refs + wxEvtHandler *peh = new wxEvtHandler; + + // Test declare when T is wxObject + wxWeakRef wro1(peh); + wxWeakRef wro2(peh); + + wxObjectTrackable *pot = new wxObjectTrackable; + wxWeakRef wro3 = pot; + wxWeakRef wro4 = pot; + + CPPUNIT_ASSERT( wro1.get() == peh ); + CPPUNIT_ASSERT( wro2.get() == peh ); + CPPUNIT_ASSERT( wro3.get() == pot ); + CPPUNIT_ASSERT( wro4.get() == pot ); + + delete peh; + delete pot; + + // Should be reset now + CPPUNIT_ASSERT( !wro1 ); + CPPUNIT_ASSERT( !wro2 ); + CPPUNIT_ASSERT( !wro3 ); + CPPUNIT_ASSERT( !wro4 ); +} + +void WeakRefTestCase::CleanupTest() +{ + // Make sure that trackable objects have no left over tracker nodes after use. + // This time the references goes out of scope before the objects. + wxEvtHandler eh; + wxObjectTrackable ots; + wxObjectTrackable otd; + + { // Scope for object destruction + wxWeakRef wro1; + wxWeakRef wro2; + wxWeakRef wro3; + wxWeakRef wro4; + + wro1 = &eh; + wro2 = &eh; // Has two tracker nodes now + wro3 = &ots; + wro4 = &otd; + + // Access members of reffed object + wro3->TestFunc(); + + CPPUNIT_ASSERT( eh.GetFirst()==&wro2 ); + CPPUNIT_ASSERT( ots.wxTrackable::GetFirst()==&wro3 ); + CPPUNIT_ASSERT( otd.wxTrackable::GetFirst()==&wro4 ); + } + + // Should be reset now + CPPUNIT_ASSERT( !eh.GetFirst() ); + CPPUNIT_ASSERT( !ots.wxTrackable::GetFirst() ); + CPPUNIT_ASSERT( !otd.wxTrackable::GetFirst() ); +} + +void WeakRefTestCase::DeleteTest() +{ + // Object is tracked by several refs + wxEvtHandler *peh = new wxEvtHandler; + + // Declared derived type of object and test deleting it + wxEvtHandlerRef wre(peh); + wxWeakRef wro(peh); + + // test size of references (see that it has selected right base class) +#ifdef HAVE_PARTIAL_SPECIALIZATION + CPPUNIT_ASSERT_EQUAL( sizeof(void*)*3, sizeof(wre) ); + CPPUNIT_ASSERT_EQUAL( sizeof(void*)*4, sizeof(wro) ); +#else + CPPUNIT_ASSERT_EQUAL( sizeof(void*)*3, sizeof(wre) ); + CPPUNIT_ASSERT_EQUAL( sizeof(void*)*3, sizeof(wro) ); +#endif + + CPPUNIT_ASSERT( wre.get() == peh ); + CPPUNIT_ASSERT( wro.get() == peh ); + + delete wre.get(); + + CPPUNIT_ASSERT( !wre ); + CPPUNIT_ASSERT( !wro ); +} + +#ifdef HAVE_DYNAMIC_CAST + +void WeakRefTestCase::DynamicRefTest() +{ + wxWeakRefDynamic wro1; + wxWeakRefDynamic wro2; + wxWeakRefDynamic wro3; + + { // Scope for object destruction + { + wxEvtHandler eh; + wro1 = &eh; + } + + CPPUNIT_ASSERT( !wro1 ); + + wxObjectTrackable otd1; + wxObjectTrackable otd2; + wro2 = &otd1; + wro3 = &otd2; + + CPPUNIT_ASSERT( wro2.get() == &otd1 ); + CPPUNIT_ASSERT( wro3.get() == &otd2 ); + + wro3 = wro2; + CPPUNIT_ASSERT( wro2.get() == &otd1 ); + CPPUNIT_ASSERT( wro3.get() == &otd1 ); + } + + // Should be reset now + CPPUNIT_ASSERT( !wro2 ); + CPPUNIT_ASSERT( !wro3 ); +} + +#endif // HAVE_DYNAMIC_CAST -- 2.45.2