File indexing completed on 2024-06-16 04:50:34
0001 /* 0002 SPDX-FileCopyrightText: 2018-2019 Daniel Vrátil <dvratil@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #pragma once 0008 0009 #include "akhelpers.h" 0010 #include "aktraits.h" 0011 0012 #include <QList> 0013 #include <QMap> 0014 #include <QSet> 0015 0016 #include <algorithm> 0017 #include <functional> 0018 #include <iterator> 0019 #include <type_traits> 0020 #include <utility> 0021 0022 namespace AkRanges 0023 { 0024 namespace detail 0025 { 0026 template<typename RangeLike, typename OutContainer, AK_REQUIRES(AkTraits::isAppendable<OutContainer>)> 0027 OutContainer copyContainer(const RangeLike &range) 0028 { 0029 OutContainer rv; 0030 rv.reserve(range.size()); 0031 for (auto &&v : range) { 0032 rv.push_back(std::move(v)); 0033 } 0034 return rv; 0035 } 0036 0037 template<typename RangeLike, typename OutContainer, AK_REQUIRES(AkTraits::isInsertable<OutContainer>)> 0038 OutContainer copyContainer(const RangeLike &range) 0039 { 0040 OutContainer rv; 0041 rv.reserve(range.size()); 0042 for (const auto &v : range) { 0043 rv.insert(v); // Qt containers lack move-enabled insert() overload 0044 } 0045 return rv; 0046 } 0047 0048 template<typename RangeList, typename OutContainer> 0049 OutContainer copyAssocContainer(const RangeList &range) 0050 { 0051 OutContainer rv; 0052 for (const auto &v : range) { 0053 rv.insert(v.first, v.second); // Qt containers lack move-enabled insert() overload 0054 } 0055 return rv; 0056 } 0057 0058 template<typename Iterator> 0059 struct IteratorTrait { 0060 using iterator_category = typename Iterator::iterator_category; 0061 using value_type = typename Iterator::value_type; 0062 using difference_type = typename Iterator::difference_type; 0063 using pointer = typename Iterator::pointer; 0064 using reference = typename Iterator::reference; 0065 }; 0066 0067 // Without QT_STRICT_ITERATORS QList and QList iterators do not satisfy STL 0068 // iterator concepts since they are nothing more but typedefs to T* - for those 0069 // we need to provide custom traits. 0070 template<typename Iterator> 0071 struct IteratorTrait<Iterator *> { 0072 // QTypedArrayData::iterator::iterator_category 0073 using iterator_category = std::random_access_iterator_tag; 0074 using value_type = Iterator; 0075 using difference_type = qsizetype; 0076 using pointer = Iterator *; 0077 using reference = Iterator &; 0078 }; 0079 0080 template<typename Iterator> 0081 struct IteratorTrait<const Iterator *> { 0082 using iterator_category = std::random_access_iterator_tag; 0083 using value_type = Iterator; 0084 using difference_type = qsizetype; 0085 using pointer = const Iterator *; 0086 using reference = const Iterator &; 0087 }; 0088 0089 template<typename IterImpl, typename RangeLike, typename Iterator = typename RangeLike::const_iterator> 0090 struct IteratorBase { 0091 public: 0092 using iterator_category = typename IteratorTrait<Iterator>::iterator_category; 0093 using value_type = typename IteratorTrait<Iterator>::value_type; 0094 using difference_type = typename IteratorTrait<Iterator>::difference_type; 0095 using pointer = typename IteratorTrait<Iterator>::pointer; 0096 using reference = typename IteratorTrait<Iterator>::reference; 0097 0098 IteratorBase(const IteratorBase<IterImpl, RangeLike> &other) 0099 : mIter(other.mIter) 0100 , mRange(other.mRange) 0101 { 0102 } 0103 0104 IterImpl &operator++() 0105 { 0106 ++static_cast<IterImpl *>(this)->mIter; 0107 return *static_cast<IterImpl *>(this); 0108 } 0109 0110 IterImpl operator++(int) 0111 { 0112 auto ret = *static_cast<IterImpl *>(this); 0113 ++static_cast<IterImpl *>(this)->mIter; 0114 return ret; 0115 } 0116 0117 bool operator==(const IterImpl &other) const 0118 { 0119 return mIter == other.mIter; 0120 } 0121 0122 bool operator!=(const IterImpl &other) const 0123 { 0124 return !(*static_cast<const IterImpl *>(this) == other); 0125 } 0126 0127 bool operator<(const IterImpl &other) const 0128 { 0129 return mIter < other.mIter; 0130 } 0131 0132 auto operator-(const IterImpl &other) const 0133 { 0134 return mIter - other.mIter; 0135 } 0136 0137 auto operator*() const 0138 { 0139 return *mIter; 0140 } 0141 0142 protected: 0143 IteratorBase(const Iterator &iter, const RangeLike &range) 0144 : mIter(iter) 0145 , mRange(range) 0146 { 0147 } 0148 IteratorBase(const Iterator &iter, RangeLike &&range) 0149 : mIter(iter) 0150 , mRange(std::move(range)) 0151 { 0152 } 0153 0154 Iterator mIter; 0155 RangeLike mRange; 0156 }; 0157 0158 template<typename RangeLike, typename TransformFn, typename Iterator = typename RangeLike::const_iterator> 0159 struct TransformIterator : public IteratorBase<TransformIterator<RangeLike, TransformFn>, RangeLike> { 0160 private: 0161 template<typename... T> 0162 struct ResultOf; 0163 0164 template<typename R, typename... Args> 0165 struct ResultOf<R(Args...)> { 0166 using type = R; 0167 }; 0168 0169 template<typename... Ts> 0170 using FuncHelper = decltype(std::invoke(std::declval<Ts>()...))(Ts...); 0171 using IteratorValueType = typename ResultOf<FuncHelper<TransformFn, typename IteratorTrait<Iterator>::value_type>>::type; 0172 0173 public: 0174 using value_type = IteratorValueType; 0175 using pointer = IteratorValueType *; // FIXME: preserve const-ness 0176 using reference = const IteratorValueType &; // FIXME: preserve const-ness 0177 0178 TransformIterator(const Iterator &iter, const TransformFn &fn, const RangeLike &range) 0179 : IteratorBase<TransformIterator<RangeLike, TransformFn>, RangeLike>(iter, range) 0180 , mFn(fn) 0181 { 0182 } 0183 0184 auto operator*() const 0185 { 0186 return std::invoke(mFn, *this->mIter); 0187 } 0188 0189 private: 0190 TransformFn mFn; 0191 }; 0192 0193 template<typename RangeLike, typename Predicate, typename Iterator = typename RangeLike::const_iterator> 0194 class FilterIterator : public IteratorBase<FilterIterator<RangeLike, Predicate>, RangeLike> 0195 { 0196 public: 0197 // Filter iterator is just an InputIterator (for complexity reasons). 0198 // It actually makes it more efficient with STL algos like std::find() 0199 using iterator_category = std::input_iterator_tag; 0200 0201 FilterIterator(const Iterator &iter, const Iterator &end, const Predicate &predicate, const RangeLike &range) 0202 : IteratorBase<FilterIterator, RangeLike>(iter, range) 0203 , mPredicate(predicate) 0204 , mEnd(end) 0205 { 0206 while (this->mIter != mEnd && !std::invoke(mPredicate, *this->mIter)) { 0207 ++this->mIter; 0208 } 0209 } 0210 0211 auto &operator++() 0212 { 0213 if (this->mIter != mEnd) { 0214 do { 0215 ++this->mIter; 0216 } while (this->mIter != mEnd && !std::invoke(mPredicate, *this->mIter)); 0217 } 0218 return *this; 0219 } 0220 0221 auto operator++(int) 0222 { 0223 auto it = *this; 0224 ++(*this); 0225 return it; 0226 } 0227 0228 private: 0229 Predicate mPredicate; 0230 Iterator mEnd; 0231 }; 0232 0233 template<typename RangeLike, typename Iterator = typename RangeLike::const_iterator> 0234 class EnumerateIterator : public IteratorBase<EnumerateIterator<RangeLike>, RangeLike> 0235 { 0236 public: 0237 using value_type = std::pair<qsizetype, typename Iterator::value_type>; 0238 using pointer = value_type *; // FIXME: preserve const-ness 0239 using reference = const value_type &; // FIXME: preserve const-ness 0240 0241 EnumerateIterator(const Iterator &iter, qsizetype start, const RangeLike &range) 0242 : IteratorBase<EnumerateIterator, RangeLike>(iter, range) 0243 , mCount(start) 0244 { 0245 } 0246 0247 auto &operator++() 0248 { 0249 ++mCount; 0250 ++this->mIter; 0251 return *this; 0252 } 0253 0254 auto &operator++(int) 0255 { 0256 auto it = *this; 0257 ++(*this); 0258 return it; 0259 } 0260 0261 QPair<qsizetype, typename Iterator::value_type> operator*() const 0262 { 0263 return qMakePair(mCount, *this->mIter); 0264 } 0265 0266 private: 0267 qsizetype mCount = 0; 0268 }; 0269 0270 template<typename Container, int Pos, typename Iterator = typename Container::const_key_value_iterator> 0271 class AssociativeContainerIterator : public IteratorBase<AssociativeContainerIterator<Container, Pos>, Container, Iterator> 0272 { 0273 public: 0274 using value_type = std::remove_const_t<std::remove_reference_t<typename std::tuple_element<Pos, typename Iterator::value_type>::type>>; 0275 using pointer = std::add_pointer_t<value_type>; 0276 using reference = std::add_lvalue_reference_t<value_type>; 0277 0278 AssociativeContainerIterator(const Iterator &iter, const Container &container) 0279 : IteratorBase<AssociativeContainerIterator<Container, Pos>, Container, Iterator>(iter, container) 0280 { 0281 } 0282 0283 auto operator*() const 0284 { 0285 return std::get<Pos>(*this->mIter); 0286 } 0287 }; 0288 0289 template<typename Container> 0290 using AssociativeContainerKeyIterator = AssociativeContainerIterator<Container, 0>; 0291 template<typename Container> 0292 using AssociativeContainerValueIterator = AssociativeContainerIterator<Container, 1>; 0293 0294 template<typename Iterator> 0295 struct Range { 0296 public: 0297 using iterator = Iterator; 0298 using const_iterator = Iterator; 0299 using value_type = typename detail::IteratorTrait<Iterator>::value_type; 0300 0301 Range(Iterator &&begin, Iterator &&end) 0302 : mBegin(std::move(begin)) 0303 , mEnd(std::move(end)) 0304 { 0305 } 0306 0307 Iterator begin() const 0308 { 0309 return mBegin; 0310 } 0311 0312 Iterator cbegin() const 0313 { 0314 return mBegin; 0315 } 0316 0317 Iterator end() const 0318 { 0319 return mEnd; 0320 } 0321 0322 Iterator cend() const 0323 { 0324 return mEnd; 0325 } 0326 0327 auto size() const 0328 { 0329 return std::distance(mBegin, mEnd); 0330 } 0331 0332 private: 0333 Iterator mBegin; 0334 Iterator mEnd; 0335 }; 0336 0337 template<typename T> 0338 using IsRange = typename std::is_same<T, Range<typename T::iterator>>; 0339 0340 // Tags 0341 0342 template<template<typename> class Cont> 0343 struct ToTag_ { 0344 template<typename T> 0345 using OutputContainer = Cont<T>; 0346 }; 0347 0348 template<template<typename, typename> class Cont> 0349 struct ToAssocTag_ { 0350 template<typename Key, typename Value> 0351 using OuputContainer = Cont<Key, Value>; 0352 }; 0353 0354 struct ValuesTag_ { 0355 }; 0356 struct KeysTag_ { 0357 }; 0358 0359 template<typename UnaryOperation> 0360 struct TransformTag_ { 0361 UnaryOperation mFn; 0362 }; 0363 0364 template<typename UnaryPredicate> 0365 struct FilterTag_ { 0366 UnaryPredicate mFn; 0367 }; 0368 0369 template<typename UnaryOperation> 0370 struct ForEachTag_ { 0371 UnaryOperation mFn; 0372 }; 0373 0374 template<typename UnaryPredicate> 0375 struct AllTag_ { 0376 UnaryPredicate mFn; 0377 }; 0378 0379 template<typename UnaryPredicate> 0380 struct AnyTag_ { 0381 UnaryPredicate mFn; 0382 }; 0383 0384 template<typename UnaryPredicate> 0385 struct NoneTag_ { 0386 UnaryPredicate mFn; 0387 }; 0388 0389 struct EnumerateTag_ { 0390 qsizetype mStart = 0; 0391 }; 0392 0393 } // namespace detail 0394 } // namespace AkRanges 0395 0396 // Generic operator| for To_<> converter 0397 template<typename RangeLike, template<typename> class OutContainer, typename T = typename RangeLike::value_type> 0398 auto operator|(const RangeLike &range, AkRanges::detail::ToTag_<OutContainer>) -> OutContainer<T> 0399 { 0400 using namespace AkRanges::detail; 0401 return copyContainer<RangeLike, OutContainer<T>>(range); 0402 } 0403 0404 // Specialization for case when InContainer and OutContainer are identical 0405 // Create a copy, but for Qt container this is very cheap due to implicit sharing. 0406 template<template<typename> class InContainer, typename T> 0407 auto operator|(const InContainer<T> &in, AkRanges::detail::ToTag_<InContainer>) -> InContainer<T> 0408 { 0409 return in; 0410 } 0411 0412 // Generic operator| for ToAssoc_<> converter 0413 template<typename RangeLike, template<typename, typename> class OutContainer, typename T = typename RangeLike::value_type> 0414 auto operator|(const RangeLike &range, AkRanges::detail::ToAssocTag_<OutContainer>) -> OutContainer<typename T::first_type, typename T::second_type> 0415 { 0416 using namespace AkRanges::detail; 0417 return copyAssocContainer<RangeLike, OutContainer<typename T::first_type, typename T::second_type>>(range); 0418 } 0419 0420 // Generic operator| for transform() 0421 template<typename RangeLike, typename UnaryOperation> 0422 auto operator|(const RangeLike &range, AkRanges::detail::TransformTag_<UnaryOperation> t) 0423 { 0424 using namespace AkRanges::detail; 0425 using OutIt = TransformIterator<RangeLike, UnaryOperation>; 0426 return Range<OutIt>(OutIt(std::cbegin(range), t.mFn, range), OutIt(std::cend(range), t.mFn, range)); 0427 } 0428 0429 // Generic operator| for filter() 0430 template<typename RangeLike, typename UnaryPredicate> 0431 auto operator|(const RangeLike &range, AkRanges::detail::FilterTag_<UnaryPredicate> p) 0432 { 0433 using namespace AkRanges::detail; 0434 using OutIt = FilterIterator<RangeLike, UnaryPredicate>; 0435 return Range<OutIt>(OutIt(std::cbegin(range), std::cend(range), p.mFn, range), OutIt(std::cend(range), std::cend(range), p.mFn, range)); 0436 } 0437 0438 // Generator operator| for enumerate() 0439 template<typename RangeLike> 0440 auto operator|(const RangeLike &range, AkRanges::detail::EnumerateTag_ tag) 0441 { 0442 using namespace AkRanges::detail; 0443 using OutIt = EnumerateIterator<RangeLike>; 0444 return Range<OutIt>(OutIt(std::cbegin(range), tag.mStart, range), OutIt(std::cend(range), tag.mStart, range)); 0445 } 0446 0447 // Generic operator| for foreach() 0448 template<typename RangeLike, typename UnaryOperation> 0449 auto operator|(const RangeLike &range, AkRanges::detail::ForEachTag_<UnaryOperation> op) 0450 { 0451 std::for_each(std::cbegin(range), std::cend(range), [op = std::move(op)](const auto &val) mutable { 0452 std::invoke(op.mFn, val); 0453 }); 0454 return range; 0455 } 0456 0457 // Generic operator| for all 0458 template<typename RangeLike, typename UnaryPredicate> 0459 auto operator|(const RangeLike &range, AkRanges::detail::AllTag_<UnaryPredicate> p) 0460 { 0461 return std::all_of(std::cbegin(range), std::cend(range), p.mFn); 0462 } 0463 0464 // Generic operator| for any 0465 template<typename RangeLike, typename PredicateFn> 0466 auto operator|(const RangeLike &range, AkRanges::detail::AnyTag_<PredicateFn> p) 0467 { 0468 return std::any_of(std::cbegin(range), std::cend(range), p.mFn); 0469 } 0470 0471 // Generic operator| for none 0472 template<typename RangeLike, typename UnaryPredicate> 0473 auto operator|(const RangeLike &range, AkRanges::detail::NoneTag_<UnaryPredicate> p) 0474 { 0475 return std::none_of(std::cbegin(range), std::cend(range), p.mFn); 0476 } 0477 0478 // Generic operator| for keys 0479 template<typename AssocContainer> 0480 auto operator|(const AssocContainer &in, AkRanges::detail::KeysTag_) 0481 { 0482 using namespace AkRanges::detail; 0483 using OutIt = AssociativeContainerKeyIterator<AssocContainer>; 0484 return Range<OutIt>(OutIt(in.constKeyValueBegin(), in), OutIt(in.constKeyValueEnd(), in)); 0485 } 0486 0487 // Generic operator| for values 0488 template<typename AssocContainer> 0489 auto operator|(const AssocContainer &in, AkRanges::detail::ValuesTag_) 0490 { 0491 using namespace AkRanges::detail; 0492 using OutIt = AssociativeContainerValueIterator<AssocContainer>; 0493 return Range<OutIt>(OutIt(in.constKeyValueBegin(), in), OutIt(in.constKeyValueEnd(), in)); 0494 } 0495 0496 namespace AkRanges 0497 { 0498 namespace Actions 0499 { 0500 /// Non-lazily convert given range or container to QList 0501 static constexpr auto toQVector = detail::ToTag_<QList>{}; 0502 /// Non-lazily convert given range or container to QSet 0503 static constexpr auto toQSet = detail::ToTag_<QSet>{}; 0504 /// Non-lazily convert given range or container to QList 0505 static constexpr auto toQList = detail::ToTag_<QList>{}; 0506 /// Non-lazily convert given range or container of pairs to QMap 0507 static constexpr auto toQMap = detail::ToAssocTag_<QMap>{}; 0508 /// Non-lazily convert given range or container of pairs to QHash 0509 static constexpr auto toQHash = detail::ToAssocTag_<QHash>{}; 0510 0511 /// Non-lazily call UnaryOperation for each element of the container or range 0512 template<typename UnaryOperation> 0513 detail::ForEachTag_<UnaryOperation> forEach(UnaryOperation &&op) 0514 { 0515 return detail::ForEachTag_<UnaryOperation>{std::forward<UnaryOperation>(op)}; 0516 } 0517 0518 /// Non-lazily check that all elements in the range satisfy given predicate 0519 template<typename UnaryPredicate> 0520 detail::AllTag_<UnaryPredicate> all(UnaryPredicate &&pred) 0521 { 0522 return detail::AllTag_<UnaryPredicate>{std::forward<UnaryPredicate>(pred)}; 0523 } 0524 0525 /// Non-lazily check that at least one element in range satisfies the given predicate 0526 template<typename UnaryPredicate> 0527 detail::AnyTag_<UnaryPredicate> any(UnaryPredicate &&pred) 0528 { 0529 return detail::AnyTag_<UnaryPredicate>{std::forward<UnaryPredicate>(pred)}; 0530 } 0531 0532 /// Non-lazily check that none of the elements in the range satisfies the given predicate 0533 template<typename UnaryPredicate> 0534 detail::NoneTag_<UnaryPredicate> none(UnaryPredicate &&pred) 0535 { 0536 return detail::NoneTag_<UnaryPredicate>{std::forward<UnaryPredicate>(pred)}; 0537 } 0538 0539 } // namespace Actions 0540 0541 namespace Views 0542 { 0543 /// Lazily extract values from an associative container 0544 static constexpr auto values = detail::ValuesTag_{}; 0545 /// Lazily extract keys from an associative container 0546 static constexpr auto keys = detail::KeysTag_{}; 0547 0548 /// Lazily transform each element of a range or container using given transformation 0549 template<typename UnaryOperation> 0550 detail::TransformTag_<UnaryOperation> transform(UnaryOperation &&op) 0551 { 0552 return detail::TransformTag_<UnaryOperation>{std::forward<UnaryOperation>(op)}; 0553 } 0554 0555 /// Lazily filters a range or container by applying given predicate on each element 0556 template<typename UnaryPredicate> 0557 detail::FilterTag_<UnaryPredicate> filter(UnaryPredicate &&pred) 0558 { 0559 return detail::FilterTag_<UnaryPredicate>{std::forward<UnaryPredicate>(pred)}; 0560 } 0561 0562 /// Lazily enumerate elements in input range 0563 inline detail::EnumerateTag_ enumerate(qsizetype start = 0) 0564 { 0565 return detail::EnumerateTag_{start}; 0566 } 0567 0568 /// Create a range, a view on a container from the given pair fo iterators 0569 template<typename Iterator1, typename Iterator2, typename It = std::remove_reference_t<Iterator1>> 0570 detail::Range<It> range(Iterator1 begin, Iterator2 end) 0571 { 0572 return detail::Range<It>(std::move(begin), std::move(end)); 0573 } 0574 0575 } // namespace Views 0576 0577 } // namespace AkRanges