1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QTTYPETRAITS_H
5#define QTTYPETRAITS_H
6
7#include <QtCore/qtconfigmacros.h>
8#include <QtCore/qtdeprecationmarkers.h>
9
10#include <optional>
11#include <tuple>
12#include <type_traits>
13#include <utility>
14#include <variant>
15
16#if 0
17#pragma qt_class(QtTypeTraits)
18#pragma qt_sync_stop_processing
19#endif
20
21QT_BEGIN_NAMESPACE
22
23// like std::to_underlying
24template <typename Enum>
25constexpr std::underlying_type_t<Enum> qToUnderlying(Enum e) noexcept
26{
27 return static_cast<std::underlying_type_t<Enum>>(e);
28}
29
30#ifndef QT_NO_QASCONST
31#if QT_DEPRECATED_SINCE(6, 6)
32
33// this adds const to non-const objects (like std::as_const)
34template <typename T>
35QT_DEPRECATED_VERSION_X_6_6("Use std::as_const() instead.")
36constexpr typename std::add_const<T>::type &qAsConst(T &t) noexcept { return t; }
37// prevent rvalue arguments:
38template <typename T>
39void qAsConst(const T &&) = delete;
40
41#endif // QT_DEPRECATED_SINCE(6, 6)
42#endif // QT_NO_QASCONST
43
44#ifndef QT_NO_QEXCHANGE
45
46// like std::exchange
47template <typename T, typename U = T>
48constexpr T qExchange(T &t, U &&newValue)
49noexcept(std::conjunction_v<std::is_nothrow_move_constructible<T>,
50 std::is_nothrow_assignable<T &, U>>)
51{
52 T old = std::move(t);
53 t = std::forward<U>(newValue);
54 return old;
55}
56
57#endif // QT_NO_QEXCHANGE
58
59namespace QtPrivate {
60// helper to be used to trigger a "dependent static_assert(false)"
61// (for instance, in a final `else` branch of a `if constexpr`.)
62template <typename T> struct type_dependent_false : std::false_type {};
63template <auto T> struct value_dependent_false : std::false_type {};
64}
65
66namespace QTypeTraits {
67
68namespace detail {
69template<typename T, typename U,
70 typename = std::enable_if_t<std::is_arithmetic_v<T> && std::is_arithmetic_v<U> &&
71 std::is_floating_point_v<T> == std::is_floating_point_v<U> &&
72 std::is_signed_v<T> == std::is_signed_v<U> &&
73 !std::is_same_v<T, bool> && !std::is_same_v<U, bool> &&
74 !std::is_same_v<T, char> && !std::is_same_v<U, char>>>
75struct Promoted
76{
77 using type = decltype(T() + U());
78};
79}
80
81template <typename T, typename U>
82using Promoted = typename detail::Promoted<T, U>::type;
83
84/*
85 The templates below aim to find out whether one can safely instantiate an operator==() or
86 operator<() for a type.
87
88 This is tricky for containers, as most containers have unconstrained comparison operators, even though they
89 rely on the corresponding operators for its content.
90 This is especially true for all of the STL template classes that have a comparison operator defined, and
91 leads to the situation, that the compiler would try to instantiate the operator, and fail if any
92 of its template arguments does not have the operator implemented.
93
94 The code tries to cover the relevant cases for Qt and the STL, by checking (recusrsively) the value_type
95 of a container (if it exists), and checking the template arguments of pair, tuple and variant.
96*/
97namespace detail {
98
99// find out whether T is a conteiner
100// this is required to check the value type of containers for the existence of the comparison operator
101template <typename, typename = void>
102struct is_container : std::false_type {};
103template <typename T>
104struct is_container<T, std::void_t<
105 typename T::value_type,
106 std::is_convertible<decltype(std::declval<T>().begin() != std::declval<T>().end()), bool>
107>> : std::true_type {};
108
109
110// Checks the existence of the comparison operator for the class itself
111QT_WARNING_PUSH
112QT_WARNING_DISABLE_FLOAT_COMPARE
113template <typename, typename = void>
114struct has_operator_equal : std::false_type {};
115template <typename T>
116struct has_operator_equal<T, std::void_t<decltype(bool(std::declval<const T&>() == std::declval<const T&>()))>>
117 : std::true_type {};
118QT_WARNING_POP
119
120// Two forward declarations
121template<typename T, bool = is_container<T>::value>
122struct expand_operator_equal_container;
123template<typename T>
124struct expand_operator_equal_tuple;
125
126// the entry point for the public method
127template<typename T>
128using expand_operator_equal = expand_operator_equal_container<T>;
129
130// if T isn't a container check if it's a tuple like object
131template<typename T, bool>
132struct expand_operator_equal_container : expand_operator_equal_tuple<T> {};
133// if T::value_type exists, check first T::value_type, then T itself
134template<typename T>
135struct expand_operator_equal_container<T, true> :
136 std::conjunction<
137 std::disjunction<
138 std::is_same<T, typename T::value_type>, // avoid endless recursion
139 expand_operator_equal<typename T::value_type>
140 >, expand_operator_equal_tuple<T>> {};
141
142// recursively check the template arguments of a tuple like object
143template<typename ...T>
144using expand_operator_equal_recursive = std::conjunction<expand_operator_equal<T>...>;
145
146template<typename T>
147struct expand_operator_equal_tuple : has_operator_equal<T> {};
148template<typename T>
149struct expand_operator_equal_tuple<std::optional<T>> : expand_operator_equal_recursive<T> {};
150template<typename T1, typename T2>
151struct expand_operator_equal_tuple<std::pair<T1, T2>> : expand_operator_equal_recursive<T1, T2> {};
152template<typename ...T>
153struct expand_operator_equal_tuple<std::tuple<T...>> : expand_operator_equal_recursive<T...> {};
154template<typename ...T>
155struct expand_operator_equal_tuple<std::variant<T...>> : expand_operator_equal_recursive<T...> {};
156
157// the same for operator<(), see above for explanations
158template <typename, typename = void>
159struct has_operator_less_than : std::false_type{};
160template <typename T>
161struct has_operator_less_than<T, std::void_t<decltype(bool(std::declval<const T&>() < std::declval<const T&>()))>>
162 : std::true_type{};
163
164template<typename T, bool = is_container<T>::value>
165struct expand_operator_less_than_container;
166template<typename T>
167struct expand_operator_less_than_tuple;
168
169template<typename T>
170using expand_operator_less_than = expand_operator_less_than_container<T>;
171
172template<typename T, bool>
173struct expand_operator_less_than_container : expand_operator_less_than_tuple<T> {};
174template<typename T>
175struct expand_operator_less_than_container<T, true> :
176 std::conjunction<
177 std::disjunction<
178 std::is_same<T, typename T::value_type>,
179 expand_operator_less_than<typename T::value_type>
180 >, expand_operator_less_than_tuple<T>
181 > {};
182
183template<typename ...T>
184using expand_operator_less_than_recursive = std::conjunction<expand_operator_less_than<T>...>;
185
186template<typename T>
187struct expand_operator_less_than_tuple : has_operator_less_than<T> {};
188template<typename T>
189struct expand_operator_less_than_tuple<std::optional<T>> : expand_operator_less_than_recursive<T> {};
190template<typename T1, typename T2>
191struct expand_operator_less_than_tuple<std::pair<T1, T2>> : expand_operator_less_than_recursive<T1, T2> {};
192template<typename ...T>
193struct expand_operator_less_than_tuple<std::tuple<T...>> : expand_operator_less_than_recursive<T...> {};
194template<typename ...T>
195struct expand_operator_less_than_tuple<std::variant<T...>> : expand_operator_less_than_recursive<T...> {};
196
197}
198
199template<typename T, typename = void>
200struct is_dereferenceable : std::false_type {};
201
202template<typename T>
203struct is_dereferenceable<T, std::void_t<decltype(std::declval<T>().operator->())> >
204 : std::true_type {};
205
206template <typename T>
207inline constexpr bool is_dereferenceable_v = is_dereferenceable<T>::value;
208
209template<typename T>
210struct has_operator_equal : detail::expand_operator_equal<T> {};
211template<typename T>
212inline constexpr bool has_operator_equal_v = has_operator_equal<T>::value;
213
214template <typename Container, typename T>
215using has_operator_equal_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_operator_equal<T>>;
216
217template<typename T>
218struct has_operator_less_than : detail::expand_operator_less_than<T> {};
219template<typename T>
220inline constexpr bool has_operator_less_than_v = has_operator_less_than<T>::value;
221
222template <typename Container, typename T>
223using has_operator_less_than_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_operator_less_than<T>>;
224
225template <typename ...T>
226using compare_eq_result = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_equal<T>...>, bool>;
227
228template <typename Container, typename ...T>
229using compare_eq_result_container = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_equal_container<Container, T>...>, bool>;
230
231template <typename ...T>
232using compare_lt_result = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_less_than<T>...>, bool>;
233
234template <typename Container, typename ...T>
235using compare_lt_result_container = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_less_than_container<Container, T>...>, bool>;
236
237namespace detail {
238
239template<typename T>
240const T &const_reference();
241template<typename T>
242T &reference();
243
244}
245
246template <typename Stream, typename, typename = void>
247struct has_ostream_operator : std::false_type {};
248template <typename Stream, typename T>
249struct has_ostream_operator<Stream, T, std::void_t<decltype(detail::reference<Stream>() << detail::const_reference<T>())>>
250 : std::true_type {};
251template <typename Stream, typename T>
252inline constexpr bool has_ostream_operator_v = has_ostream_operator<Stream, T>::value;
253
254template <typename Stream, typename Container, typename T>
255using has_ostream_operator_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_ostream_operator<Stream, T>>;
256
257template <typename Stream, typename, typename = void>
258struct has_istream_operator : std::false_type {};
259template <typename Stream, typename T>
260struct has_istream_operator<Stream, T, std::void_t<decltype(detail::reference<Stream>() >> detail::reference<T>())>>
261 : std::true_type {};
262template <typename Stream, typename T>
263inline constexpr bool has_istream_operator_v = has_istream_operator<Stream, T>::value;
264template <typename Stream, typename Container, typename T>
265using has_istream_operator_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_istream_operator<Stream, T>>;
266
267template <typename Stream, typename T>
268inline constexpr bool has_stream_operator_v = has_ostream_operator_v<Stream, T> && has_istream_operator_v<Stream, T>;
269
270} // namespace QTypeTraits
271
272QT_END_NAMESPACE
273
274#endif // QTTYPETRAITS_H
275

source code of qtbase/src/corelib/global/qttypetraits.h