Long live qxp::is_virtual_base_of
To be used in QWeakPointer. Change-Id: I5ee9dd0862a0b23d316aaadf5d68bef1ce609e8b Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>bb10
parent
bf7732baca
commit
35878fa924
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
add_subdirectory(function_ref)
|
||||
add_subdirectory(is_virtual_base_of)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
@ -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"
|
||||
Loading…
Reference in New Issue