improvements to wxWeakRef and related classes
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@51187 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
parent
0fb0ecc452
commit
cc6ceca789
@ -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
|
||||
|
@ -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}
|
||||
|
||||
<tracker.h>
|
||||
|
||||
\wxheading{Data structures}
|
||||
|
||||
|
||||
|
@ -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<T> 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<T> 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<T> is a small object and the mechanism behind it is fast
|
||||
wxWeakRef<T> 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<T> is a small object and the mechanism behind it is fast
|
||||
wxASSERT( wr==NULL );
|
||||
\end{verbatim}
|
||||
|
||||
wxWeakref<T> 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<T> 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<wxObject>}. 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<wxObject> wxObjectRef;
|
||||
typedef wxWeakRef<wxEvtHandler> wxEvtHandlerRef;
|
||||
typedef wxWeakRef<wxWindow> wxWindowRef;
|
||||
\end{verbatim}
|
||||
@ -134,7 +126,7 @@ method will cause an assert in debug mode.
|
||||
|
||||
\membersection{wxWeakRef<T>::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<T>::operator =}\label{wxweakrefoperatorassign2}
|
||||
|
||||
\func{T* operator}{operator =}{\param{wxWeakRef<T>\& }{wr}}
|
||||
\func{T*}{operator =}{\param{wxWeakRef<T>\& }{wr}}
|
||||
|
||||
Release currently tracked object and start tracking the same object as
|
||||
the wxWeakRef {\it wr}.
|
||||
|
||||
|
||||
\membersection{wxWeakRef<T>::Release}\label{wxweakrefrelease}
|
||||
|
||||
\func{void}{Release}{\void}
|
||||
|
||||
Release currently tracked object and rests object reference.
|
||||
|
||||
|
||||
\membersection{wxWeakRef<T>::OnObjectDestroy}\label{wxweakrefonobjectdestroy}
|
||||
|
||||
\func{virtual void}{OnObjectDestroy}{\void}
|
||||
|
20
docs/latex/wx/weakrefdynamic.tex
Normal file
20
docs/latex/wx/weakrefdynamic.tex
Normal file
@ -0,0 +1,20 @@
|
||||
\section{\class{wxWeakRefDynamic<T>}}\label{wxweakrefdynamic}
|
||||
|
||||
wxWeakRefDynamic<T> is a template class for weak references that is used in
|
||||
the same way as wxWeakRef<T>. 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<T> 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<T> is the better choice.
|
||||
|
||||
For API documentation, see: \helpref{wxWeakRef}{wxweakref}
|
||||
|
@ -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 */
|
||||
/* ---------------------------------------------------------------------------- */
|
||||
|
@ -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)
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Name: wx/tracker.h
|
||||
// Purpose: Support class for object lifetime tracking (wxWeakRef<T>)
|
||||
// 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 );
|
||||
|
||||
wxFAIL_MSG( "removing invalid tracker node" );
|
||||
}
|
||||
|
||||
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 *GetFirst() const { return m_first; }
|
||||
|
||||
protected:
|
||||
wxTrackerNode *m_first;
|
||||
|
||||
DECLARE_NO_COPY_CLASS(wxTrackable)
|
||||
};
|
||||
|
||||
|
||||
// The difference to wxTrackableBase is that this class adds
|
||||
// a VTable to enable dynamic_cast query for wxTrackable.
|
||||
struct wxTrackable : public wxTrackableBase
|
||||
{
|
||||
virtual ~wxTrackable(){ }
|
||||
};
|
||||
|
||||
|
||||
// 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<class T, class B>
|
||||
struct wxHasBase{
|
||||
static char Match( B* pb );
|
||||
static int Match( ... );
|
||||
enum { value = (sizeof(Match((T*)NULL))==sizeof(char)) };
|
||||
};
|
||||
|
||||
// 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<class T,bool is_static>
|
||||
struct wxTrackableCaster;
|
||||
|
||||
template <class T>
|
||||
struct wxTrackableCaster<T,true> {
|
||||
static wxTrackableBase* Cast(T* pt){ return static_cast<wxTrackableBase*>(pt); }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct wxTrackableCaster<T,false> {
|
||||
static wxTrackableBase* Cast(T* pt){ return dynamic_cast<wxTrackableBase*>(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 <class T,bool is_static>
|
||||
struct wxTrackableCaster {
|
||||
static wxTrackableBase* Cast(T* pt){ return dynamic_cast<wxTrackableBase*>(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 <class T,bool is_static>
|
||||
struct wxTrackableCaster {
|
||||
static wxTrackableBase* Cast(T* pt){ return static_cast<wxTrackableBase*>(pt); }
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // _WX_TRACKER_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,19 +11,243 @@
|
||||
#ifndef _WX_WEAKREF_H_
|
||||
#define _WX_WEAKREF_H_
|
||||
|
||||
#include <wx/tracker.h>
|
||||
#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).
|
||||
template<class T>
|
||||
class wxWeakRef : public wxTrackerNode
|
||||
#include "wx/meta/convertible.h"
|
||||
#include "wx/meta/int2type.h"
|
||||
|
||||
template <class T>
|
||||
struct wxIsStaticTrackable
|
||||
{
|
||||
enum { value = wxConvertibleTo<T, wxTrackable>::value };
|
||||
};
|
||||
|
||||
// Weak ref implementation when T has wxTrackable as a known base class
|
||||
template <class T>
|
||||
class wxWeakRefStatic : public wxTrackerNode
|
||||
{
|
||||
public:
|
||||
typedef T element_type;
|
||||
|
||||
wxWeakRef(T *pobj = NULL) : m_pobj(NULL) { Assign(pobj); }
|
||||
wxWeakRefStatic() : m_pobj(NULL) { }
|
||||
|
||||
virtual ~wxWeakRef() { Assign(NULL); }
|
||||
void Release()
|
||||
{
|
||||
// Release old object if any
|
||||
if ( m_pobj )
|
||||
{
|
||||
// Remove ourselves from object tracker list
|
||||
wxTrackable *pt = static_cast<wxTrackable*>(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<wxTrackable*>(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<class T,bool use_static>
|
||||
struct wxWeakRefImpl;
|
||||
|
||||
// Intermediate class, to select the static case above.
|
||||
template <class T>
|
||||
struct wxWeakRefImpl<T, true> : public wxWeakRefStatic<T>
|
||||
{
|
||||
enum { value = 1 };
|
||||
};
|
||||
|
||||
// Weak ref implementation when T does not have wxTrackable as known base class
|
||||
template<class T>
|
||||
struct wxWeakRefImpl<T, false> : 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 <class TDerived>
|
||||
void Assign( TDerived* pobj )
|
||||
{
|
||||
AssignHelper( pobj, wxInt2Type<wxIsStaticTrackable<TDerived>::value>() );
|
||||
}
|
||||
|
||||
template <class TDerived>
|
||||
void AssignHelper(TDerived* pobj, wxInt2Type<true>)
|
||||
{
|
||||
wxTrackable *ptbase = static_cast<wxTrackable*>(pobj);
|
||||
DoAssign( pobj, ptbase );
|
||||
}
|
||||
|
||||
#ifdef HAVE_DYNAMIC_CAST
|
||||
void AssignHelper(T* pobj, wxInt2Type<false>)
|
||||
{
|
||||
// A last way to get a trackable pointer
|
||||
wxTrackable *ptbase = dynamic_cast<wxTrackable*>(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 T>
|
||||
class wxWeakRef : public
|
||||
#ifdef HAVE_PARTIAL_SPECIALIZATION
|
||||
wxWeakRefImpl<T, wxIsStaticTrackable<T>::value>
|
||||
#else
|
||||
wxWeakRefStatic<T>
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
// Default ctor
|
||||
wxWeakRef() { }
|
||||
|
||||
// When we have the full type here, static_cast<> will always work
|
||||
// (or give a straight compiler error).
|
||||
template <class TDerived>
|
||||
wxWeakRef(TDerived* pobj)
|
||||
{
|
||||
Assign(pobj);
|
||||
}
|
||||
|
||||
template <class TDerived>
|
||||
wxWeakRef<T>& operator=(TDerived* pobj)
|
||||
{
|
||||
Assign(pobj);
|
||||
return *this;
|
||||
}
|
||||
|
||||
wxWeakRef<T>& operator=(const wxWeakRef<T>& wr)
|
||||
{
|
||||
AssignCopy(wr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
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<T>::*unspecified_bool_type)() const;
|
||||
operator unspecified_bool_type() const
|
||||
{
|
||||
return this->m_pobj ? &wxWeakRef<T>::get : NULL;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Weak ref implementation assign objects are queried for wxTrackable
|
||||
// using dynamic_cast<>
|
||||
template<class T>
|
||||
class wxWeakRefDynamic : public wxTrackerNode
|
||||
{
|
||||
public:
|
||||
wxWeakRefDynamic() : m_pobj(NULL) { }
|
||||
|
||||
wxWeakRefDynamic(T* pobj) : m_pobj(pobj)
|
||||
{
|
||||
Assign(pobj);
|
||||
}
|
||||
|
||||
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
|
||||
@ -33,92 +257,68 @@ public:
|
||||
return m_pobj ? &wxWeakRef<T>::get : 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)
|
||||
{
|
||||
Assign(pobj);
|
||||
return m_pobj;
|
||||
}
|
||||
|
||||
// Assign from another weak ref, point to same object
|
||||
T* operator = (const wxWeakRef<T> &wr)
|
||||
{
|
||||
Assign(wr);
|
||||
return m_pobj;
|
||||
}
|
||||
|
||||
virtual void OnObjectDestroy()
|
||||
T* operator = (const wxWeakRef<T> &wr) { Assign( wr.get() ); return this->m_pobj; }
|
||||
|
||||
void Release()
|
||||
{
|
||||
// Tracked object itself removes us from list of trackers
|
||||
wxASSERT( m_pobj );
|
||||
m_pobj = NULL;
|
||||
}
|
||||
|
||||
protected:
|
||||
friend class wxTrackableBase;
|
||||
friend class wxEvtHandler;
|
||||
|
||||
virtual wxTrackerNodeType GetType() { return WeakRef; }
|
||||
|
||||
void Assign(T* pobj)
|
||||
{
|
||||
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<T,wxHasBase<T,wxTrackableBase>::value >::Cast(m_pobj);
|
||||
wxTrackable *pt = dynamic_cast<wxTrackable*>(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<T,wxHasBase<T,wxTrackableBase>::value >::Cast(pobj);
|
||||
if( pt )
|
||||
// Add ourselves to object tracker list
|
||||
wxTrackable *pt = dynamic_cast<wxTrackable*>(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<wxObject> wxObjectRef;
|
||||
typedef wxWeakRef<wxEvtHandler> wxEvtHandlerRef;
|
||||
typedef wxWeakRef<wxWindow> wxWindowRef;
|
||||
|
||||
|
@ -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<wxEventConnectionRef*>(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;
|
||||
|
@ -70,6 +70,8 @@
|
||||
thread/queue.cpp
|
||||
uris/uris.cpp
|
||||
vectors/vectors.cpp
|
||||
weakref/evtconnection.cpp
|
||||
weakref/weakref.cpp
|
||||
</sources>
|
||||
<wx-lib>net</wx-lib>
|
||||
<wx-lib>base</wx-lib>
|
||||
|
247
tests/weakref/evtconnection.cpp
Normal file
247
tests/weakref/evtconnection.cpp
Normal file
@ -0,0 +1,247 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Name: tests/weakref/evtconnection.cpp
|
||||
// Purpose: wxWeakRef<T> 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 );
|
||||
}
|
||||
|
281
tests/weakref/weakref.cpp
Normal file
281
tests/weakref/weakref.cpp
Normal file
@ -0,0 +1,281 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Name: tests/weakref/weakref.cpp
|
||||
// Purpose: wxWeakRef<T> 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<wxObject> wro1(&o); // Gives compile time failure
|
||||
wxWeakRef<wxEvtHandler> wro2(&eh);
|
||||
wxWeakRef<wxObjectTrackable> 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<wxEvtHandler> wreh(&eh);
|
||||
wxWeakRef<wxObjectTrackable> wrot(&ot);
|
||||
|
||||
CPPUNIT_ASSERT( wreh.get() == &eh );
|
||||
CPPUNIT_ASSERT( wrot.get() == &ot );
|
||||
}
|
||||
}
|
||||
|
||||
void WeakRefTestCase::AssignTest()
|
||||
{
|
||||
wxWeakRef<wxEvtHandler> wro1;
|
||||
wxWeakRef<wxObjectTrackable> 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<wxEvtHandler> wro1;
|
||||
wxWeakRef<wxObjectTrackable> wro2;
|
||||
|
||||
{ // Scope for object destruction
|
||||
wxEvtHandler eh;
|
||||
wxObjectTrackable ot;
|
||||
wxWeakRef<wxEvtHandler> wro3;
|
||||
wxWeakRef<wxObjectTrackable> 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<wxEvtHandler> wro1(peh);
|
||||
wxWeakRef<wxEvtHandler> wro2(peh);
|
||||
|
||||
wxObjectTrackable *pot = new wxObjectTrackable;
|
||||
wxWeakRef<wxObjectTrackable> wro3 = pot;
|
||||
wxWeakRef<wxObjectTrackable> 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<wxEvtHandler> wro1;
|
||||
wxWeakRef<wxEvtHandler> wro2;
|
||||
wxWeakRef<wxObjectTrackable> wro3;
|
||||
wxWeakRef<wxObjectTrackable> 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<wxEvtHandler> 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<wxEvtHandler> wro1;
|
||||
wxWeakRefDynamic<wxObjectTrackable> wro2;
|
||||
wxWeakRefDynamic<wxObjectTrackable> 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
|
Loading…
Reference in New Issue
Block a user