Qt 6.x
The Qt SDK
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 if (n != N(0)) { // even if N == 0, out == nullptr or first == nullptr are UB for memcpy()
76 std::memcpy(static_cast<void *>(out),
77 static_cast<const void *>(first),
78 n * sizeof(T));
79 }
80 } else {
82 if constexpr (QTypeInfo<T>::isComplex)
83 std::destroy_n(first, n);
84 }
85}
86
88
98template <typename T>
99void q_rotate(T *first, T *mid, T *last)
100{
101 if constexpr (QTypeInfo<T>::isRelocatable) {
102 const auto cast = [](T *p) { return reinterpret_cast<uchar*>(p); };
103 std::rotate(cast(first), cast(mid), cast(last));
104 } else {
105 std::rotate(first, mid, last);
106 }
107}
108
121template <typename T, typename Predicate>
122T *q_uninitialized_remove_copy_if(T *first, T *last, T *out, Predicate &pred)
123{
124 static_assert(std::is_nothrow_destructible_v<T>,
125 "This algorithm requires that T has a non-throwing destructor");
127
128 T *dest_begin = out;
129 QT_TRY {
130 while (first != last) {
131 if (!pred(*first)) {
132 new (std::addressof(*out)) T(*first);
133 ++out;
134 }
135 ++first;
136 }
137 } QT_CATCH (...) {
138 std::destroy(std::reverse_iterator(out), std::reverse_iterator(dest_begin));
140 }
141 return out;
142}
143
144template<typename iterator, typename N>
145void q_relocate_overlap_n_left_move(iterator first, N n, iterator d_first)
146{
147 // requires: [first, n) is a valid range
148 // requires: d_first + n is reachable from d_first
149 // requires: iterator is at least a random access iterator
150 // requires: value_type(iterator) has a non-throwing destructor
151
152 Q_ASSERT(n);
153 Q_ASSERT(d_first < first); // only allow moves to the "left"
154 using T = typename std::iterator_traits<iterator>::value_type;
155
156 // Watches passed iterator. Unless commit() is called, all the elements that
157 // the watched iterator passes through are deleted at the end of object
158 // lifetime. freeze() could be used to stop watching the passed iterator and
159 // remain at current place.
160 //
161 // requires: the iterator is expected to always point to an invalid object
162 // (to uninitialized memory)
163 struct Destructor
164 {
165 iterator *iter;
166 iterator end;
167 iterator intermediate;
168
169 Destructor(iterator &it) noexcept : iter(std::addressof(it)), end(it) { }
170 void commit() noexcept { iter = std::addressof(end); }
171 void freeze() noexcept
172 {
173 intermediate = *iter;
174 iter = std::addressof(intermediate);
175 }
176 ~Destructor() noexcept
177 {
178 for (const int step = *iter < end ? 1 : -1; *iter != end;) {
179 std::advance(*iter, step);
180 (*iter)->~T();
181 }
182 }
183 } destroyer(d_first);
184
185 const iterator d_last = d_first + n;
186 // Note: use pair and explicitly copy iterators from it to prevent
187 // accidental reference semantics instead of copy. equivalent to:
188 //
189 // auto [overlapBegin, overlapEnd] = std::minmax(d_last, first);
190 auto pair = std::minmax(d_last, first);
191
192 // overlap area between [d_first, d_first + n) and [first, first + n) or an
193 // uninitialized memory area between the two ranges
194 iterator overlapBegin = pair.first;
195 iterator overlapEnd = pair.second;
196
197 // move construct elements in uninitialized region
198 while (d_first != overlapBegin) {
199 // account for std::reverse_iterator, cannot use new(d_first) directly
200 new (std::addressof(*d_first)) T(std::move_if_noexcept(*first));
201 ++d_first;
202 ++first;
203 }
204
205 // cannot commit but have to stop - there might be an overlap region
206 // which we don't want to delete (because it's part of existing data)
207 destroyer.freeze();
208
209 // move assign elements in overlap region
210 while (d_first != d_last) {
211 *d_first = std::move_if_noexcept(*first);
212 ++d_first;
213 ++first;
214 }
215
216 Q_ASSERT(d_first == destroyer.end + n);
217 destroyer.commit(); // can commit here as ~T() below does not throw
218
219 while (first != overlapEnd)
220 (--first)->~T();
221}
222
233template<typename T, typename N>
234void q_relocate_overlap_n(T *first, N n, T *d_first)
235{
236 static_assert(std::is_nothrow_destructible_v<T>,
237 "This algorithm requires that T has a non-throwing destructor");
238
239 if (n == N(0) || first == d_first || first == nullptr || d_first == nullptr)
240 return;
241
242 if constexpr (QTypeInfo<T>::isRelocatable) {
243 std::memmove(static_cast<void *>(d_first), static_cast<const void *>(first), n * sizeof(T));
244 } else { // generic version has to be used
245 if (d_first < first) {
247 } else { // first < d_first
248 auto rfirst = std::make_reverse_iterator(first + n);
249 auto rd_first = std::make_reverse_iterator(d_first + n);
250 q_relocate_overlap_n_left_move(rfirst, n, rd_first);
251 }
252 }
253}
254
255template <typename Iterator>
256using IfIsInputIterator = typename std::enable_if<
257 std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category, std::input_iterator_tag>::value,
258 bool>::type;
259
260template <typename Iterator>
261using IfIsForwardIterator = typename std::enable_if<
262 std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category, std::forward_iterator_tag>::value,
263 bool>::type;
264
265template <typename Iterator>
266using IfIsNotForwardIterator = typename std::enable_if<
267 !std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category, std::forward_iterator_tag>::value,
268 bool>::type;
269
270template <typename Container,
271 typename InputIterator,
273void reserveIfForwardIterator(Container *, InputIterator, InputIterator)
274{
275}
276
277template <typename Container,
278 typename ForwardIterator,
279 IfIsForwardIterator<ForwardIterator> = true>
280void reserveIfForwardIterator(Container *c, ForwardIterator f, ForwardIterator l)
281{
282 c->reserve(static_cast<typename Container::size_type>(std::distance(f, l)));
283}
284
285template <typename Iterator>
286using KeyAndValueTest = decltype(
287 std::declval<Iterator &>().key(),
288 std::declval<Iterator &>().value()
289);
290
291template <typename Iterator>
292using FirstAndSecondTest = decltype(
293 std::declval<Iterator &>()->first,
294 std::declval<Iterator &>()->second
295);
296
297template <typename Iterator>
299 std::enable_if_t<qxp::is_detected_v<KeyAndValueTest, Iterator>, bool>;
300
301template <typename Iterator>
303 std::enable_if_t<qxp::is_detected_v<FirstAndSecondTest, Iterator>, bool>;
304
305template <typename Iterator>
306using MoveBackwardsTest = decltype(
307 std::declval<Iterator &>().operator--()
308);
309
310template <typename Iterator>
312 std::enable_if_t<qxp::is_detected_v<MoveBackwardsTest, Iterator>, bool>;
313
314template <typename T, typename U>
316 typename std::enable_if<!std::is_same<T, U>::value, bool>::type;
317
318template<typename T, typename U>
319using IfIsNotConvertible = typename std::enable_if<!std::is_convertible<T, U>::value, bool>::type;
320
321template <typename Container, typename Predicate>
322auto sequential_erase_if(Container &c, Predicate &pred)
323{
324 // This is remove_if() modified to perform the find_if step on
325 // const_iterators to avoid shared container detaches if nothing needs to
326 // be removed. We cannot run remove_if after find_if: doing so would apply
327 // the predicate to the first matching element twice!
328
329 const auto cbegin = c.cbegin();
330 const auto cend = c.cend();
331 const auto t_it = std::find_if(cbegin, cend, pred);
332 auto result = std::distance(cbegin, t_it);
333 if (result == c.size())
334 return result - result; // `0` of the right type
335
336 // now detach:
337 const auto e = c.end();
338
339 auto it = std::next(c.begin(), result);
340 auto dest = it;
341
342 // Loop Invariants:
343 // - it != e
344 // - [next(it), e[ still to be checked
345 // - [c.begin(), dest[ are result
346 while (++it != e) {
347 if (!pred(*it)) {
348 *dest = std::move(*it);
349 ++dest;
350 }
351 }
352
353 result = std::distance(dest, e);
354 c.erase(dest, e);
355 return result;
356}
357
358template <typename Container, typename T>
359auto sequential_erase(Container &c, const T &t)
360{
361 // use the equivalence relation from http://eel.is/c++draft/list.erasure#1
362 auto cmp = [&](auto &e) { return e == t; };
363 return sequential_erase_if(c, cmp); // can't pass rvalues!
364}
365
366template <typename Container, typename T>
367auto sequential_erase_with_copy(Container &c, const T &t)
368{
369 using CopyProxy = std::conditional_t<std::is_copy_constructible_v<T>, T, const T &>;
370 const T &tCopy = CopyProxy(t);
371 return sequential_erase(c, tCopy);
372}
373
374template <typename Container, typename T>
375auto sequential_erase_one(Container &c, const T &t)
376{
377 const auto cend = c.cend();
378 const auto it = std::find(c.cbegin(), cend, t);
379 if (it == cend)
380 return false;
381 c.erase(it);
382 return true;
383}
384
385template <typename T, typename Predicate>
387{
388 qsizetype result = 0;
389 auto it = set.begin();
390 const auto e = set.end();
391 while (it != e) {
392 if (pred(*it)) {
393 ++result;
394 it = set.erase(it);
395 } else {
396 ++it;
397 }
398 }
399 return result;
400}
401
402
403// Prerequisite: F is invocable on ArgTypes
404template <typename R, typename F, typename ... ArgTypes>
405struct is_invoke_result_explicitly_convertible : std::is_constructible<R, std::invoke_result_t<F, ArgTypes...>>
406{};
407
408// is_invocable_r checks for implicit conversions, but we need to check
409// for explicit conversions in remove_if. So, roll our own trait.
410template <typename R, typename F, typename ... ArgTypes>
411constexpr bool is_invocable_explicit_r_v = std::conjunction_v<
412 std::is_invocable<F, ArgTypes...>,
414>;
415
416template <typename Container, typename Predicate>
417auto associative_erase_if(Container &c, Predicate &pred)
418{
419 // we support predicates callable with either Container::iterator
420 // or with std::pair<const Key &, Value &>
421 using Iterator = typename Container::iterator;
422 using Key = typename Container::key_type;
423 using Value = typename Container::mapped_type;
424 using KeyValuePair = std::pair<const Key &, Value &>;
425
426 typename Container::size_type result = 0;
427
428 auto it = c.begin();
429 const auto e = c.end();
430 while (it != e) {
431 if constexpr (is_invocable_explicit_r_v<bool, Predicate &, Iterator &>) {
432 if (pred(it)) {
433 it = c.erase(it);
434 ++result;
435 } else {
436 ++it;
437 }
438 } else if constexpr (is_invocable_explicit_r_v<bool, Predicate &, KeyValuePair &&>) {
439 KeyValuePair p(it.key(), it.value());
440 if (pred(std::move(p))) {
441 it = c.erase(it);
442 ++result;
443 } else {
444 ++it;
445 }
446 } else {
447 static_assert(sizeof(Container) == 0, "Predicate has an incompatible signature");
448 }
449 }
450
451 return result;
452}
453
454} // namespace QtPrivate
455
457
458#endif // QCONTAINERTOOLS_IMPL_H
Definition qset.h:18
iterator begin()
Definition qset.h:136
iterator erase(const_iterator i)
Definition qset.h:145
double e
QSet< QString >::iterator it
Combined button and popup list for selecting options.
\macro QT_NAMESPACE
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)
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)
decltype(std::declval< Iterator & >().operator--()) MoveBackwardsTest
static constexpr bool q_points_into_range(const T *p, const T *b, const T *e, Cmp less={}) noexcept
auto sequential_erase_one(Container &c, const T &t)
typename std::enable_if<!std::is_same< T, U >::value, bool >::type IfIsNotSame
typename std::enable_if<!std::is_convertible< T, U >::value, bool >::type IfIsNotConvertible
void q_relocate_overlap_n_left_move(iterator first, N n, iterator d_first)
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_if(Container &c, Predicate &pred)
auto sequential_erase_with_copy(Container &c, const T &t)
std::enable_if_t< qxp::is_detected_v< KeyAndValueTest, Iterator >, bool > IfAssociativeIteratorHasKeyAndValue
auto sequential_erase(Container &c, const T &t)
T * q_uninitialized_remove_copy_if(T *first, T *last, T *out, Predicate &pred)
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< FirstAndSecondTest, Iterator >, bool > IfAssociativeIteratorHasFirstAndSecond
typename std::enable_if< std::is_convertible< typename std::iterator_traits< Iterator >::iterator_category, std::forward_iterator_tag >::value, bool >::type IfIsForwardIterator
void q_relocate_overlap_n(T *first, N n, T *d_first)
decltype(std::declval< Iterator & >() ->first, std::declval< Iterator & >() ->second) FirstAndSecondTest
std::enable_if_t< qxp::is_detected_v< MoveBackwardsTest, Iterator >, bool > IfIteratorCanMoveBackwards
void reserveIfForwardIterator(Container *, InputIterator, InputIterator)
constexpr bool is_invocable_explicit_r_v
decltype(std::declval< Iterator & >().key(), std::declval< Iterator & >().value()) KeyAndValueTest
#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:27
ptrdiff_t qsizetype
Definition qtypes.h:70
QFuture< QSet< QChar > > set
[10]
QTextStream out(stdout)
[7]