Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qcontainertools_impl.h
Go to the documentation of this file.
1// Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
2// Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
3// Copyright (C) 2020 The Qt Company Ltd.
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5
6#if 0
7#pragma qt_sync_skip_header_check
8#pragma qt_sync_stop_processing
9#endif
10
11#ifndef QCONTAINERTOOLS_IMPL_H
12#define QCONTAINERTOOLS_IMPL_H
13
14#include <QtCore/qglobal.h>
15#include <QtCore/qtypeinfo.h>
16
17#include <QtCore/qxptype_traits.h>
18
19#include <cstring>
20#include <iterator>
21#include <memory>
22#include <algorithm>
23
25
26namespace QtPrivate
27{
28
35template<typename T, typename Cmp = std::less<>>
36static constexpr bool q_points_into_range(const T *p, const T *b, const T *e,
37 Cmp less = {}) noexcept
38{
39 return !less(p, b) && less(p, e);
40}
41
48template <typename C, typename T>
49static constexpr bool q_points_into_range(const T &p, const C &c) noexcept
50{
51 static_assert(std::is_same_v<decltype(std::data(c)), T>);
52
53 // std::distance because QArrayDataPointer has a "qsizetype size"
54 // member but no size() function
55 return q_points_into_range(p, std::data(c),
56 std::data(c) + std::distance(std::begin(c), std::end(c)));
57}
58
60QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
61
62template <typename T, typename N>
64{
65 if constexpr (std::is_nothrow_move_constructible_v<T> || !std::is_copy_constructible_v<T>)
66 std::uninitialized_move_n(first, n, out);
67 else
68 std::uninitialized_copy_n(first, n, out);
69}
70
71template <typename T, typename N>
73{
74 if constexpr (QTypeInfo<T>::isRelocatable) {
75 static_assert(std::is_copy_constructible_v<T> || std::is_move_constructible_v<T>,
76 "Refusing to relocate this non-copy/non-move-constructible type.");
77 if (n != N(0)) { // even if N == 0, out == nullptr or first == nullptr are UB for memcpy()
78 std::memcpy(static_cast<void *>(out),
79 static_cast<const void *>(first),
80 n * sizeof(T));
81 }
82 } else {
84 if constexpr (QTypeInfo<T>::isComplex)
85 std::destroy_n(first, n);
86 }
87}
88
90
100template <typename T>
101void q_rotate(T *first, T *mid, T *last)
102{
103 if constexpr (QTypeInfo<T>::isRelocatable) {
104 const auto cast = [](T *p) { return reinterpret_cast<uchar*>(p); };
105 std::rotate(cast(first), cast(mid), cast(last));
106 } else {
107 std::rotate(first, mid, last);
108 }
109}
110
123template <typename T, typename Predicate>
124T *q_uninitialized_remove_copy_if(T *first, T *last, T *out, Predicate &pred)
125{
126 static_assert(std::is_nothrow_destructible_v<T>,
127 "This algorithm requires that T has a non-throwing destructor");
129
130 T *dest_begin = out;
131 QT_TRY {
132 while (first != last) {
133 if (!pred(*first)) {
134 new (std::addressof(*out)) T(*first);
135 ++out;
136 }
137 ++first;
138 }
139 } QT_CATCH (...) {
140 std::destroy(std::reverse_iterator(out), std::reverse_iterator(dest_begin));
142 }
143 return out;
144}
145
146template<typename iterator, typename N>
147void q_relocate_overlap_n_left_move(iterator first, N n, iterator d_first)
148{
149 // requires: [first, n) is a valid range
150 // requires: d_first + n is reachable from d_first
151 // requires: iterator is at least a random access iterator
152 // requires: value_type(iterator) has a non-throwing destructor
153
154 Q_ASSERT(n);
155 Q_ASSERT(d_first < first); // only allow moves to the "left"
156 using T = typename std::iterator_traits<iterator>::value_type;
157
158 // Watches passed iterator. Unless commit() is called, all the elements that
159 // the watched iterator passes through are deleted at the end of object
160 // lifetime. freeze() could be used to stop watching the passed iterator and
161 // remain at current place.
162 //
163 // requires: the iterator is expected to always point to an invalid object
164 // (to uninitialized memory)
165 struct Destructor
166 {
167 iterator *iter;
168 iterator end;
169 iterator intermediate;
170
171 Destructor(iterator &it) noexcept : iter(std::addressof(it)), end(it) { }
172 void commit() noexcept { iter = std::addressof(end); }
173 void freeze() noexcept
174 {
175 intermediate = *iter;
176 iter = std::addressof(intermediate);
177 }
178 ~Destructor() noexcept
179 {
180 for (const int step = *iter < end ? 1 : -1; *iter != end;) {
181 std::advance(*iter, step);
182 (*iter)->~T();
183 }
184 }
185 } destroyer(d_first);
186
187 const iterator d_last = d_first + n;
188 // Note: use pair and explicitly copy iterators from it to prevent
189 // accidental reference semantics instead of copy. equivalent to:
190 //
191 // auto [overlapBegin, overlapEnd] = std::minmax(d_last, first);
192 auto pair = std::minmax(d_last, first);
193
194 // overlap area between [d_first, d_first + n) and [first, first + n) or an
195 // uninitialized memory area between the two ranges
196 iterator overlapBegin = pair.first;
197 iterator overlapEnd = pair.second;
198
199 // move construct elements in uninitialized region
200 while (d_first != overlapBegin) {
201 // account for std::reverse_iterator, cannot use new(d_first) directly
202 new (std::addressof(*d_first)) T(std::move_if_noexcept(*first));
203 ++d_first;
204 ++first;
205 }
206
207 // cannot commit but have to stop - there might be an overlap region
208 // which we don't want to delete (because it's part of existing data)
209 destroyer.freeze();
210
211 // move assign elements in overlap region
212 while (d_first != d_last) {
213 *d_first = std::move_if_noexcept(*first);
214 ++d_first;
215 ++first;
216 }
217
218 Q_ASSERT(d_first == destroyer.end + n);
219 destroyer.commit(); // can commit here as ~T() below does not throw
220
221 while (first != overlapEnd)
222 (--first)->~T();
223}
224
235template<typename T, typename N>
236void q_relocate_overlap_n(T *first, N n, T *d_first)
237{
238 static_assert(std::is_nothrow_destructible_v<T>,
239 "This algorithm requires that T has a non-throwing destructor");
240
241 if (n == N(0) || first == d_first || first == nullptr || d_first == nullptr)
242 return;
243
244 if constexpr (QTypeInfo<T>::isRelocatable) {
245 std::memmove(static_cast<void *>(d_first), static_cast<const void *>(first), n * sizeof(T));
246 } else { // generic version has to be used
247 if (d_first < first) {
249 } else { // first < d_first
250 auto rfirst = std::make_reverse_iterator(first + n);
251 auto rd_first = std::make_reverse_iterator(d_first + n);
252 q_relocate_overlap_n_left_move(rfirst, n, rd_first);
253 }
254 }
255}
256
257template <typename T>
259{
260 T t;
261 T *operator->() noexcept { return &t; }
262};
263
264template <typename Iterator>
265using IfIsInputIterator = typename std::enable_if<
266 std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category, std::input_iterator_tag>::value,
267 bool>::type;
268
269template <typename Iterator>
270using IfIsForwardIterator = typename std::enable_if<
271 std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category, std::forward_iterator_tag>::value,
272 bool>::type;
273
274template <typename Iterator>
275using IfIsNotForwardIterator = typename std::enable_if<
276 !std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category, std::forward_iterator_tag>::value,
277 bool>::type;
278
279template <typename Container,
280 typename InputIterator,
281 IfIsNotForwardIterator<InputIterator> = true>
282void reserveIfForwardIterator(Container *, InputIterator, InputIterator)
283{
284}
285
286template <typename Container,
287 typename ForwardIterator,
288 IfIsForwardIterator<ForwardIterator> = true>
289void reserveIfForwardIterator(Container *c, ForwardIterator f, ForwardIterator l)
290{
291 c->reserve(static_cast<typename Container::size_type>(std::distance(f, l)));
292}
293
294template <typename Iterator>
295using KeyAndValueTest = decltype(
296 std::declval<Iterator &>().key(),
297 std::declval<Iterator &>().value()
298);
299
300template <typename Iterator>
301using FirstAndSecondTest = decltype(
302 std::declval<Iterator &>()->first,
303 std::declval<Iterator &>()->second
304);
305
306template <typename Iterator>
308 std::enable_if_t<qxp::is_detected_v<KeyAndValueTest, Iterator>, bool>;
309
310template <typename Iterator>
312 std::enable_if_t<
313 std::conjunction_v<
314 std::negation<qxp::is_detected<KeyAndValueTest, Iterator>>,
316 >, bool>;
317
318template <typename Iterator>
319using MoveBackwardsTest = decltype(
320 std::declval<Iterator &>().operator--()
321);
322
323template <typename Iterator>
325 std::enable_if_t<qxp::is_detected_v<MoveBackwardsTest, Iterator>, bool>;
326
327template <typename T, typename U>
329 typename std::enable_if<!std::is_same<T, U>::value, bool>::type;
330
331template<typename T, typename U>
332using IfIsNotConvertible = typename std::enable_if<!std::is_convertible<T, U>::value, bool>::type;
333
334template <typename Container, typename Predicate>
335auto sequential_erase_if(Container &c, Predicate &pred)
336{
337 // This is remove_if() modified to perform the find_if step on
338 // const_iterators to avoid shared container detaches if nothing needs to
339 // be removed. We cannot run remove_if after find_if: doing so would apply
340 // the predicate to the first matching element twice!
341
342 const auto cbegin = c.cbegin();
343 const auto cend = c.cend();
344 const auto t_it = std::find_if(cbegin, cend, pred);
345 auto result = std::distance(cbegin, t_it);
346 if (result == c.size())
347 return result - result; // `0` of the right type
348
349 // now detach:
350 const auto e = c.end();
351
352 auto it = std::next(c.begin(), result);
353 auto dest = it;
354
355 // Loop Invariants:
356 // - it != e
357 // - [next(it), e[ still to be checked
358 // - [c.begin(), dest[ are result
359 while (++it != e) {
360 if (!pred(*it)) {
361 *dest = std::move(*it);
362 ++dest;
363 }
364 }
365
366 result = std::distance(dest, e);
367 c.erase(dest, e);
368 return result;
369}
370
371template <typename Container, typename T>
372auto sequential_erase(Container &c, const T &t)
373{
374 // use the equivalence relation from http://eel.is/c++draft/list.erasure#1
375 auto cmp = [&](auto &e) { return e == t; };
376 return sequential_erase_if(c, cmp); // can't pass rvalues!
377}
378
379template <typename Container, typename T>
380auto sequential_erase_with_copy(Container &c, const T &t)
381{
382 using CopyProxy = std::conditional_t<std::is_copy_constructible_v<T>, T, const T &>;
383 return sequential_erase(c, CopyProxy(t));
384}
385
386template <typename Container, typename T>
387auto sequential_erase_one(Container &c, const T &t)
388{
389 const auto cend = c.cend();
390 const auto it = std::find(c.cbegin(), cend, t);
391 if (it == cend)
392 return false;
393 c.erase(it);
394 return true;
395}
396
397template <typename T, typename Predicate>
398qsizetype qset_erase_if(QSet<T> &set, Predicate &pred)
399{
400 qsizetype result = 0;
401 auto it = set.begin();
402 const auto e = set.end();
403 while (it != e) {
404 if (pred(*it)) {
405 ++result;
406 it = set.erase(it);
407 } else {
408 ++it;
409 }
410 }
411 return result;
412}
413
414
415// Prerequisite: F is invocable on ArgTypes
416template <typename R, typename F, typename ... ArgTypes>
417struct is_invoke_result_explicitly_convertible : std::is_constructible<R, std::invoke_result_t<F, ArgTypes...>>
418{};
419
420// is_invocable_r checks for implicit conversions, but we need to check
421// for explicit conversions in remove_if. So, roll our own trait.
422template <typename R, typename F, typename ... ArgTypes>
423constexpr bool is_invocable_explicit_r_v = std::conjunction_v<
424 std::is_invocable<F, ArgTypes...>,
426>;
427
428template <typename Container, typename Predicate>
429auto associative_erase_if(Container &c, Predicate &pred)
430{
431 // we support predicates callable with either Container::iterator
432 // or with std::pair<const Key &, Value &>
433 using Iterator = typename Container::iterator;
434 using Key = typename Container::key_type;
435 using Value = typename Container::mapped_type;
436 using KeyValuePair = std::pair<const Key &, Value &>;
437
438 typename Container::size_type result = 0;
439
440 auto it = c.begin();
441 const auto e = c.end();
442 while (it != e) {
443 if constexpr (is_invocable_explicit_r_v<bool, Predicate &, Iterator &>) {
444 if (pred(it)) {
445 it = c.erase(it);
446 ++result;
447 } else {
448 ++it;
449 }
450 } else if constexpr (is_invocable_explicit_r_v<bool, Predicate &, KeyValuePair &&>) {
451 KeyValuePair p(it.key(), it.value());
452 if (pred(std::move(p))) {
453 it = c.erase(it);
454 ++result;
455 } else {
456 ++it;
457 }
458 } else {
459 static_assert(sizeof(Container) == 0, "Predicate has an incompatible signature");
460 }
461 }
462
463 return result;
464}
465
466} // namespace QtPrivate
467
469
470#endif // QCONTAINERTOOLS_IMPL_H
iterator begin()
Definition qset.h:136
iterator erase(const_iterator i)
Definition qset.h:145
QSet< QString >::iterator it
Combined button and popup list for selecting options.
\macro QT_NO_KEYWORDS >
auto associative_erase_if(Container &c, Predicate &pred)
QT_WARNING_POP void q_rotate(T *first, T *mid, T *last)
void q_uninitialized_relocate_n(T *first, N n, T *out)
std::enable_if_t< std::conjunction_v< std::negation< qxp::is_detected< KeyAndValueTest, Iterator > >, qxp::is_detected< FirstAndSecondTest, Iterator > >, bool > IfAssociativeIteratorHasFirstAndSecond
qsizetype qset_erase_if(QSet< T > &set, Predicate &pred)
QT_WARNING_PUSH void q_uninitialized_move_if_noexcept_n(T *first, N n, T *out)
static constexpr bool q_points_into_range(const T *p, const T *b, const T *e, Cmp less={}) noexcept
decltype( std::declval< Iterator & >().operator--()) MoveBackwardsTest
auto sequential_erase_one(Container &c, const T &t)
typename std::enable_if< std::is_convertible< typename std::iterator_traits< Iterator >::iterator_category, std::input_iterator_tag >::value, bool >::type IfIsInputIterator
std::enable_if_t< qxp::is_detected_v< KeyAndValueTest, Iterator >, bool > IfAssociativeIteratorHasKeyAndValue
void q_relocate_overlap_n_left_move(iterator first, N n, iterator d_first)
std::enable_if_t< qxp::is_detected_v< MoveBackwardsTest, Iterator >, bool > IfIteratorCanMoveBackwards
auto sequential_erase_if(Container &c, Predicate &pred)
typename std::enable_if<!std::is_same< T, U >::value, bool >::type IfIsNotSame
typename std::enable_if< std::is_convertible< typename std::iterator_traits< Iterator >::iterator_category, std::forward_iterator_tag >::value, bool >::type IfIsForwardIterator
auto sequential_erase_with_copy(Container &c, const T &t)
typename std::enable_if< !std::is_convertible< typename std::iterator_traits< Iterator >::iterator_category, std::forward_iterator_tag >::value, bool >::type IfIsNotForwardIterator
auto sequential_erase(Container &c, const T &t)
T * q_uninitialized_remove_copy_if(T *first, T *last, T *out, Predicate &pred)
decltype( std::declval< Iterator & >() ->first, std::declval< Iterator & >() ->second) FirstAndSecondTest
void q_relocate_overlap_n(T *first, N n, T *d_first)
void reserveIfForwardIterator(Container *, InputIterator, InputIterator)
typename std::enable_if<!std::is_convertible< T, U >::value, bool >::type IfIsNotConvertible
decltype( std::declval< Iterator & >().key(), std::declval< Iterator & >().value()) KeyAndValueTest
constexpr bool is_invocable_explicit_r_v
typename _detail::detector< qxp::nonesuch, void, Op, Args... >::value_t is_detected
#define QT_WARNING_POP
#define QT_WARNING_DISABLE_GCC(text)
#define QT_WARNING_PUSH
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter * iter
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define QT_RETHROW
#define QT_CATCH(A)
#define QT_TRY
GLboolean GLboolean GLboolean b
GLuint GLuint end
GLint GLint GLint GLint GLsizei GLsizei GLsizei GLboolean commit
GLfloat GLfloat f
GLenum type
GLint first
GLfloat n
const GLubyte * c
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
unsigned char uchar
Definition qtypes.h:32
ptrdiff_t qsizetype
Definition qtypes.h:165
QFuture< QSet< QChar > > set
[10]
QTextStream out(stdout)
[7]