Akonadi

akranges.h
1/*
2 SPDX-FileCopyrightText: 2018-2019 Daniel Vrátil <dvratil@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#pragma once
8
9#include "akhelpers.h"
10#include "aktraits.h"
11
12#include <QList>
13#include <QMap>
14#include <QSet>
15
16#include <algorithm>
17#include <functional>
18#include <iterator>
19#include <type_traits>
20#include <utility>
21
22namespace AkRanges
23{
24namespace detail
25{
26template<typename RangeLike, typename OutContainer, AK_REQUIRES(AkTraits::isAppendable<OutContainer>)>
27OutContainer copyContainer(const RangeLike &range)
28{
29 OutContainer rv;
30 rv.reserve(range.size());
31 for (auto &&v : range) {
32 rv.push_back(std::move(v));
33 }
34 return rv;
35}
36
37template<typename RangeLike, typename OutContainer, AK_REQUIRES(AkTraits::isInsertable<OutContainer>)>
38OutContainer copyContainer(const RangeLike &range)
39{
40 OutContainer rv;
41 rv.reserve(range.size());
42 for (const auto &v : range) {
43 rv.insert(v); // Qt containers lack move-enabled insert() overload
44 }
45 return rv;
46}
47
48template<typename RangeList, typename OutContainer>
49OutContainer copyAssocContainer(const RangeList &range)
50{
51 OutContainer rv;
52 for (const auto &v : range) {
53 rv.insert(v.first, v.second); // Qt containers lack move-enabled insert() overload
54 }
55 return rv;
56}
57
58template<typename Iterator>
59struct IteratorTrait {
60 using iterator_category = typename Iterator::iterator_category;
61 using value_type = typename Iterator::value_type;
62 using difference_type = typename Iterator::difference_type;
63 using pointer = typename Iterator::pointer;
64 using reference = typename Iterator::reference;
65};
66
67// Without QT_STRICT_ITERATORS QList and QList iterators do not satisfy STL
68// iterator concepts since they are nothing more but typedefs to T* - for those
69// we need to provide custom traits.
70template<typename Iterator>
71struct IteratorTrait<Iterator *> {
72 // QTypedArrayData::iterator::iterator_category
73 using iterator_category = std::random_access_iterator_tag;
74 using value_type = Iterator;
75 using difference_type = qsizetype;
76 using pointer = Iterator *;
77 using reference = Iterator &;
78};
79
80template<typename Iterator>
81struct IteratorTrait<const Iterator *> {
82 using iterator_category = std::random_access_iterator_tag;
83 using value_type = Iterator;
84 using difference_type = qsizetype;
85 using pointer = const Iterator *;
86 using reference = const Iterator &;
87};
88
89template<typename IterImpl, typename RangeLike, typename Iterator = typename RangeLike::const_iterator>
90struct IteratorBase {
91public:
92 using iterator_category = typename IteratorTrait<Iterator>::iterator_category;
93 using value_type = typename IteratorTrait<Iterator>::value_type;
94 using difference_type = typename IteratorTrait<Iterator>::difference_type;
95 using pointer = typename IteratorTrait<Iterator>::pointer;
96 using reference = typename IteratorTrait<Iterator>::reference;
97
98 IteratorBase(const IteratorBase<IterImpl, RangeLike> &other)
99 : mIter(other.mIter)
100 , mRange(other.mRange)
101 {
102 }
103
104 IterImpl &operator++()
105 {
106 ++static_cast<IterImpl *>(this)->mIter;
107 return *static_cast<IterImpl *>(this);
108 }
109
110 IterImpl operator++(int)
111 {
112 auto ret = *static_cast<IterImpl *>(this);
113 ++static_cast<IterImpl *>(this)->mIter;
114 return ret;
115 }
116
117 bool operator==(const IterImpl &other) const
118 {
119 return mIter == other.mIter;
120 }
121
122 bool operator!=(const IterImpl &other) const
123 {
124 return !(*static_cast<const IterImpl *>(this) == other);
125 }
126
127 bool operator<(const IterImpl &other) const
128 {
129 return mIter < other.mIter;
130 }
131
132 auto operator-(const IterImpl &other) const
133 {
134 return mIter - other.mIter;
135 }
136
137 auto operator*() const
138 {
139 return *mIter;
140 }
141
142protected:
143 IteratorBase(const Iterator &iter, const RangeLike &range)
144 : mIter(iter)
145 , mRange(range)
146 {
147 }
148 IteratorBase(const Iterator &iter, RangeLike &&range)
149 : mIter(iter)
150 , mRange(std::move(range))
151 {
152 }
153
154 Iterator mIter;
155 RangeLike mRange;
156};
157
158template<typename RangeLike, typename TransformFn, typename Iterator = typename RangeLike::const_iterator>
159struct TransformIterator : public IteratorBase<TransformIterator<RangeLike, TransformFn>, RangeLike> {
160private:
161 template<typename... T>
162 struct ResultOf;
163
164 template<typename R, typename... Args>
165 struct ResultOf<R(Args...)> {
166 using type = R;
167 };
168
169 using IteratorValueType = std::invoke_result_t<TransformFn, typename IteratorTrait<Iterator>::value_type>;
170
171public:
172 using value_type = IteratorValueType;
173 using pointer = IteratorValueType *; // FIXME: preserve const-ness
174 using reference = const IteratorValueType &; // FIXME: preserve const-ness
175
176 TransformIterator(const Iterator &iter, const TransformFn &fn, const RangeLike &range)
177 : IteratorBase<TransformIterator<RangeLike, TransformFn>, RangeLike>(iter, range)
178 , mFn(fn)
179 {
180 }
181
182 auto operator*() const
183 {
184 return std::invoke(mFn, *(this->mIter));
185 }
186
187private:
188 TransformFn mFn;
189};
190
191template<typename RangeLike, typename Predicate, typename Iterator = typename RangeLike::const_iterator>
192class FilterIterator : public IteratorBase<FilterIterator<RangeLike, Predicate>, RangeLike>
193{
194public:
195 // Filter iterator is just an InputIterator (for complexity reasons).
196 // It actually makes it more efficient with STL algos like std::find()
197 using iterator_category = std::input_iterator_tag;
198
199 FilterIterator(const Iterator &iter, const Iterator &end, const Predicate &predicate, const RangeLike &range)
200 : IteratorBase<FilterIterator, RangeLike>(iter, range)
201 , mPredicate(predicate)
202 , mEnd(end)
203 {
204 while (this->mIter != mEnd && !std::invoke(mPredicate, *this->mIter)) {
205 ++this->mIter;
206 }
207 }
208
209 auto &operator++()
210 {
211 if (this->mIter != mEnd) {
212 do {
213 ++this->mIter;
214 } while (this->mIter != mEnd && !std::invoke(mPredicate, *this->mIter));
215 }
216 return *this;
217 }
218
219 auto operator++(int)
220 {
221 auto it = *this;
222 ++(*this);
223 return it;
224 }
225
226private:
227 Predicate mPredicate;
228 Iterator mEnd;
229};
230
231template<typename RangeLike, typename Iterator = typename RangeLike::const_iterator>
232class EnumerateIterator : public IteratorBase<EnumerateIterator<RangeLike>, RangeLike>
233{
234public:
235 using value_type = std::pair<qsizetype, typename Iterator::value_type>;
236 using pointer = value_type *; // FIXME: preserve const-ness
237 using reference = const value_type &; // FIXME: preserve const-ness
238
239 EnumerateIterator(const Iterator &iter, qsizetype start, const RangeLike &range)
240 : IteratorBase<EnumerateIterator, RangeLike>(iter, range)
241 , mCount(start)
242 {
243 }
244
245 auto &operator++()
246 {
247 ++mCount;
248 ++this->mIter;
249 return *this;
250 }
251
252 auto &operator++(int)
253 {
254 auto it = *this;
255 ++(*this);
256 return it;
257 }
258
259 QPair<qsizetype, typename Iterator::value_type> operator*() const
260 {
261 return qMakePair(mCount, *this->mIter);
262 }
263
264private:
265 qsizetype mCount = 0;
266};
267
268template<typename Container, int Pos, typename Iterator = typename Container::const_key_value_iterator>
269class AssociativeContainerIterator : public IteratorBase<AssociativeContainerIterator<Container, Pos>, Container, Iterator>
270{
271public:
272 using value_type = std::remove_const_t<std::remove_reference_t<typename std::tuple_element<Pos, typename Iterator::value_type>::type>>;
273 using pointer = std::add_pointer_t<value_type>;
274 using reference = std::add_lvalue_reference_t<value_type>;
275
276 AssociativeContainerIterator(const Iterator &iter, const Container &container)
277 : IteratorBase<AssociativeContainerIterator<Container, Pos>, Container, Iterator>(iter, container)
278 {
279 }
280
281 auto operator*() const
282 {
283 return std::get<Pos>(*this->mIter);
284 }
285};
286
287template<typename Container>
288using AssociativeContainerKeyIterator = AssociativeContainerIterator<Container, 0>;
289template<typename Container>
290using AssociativeContainerValueIterator = AssociativeContainerIterator<Container, 1>;
291
292template<typename Iterator>
293struct Range {
294public:
295 using iterator = Iterator;
296 using const_iterator = Iterator;
297 using value_type = typename detail::IteratorTrait<Iterator>::value_type;
298
299 Range(Iterator &&begin, Iterator &&end)
300 : mBegin(std::move(begin))
301 , mEnd(std::move(end))
302 {
303 }
304
305 Iterator begin() const
306 {
307 return mBegin;
308 }
309
310 Iterator cbegin() const
311 {
312 return mBegin;
313 }
314
315 Iterator end() const
316 {
317 return mEnd;
318 }
319
320 Iterator cend() const
321 {
322 return mEnd;
323 }
324
325 auto size() const
326 {
327 return std::distance(mBegin, mEnd);
328 }
329
330private:
331 Iterator mBegin;
332 Iterator mEnd;
333};
334
335template<typename T>
336using IsRange = typename std::is_same<T, Range<typename T::iterator>>;
337
338// Tags
339
340template<template<typename> class Cont>
341struct ToTag_ {
342 template<typename T>
343 using OutputContainer = Cont<T>;
344};
345
346template<template<typename, typename> class Cont>
347struct ToAssocTag_ {
348 template<typename Key, typename Value>
349 using OuputContainer = Cont<Key, Value>;
350};
351
352struct ValuesTag_ {
353};
354struct KeysTag_ {
355};
356
357template<typename UnaryOperation>
358struct TransformTag_ {
359 UnaryOperation mFn;
360};
361
362template<typename UnaryPredicate>
363struct FilterTag_ {
364 UnaryPredicate mFn;
365};
366
367template<typename UnaryOperation>
368struct ForEachTag_ {
369 UnaryOperation mFn;
370};
371
372template<typename UnaryPredicate>
373struct AllTag_ {
374 UnaryPredicate mFn;
375};
376
377template<typename UnaryPredicate>
378struct AnyTag_ {
379 UnaryPredicate mFn;
380};
381
382template<typename UnaryPredicate>
383struct NoneTag_ {
384 UnaryPredicate mFn;
385};
386
387struct EnumerateTag_ {
388 qsizetype mStart = 0;
389};
390
391} // namespace detail
392} // namespace AkRanges
393
394// Generic operator| for To_<> converter
395template<typename RangeLike, template<typename> class OutContainer, typename T = typename RangeLike::value_type>
396auto operator|(const RangeLike &range, AkRanges::detail::ToTag_<OutContainer>) -> OutContainer<T>
397{
398 using namespace AkRanges::detail;
399 return copyContainer<RangeLike, OutContainer<T>>(range);
400}
401
402// Specialization for case when InContainer and OutContainer are identical
403// Create a copy, but for Qt container this is very cheap due to implicit sharing.
404template<template<typename> class InContainer, typename T>
405auto operator|(const InContainer<T> &in, AkRanges::detail::ToTag_<InContainer>) -> InContainer<T>
406{
407 return in;
408}
409
410// Generic operator| for ToAssoc_<> converter
411template<typename RangeLike, template<typename, typename> class OutContainer, typename T = typename RangeLike::value_type>
412auto operator|(const RangeLike &range, AkRanges::detail::ToAssocTag_<OutContainer>) -> OutContainer<typename T::first_type, typename T::second_type>
413{
414 using namespace AkRanges::detail;
415 return copyAssocContainer<RangeLike, OutContainer<typename T::first_type, typename T::second_type>>(range);
416}
417
418// Generic operator| for transform()
419template<typename RangeLike, typename UnaryOperation>
420auto operator|(const RangeLike &range, AkRanges::detail::TransformTag_<UnaryOperation> t)
421{
422 using namespace AkRanges::detail;
423 using OutIt = TransformIterator<RangeLike, UnaryOperation>;
424 return Range<OutIt>(OutIt(std::cbegin(range), t.mFn, range), OutIt(std::cend(range), t.mFn, range));
425}
426
427// Generic operator| for filter()
428template<typename RangeLike, typename UnaryPredicate>
429auto operator|(const RangeLike &range, AkRanges::detail::FilterTag_<UnaryPredicate> p)
430{
431 using namespace AkRanges::detail;
432 using OutIt = FilterIterator<RangeLike, UnaryPredicate>;
433 return Range<OutIt>(OutIt(std::cbegin(range), std::cend(range), p.mFn, range), OutIt(std::cend(range), std::cend(range), p.mFn, range));
434}
435
436// Generator operator| for enumerate()
437template<typename RangeLike>
438auto operator|(const RangeLike &range, AkRanges::detail::EnumerateTag_ tag)
439{
440 using namespace AkRanges::detail;
441 using OutIt = EnumerateIterator<RangeLike>;
442 return Range<OutIt>(OutIt(std::cbegin(range), tag.mStart, range), OutIt(std::cend(range), tag.mStart, range));
443}
444
445// Generic operator| for foreach()
446template<typename RangeLike, typename UnaryOperation>
447auto operator|(const RangeLike &range, AkRanges::detail::ForEachTag_<UnaryOperation> op)
448{
449 std::for_each(std::cbegin(range), std::cend(range), [op = std::move(op)](const auto &val) mutable {
450 std::invoke(op.mFn, val);
451 });
452 return range;
453}
454
455// Generic operator| for all
456template<typename RangeLike, typename UnaryPredicate>
457auto operator|(const RangeLike &range, AkRanges::detail::AllTag_<UnaryPredicate> p)
458{
459 return std::all_of(std::cbegin(range), std::cend(range), p.mFn);
460}
461
462// Generic operator| for any
463template<typename RangeLike, typename PredicateFn>
464auto operator|(const RangeLike &range, AkRanges::detail::AnyTag_<PredicateFn> p)
465{
466 return std::any_of(std::cbegin(range), std::cend(range), p.mFn);
467}
468
469// Generic operator| for none
470template<typename RangeLike, typename UnaryPredicate>
471auto operator|(const RangeLike &range, AkRanges::detail::NoneTag_<UnaryPredicate> p)
472{
473 return std::none_of(std::cbegin(range), std::cend(range), p.mFn);
474}
475
476// Generic operator| for keys
477template<typename AssocContainer>
478auto operator|(const AssocContainer &in, AkRanges::detail::KeysTag_)
479{
480 using namespace AkRanges::detail;
481 using OutIt = AssociativeContainerKeyIterator<AssocContainer>;
482 return Range<OutIt>(OutIt(in.constKeyValueBegin(), in), OutIt(in.constKeyValueEnd(), in));
483}
484
485// Generic operator| for values
486template<typename AssocContainer>
487auto operator|(const AssocContainer &in, AkRanges::detail::ValuesTag_)
488{
489 using namespace AkRanges::detail;
490 using OutIt = AssociativeContainerValueIterator<AssocContainer>;
491 return Range<OutIt>(OutIt(in.constKeyValueBegin(), in), OutIt(in.constKeyValueEnd(), in));
492}
493
494namespace AkRanges
495{
496namespace Actions
497{
498/// Non-lazily convert given range or container to QList
499static constexpr auto toQVector = detail::ToTag_<QList>{};
500/// Non-lazily convert given range or container to QSet
501static constexpr auto toQSet = detail::ToTag_<QSet>{};
502/// Non-lazily convert given range or container to QList
503static constexpr auto toQList = detail::ToTag_<QList>{};
504/// Non-lazily convert given range or container of pairs to QMap
505static constexpr auto toQMap = detail::ToAssocTag_<QMap>{};
506/// Non-lazily convert given range or container of pairs to QHash
507static constexpr auto toQHash = detail::ToAssocTag_<QHash>{};
508
509/// Non-lazily call UnaryOperation for each element of the container or range
510template<typename UnaryOperation>
511detail::ForEachTag_<UnaryOperation> forEach(UnaryOperation &&op)
512{
513 return detail::ForEachTag_<UnaryOperation>{std::forward<UnaryOperation>(op)};
514}
515
516/// Non-lazily check that all elements in the range satisfy given predicate
517template<typename UnaryPredicate>
518detail::AllTag_<UnaryPredicate> all(UnaryPredicate &&pred)
519{
520 return detail::AllTag_<UnaryPredicate>{std::forward<UnaryPredicate>(pred)};
521}
522
523/// Non-lazily check that at least one element in range satisfies the given predicate
524template<typename UnaryPredicate>
525detail::AnyTag_<UnaryPredicate> any(UnaryPredicate &&pred)
526{
527 return detail::AnyTag_<UnaryPredicate>{std::forward<UnaryPredicate>(pred)};
528}
529
530/// Non-lazily check that none of the elements in the range satisfies the given predicate
531template<typename UnaryPredicate>
532detail::NoneTag_<UnaryPredicate> none(UnaryPredicate &&pred)
533{
534 return detail::NoneTag_<UnaryPredicate>{std::forward<UnaryPredicate>(pred)};
535}
536
537} // namespace Actions
538
539namespace Views
540{
541/// Lazily extract values from an associative container
542static constexpr auto values = detail::ValuesTag_{};
543/// Lazily extract keys from an associative container
544static constexpr auto keys = detail::KeysTag_{};
545
546/// Lazily transform each element of a range or container using given transformation
547template<typename UnaryOperation>
548detail::TransformTag_<UnaryOperation> transform(UnaryOperation &&op)
549{
550 return detail::TransformTag_<UnaryOperation>{std::forward<UnaryOperation>(op)};
551}
552
553/// Lazily filters a range or container by applying given predicate on each element
554template<typename UnaryPredicate>
555detail::FilterTag_<UnaryPredicate> filter(UnaryPredicate &&pred)
556{
557 return detail::FilterTag_<UnaryPredicate>{std::forward<UnaryPredicate>(pred)};
558}
559
560/// Lazily enumerate elements in input range
561inline detail::EnumerateTag_ enumerate(qsizetype start = 0)
562{
563 return detail::EnumerateTag_{start};
564}
565
566/// Create a range, a view on a container from the given pair fo iterators
567template<typename Iterator1, typename Iterator2, typename It = std::remove_reference_t<Iterator1>>
568detail::Range<It> range(Iterator1 begin, Iterator2 end)
569{
570 return detail::Range<It>(std::move(begin), std::move(end));
571}
572
573} // namespace Views
574
575} // namespace AkRanges
Q_SCRIPTABLE Q_NOREPLY void start()
KDOCTOOLS_EXPORT QString transform(const QString &file, const QString &stylesheet, const QList< const char * > &params=QList< const char * >())
KIOCORE_EXPORT CopyJob * move(const QList< QUrl > &src, const QUrl &dest, JobFlags flags=DefaultFlags)
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
const QList< QKeySequence > & end()
A glue between Qt and the standard library.
QFuture< void > filter(QThreadPool *pool, Sequence &sequence, KeepFunctor &&filterFunction)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:13:38 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.