QtBase  v6.3.1
qcontainertools_impl.h
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
4 ** Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
5 ** Copyright (C) 2020 The Qt Company Ltd.
6 ** Contact: https://www.qt.io/licensing/
7 **
8 ** This file is part of the QtCore module of the Qt Toolkit.
9 **
10 ** $QT_BEGIN_LICENSE:LGPL$
11 ** Commercial License Usage
12 ** Licensees holding valid commercial Qt licenses may use this file in
13 ** accordance with the commercial license agreement provided with the
14 ** Software or, alternatively, in accordance with the terms contained in
15 ** a written agreement between you and The Qt Company. For licensing terms
16 ** and conditions see https://www.qt.io/terms-conditions. For further
17 ** information use the contact form at https://www.qt.io/contact-us.
18 **
19 ** GNU Lesser General Public License Usage
20 ** Alternatively, this file may be used under the terms of the GNU Lesser
21 ** General Public License version 3 as published by the Free Software
22 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
23 ** packaging of this file. Please review the following information to
24 ** ensure the GNU Lesser General Public License version 3 requirements
25 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
26 **
27 ** GNU General Public License Usage
28 ** Alternatively, this file may be used under the terms of the GNU
29 ** General Public License version 2.0 or (at your option) the GNU General
30 ** Public license version 3 or any later version approved by the KDE Free
31 ** Qt Foundation. The licenses are as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
33 ** included in the packaging of this file. Please review the following
34 ** information to ensure the GNU General Public License requirements will
35 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
36 ** https://www.gnu.org/licenses/gpl-3.0.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #if 0
43 #pragma qt_sync_skip_header_check
44 #pragma qt_sync_stop_processing
45 #endif
46 
47 #ifndef QCONTAINERTOOLS_IMPL_H
48 #define QCONTAINERTOOLS_IMPL_H
49 
50 #include <QtCore/qglobal.h>
51 #include <QtCore/qtypeinfo.h>
52 
53 #include <cstring>
54 #include <iterator>
55 #include <memory>
56 #include <algorithm>
57 
59 
60 namespace QtPrivate
61 {
62 
69 template<typename T, typename Cmp = std::less<>>
70 static constexpr bool q_points_into_range(const T *p, const T *b, const T *e,
71  Cmp less = {}) noexcept
72 {
73  return !less(p, b) && less(p, e);
74 }
75 
76 template <typename T, typename N>
78 {
79  if constexpr (std::is_nothrow_move_constructible_v<T> || !std::is_copy_constructible_v<T>)
80  std::uninitialized_move_n(first, n, out);
81  else
82  std::uninitialized_copy_n(first, n, out);
83 }
84 
85 template <typename T, typename N>
87 {
88  if constexpr (QTypeInfo<T>::isRelocatable) {
89  if (n != N(0)) { // even if N == 0, out == nullptr or first == nullptr are UB for memmove()
90  std::memmove(static_cast<void*>(out),
91  static_cast<const void*>(first),
92  n * sizeof(T));
93  }
94  } else {
96  if constexpr (QTypeInfo<T>::isComplex)
97  std::destroy_n(first, n);
98  }
99 }
100 
101 template<typename iterator, typename N>
103 {
104  // requires: [first, n) is a valid range
105  // requires: d_first + n is reachable from d_first
106  // requires: iterator is at least a random access iterator
107  // requires: value_type(iterator) has a non-throwing destructor
108 
109  Q_ASSERT(n);
110  Q_ASSERT(d_first < first); // only allow moves to the "left"
112 
113  // Watches passed iterator. Unless commit() is called, all the elements that
114  // the watched iterator passes through are deleted at the end of object
115  // lifetime. freeze() could be used to stop watching the passed iterator and
116  // remain at current place.
117  //
118  // requires: the iterator is expected to always point to an invalid object
119  // (to uninitialized memory)
120  struct Destructor
121  {
122  iterator *iter;
123  iterator end;
124  iterator intermediate;
125 
126  Destructor(iterator &it) noexcept : iter(std::addressof(it)), end(it) { }
127  void commit() noexcept { iter = std::addressof(end); }
128  void freeze() noexcept
129  {
130  intermediate = *iter;
131  iter = std::addressof(intermediate);
132  }
133  ~Destructor() noexcept
134  {
135  for (const int step = *iter < end ? 1 : -1; *iter != end;) {
136  std::advance(*iter, step);
137  (*iter)->~T();
138  }
139  }
140  } destroyer(d_first);
141 
142  const iterator d_last = d_first + n;
143  // Note: use pair and explicitly copy iterators from it to prevent
144  // accidental reference semantics instead of copy. equivalent to:
145  //
146  // auto [overlapBegin, overlapEnd] = std::minmax(d_last, first);
147  auto pair = std::minmax(d_last, first);
148 
149  // overlap area between [d_first, d_first + n) and [first, first + n) or an
150  // uninitialized memory area between the two ranges
151  iterator overlapBegin = pair.first;
152  iterator overlapEnd = pair.second;
153 
154  // move construct elements in uninitialized region
155  while (d_first != overlapBegin) {
156  // account for std::reverse_iterator, cannot use new(d_first) directly
157  new (std::addressof(*d_first)) T(std::move_if_noexcept(*first));
158  ++d_first;
159  ++first;
160  }
161 
162  // cannot commit but have to stop - there might be an overlap region
163  // which we don't want to delete (because it's part of existing data)
164  destroyer.freeze();
165 
166  // move assign elements in overlap region
167  while (d_first != d_last) {
168  *d_first = std::move_if_noexcept(*first);
169  ++d_first;
170  ++first;
171  }
172 
173  Q_ASSERT(d_first == destroyer.end + n);
174  destroyer.commit(); // can commit here as ~T() below does not throw
175 
176  while (first != overlapEnd)
177  (--first)->~T();
178 }
179 
190 template<typename T, typename N>
191 void q_relocate_overlap_n(T *first, N n, T *d_first)
192 {
193  static_assert(std::is_nothrow_destructible_v<T>,
194  "This algorithm requires that T has a non-throwing destructor");
195 
196  if (n == N(0) || first == d_first || first == nullptr || d_first == nullptr)
197  return;
198 
199  if constexpr (QTypeInfo<T>::isRelocatable) {
200  std::memmove(static_cast<void *>(d_first), static_cast<const void *>(first), n * sizeof(T));
201  } else { // generic version has to be used
202  if (d_first < first) {
204  } else { // first < d_first
205  auto rfirst = std::make_reverse_iterator(first + n);
206  auto rd_first = std::make_reverse_iterator(d_first + n);
207  q_relocate_overlap_n_left_move(rfirst, n, rd_first);
208  }
209  }
210 }
211 
212 template <typename Iterator>
213 using IfIsInputIterator = typename std::enable_if<
214  std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category, std::input_iterator_tag>::value,
215  bool>::type;
216 
217 template <typename Iterator>
218 using IfIsForwardIterator = typename std::enable_if<
219  std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category, std::forward_iterator_tag>::value,
220  bool>::type;
221 
222 template <typename Iterator>
223 using IfIsNotForwardIterator = typename std::enable_if<
224  !std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category, std::forward_iterator_tag>::value,
225  bool>::type;
226 
227 template <typename Container,
228  typename InputIterator,
230 void reserveIfForwardIterator(Container *, InputIterator, InputIterator)
231 {
232 }
233 
234 template <typename Container,
235  typename ForwardIterator,
236  IfIsForwardIterator<ForwardIterator> = true>
237 void reserveIfForwardIterator(Container *c, ForwardIterator f, ForwardIterator l)
238 {
239  c->reserve(static_cast<typename Container::size_type>(std::distance(f, l)));
240 }
241 
242 template <typename Iterator, typename = std::void_t<>>
243 struct AssociativeIteratorHasKeyAndValue : std::false_type
244 {
245 };
246 
247 template <typename Iterator>
249  Iterator,
250  std::void_t<decltype(std::declval<Iterator &>().key()),
251  decltype(std::declval<Iterator &>().value())>
252  >
253  : std::true_type
254 {
255 };
256 
257 template <typename Iterator, typename = std::void_t<>, typename = std::void_t<>>
258 struct AssociativeIteratorHasFirstAndSecond : std::false_type
259 {
260 };
261 
262 template <typename Iterator>
264  Iterator,
265  std::void_t<decltype(std::declval<Iterator &>()->first),
266  decltype(std::declval<Iterator &>()->second)>
267  >
268  : std::true_type
269 {
270 };
271 
272 template <typename Iterator>
275 
276 template <typename Iterator>
279 
280 template <typename T, typename U>
281 using IfIsNotSame =
283 
284 template<typename T, typename U>
286 
287 template <typename Container, typename Predicate>
289 {
290  // This is remove_if() modified to perform the find_if step on
291  // const_iterators to avoid shared container detaches if nothing needs to
292  // be removed. We cannot run remove_if after find_if: doing so would apply
293  // the predicate to the first matching element twice!
294 
295  const auto cbegin = c.cbegin();
296  const auto cend = c.cend();
297  const auto t_it = std::find_if(cbegin, cend, pred);
298  auto result = std::distance(cbegin, t_it);
299  if (result == c.size())
300  return result - result; // `0` of the right type
301 
302  // now detach:
303  const auto e = c.end();
304 
305  auto it = std::next(c.begin(), result);
306  auto dest = it;
307 
308  // Loop Invariants:
309  // - it != e
310  // - [next(it), e[ still to be checked
311  // - [c.begin(), dest[ are result
312  while (++it != e) {
313  if (!pred(*it)) {
314  *dest = std::move(*it);
315  ++dest;
316  }
317  }
318 
319  result = std::distance(dest, e);
320  c.erase(dest, e);
321  return result;
322 }
323 
324 template <typename Container, typename T>
326 {
327  // use the equivalence relation from http://eel.is/c++draft/list.erasure#1
328  auto cmp = [&](auto &e) { return e == t; };
329  return sequential_erase_if(c, cmp); // can't pass rvalues!
330 }
331 
332 template <typename Container, typename T>
334 {
335  using CopyProxy = std::conditional_t<std::is_copy_constructible_v<T>, T, const T &>;
336  const T &tCopy = CopyProxy(t);
337  return sequential_erase(c, tCopy);
338 }
339 
340 template <typename Container, typename T>
342 {
343  const auto cend = c.cend();
344  const auto it = std::find(c.cbegin(), cend, t);
345  if (it == cend)
346  return false;
347  c.erase(it);
348  return true;
349 }
350 
351 template <typename T, typename Predicate>
353 {
354  qsizetype result = 0;
355  auto it = set.begin();
356  const auto e = set.end();
357  while (it != e) {
358  if (pred(*it)) {
359  ++result;
360  it = set.erase(it);
361  } else {
362  ++it;
363  }
364  }
365  return result;
366 }
367 
368 
369 // Prerequisite: F is invocable on ArgTypes
370 template <typename R, typename F, typename ... ArgTypes>
371 struct is_invoke_result_explicitly_convertible : std::is_constructible<R, std::invoke_result_t<F, ArgTypes...>>
372 {};
373 
374 // is_invocable_r checks for implicit conversions, but we need to check
375 // for explicit conversions in remove_if. So, roll our own trait.
376 template <typename R, typename F, typename ... ArgTypes>
377 constexpr bool is_invocable_explicit_r_v = std::conjunction_v<
378  std::is_invocable<F, ArgTypes...>,
380 >;
381 
382 template <typename Container, typename Predicate>
384 {
385  // we support predicates callable with either Container::iterator
386  // or with std::pair<const Key &, Value &>
387  using Iterator = typename Container::iterator;
388  using Key = typename Container::key_type;
389  using Value = typename Container::mapped_type;
390  using KeyValuePair = std::pair<const Key &, Value &>;
391 
392  typename Container::size_type result = 0;
393 
394  auto it = c.begin();
395  const auto e = c.end();
396  while (it != e) {
397  if constexpr (is_invocable_explicit_r_v<bool, Predicate &, Iterator &>) {
398  if (pred(it)) {
399  it = c.erase(it);
400  ++result;
401  } else {
402  ++it;
403  }
404  } else if constexpr (is_invocable_explicit_r_v<bool, Predicate &, KeyValuePair &&>) {
405  KeyValuePair p(it.key(), it.value());
406  if (pred(std::move(p))) {
407  it = c.erase(it);
408  ++result;
409  } else {
410  ++it;
411  }
412  } else {
413  static_assert(sizeof(Container) == 0, "Predicate has an incompatible signature");
414  }
415  }
416 
417  return result;
418 }
419 
420 } // namespace QtPrivate
421 
423 
424 #endif // QCONTAINERTOOLS_IMPL_H
#define value
[5]
Definition: qset.h:54
float step
#define T(x)
Definition: main.cpp:42
double e
short next
Definition: keywords.cpp:454
#define F(x, y, z)
Definition: md5.c:51
Generic::PredicateMatcher< T > Predicate(std::function< bool(T const &)> const &predicate, std::string const &description="")
Definition: catch_p_p.h:3521
typename C::value_type value_type
typename C::key_type key_type
typename C::mapped_type mapped_type
typename C::iterator iterator
const PluginKeyMapConstIterator cend
auto associative_erase_if(Container &c, Predicate &pred)
typename std::enable_if< AssociativeIteratorHasKeyAndValue< Iterator >::value, bool >::type IfAssociativeIteratorHasKeyAndValue
void q_uninitialized_relocate_n(T *first, N n, T *out)
qsizetype qset_erase_if(QSet< T > &set, Predicate &pred)
void q_uninitialized_move_if_noexcept_n(T *first, N n, T *out)
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)
typename std::enable_if< AssociativeIteratorHasFirstAndSecond< Iterator >::value, bool >::type IfAssociativeIteratorHasFirstAndSecond
auto sequential_erase(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
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)
void reserveIfForwardIterator(Container *, InputIterator, InputIterator)
constexpr bool is_invocable_explicit_r_v
decltype(auto) cbegin(const T &t)
Definition: qfloat16.h:381
int distance(TestIterator &a, TestIterator &b)
void *PRIV() memmove(void *d, const void *s, size_t n)
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
ptrdiff_t qsizetype
Definition: qglobal.h:308
GLenum type
Definition: qopengl.h:270
GLboolean GLboolean GLboolean b
GLuint GLuint end
GLint GLint GLint GLint GLsizei GLsizei GLsizei GLboolean commit
GLfloat GLfloat f
GLint first
GLfloat n
const GLubyte * c
Definition: qopenglext.h:12701
GLdouble GLdouble t
[9]
Definition: qopenglext.h:243
GLuint64EXT * result
[6]
Definition: qopenglext.h:10932
GLfloat GLfloat p
[1]
Definition: qopenglext.h:12698
#define Q_ASSERT(cond)
Definition: qrandom.cpp:84
QFuture< QSet< QChar > > set
[10]
QTextStream out(stdout)
[7]
QStringList::Iterator it
Definition: main.cpp:38
QDomElement find(const QString &tagName, const QDomElement &e)
Definition: main.cpp:39