summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/global/qxptype_traits.h60
-rw-r--r--tests/auto/corelib/global/qxp/CMakeLists.txt1
-rw-r--r--tests/auto/corelib/global/qxp/is_virtual_base_of/CMakeLists.txt16
-rw-r--r--tests/auto/corelib/global/qxp/is_virtual_base_of/tst_is_virtual_base_of.cpp98
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"