Skip to content

Commit

Permalink
Merge pull request #30155 from m-kuhn/qobjectuniqueptr
Browse files Browse the repository at this point in the history
QObjectUniquePtr
  • Loading branch information
m-kuhn committed Jun 11, 2019
2 parents a9e3950 + 3f136b8 commit b34539a
Show file tree
Hide file tree
Showing 4 changed files with 380 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -979,6 +979,7 @@ SET(QGIS_CORE_HDRS
qgstilecache.h
qgstracer.h
qgstranslationcontext.h
qobjectuniqueptr.h

qgsvectordataprovider.h
qgsvectorlayercache.h
Expand Down
251 changes: 251 additions & 0 deletions src/core/qobjectuniqueptr.h
@@ -0,0 +1,251 @@
/***************************************************************************
qobjectuniqueptr.h
A unique pointer to a QObject.
-------------------
begin : June 2019
copyright : (C) 2009 by Matthias Kuhn
email : matthias@opengis.ch
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QOBJECTUNIQUEPTR_H
#define QOBJECTUNIQUEPTR_H

#define SIP_NO_FILE

#include <qsharedpointer.h>
#include <qtypeinfo.h>

class QVariant;

/**
* Keeps a pointer to a QObject and deletes it whenever this object is deleted.
* It keeps a weak pointer to the QObject internally and will be set to ``nullptr``
* whenever the QObject is deleted.
*
* \ingroup core
* \since QGIS 3.8
*/
template <class T>
class QObjectUniquePtr
{
Q_STATIC_ASSERT_X( !std::is_pointer<T>::value, "QObjectUniquePtr's template type must not be a pointer type" );

template<typename U>
struct TypeSelector
{
typedef QObject Type;
};
template<typename U>
struct TypeSelector<const U>
{
typedef const QObject Type;
};
typedef typename TypeSelector<T>::Type QObjectType;
QWeakPointer<QObjectType> mPtr;
public:

/**
* Creates a new empty QObjectUniquePtr.
*/
inline QObjectUniquePtr()
{ }

/**
* Takes a new QObjectUniquePtr and assigned \a p to it.
*/
inline QObjectUniquePtr( T *p ) : mPtr( p )
{ }
// compiler-generated copy/move ctor/assignment operators are fine!

/**
* Will delete the contained QObject if it still exists.
*/
~QObjectUniquePtr()
{
// Will be a nullptr if the QObject has been deleted from somewhere else (e.g. through parent ownership)
delete mPtr.data();
}

/**
* Swaps the pointer managed by this instance with the pointer managed by \a other.
*/
inline void swap( QObjectUniquePtr &other )
{
mPtr.swap( other.mPtr );
}

inline QObjectUniquePtr<T> &operator=( T *p )
{
mPtr.assign( static_cast<QObjectType *>( p ) );
return *this;
}

/**
* Returns the raw pointer to the managed QObject.
*/
inline T *data() const
{
return static_cast<T *>( mPtr.data() );
}

/**
* Returns the raw pointer to the managed QObject.
*/
inline T *get() const
{
return static_cast<T *>( mPtr.data() );
}

/**
* Returns a raw pointer to the managed QObject.
*/
inline T *operator->() const
{
return data();
}

/**
* Dereferences the managed QObject.
*/
inline T &operator*() const
{
return *data();
}

/**
* Const getter for the managed raw pointer.
*/
inline operator T *() const
{
return data();
}

/**
* Checks if the managed pointer is ``nullptr``.
*/
inline bool isNull() const
{
return mPtr.isNull();
}

/**
* Checks if the pointer managed by this object is ``nullptr``.
* If it is not ``nullptr`` TRUE will be returned, if it is ``nullptr``
* FALSE will be returned.
*/
inline operator bool() const
{
return !mPtr.isNull();
}

/**
* Clears the pointer. The managed object is set to ``nullptr`` and will not be deleted.
*/
inline void clear()
{
mPtr.clear();
}

/**
* Clears the pointer and returns it. The managed object will not be deleted and it is the callers
* responsibility to guarantee that no memory is leaked.
*/
inline T *release()
{
T *p = qobject_cast<T *>( mPtr.data() );
mPtr.clear();
return p;
}

/**
* Will reset the managed pointer to ``p``. If there is already a QObject managed currently
* it will be deleted. If ``p`` is not specified the managed QObject will be deleted and
* this object reset to ``nullptr``.
*/
void reset( T *p = nullptr )
{
delete mPtr.data();
mPtr = p;
}
};
template <class T> Q_DECLARE_TYPEINFO_BODY( QObjectUniquePtr<T>, Q_MOVABLE_TYPE );

template <class T>
inline bool operator==( const T *o, const QObjectUniquePtr<T> &p )
{
return o == p.operator->();
}

template<class T>
inline bool operator==( const QObjectUniquePtr<T> &p, const T *o )
{
return p.operator->() == o;
}

template <class T>
inline bool operator==( T *o, const QObjectUniquePtr<T> &p )
{
return o == p.operator->();
}

template<class T>
inline bool operator==( const QObjectUniquePtr<T> &p, T *o )
{
return p.operator->() == o;
}

template<class T>
inline bool operator==( const QObjectUniquePtr<T> &p1, const QObjectUniquePtr<T> &p2 )
{
return p1.operator->() == p2.operator->();
}

template <class T>
inline bool operator!=( const T *o, const QObjectUniquePtr<T> &p )
{
return o != p.operator->();
}

template<class T>
inline bool operator!= ( const QObjectUniquePtr<T> &p, const T *o )
{
return p.operator->() != o;
}

template <class T>
inline bool operator!=( T *o, const QObjectUniquePtr<T> &p )
{
return o != p.operator->();
}

template<class T>
inline bool operator!= ( const QObjectUniquePtr<T> &p, T *o )
{
return p.operator->() != o;
}

template<class T>
inline bool operator!= ( const QObjectUniquePtr<T> &p1, const QObjectUniquePtr<T> &p2 )
{
return p1.operator->() != p2.operator->() ;
}

template<typename T>
QObjectUniquePtr<T>
QObjectUniquePtrFromVariant( const QVariant &variant )
{
return QObjectUniquePtr<T>( qobject_cast<T *>( QtSharedPointer::weakPointerFromVariant_internal( variant ).data() ) );
}

#endif // QOBJECTUNIQUEPTR_H
1 change: 1 addition & 0 deletions tests/src/core/CMakeLists.txt
Expand Up @@ -219,6 +219,7 @@ SET(TESTS
testqgsmimedatautils.cpp
testqgsofflineediting.cpp
testqgstranslateproject.cpp
testqobjectuniqueptr.cpp
)

IF(WITH_QTWEBKIT)
Expand Down
127 changes: 127 additions & 0 deletions tests/src/core/testqobjectuniqueptr.cpp
@@ -0,0 +1,127 @@
/***************************************************************************
testqobjectuniqueptr.cpp
--------------------------------------
Date :
Copyright : (C) 2019 by Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgstest.h"
#include "qobjectuniqueptr.h"

#include "qgstest.h"

class TestQObjectUniquePtr : public QObject
{
Q_OBJECT

private slots:
void testMemLeak();
void testParentDeletedFirst();
void testParentDeletedAfter();
void testOperatorBool();
void testSwap();
void testOperatorArrow();
void testDeleteLater();
};

void TestQObjectUniquePtr::testMemLeak()
{
QObject *myobj = new QObject();
QObjectUniquePtr<QObject> obj( myobj );
}

void TestQObjectUniquePtr::testParentDeletedFirst()
{
QObject *parent = new QObject();
QObject *child = new QObject( parent );

QObjectUniquePtr<QObject> obj( child );
QVERIFY( !obj.isNull() );
QVERIFY( obj );
QCOMPARE( child, obj.get() );
QCOMPARE( child, obj.data() );

delete parent;
QVERIFY( obj.isNull() );
QVERIFY( !obj );
}

void TestQObjectUniquePtr::testParentDeletedAfter()
{
QObject *parent = new QObject();
QObject *child = new QObject( parent );
QPointer<QObject> observer( child );

{
QObjectUniquePtr<QObject> obj( child );
QVERIFY( !observer.isNull() );
}
QVERIFY( observer.isNull() );


// Basically shouldn't crash because of double delete on this line
delete parent;
QVERIFY( observer.isNull() );
}

void TestQObjectUniquePtr::testOperatorBool()
{
QObjectUniquePtr<QObject> obj;
QVERIFY( !obj );
QObjectUniquePtr<QObject> obj2( new QObject() );
QVERIFY( obj2 );
}

void TestQObjectUniquePtr::testSwap()
{
QObject *o = new QObject();
QObjectUniquePtr<QObject> obj;
QObjectUniquePtr<QObject> obj2( o );
obj.swap( obj2 );
QCOMPARE( o, obj.get() );
QCOMPARE( nullptr, obj2.get() );

QObject *o2 = new QObject();
QObjectUniquePtr<QObject> obj3( o2 );
obj.swap( obj3 );
QCOMPARE( o, obj3.get() );
QCOMPARE( o2, obj.get() );
}

void TestQObjectUniquePtr::testOperatorArrow()
{
QObject *o = new QObject();
o->setObjectName( "Teddy" );
QObjectUniquePtr<QObject> obj( o );
QCOMPARE( obj->objectName(), QStringLiteral( "Teddy" ) );
}

void TestQObjectUniquePtr::testDeleteLater()
{
QObject *o = new QObject();
QObject *o2 = new QObject();

QObjectUniquePtr<QObject> obj( o );
QObjectUniquePtr<QObject> obj2( o2 );

obj2->deleteLater();
obj->deleteLater();

obj2.reset();

connect( o, &QObject::destroyed, QgsApplication::instance(), &QgsApplication::quit );
QgsApplication::instance()->exec();
QVERIFY( obj.isNull() );
QVERIFY( obj2.isNull() );
}

QGSTEST_MAIN( TestQObjectUniquePtr )
#include "testqobjectuniqueptr.moc"

0 comments on commit b34539a

Please sign in to comment.