1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#ifndef QTYPEINFO_H
6#define QTYPEINFO_H
7
8#include <QtCore/qcompilerdetection.h>
9#include <QtCore/qcontainerfwd.h>
10
11#include <variant>
12#include <optional>
13#include <tuple>
14#include <type_traits>
15
16QT_BEGIN_NAMESPACE
17
18class QDebug;
19
20/*
21 QTypeInfo - type trait functionality
22*/
23
24namespace QtPrivate {
25
26template <typename T>
27inline constexpr bool qIsRelocatable = std::is_trivially_copyable_v<T> && std::is_trivially_destructible_v<T>;
28
29// Denotes types that are trivially default constructible, and for which
30// value-initialization can be achieved by filling their storage with 0 bits.
31// There is no type trait we can use for this, so we hardcode a list of
32// possibilities that we know are OK on the architectures that we support.
33// The most notable exception are pointers to data members, which for instance
34// on the Itanium ABI are initialized to -1.
35template <typename T>
36inline constexpr bool qIsValueInitializationBitwiseZero =
37 std::is_scalar_v<T> && !std::is_member_object_pointer_v<T>;
38
39}
40
41/*
42 The catch-all template.
43*/
44
45template <typename T>
46class QTypeInfo
47{
48public:
49 enum {
50 isPointer [[deprecated("Use std::is_pointer instead")]] = std::is_pointer_v<T>,
51 isIntegral [[deprecated("Use std::is_integral instead")]] = std::is_integral_v<T>,
52 isComplex = !std::is_trivial_v<T>,
53 isRelocatable = QtPrivate::qIsRelocatable<T>,
54 isValueInitializationBitwiseZero = QtPrivate::qIsValueInitializationBitwiseZero<T>,
55 };
56};
57
58template<>
59class QTypeInfo<void>
60{
61public:
62 enum {
63 isPointer [[deprecated("Use std::is_pointer instead")]] = false,
64 isIntegral [[deprecated("Use std::is_integral instead")]] = false,
65 isComplex = false,
66 isRelocatable = false,
67 isValueInitializationBitwiseZero = false,
68 };
69};
70
71/*!
72 \class QTypeInfoMerger
73 \inmodule QtCore
74 \internal
75
76 \brief QTypeInfoMerger merges the QTypeInfo flags of T1, T2... and presents them
77 as a QTypeInfo<T> would do.
78
79 Let's assume that we have a simple set of structs:
80
81 \snippet code/src_corelib_global_qglobal.cpp 50
82
83 To create a proper QTypeInfo specialization for A struct, we have to check
84 all sub-components; B, C and D, then take the lowest common denominator and call
85 Q_DECLARE_TYPEINFO with the resulting flags. An easier and less fragile approach is to
86 use QTypeInfoMerger, which does that automatically. So struct A would have
87 the following QTypeInfo definition:
88
89 \snippet code/src_corelib_global_qglobal.cpp 51
90*/
91template <class T, class...Ts>
92class QTypeInfoMerger
93{
94 static_assert(sizeof...(Ts) > 0);
95public:
96 static constexpr bool isComplex = ((QTypeInfo<Ts>::isComplex) || ...);
97 static constexpr bool isRelocatable = ((QTypeInfo<Ts>::isRelocatable) && ...);
98 [[deprecated("Use std::is_pointer instead")]] static constexpr bool isPointer = false;
99 [[deprecated("Use std::is_integral instead")]] static constexpr bool isIntegral = false;
100 static constexpr bool isValueInitializationBitwiseZero = false;
101 static_assert(!isRelocatable ||
102 std::is_copy_constructible_v<T> ||
103 std::is_move_constructible_v<T>,
104 "All Ts... are Q_RELOCATABLE_TYPE, but T is neither copy- nor move-constructible, "
105 "so cannot be Q_RELOCATABLE_TYPE. Please mark T as Q_COMPLEX_TYPE manually.");
106};
107
108// QTypeInfo for std::pair:
109// std::pair is spec'ed to be struct { T1 first; T2 second; }, so, unlike tuple<>,
110// we _can_ specialize QTypeInfo for pair<>:
111template <class T1, class T2>
112class QTypeInfo<std::pair<T1, T2>> : public QTypeInfoMerger<std::pair<T1, T2>, T1, T2> {};
113
114#define Q_DECLARE_MOVABLE_CONTAINER(CONTAINER) \
115template <typename ...T> \
116class QTypeInfo<CONTAINER<T...>> \
117{ \
118public: \
119 enum { \
120 isPointer [[deprecated("Use std::is_pointer instead")]] = false, \
121 isIntegral [[deprecated("Use std::is_integral instead")]] = false, \
122 isComplex = true, \
123 isRelocatable = true, \
124 isValueInitializationBitwiseZero = false, \
125 }; \
126}
127
128Q_DECLARE_MOVABLE_CONTAINER(QList);
129Q_DECLARE_MOVABLE_CONTAINER(QQueue);
130Q_DECLARE_MOVABLE_CONTAINER(QStack);
131Q_DECLARE_MOVABLE_CONTAINER(QSet);
132Q_DECLARE_MOVABLE_CONTAINER(QMap);
133Q_DECLARE_MOVABLE_CONTAINER(QMultiMap);
134Q_DECLARE_MOVABLE_CONTAINER(QHash);
135Q_DECLARE_MOVABLE_CONTAINER(QMultiHash);
136Q_DECLARE_MOVABLE_CONTAINER(QCache);
137
138#undef Q_DECLARE_MOVABLE_CONTAINER
139
140/*
141 Specialize a specific type with:
142
143 Q_DECLARE_TYPEINFO(type, flags);
144
145 where 'type' is the name of the type to specialize and 'flags' is
146 logically-OR'ed combination of the flags below.
147*/
148enum { /* TYPEINFO flags */
149 Q_COMPLEX_TYPE = 0,
150 Q_PRIMITIVE_TYPE = 0x1,
151 Q_RELOCATABLE_TYPE = 0x2,
152 Q_MOVABLE_TYPE = 0x2,
153 Q_DUMMY_TYPE = 0x4,
154};
155
156#define Q_DECLARE_TYPEINFO_BODY(TYPE, FLAGS) \
157class QTypeInfo<TYPE > \
158{ \
159public: \
160 enum { \
161 isComplex = (((FLAGS) & Q_PRIMITIVE_TYPE) == 0) && !std::is_trivial_v<TYPE>, \
162 isRelocatable = !isComplex || ((FLAGS) & Q_RELOCATABLE_TYPE) || QtPrivate::qIsRelocatable<TYPE>, \
163 isPointer [[deprecated("Use std::is_pointer instead")]] = std::is_pointer_v< TYPE >, \
164 isIntegral [[deprecated("Use std::is_integral instead")]] = std::is_integral< TYPE >::value, \
165 isValueInitializationBitwiseZero = QtPrivate::qIsValueInitializationBitwiseZero<TYPE>, \
166 }; \
167 static_assert(!isRelocatable || \
168 std::is_copy_constructible_v<TYPE > || \
169 std::is_move_constructible_v<TYPE >, \
170 #TYPE " is neither copy- nor move-constructible, so cannot be Q_RELOCATABLE_TYPE"); \
171}
172
173#define Q_DECLARE_TYPEINFO(TYPE, FLAGS) \
174template<> \
175Q_DECLARE_TYPEINFO_BODY(TYPE, FLAGS)
176
177/* Specialize QTypeInfo for QFlags<T> */
178template<typename T> class QFlags;
179template<typename T>
180Q_DECLARE_TYPEINFO_BODY(QFlags<T>, Q_PRIMITIVE_TYPE);
181
182namespace QTypeTraits
183{
184
185/*
186 The templates below aim to find out whether one can safely instantiate an operator==() or
187 operator<() for a type.
188
189 This is tricky for containers, as most containers have unconstrained comparison operators, even though they
190 rely on the corresponding operators for its content.
191 This is especially true for all of the STL template classes that have a comparison operator defined, and
192 leads to the situation, that the compiler would try to instantiate the operator, and fail if any
193 of its template arguments does not have the operator implemented.
194
195 The code tries to cover the relevant cases for Qt and the STL, by checking (recusrsively) the value_type
196 of a container (if it exists), and checking the template arguments of pair, tuple and variant.
197*/
198namespace detail {
199
200// find out whether T is a conteiner
201// this is required to check the value type of containers for the existence of the comparison operator
202template <typename, typename = void>
203struct is_container : std::false_type {};
204template <typename T>
205struct is_container<T, std::void_t<
206 typename T::value_type,
207 std::is_convertible<decltype(std::declval<T>().begin() != std::declval<T>().end()), bool>
208>> : std::true_type {};
209
210
211// Checks the existence of the comparison operator for the class itself
212QT_WARNING_PUSH
213QT_WARNING_DISABLE_FLOAT_COMPARE
214template <typename, typename = void>
215struct has_operator_equal : std::false_type {};
216template <typename T>
217struct has_operator_equal<T, std::void_t<decltype(bool(std::declval<const T&>() == std::declval<const T&>()))>>
218 : std::true_type {};
219QT_WARNING_POP
220
221// Two forward declarations
222template<typename T, bool = is_container<T>::value>
223struct expand_operator_equal_container;
224template<typename T>
225struct expand_operator_equal_tuple;
226
227// the entry point for the public method
228template<typename T>
229using expand_operator_equal = expand_operator_equal_container<T>;
230
231// if T isn't a container check if it's a tuple like object
232template<typename T, bool>
233struct expand_operator_equal_container : expand_operator_equal_tuple<T> {};
234// if T::value_type exists, check first T::value_type, then T itself
235template<typename T>
236struct expand_operator_equal_container<T, true> :
237 std::conjunction<
238 std::disjunction<
239 std::is_same<T, typename T::value_type>, // avoid endless recursion
240 expand_operator_equal<typename T::value_type>
241 >, expand_operator_equal_tuple<T>> {};
242
243// recursively check the template arguments of a tuple like object
244template<typename ...T>
245using expand_operator_equal_recursive = std::conjunction<expand_operator_equal<T>...>;
246
247template<typename T>
248struct expand_operator_equal_tuple : has_operator_equal<T> {};
249template<typename T>
250struct expand_operator_equal_tuple<std::optional<T>> : expand_operator_equal_recursive<T> {};
251template<typename T1, typename T2>
252struct expand_operator_equal_tuple<std::pair<T1, T2>> : expand_operator_equal_recursive<T1, T2> {};
253template<typename ...T>
254struct expand_operator_equal_tuple<std::tuple<T...>> : expand_operator_equal_recursive<T...> {};
255template<typename ...T>
256struct expand_operator_equal_tuple<std::variant<T...>> : expand_operator_equal_recursive<T...> {};
257
258// the same for operator<(), see above for explanations
259template <typename, typename = void>
260struct has_operator_less_than : std::false_type{};
261template <typename T>
262struct has_operator_less_than<T, std::void_t<decltype(bool(std::declval<const T&>() < std::declval<const T&>()))>>
263 : std::true_type{};
264
265template<typename T, bool = is_container<T>::value>
266struct expand_operator_less_than_container;
267template<typename T>
268struct expand_operator_less_than_tuple;
269
270template<typename T>
271using expand_operator_less_than = expand_operator_less_than_container<T>;
272
273template<typename T, bool>
274struct expand_operator_less_than_container : expand_operator_less_than_tuple<T> {};
275template<typename T>
276struct expand_operator_less_than_container<T, true> :
277 std::conjunction<
278 std::disjunction<
279 std::is_same<T, typename T::value_type>,
280 expand_operator_less_than<typename T::value_type>
281 >, expand_operator_less_than_tuple<T>
282 > {};
283
284template<typename ...T>
285using expand_operator_less_than_recursive = std::conjunction<expand_operator_less_than<T>...>;
286
287template<typename T>
288struct expand_operator_less_than_tuple : has_operator_less_than<T> {};
289template<typename T>
290struct expand_operator_less_than_tuple<std::optional<T>> : expand_operator_less_than_recursive<T> {};
291template<typename T1, typename T2>
292struct expand_operator_less_than_tuple<std::pair<T1, T2>> : expand_operator_less_than_recursive<T1, T2> {};
293template<typename ...T>
294struct expand_operator_less_than_tuple<std::tuple<T...>> : expand_operator_less_than_recursive<T...> {};
295template<typename ...T>
296struct expand_operator_less_than_tuple<std::variant<T...>> : expand_operator_less_than_recursive<T...> {};
297
298}
299
300template<typename T, typename = void>
301struct is_dereferenceable : std::false_type {};
302
303template<typename T>
304struct is_dereferenceable<T, std::void_t<decltype(std::declval<T>().operator->())> >
305 : std::true_type {};
306
307template <typename T>
308inline constexpr bool is_dereferenceable_v = is_dereferenceable<T>::value;
309
310template<typename T>
311struct has_operator_equal : detail::expand_operator_equal<T> {};
312template<typename T>
313inline constexpr bool has_operator_equal_v = has_operator_equal<T>::value;
314
315template <typename Container, typename T>
316using has_operator_equal_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_operator_equal<T>>;
317
318template<typename T>
319struct has_operator_less_than : detail::expand_operator_less_than<T> {};
320template<typename T>
321inline constexpr bool has_operator_less_than_v = has_operator_less_than<T>::value;
322
323template <typename Container, typename T>
324using has_operator_less_than_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_operator_less_than<T>>;
325
326template <typename ...T>
327using compare_eq_result = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_equal<T>...>, bool>;
328
329template <typename Container, typename ...T>
330using compare_eq_result_container = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_equal_container<Container, T>...>, bool>;
331
332template <typename ...T>
333using compare_lt_result = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_less_than<T>...>, bool>;
334
335template <typename Container, typename ...T>
336using compare_lt_result_container = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_less_than_container<Container, T>...>, bool>;
337
338namespace detail {
339
340template<typename T>
341const T &const_reference();
342template<typename T>
343T &reference();
344
345}
346
347template <typename Stream, typename, typename = void>
348struct has_ostream_operator : std::false_type {};
349template <typename Stream, typename T>
350struct has_ostream_operator<Stream, T, std::void_t<decltype(detail::reference<Stream>() << detail::const_reference<T>())>>
351 : std::true_type {};
352template <typename Stream, typename T>
353inline constexpr bool has_ostream_operator_v = has_ostream_operator<Stream, T>::value;
354
355template <typename Stream, typename Container, typename T>
356using has_ostream_operator_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_ostream_operator<Stream, T>>;
357
358template <typename Stream, typename, typename = void>
359struct has_istream_operator : std::false_type {};
360template <typename Stream, typename T>
361struct has_istream_operator<Stream, T, std::void_t<decltype(detail::reference<Stream>() >> detail::reference<T>())>>
362 : std::true_type {};
363template <typename Stream, typename T>
364inline constexpr bool has_istream_operator_v = has_istream_operator<Stream, T>::value;
365template <typename Stream, typename Container, typename T>
366using has_istream_operator_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_istream_operator<Stream, T>>;
367
368template <typename Stream, typename T>
369inline constexpr bool has_stream_operator_v = has_ostream_operator_v<Stream, T> && has_istream_operator_v<Stream, T>;
370
371}
372
373
374QT_END_NAMESPACE
375#endif // QTYPEINFO_H
376