1 | /**************************************************************************** |
---|---|

2 | ** |

3 | ** Copyright (C) 2016 The Qt Company Ltd. |

4 | ** Copyright (C) 2016 Intel Corporation. |

5 | ** Contact: https://www.qt.io/licensing/ |

6 | ** |

7 | ** This file is part of the QtCore module of the Qt Toolkit. |

8 | ** |

9 | ** $QT_BEGIN_LICENSE:LGPL$ |

10 | ** Commercial License Usage |

11 | ** Licensees holding valid commercial Qt licenses may use this file in |

12 | ** accordance with the commercial license agreement provided with the |

13 | ** Software or, alternatively, in accordance with the terms contained in |

14 | ** a written agreement between you and The Qt Company. For licensing terms |

15 | ** and conditions see https://www.qt.io/terms-conditions. For further |

16 | ** information use the contact form at https://www.qt.io/contact-us. |

17 | ** |

18 | ** GNU Lesser General Public License Usage |

19 | ** Alternatively, this file may be used under the terms of the GNU Lesser |

20 | ** General Public License version 3 as published by the Free Software |

21 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |

22 | ** packaging of this file. Please review the following information to |

23 | ** ensure the GNU Lesser General Public License version 3 requirements |

24 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |

25 | ** |

26 | ** GNU General Public License Usage |

27 | ** Alternatively, this file may be used under the terms of the GNU |

28 | ** General Public License version 2.0 or (at your option) the GNU General |

29 | ** Public license version 3 or any later version approved by the KDE Free |

30 | ** Qt Foundation. The licenses are as published by the Free Software |

31 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |

32 | ** included in the packaging of this file. Please review the following |

33 | ** information to ensure the GNU General Public License requirements will |

34 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |

35 | ** https://www.gnu.org/licenses/gpl-3.0.html. |

36 | ** |

37 | ** $QT_END_LICENSE$ |

38 | ** |

39 | ****************************************************************************/ |

40 | |

41 | #include <QtCore/qglobal.h> |

42 | #include <QtCore/qcontainerfwd.h> |

43 | #include <variant> |

44 | #include <optional> |

45 | #include <tuple> |

46 | |

47 | #ifndef QTYPEINFO_H |

48 | #define QTYPEINFO_H |

49 | |

50 | QT_BEGIN_NAMESPACE |

51 | |

52 | class QDebug; |

53 | |

54 | /* |

55 | QTypeInfo - type trait functionality |

56 | */ |

57 | |

58 | template <typename T> |

59 | inline constexpr bool qIsRelocatable = std::is_trivially_copyable_v<T> && std::is_trivially_destructible_v<T>; |

60 | |

61 | /* |

62 | The catch-all template. |

63 | */ |

64 | |

65 | template <typename T> |

66 | class QTypeInfo |

67 | { |

68 | public: |

69 | enum { |

70 | isPointer = std::is_pointer_v<T>, |

71 | isIntegral = std::is_integral_v<T>, |

72 | isComplex = !std::is_trivial_v<T>, |

73 | isRelocatable = qIsRelocatable<T>, |

74 | }; |

75 | }; |

76 | |

77 | template<> |

78 | class QTypeInfo<void> |

79 | { |

80 | public: |

81 | enum { |

82 | isPointer = false, |

83 | isIntegral = false, |

84 | isComplex = false, |

85 | isRelocatable = false, |

86 | }; |

87 | }; |

88 | |

89 | /*! |

90 | \class QTypeInfoMerger |

91 | \inmodule QtCore |

92 | \internal |

93 | |

94 | \brief QTypeInfoMerger merges the QTypeInfo flags of T1, T2... and presents them |

95 | as a QTypeInfo<T> would do. |

96 | |

97 | Let's assume that we have a simple set of structs: |

98 | |

99 | \snippet code/src_corelib_global_qglobal.cpp 50 |

100 | |

101 | To create a proper QTypeInfo specialization for A struct, we have to check |

102 | all sub-components; B, C and D, then take the lowest common denominator and call |

103 | Q_DECLARE_TYPEINFO with the resulting flags. An easier and less fragile approach is to |

104 | use QTypeInfoMerger, which does that automatically. So struct A would have |

105 | the following QTypeInfo definition: |

106 | |

107 | \snippet code/src_corelib_global_qglobal.cpp 51 |

108 | */ |

109 | template <class T, class...Ts> |

110 | class QTypeInfoMerger |

111 | { |

112 | static_assert(sizeof...(Ts) > 0); |

113 | public: |

114 | static constexpr bool isComplex = ((QTypeInfo<Ts>::isComplex) || ...); |

115 | static constexpr bool isRelocatable = ((QTypeInfo<Ts>::isRelocatable) && ...); |

116 | static constexpr bool isPointer = false; |

117 | static constexpr bool isIntegral = false; |

118 | }; |

