diff options
4 files changed, 174 insertions, 1 deletions
diff --git a/src/corelib/global/qxptype_traits.h b/src/corelib/global/qxptype_traits.h index 7da0478583..d1641e1d0d 100644 --- a/src/corelib/global/qxptype_traits.h +++ b/src/corelib/global/qxptype_traits.h @@ -1,4 +1,4 @@ -// Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com> +// Copyright (C) 2023 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>, Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only @@ -6,6 +6,7 @@ #define QXPTYPE_TRAITS_H #include <QtCore/qtconfigmacros.h> +#include <QtCore/qcompilerdetection.h> #include <type_traits> @@ -55,6 +56,63 @@ using is_detected = typename _detail::detector<qxp::nonesuch, void, Op, Args...> template <template <typename...> class Op, typename...Args> constexpr inline bool is_detected_v = is_detected<Op, Args...>::value; + +// qxp::is_virtual_base_of_v<B, D> is true if and only if B is a virtual base class of D. +// Just like is_base_of: +// * only works on complete types; +// * B and D must be class types; +// * ignores cv-qualifications; +// * B may be inaccessibile. + +namespace _detail { + // Check that From* can be converted to To*, ignoring accessibility. + // This can be done using a C cast (see [expr.cast]/4). +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Wold-style-cast") +QT_WARNING_DISABLE_CLANG("-Wold-style-cast") + template <typename From, typename To> + using is_virtual_base_conversion_test = decltype( + (To *)std::declval<From *>() + ); +QT_WARNING_POP + + template <typename Base, typename Derived, typename = void> + struct is_virtual_base_of : std::false_type {}; + + template <typename Base, typename Derived> + struct is_virtual_base_of< + Base, Derived, + std::enable_if_t< + std::conjunction_v< + // Base is a base class of Derived. + std::is_base_of<Base, Derived>, + + // Check that Derived* can be converted to Base*, ignoring + // accessibility. If this is possible, then Base is + // an unambiguous base of Derived (=> virtual bases are always + // unambiguous). + qxp::is_detected<is_virtual_base_conversion_test, Derived, Base>, + + // Check that Base* can _not_ be converted to Derived*, + // again ignoring accessibility. This seals the deal: + // if this conversion cannot happen, it means that Base is an + // ambiguous base and/or it is a virtual base. + // But we have already established that Base is an unambiguous + // base, hence: Base is a virtual base. + std::negation< + qxp::is_detected<is_virtual_base_conversion_test, Base, Derived> + > + > + > + > : std::true_type {}; +} + +template <typename Base, typename Derived> +using is_virtual_base_of = _detail::is_virtual_base_of<std::remove_cv_t<Base>, std::remove_cv_t<Derived>>; + +template <typename Base, typename Derived> +constexpr inline bool is_virtual_base_of_v = is_virtual_base_of<Base, Derived>::value; + } // namespace qxp QT_END_NAMESPACE diff --git a/tests/auto/corelib/global/qxp/CMakeLists.txt b/tests/auto/corelib/global/qxp/CMakeLists.txt index f289a04701..2178f446db 100644 --- a/tests/auto/corelib/global/qxp/CMakeLists.txt +++ b/tests/auto/corelib/global/qxp/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(function_ref) +add_subdirectory(is_virtual_base_of) diff --git a/tests/auto/corelib/global/qxp/is_virtual_base_of/CMakeLists.txt b/tests/auto/corelib/global/qxp/is_virtual_base_of/CMakeLists.txt new file mode 100644 index 0000000000..5762a32f48 --- /dev/null +++ b/tests/auto/corelib/global/qxp/is_virtual_base_of/CMakeLists.txt @@ -0,0 +1,16 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qxp_is_virtual_base_of LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qxp_is_virtual_base_of + EXCEPTIONS + SOURCES + tst_is_virtual_base_of.cpp + LIBRARIES + Qt::Core +) diff --git a/tests/auto/corelib/global/qxp/is_virtual_base_of/tst_is_virtual_base_of.cpp b/tests/auto/corelib/global/qxp/is_virtual_base_of/tst_is_virtual_base_of.cpp new file mode 100644 index 0000000000..2f022ee3de --- /dev/null +++ b/tests/auto/corelib/global/qxp/is_virtual_base_of/tst_is_virtual_base_of.cpp @@ -0,0 +1,98 @@ +// Copyright (C) 2023 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <QtCore/qxptype_traits.h> + +#include <QTest> + +class tst_qxp_is_virtual_base_of : public QObject +{ + Q_OBJECT +}; + +class Base { +public: + virtual ~Base() {} +}; + +// Only works with classes +static_assert(!qxp::is_virtual_base_of_v<int, int>); +static_assert(!qxp::is_virtual_base_of_v<int, Base>); +static_assert(!qxp::is_virtual_base_of_v<Base, int>); + +// A class isn't a virtual base of itself +static_assert(!qxp::is_virtual_base_of_v<Base, Base>); + +// Non-virtual bases +class NonVirtualDerived : public Base {}; +class NonVirtualPrivateDerived : private Base {}; + +static_assert(!qxp::is_virtual_base_of_v<Base, NonVirtualDerived>); +static_assert(!qxp::is_virtual_base_of_v<Base, NonVirtualPrivateDerived>); + +static_assert(!qxp::is_virtual_base_of_v<NonVirtualPrivateDerived, NonVirtualDerived>); +static_assert(!qxp::is_virtual_base_of_v<NonVirtualDerived, NonVirtualPrivateDerived>); + +static_assert(!qxp::is_virtual_base_of_v<tst_qxp_is_virtual_base_of, QObject>); + +// Virtual bases +class VirtualDerived1 : public virtual Base {}; +class VirtualDerived2 : public virtual Base {}; +class VirtualDerived3 : public VirtualDerived1, public VirtualDerived2 {}; +class VirtualDerived4 : public VirtualDerived3, public virtual Base {}; +class VirtualPrivateDerived : private virtual Base {}; + +static_assert(qxp::is_virtual_base_of_v<Base, VirtualDerived1>); +static_assert(qxp::is_virtual_base_of_v<Base, VirtualDerived2>); +static_assert(qxp::is_virtual_base_of_v<Base, VirtualDerived3>); +static_assert(!qxp::is_virtual_base_of_v<VirtualDerived1, VirtualDerived3>); +static_assert(qxp::is_virtual_base_of_v<Base, VirtualDerived4>); +static_assert(qxp::is_virtual_base_of_v<Base, VirtualPrivateDerived>); + +// Ambiguous non-virtual base +class IntermediateDerived : public Base {}; +class AmbiguousBase1 : public IntermediateDerived, public Base {}; +class AmbiguousBase2 : public IntermediateDerived, public virtual Base {}; + +static_assert(!qxp::is_virtual_base_of_v<Base, AmbiguousBase1>); +#ifndef Q_CC_MSVC_ONLY // https://developercommunity.visualstudio.com/t/c-templates-multiple-inheritance-ambiguous-access/185674 +static_assert(!qxp::is_virtual_base_of_v<Base, AmbiguousBase2>); +#endif + +// Const +static_assert(!qxp::is_virtual_base_of_v< Base, const NonVirtualDerived>); +static_assert(!qxp::is_virtual_base_of_v<const Base, NonVirtualDerived>); +static_assert(!qxp::is_virtual_base_of_v<const Base, const NonVirtualDerived>); + +static_assert(!qxp::is_virtual_base_of_v< Base, const NonVirtualPrivateDerived>); +static_assert(!qxp::is_virtual_base_of_v<const Base, NonVirtualPrivateDerived>); +static_assert(!qxp::is_virtual_base_of_v<const Base, const NonVirtualPrivateDerived>); + +static_assert(qxp::is_virtual_base_of_v< Base, const VirtualDerived1>); +static_assert(qxp::is_virtual_base_of_v<const Base, VirtualDerived1>); +static_assert(qxp::is_virtual_base_of_v<const Base, const VirtualDerived1>); + +static_assert(qxp::is_virtual_base_of_v< Base, const VirtualDerived2>); +static_assert(qxp::is_virtual_base_of_v<const Base, VirtualDerived2>); +static_assert(qxp::is_virtual_base_of_v<const Base, const VirtualDerived2>); + +static_assert(qxp::is_virtual_base_of_v< Base, const VirtualDerived3>); +static_assert(qxp::is_virtual_base_of_v<const Base, VirtualDerived3>); +static_assert(qxp::is_virtual_base_of_v<const Base, const VirtualDerived3>); + +static_assert(qxp::is_virtual_base_of_v< Base, const VirtualDerived4>); +static_assert(qxp::is_virtual_base_of_v<const Base, VirtualDerived4>); +static_assert(qxp::is_virtual_base_of_v<const Base, const VirtualDerived4>); + +static_assert(qxp::is_virtual_base_of_v< Base, const VirtualDerived4>); +static_assert(qxp::is_virtual_base_of_v<const Base, VirtualDerived4>); +static_assert(qxp::is_virtual_base_of_v<const Base, const VirtualDerived4>); + +static_assert(qxp::is_virtual_base_of_v< Base, const VirtualPrivateDerived>); +static_assert(qxp::is_virtual_base_of_v<const Base, VirtualPrivateDerived>); +static_assert(qxp::is_virtual_base_of_v<const Base, const VirtualPrivateDerived>); + + +QTEST_APPLESS_MAIN(tst_qxp_is_virtual_base_of); + +#include "tst_is_virtual_base_of.moc" |