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

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