119 | |

120 | #define Q_DECLARE_MOVABLE_CONTAINER(CONTAINER) \ |

121 | template <typename ...T> \ |

122 | class QTypeInfo<CONTAINER<T...>> \ |

123 | { \ |

124 | public: \ |

125 | enum { \ |

126 | isPointer = false, \ |

127 | isIntegral = false, \ |

128 | isComplex = true, \ |

129 | isRelocatable = true, \ |

130 | }; \ |

131 | } |

132 | |

133 | Q_DECLARE_MOVABLE_CONTAINER(QList); |

134 | Q_DECLARE_MOVABLE_CONTAINER(QQueue); |

135 | Q_DECLARE_MOVABLE_CONTAINER(QStack); |

136 | Q_DECLARE_MOVABLE_CONTAINER(QSet); |

137 | Q_DECLARE_MOVABLE_CONTAINER(QMap); |

138 | Q_DECLARE_MOVABLE_CONTAINER(QMultiMap); |

139 | Q_DECLARE_MOVABLE_CONTAINER(QHash); |

140 | Q_DECLARE_MOVABLE_CONTAINER(QMultiHash); |

141 | Q_DECLARE_MOVABLE_CONTAINER(QCache); |

142 | |

143 | #undef Q_DECLARE_MOVABLE_CONTAINER |

144 | |

145 | /* |

146 | Specialize a specific type with: |

147 | |

148 | Q_DECLARE_TYPEINFO(type, flags); |

149 | |

150 | where 'type' is the name of the type to specialize and 'flags' is |

151 | logically-OR'ed combination of the flags below. |

152 | */ |

153 | enum { /* TYPEINFO flags */ |

154 | Q_COMPLEX_TYPE = 0, |

155 | Q_PRIMITIVE_TYPE = 0x1, |

156 | Q_RELOCATABLE_TYPE = 0x2, |

157 | Q_MOVABLE_TYPE = 0x2, |

158 | Q_DUMMY_TYPE = 0x4, |

159 | }; |

160 | |

161 | #define Q_DECLARE_TYPEINFO_BODY(TYPE, FLAGS) \ |

162 | class QTypeInfo<TYPE > \ |

163 | { \ |

164 | public: \ |

165 | enum { \ |

166 | isComplex = (((FLAGS) & Q_PRIMITIVE_TYPE) == 0) && !std::is_trivial_v<TYPE>, \ |

167 | isRelocatable = !isComplex || ((FLAGS) & Q_RELOCATABLE_TYPE) || qIsRelocatable<TYPE>, \ |

168 | isPointer = false, \ |

169 | isIntegral = std::is_integral< TYPE >::value, \ |

170 | }; \ |

171 | } |

172 | |

173 | #define Q_DECLARE_TYPEINFO(TYPE, FLAGS) \ |

174 | template<> \ |

175 | Q_DECLARE_TYPEINFO_BODY(TYPE, FLAGS) |

176 | |

177 | /* Specialize QTypeInfo for QFlags<T> */ |

178 | template<typename T> class QFlags; |

179 | template<typename T> |

180 | Q_DECLARE_TYPEINFO_BODY(QFlags<T>, Q_PRIMITIVE_TYPE); |

181 | |

182 | /* |

183 | Specialize a shared type with: |

184 | |

185 | Q_DECLARE_SHARED(type) |

186 | |

187 | where 'type' is the name of the type to specialize. NOTE: shared |

188 | types must define a member-swap, and be defined in the same |

189 | namespace as Qt for this to work. |

190 | |

191 | If the type was already released without Q_DECLARE_SHARED applied, |

192 | _and_ without an explicit Q_DECLARE_TYPEINFO(type, Q_RELOCATABLE_TYPE), |

193 | then use Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(type) to mark the |

194 | type shared (incl. swap()), without marking it movable (which |

195 | would change the memory layout of QList, a BiC change. |

196 | */ |

197 | |

198 | #define Q_DECLARE_SHARED_IMPL(TYPE, FLAGS) \ |

199 | Q_DECLARE_TYPEINFO(TYPE, FLAGS); \ |

200 | inline void swap(TYPE &value1, TYPE &value2) \ |

201 | noexcept(noexcept(value1.swap(value2))) \ |

202 | { value1.swap(value2); } |

203 | #define Q_DECLARE_SHARED(TYPE) Q_DECLARE_SHARED_IMPL(TYPE, Q_RELOCATABLE_TYPE) |

204 | #define Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(TYPE) \ |

205 | Q_DECLARE_SHARED_IMPL(TYPE, Q_RELOCATABLE_TYPE) |

