From e2b2bb7ff19d889ac5582a80a90861819c213591 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 9 Jan 2023 11:06:45 +0100 Subject: [PATCH] Short live q20::to_address! An implementation of C++20 std::to_address, a prerequesite for QSpan<>. The test cases are inspired by libstdc++'s test suite, just to avoid missing some cases, but the to_address implementation is white-room. Fixes: QTBUG-108430 Change-Id: I4c092fdd7a56c0b279068e341bbf91a725ca3b1f Reviewed-by: Thiago Macieira --- src/corelib/global/q20memory.h | 45 ++++- tests/auto/corelib/global/CMakeLists.txt | 1 + tests/auto/corelib/global/q20/CMakeLists.txt | 1 + .../corelib/global/q20/memory/CMakeLists.txt | 10 ++ .../global/q20/memory/tst_q20_memory.cpp | 161 ++++++++++++++++++ 5 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 tests/auto/corelib/global/q20/CMakeLists.txt create mode 100644 tests/auto/corelib/global/q20/memory/CMakeLists.txt create mode 100644 tests/auto/corelib/global/q20/memory/tst_q20_memory.cpp diff --git a/src/corelib/global/q20memory.h b/src/corelib/global/q20memory.h index bb337a9f7f2..008f4ddeb20 100644 --- a/src/corelib/global/q20memory.h +++ b/src/corelib/global/q20memory.h @@ -1,4 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. +// Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef Q20MEMORY_H @@ -7,9 +8,9 @@ #include #include -#include #include +#include // // W A R N I N G @@ -45,6 +46,48 @@ T *construct_at(T *ptr, Args && ... args) #endif // __cpp_lib_constexpr_dynamic_alloc } // namespace q20 + +namespace q20 { +// like std::to_address +#ifdef __cpp_lib_to_address +using std::to_address; +#else +// http://eel.is/c++draft/pointer.conversion +template +constexpr T *to_address(T *p) noexcept { + // http://eel.is/c++draft/pointer.conversion#1: + // Mandates: T is not a function type. + static_assert(!std::is_function_v, "to_address must not be used on function types"); + return p; +} + +template , bool> = true> +constexpr auto to_address(const Ptr &ptr) noexcept; // fwd declared + +namespace detail { + // http://eel.is/c++draft/pointer.conversion#3 + template + struct to_address_helper { + static auto get(const Ptr &ptr) noexcept + { return q20::to_address(ptr.operator->()); } + }; + template + struct to_address_helper::to_address(std::declval())) + >> + { + static auto get(const Ptr &ptr) noexcept + { return std::pointer_traits::to_address(ptr); } + }; +} // namespace detail + +template , bool>> +constexpr auto to_address(const Ptr &ptr) noexcept +{ return detail::to_address_helper::get(ptr); } + +#endif // __cpp_lib_to_address +} // namespace q20 + QT_END_NAMESPACE #endif /* Q20MEMORY_H */ diff --git a/tests/auto/corelib/global/CMakeLists.txt b/tests/auto/corelib/global/CMakeLists.txt index 68574c396dd..2204f82ba3c 100644 --- a/tests/auto/corelib/global/CMakeLists.txt +++ b/tests/auto/corelib/global/CMakeLists.txt @@ -21,3 +21,4 @@ add_subdirectory(qglobalstatic) add_subdirectory(qhooks) add_subdirectory(qoperatingsystemversion) add_subdirectory(qxp) +add_subdirectory(q20) diff --git a/tests/auto/corelib/global/q20/CMakeLists.txt b/tests/auto/corelib/global/q20/CMakeLists.txt new file mode 100644 index 00000000000..bd28f8b999b --- /dev/null +++ b/tests/auto/corelib/global/q20/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(memory) diff --git a/tests/auto/corelib/global/q20/memory/CMakeLists.txt b/tests/auto/corelib/global/q20/memory/CMakeLists.txt new file mode 100644 index 00000000000..f928f75b7ad --- /dev/null +++ b/tests/auto/corelib/global/q20/memory/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_test(tst_q20_memory + EXCEPTIONS + SOURCES + tst_q20_memory.cpp + LIBRARIES + Qt::Core +) diff --git a/tests/auto/corelib/global/q20/memory/tst_q20_memory.cpp b/tests/auto/corelib/global/q20/memory/tst_q20_memory.cpp new file mode 100644 index 00000000000..e6da7b5cf82 --- /dev/null +++ b/tests/auto/corelib/global/q20/memory/tst_q20_memory.cpp @@ -0,0 +1,161 @@ +// Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + + +#include +#include + +#include +#include +#include +#include + +struct Private : QSharedData {}; + +class tst_q20_memory : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void raw(); + void smart(); + void recursion(); + void usesPointerTraits(); + void prefersPointerTraits(); + +private Q_SLOTS: + void to_address_broken_const_propagation_QExplicitlySharedDataPointer() + { to_address_broken_const_propagation>(); } + void to_address_broken_const_propagation_QSharedDataPointer() + { to_address_broken_const_propagation>(); } + void to_address_broken_const_propagation_shared_ptr() + { to_address_broken_const_propagation>(); } + void to_address_broken_const_propagation_unique_ptr() + { to_address_broken_const_propagation>(); } + +private: + template + void to_address_broken_const_propagation(); +}; + +void tst_q20_memory::raw() +{ + auto i = 0; + auto p = &i; + QVERIFY(q20::to_address(p) == &i); +} + +template +class MinimalPtr { +public: + using element_type = T; + + explicit MinimalPtr(T *d) : d(d) {} + + T *operator->() const noexcept { return d; } + +private: + T *d; +}; + +void tst_q20_memory::smart() +{ + int i; + MinimalPtr ptr(&i); + QCOMPARE_EQ(q20::to_address(ptr), &i); +} + +template +class RecursivePtr { +public: + using element_type = T; + + explicit RecursivePtr(T *d) : d(d) {} + + MinimalPtr operator->() const noexcept { return d; } + +private: + MinimalPtr d; +}; + +void tst_q20_memory::recursion() +{ + int i; + RecursivePtr ptr(&i); + QCOMPARE_EQ(q20::to_address(ptr), &i); +} + +template +class NoDerefOperatorPtr { +public: + using element_type = T; + + explicit NoDerefOperatorPtr(T *d) : d(d) {} + + T *get() const noexcept { return d; } + +private: + T *d; +}; + +namespace std { +template +struct pointer_traits> +{ + static T *to_address(const NoDerefOperatorPtr &ptr) noexcept { return ptr.get(); } +}; +} // namespace std + +void tst_q20_memory::usesPointerTraits() +{ + int i; + NoDerefOperatorPtr ptr(&i); + QCOMPARE_EQ(q20::to_address(ptr), &i); +} + +template +class PrefersPointerTraitsPtr +{ +public: + using element_type = T; + + explicit PrefersPointerTraitsPtr(T *d) : d(d) {} + + T *operator->() const noexcept { return nullptr; } + + T *get() const noexcept { return d; } + +private: + T *d; +}; + +namespace std { +template +struct pointer_traits> +{ + static T *to_address(const PrefersPointerTraitsPtr &p) noexcept { return p.get(); } +}; +} // namespace std + +void tst_q20_memory::prefersPointerTraits() +{ + int i; + PrefersPointerTraitsPtr ptr(&i); + QCOMPARE_EQ(q20::to_address(ptr), &i); +} + +template +void tst_q20_memory::to_address_broken_const_propagation() +{ + Pointer p(nullptr); + QCOMPARE_EQ(q20::to_address(p), nullptr); + p = Pointer{new Private()}; + QCOMPARE_EQ(q20::to_address(p), p.operator->()); + static_assert(std::is_same_v())>); +} + +QTEST_GUILESS_MAIN(tst_q20_memory) +#include "tst_q20_memory.moc" +