summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Komissarov <abbapoh@gmail.com>2023-05-05 23:05:10 +0300
committerIvan Komissarov <ABBAPOH@gmail.com>2023-05-10 09:40:29 +0000
commit99c1fa4037c106d2f5268826ee392e00208bc24e (patch)
tree3775fdfd3417a72ed0ad87274ecff48ee736bbe2
parentd849e77fd2bc748f04cc3f15faafb8f1c8328670 (diff)
downloadqbs-99c1fa4037c106d2f5268826ee392e00208bc24e.tar.gz
Implement Pimpl class using std::unique_ptr
... and propagate_const from KDAB tools. The propagate_const class fixes the issue that d-pointer in const-methods is not const. Such Pimpl class has several advantages over the raw pointer. - the d-tor is trivial and removes the burden for the author/reviewer to remember to check if pointer is deleted correctly. - constness is now propageted correctly to the d-pointer in const methods. - makes the intention clear that we're using the PIMPL idiom Change-Id: Ibc42cdaeb6d3303fea81ac6edd63bea0da2ac08d Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
-rw-r--r--src/lib/corelib/CMakeLists.txt2
-rw-r--r--src/lib/corelib/corelib.qbs2
-rw-r--r--src/lib/corelib/loader/groupshandler.cpp4
-rw-r--r--src/lib/corelib/loader/groupshandler.h3
-rw-r--r--src/lib/corelib/loader/localprofiles.cpp4
-rw-r--r--src/lib/corelib/loader/localprofiles.h3
-rw-r--r--src/lib/corelib/loader/moduleinstantiator.cpp4
-rw-r--r--src/lib/corelib/loader/moduleinstantiator.h3
-rw-r--r--src/lib/corelib/loader/moduleloader.cpp4
-rw-r--r--src/lib/corelib/loader/moduleloader.h4
-rw-r--r--src/lib/corelib/loader/modulepropertymerger.cpp4
-rw-r--r--src/lib/corelib/loader/modulepropertymerger.h4
-rw-r--r--src/lib/corelib/loader/productitemmultiplexer.cpp4
-rw-r--r--src/lib/corelib/loader/productitemmultiplexer.h4
-rw-r--r--src/lib/corelib/loader/projectresolver.cpp7
-rw-r--r--src/lib/corelib/loader/projectresolver.h3
-rw-r--r--src/lib/corelib/loader/projecttreebuilder.cpp4
-rw-r--r--src/lib/corelib/loader/projecttreebuilder.h3
-rw-r--r--src/lib/corelib/parser/qmlerror.cpp22
-rw-r--r--src/lib/corelib/parser/qmlerror.h4
-rw-r--r--src/lib/corelib/tools/pimpl.h61
-rw-r--r--src/lib/corelib/tools/propagate_const.h418
22 files changed, 527 insertions, 44 deletions
diff --git a/src/lib/corelib/CMakeLists.txt b/src/lib/corelib/CMakeLists.txt
index 0df30295b..321f6bad6 100644
--- a/src/lib/corelib/CMakeLists.txt
+++ b/src/lib/corelib/CMakeLists.txt
@@ -343,6 +343,7 @@ set(TOOLS_SOURCES
msvcinfo.cpp
msvcinfo.h
pathutils.h
+ pimpl.h
persistence.cpp
persistence.h
porting.h
@@ -357,6 +358,7 @@ set(TOOLS_SOURCES
progressobserver.cpp
progressobserver.h
projectgeneratormanager.cpp
+ propagate_const.h
qbsassert.cpp
qbsassert.h
qbspluginmanager.cpp
diff --git a/src/lib/corelib/corelib.qbs b/src/lib/corelib/corelib.qbs
index 532148d2c..9f14ca8bd 100644
--- a/src/lib/corelib/corelib.qbs
+++ b/src/lib/corelib/corelib.qbs
@@ -443,6 +443,7 @@ QbsLibrary {
"msvcinfo.cpp",
"msvcinfo.h",
"pathutils.h",
+ "pimpl.h",
"persistence.cpp",
"persistence.h",
"porting.h",
@@ -457,6 +458,7 @@ QbsLibrary {
"progressobserver.cpp",
"progressobserver.h",
"projectgeneratormanager.cpp",
+ "propagate_const.h",
"qbsassert.cpp",
"qbsassert.h",
"qbspluginmanager.cpp",
diff --git a/src/lib/corelib/loader/groupshandler.cpp b/src/lib/corelib/loader/groupshandler.cpp
index dd183eb16..04970a9a3 100644
--- a/src/lib/corelib/loader/groupshandler.cpp
+++ b/src/lib/corelib/loader/groupshandler.cpp
@@ -80,8 +80,8 @@ public:
GroupsHandler::GroupsHandler(const SetupProjectParameters &parameters,
ModuleInstantiator &instantiator, Evaluator &evaluator, Logger &logger)
- : d(new Private(parameters, instantiator, evaluator, logger)) {}
-GroupsHandler::~GroupsHandler() { delete d; }
+ : d(makePimpl<Private>(parameters, instantiator, evaluator, logger)) {}
+GroupsHandler::~GroupsHandler() = default;
void GroupsHandler::setupGroups(Item *product, Item *productScope)
{
diff --git a/src/lib/corelib/loader/groupshandler.h b/src/lib/corelib/loader/groupshandler.h
index 3f787903f..6680596f7 100644
--- a/src/lib/corelib/loader/groupshandler.h
+++ b/src/lib/corelib/loader/groupshandler.h
@@ -41,6 +41,7 @@
#include <language/qualifiedid.h>
#include <tools/set.h>
+#include <tools/pimpl.h>
#include <unordered_map>
#include <utility>
@@ -76,7 +77,7 @@ public:
private:
class Private;
- Private * const d;
+ Pimpl<Private> d;
};
} // namespace Internal
diff --git a/src/lib/corelib/loader/localprofiles.cpp b/src/lib/corelib/loader/localprofiles.cpp
index 0b73a6ab0..fabe3fab9 100644
--- a/src/lib/corelib/loader/localprofiles.cpp
+++ b/src/lib/corelib/loader/localprofiles.cpp
@@ -69,8 +69,8 @@ public:
LocalProfiles::LocalProfiles(const SetupProjectParameters &parameters, Evaluator &evaluator,
Logger &logger)
- : d(new Private(parameters, evaluator, logger)) {}
-LocalProfiles::~LocalProfiles() { delete d; }
+ : d(makePimpl<Private>(parameters, evaluator, logger)) {}
+LocalProfiles::~LocalProfiles() = default;
void LocalProfiles::collectProfilesFromItems(Item *productOrProject, Item *projectScope)
{
diff --git a/src/lib/corelib/loader/localprofiles.h b/src/lib/corelib/loader/localprofiles.h
index 3e6b77f4d..b89b5de9c 100644
--- a/src/lib/corelib/loader/localprofiles.h
+++ b/src/lib/corelib/loader/localprofiles.h
@@ -39,6 +39,7 @@
#pragma once
+#include <tools/pimpl.h>
#include <QVariantMap>
namespace qbs {
@@ -60,7 +61,7 @@ public:
private:
class Private;
- Private * const d;
+ Pimpl<Private> d;
};
} // namespace Internal
diff --git a/src/lib/corelib/loader/moduleinstantiator.cpp b/src/lib/corelib/loader/moduleinstantiator.cpp
index 35f5332af..8e6e08f01 100644
--- a/src/lib/corelib/loader/moduleinstantiator.cpp
+++ b/src/lib/corelib/loader/moduleinstantiator.cpp
@@ -81,8 +81,8 @@ public:
ModuleInstantiator::ModuleInstantiator(
const SetupProjectParameters &parameters, ItemPool &itemPool,
ModulePropertyMerger &propertyMerger, Logger &logger)
- : d(new Private(parameters, itemPool, propertyMerger, logger)) {}
-ModuleInstantiator::~ModuleInstantiator() { delete d; }
+ : d(makePimpl<Private>(parameters, itemPool, propertyMerger, logger)) {}
+ModuleInstantiator::~ModuleInstantiator() = default;
void ModuleInstantiator::instantiate(const Context &context)
{
diff --git a/src/lib/corelib/loader/moduleinstantiator.h b/src/lib/corelib/loader/moduleinstantiator.h
index f235b83fa..9278c27ee 100644
--- a/src/lib/corelib/loader/moduleinstantiator.h
+++ b/src/lib/corelib/loader/moduleinstantiator.h
@@ -39,6 +39,7 @@
#pragma once
+#include <tools/pimpl.h>
#include <QtGlobal>
QT_BEGIN_NAMESPACE
@@ -93,7 +94,7 @@ public:
private:
class Private;
- Private * const d;
+ Pimpl<Private> d;
};
} // namespace Internal
diff --git a/src/lib/corelib/loader/moduleloader.cpp b/src/lib/corelib/loader/moduleloader.cpp
index 2ad3d8e71..4be08616a 100644
--- a/src/lib/corelib/loader/moduleloader.cpp
+++ b/src/lib/corelib/loader/moduleloader.cpp
@@ -105,9 +105,9 @@ public:
ModuleLoader::ModuleLoader(
const SetupProjectParameters &setupParameters, ModuleProviderLoader &providerLoader,
ItemReader &itemReader, Evaluator &evaluator, Logger &logger)
- : d(new Private(setupParameters, providerLoader, itemReader, evaluator, logger)) { }
+ : d(makePimpl<Private>(setupParameters, providerLoader, itemReader, evaluator, logger)) { }
-ModuleLoader::~ModuleLoader() { delete d; }
+ModuleLoader::~ModuleLoader() = default;
struct PrioritizedItem
{
diff --git a/src/lib/corelib/loader/moduleloader.h b/src/lib/corelib/loader/moduleloader.h
index 6f55ee052..35c7102d7 100644
--- a/src/lib/corelib/loader/moduleloader.h
+++ b/src/lib/corelib/loader/moduleloader.h
@@ -39,8 +39,10 @@
#pragma once
+
#include <language/forward_decls.h>
#include <language/item.h>
+#include <tools/pimpl.h>
#include <QString>
#include <QVariantMap>
@@ -91,7 +93,7 @@ public:
private:
class Private;
- Private * const d;
+ Pimpl<Private> d;
};
} // namespace Internal
diff --git a/src/lib/corelib/loader/modulepropertymerger.cpp b/src/lib/corelib/loader/modulepropertymerger.cpp
index e3cf1a633..3bec5adc3 100644
--- a/src/lib/corelib/loader/modulepropertymerger.cpp
+++ b/src/lib/corelib/loader/modulepropertymerger.cpp
@@ -120,8 +120,8 @@ void ModulePropertyMerger::printProfilingInfo(int indent)
ModulePropertyMerger::ModulePropertyMerger(
const SetupProjectParameters &parameters, Evaluator &evaluator, Logger &logger)
- : d(new Private(parameters, evaluator, logger)) { }
-ModulePropertyMerger::~ModulePropertyMerger() { delete d; }
+ : d(makePimpl<Private>(parameters, evaluator, logger)) { }
+ModulePropertyMerger::~ModulePropertyMerger() = default;
int ModulePropertyMerger::Private::compareValuePriorities(
const Item *productItem, const ValueConstPtr &v1, const ValueConstPtr &v2)
diff --git a/src/lib/corelib/loader/modulepropertymerger.h b/src/lib/corelib/loader/modulepropertymerger.h
index fc388cfbf..2175d212f 100644
--- a/src/lib/corelib/loader/modulepropertymerger.h
+++ b/src/lib/corelib/loader/modulepropertymerger.h
@@ -39,6 +39,8 @@
#pragma once
+#include <tools/pimpl.h>
+
#include <QtGlobal>
namespace qbs {
@@ -92,7 +94,7 @@ public:
private:
class Private;
- Private * const d;
+ Pimpl<Private> d;
};
} // namespace Internal
diff --git a/src/lib/corelib/loader/productitemmultiplexer.cpp b/src/lib/corelib/loader/productitemmultiplexer.cpp
index 5f8e9f472..aec0400d2 100644
--- a/src/lib/corelib/loader/productitemmultiplexer.cpp
+++ b/src/lib/corelib/loader/productitemmultiplexer.cpp
@@ -93,9 +93,9 @@ public:
ProductItemMultiplexer::ProductItemMultiplexer(
const SetupProjectParameters &parameters, Evaluator &evaluator, Logger &logger,
const QbsItemRetriever &qbsItemRetriever)
- : d(new Private(parameters, evaluator, logger, qbsItemRetriever)) {}
+ : d(makePimpl<Private>(parameters, evaluator, logger, qbsItemRetriever)) {}
-ProductItemMultiplexer::~ProductItemMultiplexer() { delete d; }
+ProductItemMultiplexer::~ProductItemMultiplexer() = default;
QList<Item *> ProductItemMultiplexer::multiplex(
const QString &productName,
diff --git a/src/lib/corelib/loader/productitemmultiplexer.h b/src/lib/corelib/loader/productitemmultiplexer.h
index d99267336..795b77b08 100644
--- a/src/lib/corelib/loader/productitemmultiplexer.h
+++ b/src/lib/corelib/loader/productitemmultiplexer.h
@@ -39,6 +39,8 @@
#pragma once
+#include <tools/pimpl.h>
+
#include <QList>
#include <QVariantMap>
@@ -77,7 +79,7 @@ public:
private:
class Private;
- Private * const d;
+ Pimpl<Private> d;
};
} // namespace Internal
diff --git a/src/lib/corelib/loader/projectresolver.cpp b/src/lib/corelib/loader/projectresolver.cpp
index 994436b61..97b701624 100644
--- a/src/lib/corelib/loader/projectresolver.cpp
+++ b/src/lib/corelib/loader/projectresolver.cpp
@@ -251,15 +251,12 @@ public:
ProjectResolver::ProjectResolver(ScriptEngine *engine, Logger logger)
- : d(new Private(engine, std::move(logger)))
+ : d(makePimpl<Private>(engine, std::move(logger)))
{
d->logger.storeWarnings();
}
-ProjectResolver::~ProjectResolver()
-{
- delete d;
-}
+ProjectResolver::~ProjectResolver() = default;
void ProjectResolver::setProgressObserver(ProgressObserver *observer)
{
diff --git a/src/lib/corelib/loader/projectresolver.h b/src/lib/corelib/loader/projectresolver.h
index 94a88c614..2b5a55066 100644
--- a/src/lib/corelib/loader/projectresolver.h
+++ b/src/lib/corelib/loader/projectresolver.h
@@ -42,6 +42,7 @@
#include <language/forward_decls.h>
#include <logging/logger.h>
+#include <tools/pimpl.h>
#include <tools/qbs_export.h>
#include <QHash>
@@ -74,7 +75,7 @@ public:
private:
class Private;
- Private * const d;
+ Pimpl<Private> d;
};
} // namespace Internal
diff --git a/src/lib/corelib/loader/projecttreebuilder.cpp b/src/lib/corelib/loader/projecttreebuilder.cpp
index 59e2873e5..a13c3c2e7 100644
--- a/src/lib/corelib/loader/projecttreebuilder.cpp
+++ b/src/lib/corelib/loader/projecttreebuilder.cpp
@@ -328,8 +328,8 @@ private:
ProjectTreeBuilder::ProjectTreeBuilder(const SetupProjectParameters &parameters, ItemPool &itemPool,
Evaluator &evaluator, Logger &logger)
- : d(new Private(parameters, itemPool, evaluator, logger)) {}
-ProjectTreeBuilder::~ProjectTreeBuilder() { delete d; }
+ : d(makePimpl<Private>(parameters, itemPool, evaluator, logger)) {}
+ProjectTreeBuilder::~ProjectTreeBuilder() = default;
void ProjectTreeBuilder::setProgressObserver(ProgressObserver *progressObserver)
{
diff --git a/src/lib/corelib/loader/projecttreebuilder.h b/src/lib/corelib/loader/projecttreebuilder.h
index 15a329e19..d70e9b2b0 100644
--- a/src/lib/corelib/loader/projecttreebuilder.h
+++ b/src/lib/corelib/loader/projecttreebuilder.h
@@ -44,6 +44,7 @@
#include <language/forward_decls.h>
#include <language/moduleproviderinfo.h>
#include <language/qualifiedid.h>
+#include <tools/pimpl.h>
#include <QString>
#include <QVariant>
@@ -93,7 +94,7 @@ public:
private:
class Private;
- Private * const d;
+ Pimpl<Private> d;
};
} // namespace Internal
diff --git a/src/lib/corelib/parser/qmlerror.cpp b/src/lib/corelib/parser/qmlerror.cpp
index d9fbdd703..42ef8ea29 100644
--- a/src/lib/corelib/parser/qmlerror.cpp
+++ b/src/lib/corelib/parser/qmlerror.cpp
@@ -95,16 +95,12 @@ QmlErrorPrivate::QmlErrorPrivate()
/*!
Creates an empty error object.
*/
-QmlError::QmlError()
-: d(nullptr)
-{
-}
+QmlError::QmlError() = default;
/*!
Creates a copy of \a other.
*/
QmlError::QmlError(const QmlError &other)
-: d(nullptr)
{
*this = other;
}
@@ -118,10 +114,9 @@ QmlError &QmlError::operator=(const QmlError &other)
return *this;
if (!other.d) {
- delete d;
d = nullptr;
} else {
- if (!d) d = new QmlErrorPrivate;
+ if (!d) d = qbs::Internal::makePimpl<QmlErrorPrivate>();
d->url = other.d->url;
d->description = other.d->description;
d->line = other.d->line;
@@ -133,10 +128,7 @@ QmlError &QmlError::operator=(const QmlError &other)
/*!
\internal
*/
-QmlError::~QmlError()
-{
- delete d; d = nullptr;
-}
+QmlError::~QmlError() = default;
/*!
Returns true if this error is valid, otherwise false.
@@ -160,7 +152,7 @@ QUrl QmlError::url() const
*/
void QmlError::setUrl(const QUrl &url)
{
- if (!d) d = new QmlErrorPrivate;
+ if (!d) d = qbs::Internal::makePimpl<QmlErrorPrivate>();
d->url = url;
}
@@ -178,7 +170,7 @@ QString QmlError::description() const
*/
void QmlError::setDescription(const QString &description)
{
- if (!d) d = new QmlErrorPrivate;
+ if (!d) d = qbs::Internal::makePimpl<QmlErrorPrivate>();
d->description = description;
}
@@ -196,7 +188,7 @@ int QmlError::line() const
*/
void QmlError::setLine(int line)
{
- if (!d) d = new QmlErrorPrivate;
+ if (!d) qbs::Internal::makePimpl<QmlErrorPrivate>();
d->line = line;
}
@@ -214,7 +206,7 @@ int QmlError::column() const
*/
void QmlError::setColumn(int column)
{
- if (!d) d = new QmlErrorPrivate;
+ if (!d) qbs::Internal::makePimpl<QmlErrorPrivate>();
d->column = column;
}
diff --git a/src/lib/corelib/parser/qmlerror.h b/src/lib/corelib/parser/qmlerror.h
index cfac506bb..4f7bf8a07 100644
--- a/src/lib/corelib/parser/qmlerror.h
+++ b/src/lib/corelib/parser/qmlerror.h
@@ -40,7 +40,7 @@
#ifndef QQMLERROR_H
#define QQMLERROR_H
-
+#include <tools/pimpl.h>
#include <QtCore/qurl.h>
#include <QtCore/qstring.h>
@@ -73,7 +73,7 @@ public:
QString toString() const;
private:
- QmlErrorPrivate *d;
+ qbs::Internal::Pimpl<QmlErrorPrivate> d;
};
} // namespace QbsQmlJS
diff --git a/src/lib/corelib/tools/pimpl.h b/src/lib/corelib/tools/pimpl.h
new file mode 100644
index 000000000..ab672aabe
--- /dev/null
+++ b/src/lib/corelib/tools/pimpl.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PIMPL_H
+#define PIMPL_H
+
+#include <tools/propagate_const.h>
+#include <memory>
+
+namespace qbs {
+namespace Internal {
+
+template<typename T>
+using Pimpl = KDToolBox::propagate_const<std::unique_ptr<T>>;
+
+template<typename T, typename... Args>
+Pimpl<T> makePimpl(Args&&... args)
+{
+ return Pimpl<T>(std::make_unique<T>(std::forward<Args>(args)...));
+}
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // PIMPL_H
diff --git a/src/lib/corelib/tools/propagate_const.h b/src/lib/corelib/tools/propagate_const.h
new file mode 100644
index 000000000..65e35fc8c
--- /dev/null
+++ b/src/lib/corelib/tools/propagate_const.h
@@ -0,0 +1,418 @@
+/****************************************************************************
+** MIT License
+**
+** Copyright (C) 2022-2023 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
+** Author: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
+**
+** This file is part of KDToolBox (https://github.com/KDAB/KDToolBox).
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+** copies of the Software, ** and to permit persons to whom the Software is
+** furnished to do so, subject to the following conditions:
+**
+** The above copyright notice and this permission notice (including the next paragraph)
+** shall be included in all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+** LIABILITY, WHETHER IN AN ACTION OF ** CONTRACT, TORT OR OTHERWISE,
+** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+** DEALINGS IN THE SOFTWARE.
+****************************************************************************/
+
+#ifndef KDTOOLBOX_PROPAGATE_CONST
+#define KDTOOLBOX_PROPAGATE_CONST
+
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+// Our SFINAE is too noisy and clang-format gets confused.
+// clang-format off
+
+namespace KDToolBox
+{
+
+template <typename T>
+class propagate_const;
+
+namespace detail {
+// Type traits to detect propagate_const specializations
+template <typename T>
+struct is_propagate_const : std::false_type
+{
+};
+
+template <typename T>
+struct is_propagate_const<propagate_const<T>> : std::true_type
+{
+};
+
+// Using SFINAE in a base class to constrain the conversion operators.
+// Otherwise, if we make them function templates and apply SFINAE directly on
+// them, we would not be able to do certain conversions (cf.
+// [over.ics.user/3]).
+template <typename T>
+using propagate_const_element_type = std::remove_reference_t<decltype(*std::declval<T &>())>;
+
+// Const conversion.
+// NON-STANDARD: checks that `const T` is convertible, not `T`.
+// See https://wg21.link/lwg3812
+template <typename T,
+ bool = std::disjunction_v<
+ std::is_pointer<T>,
+ std::is_convertible<const T, const propagate_const_element_type<T> *>
+ >
+ >
+struct propagate_const_const_conversion_operator_base
+{
+};
+
+template <typename T>
+struct propagate_const_const_conversion_operator_base<T, true>
+{
+ constexpr operator const propagate_const_element_type<T> *() const;
+};
+
+// Non-const conversion
+template <typename T,
+ bool = std::disjunction_v<
+ std::is_pointer<T>,
+ std::is_convertible<T, propagate_const_element_type<T> *>
+ >
+ >
+struct propagate_const_non_const_conversion_operator_base
+{
+};
+
+template <typename T>
+struct propagate_const_non_const_conversion_operator_base<T, true>
+{
+ constexpr operator propagate_const_element_type<T> *();
+};
+
+template <typename T>
+struct propagate_const_base
+ : propagate_const_const_conversion_operator_base<T>,
+ propagate_const_non_const_conversion_operator_base<T>
+{};
+
+} // namespace detail
+
+/*
+ TODO: This code could benefit from a C++20 overhaul:
+ * concepts
+ * three-way comparisons
+ * explicit(bool)
+
+ However we can't depend on C++20 yet...
+*/
+
+template <typename T>
+class propagate_const : public detail::propagate_const_base<T>
+{
+public:
+ using element_type = detail::propagate_const_element_type<T>;
+
+ // Special member functions
+ propagate_const() = default;
+ propagate_const(propagate_const &&) = default;
+ propagate_const &operator=(propagate_const &&) = default;
+ propagate_const(const propagate_const &) = delete;
+ propagate_const &operator=(const propagate_const &) = delete;
+ ~propagate_const() = default;
+
+ // Constructor from values
+
+ template <
+ typename U,
+ std::enable_if_t< // This constructor is enabled if:
+ std::conjunction_v<
+ std::is_constructible<T, U>, // 1) we can build a T from a U,
+ std::negation<detail::is_propagate_const<std::decay_t<U>>>, // 2) we are not making a
+ // converting constructor,
+ std::is_convertible<U, T> // 3) and the conversion from U to T is implicit;
+ >,
+ bool> = true>
+ /* implicit */ // then, this constructor is implicit too.
+ propagate_const(U &&other)
+ : m_t(std::forward<U>(other))
+ {
+ }
+
+ template <
+ typename U,
+ std::enable_if_t< // This constructor is enabled if:
+ std::conjunction_v<
+ std::is_constructible<T, U>, // 1) we can build a T from a U,
+ std::negation<detail::is_propagate_const<std::decay_t<U>>>, // 2) we are not making a
+ // converting constructor,
+ std::negation<std::is_convertible<U, T>> // 3) and the conversion from U to T is
+ // explicit;
+ >,
+ bool> = true>
+ explicit // then, this constructor is explicit.
+ propagate_const(U &&other)
+ : m_t(std::forward<U>(other))
+ {
+ }
+
+ // Constructors from other propagate_const (converting constructors)
+ template <typename U,
+ std::enable_if_t< // This constructor is enabled if:
+ std::conjunction_v<std::is_constructible<T, U>, // 1) we can do the conversion,
+ std::is_convertible<U, T> // 2) and the conversion is implicit;
+ >,
+ bool> = true>
+ /* implicit */ // then, this constructor is implicit.
+ constexpr propagate_const(propagate_const<U> &&other)
+ : m_t(std::move(get_underlying(other)))
+ {
+ }
+
+ template <typename U,
+ std::enable_if_t< // This constructor is enabled if:
+ std::conjunction_v<std::is_constructible<T, U>, // 1) we can do the conversion,
+ std::negation<std::is_convertible<U, T>> // 2) and the
+ // conversion is
+ // explicit;
+ >,
+ bool> = true>
+ explicit // then, this constructor is explicit.
+ constexpr propagate_const(propagate_const<U> &&other)
+ : m_t(std::move(get_underlying(other)))
+ {
+ }
+
+ // Converting assignment
+ template <typename U,
+ std::enable_if_t< // This assignment operator is enabled if
+ std::is_convertible_v<U, T>, // the conversion from U to T is implicit
+ bool> = true>
+ constexpr propagate_const &operator=(propagate_const<U> &&other)
+ {
+ m_t = std::move(get_underlying(other));
+ return *this;
+ }
+
+ template <typename U,
+ std::enable_if_t< // This assignment operator is enabled if:
+ std::conjunction_v<
+ std::is_convertible<U, T>, // 1) the conversion from U to T is implicit,
+ std::negation<detail::is_propagate_const<std::decay_t<U>>> // 2) and U is not a
+ // propagate_const
+ >,
+ bool> = true>
+ constexpr propagate_const &operator=(U &&other)
+ {
+ m_t = std::forward<U>(other);
+ return *this;
+ }
+
+ // Swap
+ constexpr void swap(propagate_const &other) noexcept(std::is_nothrow_swappable_v<T>)
+ {
+ using std::swap;
+ swap(m_t, other.m_t);
+ }
+
+ // Const observers
+ constexpr explicit operator bool() const { return static_cast<bool>(m_t); }
+
+ constexpr const element_type *get() const { return get_impl(m_t); }
+
+ constexpr const element_type &operator*() const { return *get(); }
+
+ constexpr const element_type *operator->() const { return get(); }
+
+ // Non-const observers
+ constexpr element_type *get() { return get_impl(m_t); }
+
+ constexpr element_type &operator*() { return *get(); }
+
+ constexpr element_type *operator->() { return get(); }
+
+ // Non-member utilities: extract the contained object
+ template <typename U>
+ friend constexpr auto &get_underlying(propagate_const<U> &p);
+
+ template <typename U>
+ friend constexpr const auto &get_underlying(const propagate_const<U> &p);
+
+private:
+ // Implementation of get() that works with raw pointers and smart
+ // pointers. Similar to std::to_address, but to_address is C++20,
+ // and propagate_const spec does not match it.
+ template <typename U>
+ static constexpr element_type *get_impl(U *u)
+ {
+ return u;
+ }
+
+ template <typename U>
+ static constexpr element_type *get_impl(U &u)
+ {
+ return u.get();
+ }
+
+ template <typename U>
+ static constexpr const element_type *get_impl(const U *u)
+ {
+ return u;
+ }
+
+ template <typename U>
+ static constexpr const element_type *get_impl(const U &u)
+ {
+ return u.get();
+ }
+
+ T m_t;
+};
+
+// Swap
+template <typename T,
+ std::enable_if_t<std::is_swappable_v<T>, bool> = true>
+constexpr void swap(propagate_const<T> &lhs, propagate_const<T> &rhs)
+ noexcept(noexcept(lhs.swap(rhs)))
+{
+ lhs.swap(rhs);
+}
+
+// Implementation of get_underlying
+template <typename T>
+constexpr auto &get_underlying(propagate_const<T> &p)
+{
+ return p.m_t;
+}
+
+template <typename T>
+constexpr const auto &get_underlying(const propagate_const<T> &p)
+{
+ return p.m_t;
+}
+
+// Implementation of the conversion operators
+template <typename T>
+constexpr detail::propagate_const_const_conversion_operator_base<T, true>
+ ::operator const detail::propagate_const_element_type<T> *() const
+{
+ return static_cast<const propagate_const<T> *>(this)->get();
+}
+
+template <typename T>
+constexpr detail::propagate_const_non_const_conversion_operator_base<T, true>
+ ::operator detail::propagate_const_element_type<T> *()
+{
+ return static_cast<propagate_const<T> *>(this)->get();
+}
+
+// Comparisons. As per spec, they're free function templates.
+
+// Comparisons against nullptr.
+template <typename T>
+constexpr bool operator==(const propagate_const<T> &p, std::nullptr_t)
+{
+ return get_underlying(p) == nullptr;
+}
+
+template <typename T>
+constexpr bool operator!=(const propagate_const<T> &p, std::nullptr_t)
+{
+ return get_underlying(p) != nullptr;
+}
+
+template <typename T>
+constexpr bool operator==(std::nullptr_t, const propagate_const<T> &p)
+{
+ return nullptr == get_underlying(p);
+}
+
+template <typename T>
+constexpr bool operator!=(std::nullptr_t, const propagate_const<T> &p)
+{
+ return nullptr == get_underlying(p);
+}
+
+// Comparisons between propagate_const
+#define DEFINE_PROPAGATE_CONST_COMPARISON_OP(op) \
+template <typename T, typename U> \
+ constexpr bool operator op (const propagate_const<T> &lhs, const propagate_const<U> &rhs) \
+{ \
+ return get_underlying(lhs) op get_underlying(rhs); \
+} \
+
+ DEFINE_PROPAGATE_CONST_COMPARISON_OP(==)
+ DEFINE_PROPAGATE_CONST_COMPARISON_OP(!=)
+ DEFINE_PROPAGATE_CONST_COMPARISON_OP(<)
+ DEFINE_PROPAGATE_CONST_COMPARISON_OP(<=)
+ DEFINE_PROPAGATE_CONST_COMPARISON_OP(>)
+ DEFINE_PROPAGATE_CONST_COMPARISON_OP(>=)
+
+#undef DEFINE_PROPAGATE_CONST_COMPARISON_OP
+
+// Comparisons against other (smart) pointers
+#define DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP(op) \
+ template <typename T, typename U> \
+ constexpr bool operator op (const propagate_const<T> &lhs, const U &rhs) \
+{ \
+ return get_underlying(lhs) op rhs; \
+} \
+ template <typename T, typename U> \
+ constexpr bool operator op (const T &lhs, const propagate_const<U> &rhs) \
+{ \
+ return lhs op get_underlying(rhs); \
+} \
+
+ DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP(==)
+ DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP(!=)
+ DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP(<)
+ DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP(<=)
+ DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP(>)
+ DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP(>=)
+
+#undef DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP
+
+} // namespace KDToolBox
+
+// std::hash specialization
+namespace std
+{
+template <typename T>
+struct hash<KDToolBox::propagate_const<T>>
+{
+ constexpr size_t operator()(const KDToolBox::propagate_const<T> &t) const
+ noexcept(noexcept(hash<T>{}(get_underlying(t))))
+ {
+ return hash<T>{}(get_underlying(t));
+ }
+};
+
+#define DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(COMP) \
+template <typename T> \
+ struct COMP<KDToolBox::propagate_const<T>> \
+{ \
+ constexpr bool operator()(const KDToolBox::propagate_const<T> &lhs, \
+ const KDToolBox::propagate_const<T> &rhs) \
+ { \
+ return COMP<T>{}(get_underlying(lhs), get_underlying(rhs)); \
+ } \
+}; \
+
+ DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(equal_to)
+ DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(not_equal_to)
+ DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(less)
+ DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(greater)
+ DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(less_equal)
+ DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(greater_equal)
+
+#undef DEFINE_COMP_OBJECT_SPECIALIZATION
+
+} // namespace std
+
+#endif // KDTOOLBOX_PROPAGATE_CONST