206 | |

207 | namespace QTypeTraits |

208 | { |

209 | |

210 | /* |

211 | The templates below aim to find out whether one can safely instantiate an operator==() or |

212 | operator<() for a type. |

213 | |

214 | This is tricky for containers, as most containers have unconstrained comparison operators, even though they |

215 | rely on the corresponding operators for its content. |

216 | This is especially true for all of the STL template classes that have a comparison operator defined, and |

217 | leads to the situation, that the compiler would try to instantiate the operator, and fail if any |

218 | of its template arguments does not have the operator implemented. |

219 | |

220 | The code tries to cover the relevant cases for Qt and the STL, by checking (recusrsively) the value_type |

221 | of a container (if it exists), and checking the template arguments of pair, tuple and variant. |

222 | */ |

223 | namespace detail { |

224 | |

225 | // find out whether T is a conteiner |

226 | // this is required to check the value type of containers for the existence of the comparison operator |

227 | template <typename, typename = void> |

228 | struct is_container : std::false_type {}; |

229 | template <typename T> |

230 | struct is_container<T, std::void_t< |

231 | typename T::value_type, |

232 | std::is_convertible<decltype(std::declval<T>().begin() != std::declval<T>().end()), bool> |

233 | >> : std::true_type {}; |

234 | |

235 | |

236 | // Checks the existence of the comparison operator for the class itself |

237 | QT_WARNING_PUSH |

238 | QT_WARNING_DISABLE_FLOAT_COMPARE |

239 | template <typename, typename = void> |

240 | struct has_operator_equal : std::false_type {}; |

241 | template <typename T> |

242 | struct has_operator_equal<T, std::void_t<decltype(bool(std::declval<const T&>() == std::declval<const T&>()))>> |

243 | : std::true_type {}; |

244 | QT_WARNING_POP |

245 | |

246 | // Two forward declarations |

247 | template<typename T, bool = is_container<T>::value> |

248 | struct expand_operator_equal_container; |

249 | template<typename T> |

250 | struct expand_operator_equal_tuple; |

251 | |

252 | // the entry point for the public method |

253 | template<typename T> |

254 | using expand_operator_equal = expand_operator_equal_container<T>; |

255 | |

256 | // if T isn't a container check if it's a tuple like object |

257 | template<typename T, bool> |

258 | struct expand_operator_equal_container : expand_operator_equal_tuple<T> {}; |

259 | // if T::value_type exists, check first T::value_type, then T itself |

260 | template<typename T> |

261 | struct expand_operator_equal_container<T, true> : |

262 | std::conjunction< |

263 | std::disjunction< |

264 | std::is_same<T, typename T::value_type>, // avoid endless recursion |

265 | expand_operator_equal<typename T::value_type> |

266 | >, expand_operator_equal_tuple<T>> {}; |

267 | |

268 | // recursively check the template arguments of a tuple like object |

269 | template<typename ...T> |

270 | using expand_operator_equal_recursive = std::conjunction<expand_operator_equal<T>...>; |

271 | |

272 | template<typename T> |

273 | struct expand_operator_equal_tuple : has_operator_equal<T> {}; |

274 | template<typename T> |

275 | struct expand_operator_equal_tuple<std::optional<T>> : has_operator_equal<T> {}; |

276 | template<typename T1, typename T2> |

277 | struct expand_operator_equal_tuple<std::pair<T1, T2>> : expand_operator_equal_recursive<T1, T2> {}; |

278 | template<typename ...T> |

279 | struct expand_operator_equal_tuple<std::tuple<T...>> : expand_operator_equal_recursive<T...> {}; |

280 | template<typename ...T> |

281 | struct expand_operator_equal_tuple<std::variant<T...>> : expand_operator_equal_recursive<T...> {}; |

282 | |

283 | // the same for operator<(), see above for explanations |

284 | template <typename, typename = void> |

285 | struct has_operator_less_than : std::false_type{}; |

286 | template <typename T> |

287 | struct has_operator_less_than<T, std::void_t<decltype(bool(std::declval<const T&>() < std::declval<const T&>()))>> |

288 | : std::true_type{}; |

289 | |

290 | template<typename T, bool = is_container<T>::value> |

291 | struct expand_operator_less_than_container; |

292 | template<typename T> |

293 | struct expand_operator_less_than_tuple; |

294 | |

295 | template<typename T> |

296 | using expand_operator_less_than = expand_operator_less_than_container<T>; |

297 | |

298 | template<typename T, bool> |

299 | struct expand_operator_less_than_container : expand_operator_less_than_tuple<T> {}; |

300 | template<typename T> |

301 | struct expand_operator_less_than_container<T, true> : |

302 | std::conjunction< |

303 | std::disjunction< |

304 | std::is_same<T, typename T::value_type>, |

305 | expand_operator_less_than<typename T::value_type> |

306 | >, expand_operator_less_than_tuple<T> |

307 | > {}; |

308 | |

309 | template<typename ...T> |

310 | using expand_operator_less_than_recursive = std::conjunction<expand_operator_less_than<T>...>; |

311 | |

312 | template<typename T> |

313 | struct expand_operator_less_than_tuple : has_operator_less_than<T> {}; |

314 | template<typename T> |

315 | struct expand_operator_less_than_tuple<std::optional<T>> : has_operator_less_than<T> {}; |

316 | template<typename T1, typename T2> |

317 | struct expand_operator_less_than_tuple<std::pair<T1, T2>> : expand_operator_less_than_recursive<T1, T2> {}; |

318 | template<typename ...T> |

319 | struct expand_operator_less_than_tuple<std::tuple<T...>> : expand_operator_less_than_recursive<T...> {}; |

320 | template<typename ...T> |

321 | struct expand_operator_less_than_tuple<std::variant<T...>> : expand_operator_less_than_recursive<T...> {}; |

322 | |

323 | } |

324 | |

325 | template<typename T, typename = void> |

326 | struct is_dereferenceable : std::false_type {}; |

327 | |

328 | template<typename T> |

329 | struct is_dereferenceable<T, std::void_t<decltype(std::declval<T>().operator->())> > |

330 | : std::true_type {}; |

331 | |

332 | template <typename T> |

333 | inline constexpr bool is_dereferenceable_v = is_dereferenceable<T>::value; |

334 | |

335 | template<typename T> |

336 | struct has_operator_equal : detail::expand_operator_equal<T> {}; |

337 | template<typename T> |

338 | inline constexpr bool has_operator_equal_v = has_operator_equal<T>::value; |

339 | |

340 | template <typename Container, typename T> |

341 | using has_operator_equal_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_operator_equal<T>>; |

342 | |

343 | template<typename T> |

344 | struct has_operator_less_than : detail::expand_operator_less_than<T> {}; |

345 | template<typename T> |

346 | inline constexpr bool has_operator_less_than_v = has_operator_less_than<T>::value; |

347 | |

348 | template <typename Container, typename T> |

349 | using has_operator_less_than_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_operator_less_than<T>>; |

350 | |

351 | template <typename ...T> |

352 | using compare_eq_result = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_equal<T>...>, bool>; |

353 | |

354 | template <typename Container, typename ...T> |

355 | using compare_eq_result_container = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_equal_container<Container, T>...>, bool>; |

356 | |

357 | template <typename ...T> |

358 | using compare_lt_result = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_less_than<T>...>, bool>; |

359 | |

360 | template <typename Container, typename ...T> |

361 | using compare_lt_result_container = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_less_than_container<Container, T>...>, bool>; |

362 | |

363 | namespace detail { |

364 | |

365 | template<typename T> |

366 | const T &const_reference(); |

367 | template<typename T> |

368 | T &reference(); |

369 | |

370 | } |

371 | |

372 | template <typename Stream, typename, typename = void> |

373 | struct has_ostream_operator : std::false_type {}; |

374 | template <typename Stream, typename T> |

375 | struct has_ostream_operator<Stream, T, std::void_t<decltype(detail::reference<Stream>() << detail::const_reference<T>())>> |

376 | : std::true_type {}; |

377 | template <typename Stream, typename T> |

378 | inline constexpr bool has_ostream_operator_v = has_ostream_operator<Stream, T>::value; |

379 | |

380 | template <typename Stream, typename Container, typename T> |

381 | using has_ostream_operator_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_ostream_operator<Stream, T>>; |

382 | |

383 | template <typename Stream, typename, typename = void> |

384 | struct has_istream_operator : std::false_type {}; |

385 | template <typename Stream, typename T> |

386 | struct has_istream_operator<Stream, T, std::void_t<decltype(detail::reference<Stream>() >> detail::reference<T>())>> |

387 | : std::true_type {}; |

388 | template <typename Stream, typename T> |

389 | inline constexpr bool has_istream_operator_v = has_istream_operator<Stream, T>::value; |

390 | template <typename Stream, typename Container, typename T> |

391 | using has_istream_operator_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_istream_operator<Stream, T>>; |

392 | |

393 | template <typename Stream, typename T> |

394 | inline constexpr bool has_stream_operator_v = has_ostream_operator_v<Stream, T> && has_istream_operator_v<Stream, T>; |

395 | |

396 | } |

397 | |

398 | |

399 | QT_END_NAMESPACE |

400 | #endif // QTYPEINFO_H |

401 |