Commit f4252d3b authored by Tusooa Zhu's avatar Tusooa Zhu 🔼 Committed by Blackbeard (alberto flores)

Add classes KisDescendent and KisSharedDescendent

These two class templates are used to store any subclass of a base class.
KisSharedDescendent is also inherited from QSharedData, and its use for
copy-on-write is documented in the comments.
parent fdda279f
/*
* Copyright (c) 2019 Tusooa Zhu <tusooa@vista.aero>
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_DESCENDENT_H_
#define KIS_DESCENDENT_H_
#include <memory>
#include <utility>
/**
* KisDescendent<T> holds an instance of any subclass U of T.
*
* Its copy/move constructor will call the respective constructor in class U.
* Same for assignments.
*
* After moving, the original KisDescendent will be invalidated.
*
* Example: (suppose Derived is a subclass of Base)
* KisDescendent<Base> ins1 = Derived();
* ins1->someMethod();
* KisDescendent<Base> ins2 = ins1; // ins2 is a clone of ins1, created by Derived's copy constructor
* KisDescendent<Base> ins3 = std::move(ins2); // move-constructs ins3; ins2 is now invalidated.
*/
template<typename T>
class KisDescendent
{
struct concept
{
virtual ~concept() = default;
virtual const T *ptr() const = 0;
virtual T *ptr() = 0;
virtual std::unique_ptr<concept> clone() const = 0;
};
template<typename U>
struct model : public concept
{
model(U x) : instance(std::move(x)) {}
const T *ptr() const { return &instance; }
T *ptr() { return &instance; }
std::unique_ptr<concept> clone() const { return std::unique_ptr<model<U> >(new model<U>(U(instance))); }
U instance;
};
std::unique_ptr<concept> m_d;
public:
template<typename U>
KisDescendent(U x) : m_d(std::unique_ptr<model<U> >(new model<U>(std::move(x)))) {}
/**
* Copy-constructs the instance stored by that. The constructor of type U will be used.
*/
KisDescendent(const KisDescendent &that) : m_d(std::move(that.m_d->clone())) {}
/**
* Transfers the ownership of the instance from that to this.
* This will NOT move-construct any instance of U nor T.
*/
KisDescendent(KisDescendent &&that) : m_d(std::move(that.m_d)) {}
KisDescendent & operator=(const KisDescendent &that) { Descendent t(that); *this = std::move(t); return *this; }
KisDescendent & operator=(KisDescendent &&that) { m_d = std::move(that.m_d); return *this; }
/**
* Returns a const pointer to the stored instance. The pointer can be cast to const U*.
*/
const T *data() const { return m_d->ptr(); }
const T *constData() const { return m_d->ptr(); }
/**
* Returns a non-const pointer to the stored instance. The pointer can be cast to U*.
*/
T *data() { return m_d->ptr(); }
const T *operator->() const { return m_d->ptr(); }
T *operator->() { return m_d->ptr(); }
};
#endif // KIS_DESCENDENT_H_
/*
* Copyright (c) 2019 Tusooa Zhu <tusooa@vista.aero>
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_SHARED_DESCENDENT_H_
#define KIS_SHARED_DESCENDENT_H_
#include "KisDescendent.h"
#include <QSharedData>
/**
* To implement copy-on-write for your class:
* (1) If it is a class without subclasses, or the subclasses do
* not share a d-pointer with the base class:
*
* Make YourClass::Private inherit from QSharedData.
* Replace `QScopedPointer<Private> d;` with `QSharedDataPointer<Private> d;`.
*
* (2) If it is a class with subclasses, and the subclasses share
* a (inherited) d-pointer with the base class:
*
* Replace `QScopedPointer<BaseClassPrivate> d_ptr;` with
* `QSharedDataPointer<KisSharedDescendent<BaseClassPrivate> > d_ptr;` in your base class.
* Remove all `Q_DECLARE_PRIVATE()`s.
* Remove all `Q_DISABLE_COPY()`s. (To obtain copy-on-write you must first allow copies.)
* Replace `explicit DerivedClass(BaseClassPrivate &dd) : BaseClass(dd) {}` with
* `explicit DerivedClass(KisSharedDescendent<BaseClassPrivate> &dd) : BaseClass(dd) {}`.
* Replace `DerivedClass() : BaseClass(*(new DerivedClassPrivate())) {}` with
* `DerivedClass() : BaseClass(KisSharedDescendent<BaseClassPrivate>::of(DerivedClassPrivate())) {}`.
* Replace all `Q_D()` macros that get non-const d-pointers with `SHARED_D(YourClass)`.
* Replace all `Q_D()` macros that get const d-pointers with `CONST_SHARED_D(YourClass)`.
*/
#define CONST_SHARED_D(Class) const Class##Private *const d = reinterpret_cast<const Class##Private *>(d_ptr.constData()->constData())
#define SHARED_D(Class) Class##Private *const d = reinterpret_cast<Class##Private *>(d_ptr.data()->data())
template<typename T>
class KisSharedDescendent : public KisDescendent<T>, public QSharedData
{
public:
template<typename U>
KisSharedDescendent(U x) : KisDescendent<T>(std::move(x)) {}
template<typename U>
constexpr static KisSharedDescendent &of(U x) { return *(new KisSharedDescendent<T>(std::move(x))); }
template<typename U>
constexpr static KisSharedDescendent *pointerOf(U x) { return *(new KisSharedDescendent<T>(std::move(x))); }
};
#endif // KIS_SHARED_DESCENDENT_H_
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment