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