From 99c1fa4037c106d2f5268826ee392e00208bc24e Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Fri, 5 May 2023 23:05:10 +0300 Subject: 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 --- src/lib/corelib/CMakeLists.txt | 2 + src/lib/corelib/corelib.qbs | 2 + src/lib/corelib/loader/groupshandler.cpp | 4 +- src/lib/corelib/loader/groupshandler.h | 3 +- src/lib/corelib/loader/localprofiles.cpp | 4 +- src/lib/corelib/loader/localprofiles.h | 3 +- src/lib/corelib/loader/moduleinstantiator.cpp | 4 +- src/lib/corelib/loader/moduleinstantiator.h | 3 +- src/lib/corelib/loader/moduleloader.cpp | 4 +- src/lib/corelib/loader/moduleloader.h | 4 +- src/lib/corelib/loader/modulepropertymerger.cpp | 4 +- src/lib/corelib/loader/modulepropertymerger.h | 4 +- src/lib/corelib/loader/productitemmultiplexer.cpp | 4 +- src/lib/corelib/loader/productitemmultiplexer.h | 4 +- src/lib/corelib/loader/projectresolver.cpp | 7 +- src/lib/corelib/loader/projectresolver.h | 3 +- src/lib/corelib/loader/projecttreebuilder.cpp | 4 +- src/lib/corelib/loader/projecttreebuilder.h | 3 +- src/lib/corelib/parser/qmlerror.cpp | 22 +- src/lib/corelib/parser/qmlerror.h | 4 +- src/lib/corelib/tools/pimpl.h | 61 ++++ src/lib/corelib/tools/propagate_const.h | 418 ++++++++++++++++++++++ 22 files changed, 527 insertions(+), 44 deletions(-) create mode 100644 src/lib/corelib/tools/pimpl.h create mode 100644 src/lib/corelib/tools/propagate_const.h 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 ¶meters, ModuleInstantiator &instantiator, Evaluator &evaluator, Logger &logger) - : d(new Private(parameters, instantiator, evaluator, logger)) {} -GroupsHandler::~GroupsHandler() { delete d; } + : d(makePimpl(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 #include +#include #include #include @@ -76,7 +77,7 @@ public: private: class Private; - Private * const d; + Pimpl 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 ¶meters, Evaluator &evaluator, Logger &logger) - : d(new Private(parameters, evaluator, logger)) {} -LocalProfiles::~LocalProfiles() { delete d; } + : d(makePimpl(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 #include namespace qbs { @@ -60,7 +61,7 @@ public: private: class Private; - Private * const d; + Pimpl 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 ¶meters, ItemPool &itemPool, ModulePropertyMerger &propertyMerger, Logger &logger) - : d(new Private(parameters, itemPool, propertyMerger, logger)) {} -ModuleInstantiator::~ModuleInstantiator() { delete d; } + : d(makePimpl(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 #include QT_BEGIN_NAMESPACE @@ -93,7 +94,7 @@ public: private: class Private; - Private * const d; + Pimpl 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(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 #include +#include #include #include @@ -91,7 +93,7 @@ public: private: class Private; - Private * const d; + Pimpl 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 ¶meters, Evaluator &evaluator, Logger &logger) - : d(new Private(parameters, evaluator, logger)) { } -ModulePropertyMerger::~ModulePropertyMerger() { delete d; } + : d(makePimpl(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 + #include namespace qbs { @@ -92,7 +94,7 @@ public: private: class Private; - Private * const d; + Pimpl 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 ¶meters, Evaluator &evaluator, Logger &logger, const QbsItemRetriever &qbsItemRetriever) - : d(new Private(parameters, evaluator, logger, qbsItemRetriever)) {} + : d(makePimpl(parameters, evaluator, logger, qbsItemRetriever)) {} -ProductItemMultiplexer::~ProductItemMultiplexer() { delete d; } +ProductItemMultiplexer::~ProductItemMultiplexer() = default; QList 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 + #include #include @@ -77,7 +79,7 @@ public: private: class Private; - Private * const d; + Pimpl 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(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 #include +#include #include #include @@ -74,7 +75,7 @@ public: private: class Private; - Private * const d; + Pimpl 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 ¶meters, ItemPool &itemPool, Evaluator &evaluator, Logger &logger) - : d(new Private(parameters, itemPool, evaluator, logger)) {} -ProjectTreeBuilder::~ProjectTreeBuilder() { delete d; } + : d(makePimpl(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 #include #include +#include #include #include @@ -93,7 +94,7 @@ public: private: class Private; - Private * const d; + Pimpl 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(); 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(); 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(); 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(); 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(); 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 #include #include @@ -73,7 +73,7 @@ public: QString toString() const; private: - QmlErrorPrivate *d; + qbs::Internal::Pimpl 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 +#include + +namespace qbs { +namespace Internal { + +template +using Pimpl = KDToolBox::propagate_const>; + +template +Pimpl makePimpl(Args&&... args) +{ + return Pimpl(std::make_unique(std::forward(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 +** +** 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 +#include +#include + +// Our SFINAE is too noisy and clang-format gets confused. +// clang-format off + +namespace KDToolBox +{ + +template +class propagate_const; + +namespace detail { +// Type traits to detect propagate_const specializations +template +struct is_propagate_const : std::false_type +{ +}; + +template +struct is_propagate_const> : 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 +using propagate_const_element_type = std::remove_reference_t())>; + +// Const conversion. +// NON-STANDARD: checks that `const T` is convertible, not `T`. +// See https://wg21.link/lwg3812 +template , + std::is_convertible *> + > + > +struct propagate_const_const_conversion_operator_base +{ +}; + +template +struct propagate_const_const_conversion_operator_base +{ + constexpr operator const propagate_const_element_type *() const; +}; + +// Non-const conversion +template , + std::is_convertible *> + > + > +struct propagate_const_non_const_conversion_operator_base +{ +}; + +template +struct propagate_const_non_const_conversion_operator_base +{ + constexpr operator propagate_const_element_type *(); +}; + +template +struct propagate_const_base + : propagate_const_const_conversion_operator_base, + propagate_const_non_const_conversion_operator_base +{}; + +} // 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 +class propagate_const : public detail::propagate_const_base +{ +public: + using element_type = detail::propagate_const_element_type; + + // 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, // 1) we can build a T from a U, + std::negation>>, // 2) we are not making a + // converting constructor, + std::is_convertible // 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(other)) + { + } + + template < + typename U, + std::enable_if_t< // This constructor is enabled if: + std::conjunction_v< + std::is_constructible, // 1) we can build a T from a U, + std::negation>>, // 2) we are not making a + // converting constructor, + std::negation> // 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(other)) + { + } + + // Constructors from other propagate_const (converting constructors) + template , // 1) we can do the conversion, + std::is_convertible // 2) and the conversion is implicit; + >, + bool> = true> + /* implicit */ // then, this constructor is implicit. + constexpr propagate_const(propagate_const &&other) + : m_t(std::move(get_underlying(other))) + { + } + + template , // 1) we can do the conversion, + std::negation> // 2) and the + // conversion is + // explicit; + >, + bool> = true> + explicit // then, this constructor is explicit. + constexpr propagate_const(propagate_const &&other) + : m_t(std::move(get_underlying(other))) + { + } + + // Converting assignment + template , // the conversion from U to T is implicit + bool> = true> + constexpr propagate_const &operator=(propagate_const &&other) + { + m_t = std::move(get_underlying(other)); + return *this; + } + + template , // 1) the conversion from U to T is implicit, + std::negation>> // 2) and U is not a + // propagate_const + >, + bool> = true> + constexpr propagate_const &operator=(U &&other) + { + m_t = std::forward(other); + return *this; + } + + // Swap + constexpr void swap(propagate_const &other) noexcept(std::is_nothrow_swappable_v) + { + using std::swap; + swap(m_t, other.m_t); + } + + // Const observers + constexpr explicit operator bool() const { return static_cast(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 + friend constexpr auto &get_underlying(propagate_const &p); + + template + friend constexpr const auto &get_underlying(const propagate_const &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 + static constexpr element_type *get_impl(U *u) + { + return u; + } + + template + static constexpr element_type *get_impl(U &u) + { + return u.get(); + } + + template + static constexpr const element_type *get_impl(const U *u) + { + return u; + } + + template + static constexpr const element_type *get_impl(const U &u) + { + return u.get(); + } + + T m_t; +}; + +// Swap +template , bool> = true> +constexpr void swap(propagate_const &lhs, propagate_const &rhs) + noexcept(noexcept(lhs.swap(rhs))) +{ + lhs.swap(rhs); +} + +// Implementation of get_underlying +template +constexpr auto &get_underlying(propagate_const &p) +{ + return p.m_t; +} + +template +constexpr const auto &get_underlying(const propagate_const &p) +{ + return p.m_t; +} + +// Implementation of the conversion operators +template +constexpr detail::propagate_const_const_conversion_operator_base + ::operator const detail::propagate_const_element_type *() const +{ + return static_cast *>(this)->get(); +} + +template +constexpr detail::propagate_const_non_const_conversion_operator_base + ::operator detail::propagate_const_element_type *() +{ + return static_cast *>(this)->get(); +} + +// Comparisons. As per spec, they're free function templates. + +// Comparisons against nullptr. +template +constexpr bool operator==(const propagate_const &p, std::nullptr_t) +{ + return get_underlying(p) == nullptr; +} + +template +constexpr bool operator!=(const propagate_const &p, std::nullptr_t) +{ + return get_underlying(p) != nullptr; +} + +template +constexpr bool operator==(std::nullptr_t, const propagate_const &p) +{ + return nullptr == get_underlying(p); +} + +template +constexpr bool operator!=(std::nullptr_t, const propagate_const &p) +{ + return nullptr == get_underlying(p); +} + +// Comparisons between propagate_const +#define DEFINE_PROPAGATE_CONST_COMPARISON_OP(op) \ +template \ + constexpr bool operator op (const propagate_const &lhs, const propagate_const &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 \ + constexpr bool operator op (const propagate_const &lhs, const U &rhs) \ +{ \ + return get_underlying(lhs) op rhs; \ +} \ + template \ + constexpr bool operator op (const T &lhs, const propagate_const &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 +struct hash> +{ + constexpr size_t operator()(const KDToolBox::propagate_const &t) const + noexcept(noexcept(hash{}(get_underlying(t)))) + { + return hash{}(get_underlying(t)); + } +}; + +#define DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(COMP) \ +template \ + struct COMP> \ +{ \ + constexpr bool operator()(const KDToolBox::propagate_const &lhs, \ + const KDToolBox::propagate_const &rhs) \ + { \ + return COMP{}(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 -- cgit v1.2.1