|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2022 - Raw Material Software Limited
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- The code included in this file is provided under the terms of the ISC license
- http://www.isc.org/downloads/software-support-policy/isc-license. Permission
- To use, copy, modify, and/or distribute this software for any purpose with or
- without fee is hereby granted provided that the above copyright notice and
- this permission notice appear in all copies.
-
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
-
- ==============================================================================
- */
-
- namespace juce
- {
-
- namespace detail
- {
-
- template <typename T, typename = void>
- constexpr auto canPreDecrement = false;
-
- template <typename T>
- constexpr auto canPreDecrement<T, std::void_t<decltype (--std::declval<T>())>> = true;
-
- template <typename T, typename I, typename = void>
- constexpr auto canAddAssign = false;
-
- template <typename T, typename I>
- constexpr auto canAddAssign<T, I, std::void_t<decltype (std::declval<T>() += std::declval<I>())>> = true;
-
- template <typename T, typename I, typename = void>
- constexpr auto canSubAssign = false;
-
- template <typename T, typename I>
- constexpr auto canSubAssign<T, I, std::void_t<decltype (std::declval<T>() -= std::declval<I>())>> = true;
-
- template <typename T, typename I, typename = void>
- constexpr auto canAdd = false;
-
- template <typename T, typename I>
- constexpr auto canAdd<T, I, std::void_t<decltype (std::declval<T>() + std::declval<I>())>> = true;
-
- template <typename T, typename I, typename = void>
- constexpr auto canSub = false;
-
- template <typename T, typename I>
- constexpr auto canSub<T, I, std::void_t<decltype (std::declval<T>() - std::declval<I>())>> = true;
-
- template <typename T, typename I, typename = void>
- constexpr auto canLessThan = false;
-
- template <typename T, typename I>
- constexpr auto canLessThan<T, I, std::void_t<decltype (std::declval<T>() < std::declval<I>())>> = true;
-
- template <typename T, typename I, typename = void>
- constexpr auto canLessThanEqual = false;
-
- template <typename T, typename I>
- constexpr auto canLessThanEqual<T, I, std::void_t<decltype (std::declval<T>() <= std::declval<I>())>> = true;
-
- template <typename T, typename I, typename = void>
- constexpr auto canGreaterThan = false;
-
- template <typename T, typename I>
- constexpr auto canGreaterThan<T, I, std::void_t<decltype (std::declval<T>() > std::declval<I>())>> = true;
-
- template <typename T, typename I, typename = void>
- constexpr auto canGreaterThanEqual = false;
-
- template <typename T, typename I>
- constexpr auto canGreaterThanEqual<T, I, std::void_t<decltype (std::declval<T>() >= std::declval<I>())>> = true;
-
- namespace withAdlSize
- {
- using std::size;
-
- template <typename Range>
- using AdlSize = decltype (size (std::declval<Range>()));
-
- template <typename Range>
- using AdlSignedSize = std::common_type_t<std::ptrdiff_t, std::make_signed_t<AdlSize<Range>>>;
- }
-
- } // namespace detail
-
- /**
- Returned when dereferencing an EnumerateIterator.
-
- Allows querying the index associated with an element, along with a reference to the element
- itself.
-
- You should never need to construct an instance of this type yourself. Instead, use the
- enumerate() function to construct a range that can be enumerated.
-
- @see enumerate()
- @tags{Core}
- */
- template <typename Index, typename Value>
- struct Enumerated
- {
- Index index;
- Value value;
- };
-
- /**
- An iterator that wraps some other iterator, keeping track of the relative position of that
- iterator based on calls to arithmetic operators such as
- operator++(), operator--(), operator+(), and operator-().
-
- You should never need to construct an instance of this type yourself. Instead, use the
- enumerate() function to construct a range that can be enumerated.
-
- @see enumerate()
- @tags{Core}
- */
- template <typename Iter, typename Index = ptrdiff_t>
- class EnumerateIterator
- {
- public:
- /** Default constructor. */
- constexpr EnumerateIterator() = default;
-
- /** Wraps the provided iterator, and sets the internal count to 0. */
- constexpr explicit EnumerateIterator (Iter iter)
- : EnumerateIterator (std::move (iter), Index{}) {}
-
- /** Wraps the provided iterator, and sets the internal count to the provided value. */
- constexpr EnumerateIterator (Iter iter, Index ind)
- : iterator (std::move (iter)), index (ind) {}
-
- /** Two EnumerateIterators are considered equal if the wrapped iterators are equal. */
- template <typename OtherIter, typename OtherInd>
- [[nodiscard]] constexpr bool operator== (const EnumerateIterator<OtherIter, OtherInd>& other) const
- {
- return iterator == other.iterator;
- }
-
- /** @see operator==() */
- template <typename OtherIter, typename OtherInd>
- [[nodiscard]] constexpr bool operator!= (const EnumerateIterator<OtherIter, OtherInd>& other) const
- {
- return ! operator== (other);
- }
-
- /** Dereferencing the iterator produces an Enumerated instance *by value*. This type holds
- a copy of the iterator's current index, along with the result of dereferencing the
- wrapped iterator (normally a reference type).
- */
- [[nodiscard]] constexpr Enumerated<Index, decltype (*std::declval<Iter>())> operator*() const
- {
- return { index, *iterator };
- }
-
- /** Increments the iterator and the index. */
- constexpr EnumerateIterator& operator++()
- {
- ++iterator;
- ++index;
- return *this;
- }
-
- /** Increments the iterator and the index. */
- constexpr EnumerateIterator operator++ (int)
- {
- auto copy = *this;
- operator++();
- return copy;
- }
-
- /** Decrements the iterator and the index.
- Only participates in overload resolution if the iterator can be pre-decremented.
- */
- template <typename T = Iter, std::enable_if_t<detail::canPreDecrement<T>, int> = 0>
- constexpr EnumerateIterator& operator--()
- {
- --iterator;
- --index;
- return *this;
- }
-
- /** Decrements the iterator and the index.
- Only participates in overload resolution if the iterator can be pre-decremented.
- */
- template <typename T = Iter, std::enable_if_t<detail::canPreDecrement<T>, int> = 0>
- constexpr EnumerateIterator operator-- (int)
- {
- auto copy = *this;
- operator--();
- return copy;
- }
-
- /** Adds an integral value to both the iterator and the index.
- Only participates in overload resolution if the iterator can be add-assigned.
- */
- template <typename I, std::enable_if_t<detail::canAddAssign<Iter&, I>, int> = 0>
- constexpr EnumerateIterator& operator+= (I diff)
- {
- iterator += diff;
- index += static_cast<Index> (diff);
- return *this;
- }
-
- /** Subtracts an integral value from both the iterator and the index.
- Only participates in overload resolution if the iterator can be sub-assigned.
- */
- template <typename I, std::enable_if_t<detail::canSubAssign<Iter&, I>, int> = 0>
- constexpr EnumerateIterator& operator-= (I diff)
- {
- iterator -= diff;
- index -= static_cast<Index> (diff);
- return *this;
- }
-
- /** Subtracts another enumerate iterator from this one, producing the same result as
- subtracting the two wrapped iterators. For random-access iterators, this will normally
- return the distance between the two iterators.
- Only participates in overload resolution if the wrapped iterators can be subtracted.
- */
- template <typename OtherIter, typename OtherInd, std::enable_if_t<detail::canSub<Iter, OtherIter>, int> = 0>
- [[nodiscard]] constexpr auto operator- (const EnumerateIterator<OtherIter, OtherInd>& other) const
- {
- return iterator - other.iterator;
- }
-
- /** Indexes into this iterator, equivalent to adding an integral value to this iterator and
- then dereferencing the result.
- Only participates in overload resolution if the wrapped iterator allows addition of
- integral values.
- */
- template <typename I, std::enable_if_t<detail::canAdd<EnumerateIterator, I>, int> = 0>
- [[nodiscard]] constexpr auto operator[] (I diff) const
- {
- return *(*this + diff);
- }
-
- /** Returns the result of comparing the two wrapped iterators.
- Only participates in overload resolution if the wrapped iterators are comparable.
- */
- template <typename OtherIter, typename OtherInd, std::enable_if_t<detail::canLessThan<Iter, OtherIter>, int> = 0>
- [[nodiscard]] constexpr bool operator< (const EnumerateIterator<OtherIter, OtherInd>& other) const
- {
- return iterator < other.iterator;
- }
-
- /** Returns the result of comparing the two wrapped iterators.
- Only participates in overload resolution if the wrapped iterators are comparable.
- */
- template <typename OtherIter, typename OtherInd, std::enable_if_t<detail::canLessThanEqual<Iter, OtherIter>, int> = 0>
- [[nodiscard]] constexpr bool operator<= (const EnumerateIterator<OtherIter, OtherInd>& other) const
- {
- return iterator <= other.iterator;
- }
-
- /** Returns the result of comparing the two wrapped iterators.
- Only participates in overload resolution if the wrapped iterators are comparable.
- */
- template <typename OtherIter, typename OtherInd, std::enable_if_t<detail::canGreaterThan<Iter, OtherIter>, int> = 0>
- [[nodiscard]] constexpr bool operator> (const EnumerateIterator<OtherIter, OtherInd>& other) const
- {
- return iterator > other.iterator;
- }
-
- /** Returns the result of comparing the two wrapped iterators.
- Only participates in overload resolution if the wrapped iterators are comparable.
- */
- template <typename OtherIter, typename OtherInd, std::enable_if_t<detail::canGreaterThanEqual<Iter, OtherIter>, int> = 0>
- [[nodiscard]] constexpr bool operator>= (const EnumerateIterator<OtherIter, OtherInd>& other) const
- {
- return iterator >= other.iterator;
- }
-
- /** Returns the result of adding an integral value to this iterator.
- Only participates in overload resolution if addition is supported by the wrapped iterator.
- */
- template <typename I, std::enable_if_t<detail::canAddAssign<EnumerateIterator&, I>, int> = 0>
- constexpr friend auto operator+ (EnumerateIterator iter, I ind)
- {
- return iter += ind;
- }
-
- /** Returns the result of adding an integral value to this iterator.
- Only participates in overload resolution if addition is supported by the wrapped iterator.
- */
- template <typename I, std::enable_if_t<detail::canAddAssign<EnumerateIterator&, I>, int> = 0>
- constexpr friend auto operator+ (I ind, EnumerateIterator iter)
- {
- return iter += ind;
- }
-
- /** Returns the result of subtracting an integral value from this iterator.
- Only participates in overload resolution if subtraction is supported by the wrapped iterator.
- */
- template <typename I, std::enable_if_t<detail::canSubAssign<EnumerateIterator&, I>, int> = 0>
- constexpr friend auto operator- (EnumerateIterator iter, I ind)
- {
- return iter -= ind;
- }
-
- private:
- Iter iterator{};
- Index index = 0;
- };
-
- //==============================================================================
- /**
- Wraps a pair of iterators, providing member begin() and end() functions that return
- those iterators.
- This is useful in situations where you have an iterator pair, but want to use that
- pair somewhere that requires an iterable range, such as in a ranged-for loop.
-
- @see makeRange()
- @tags{Core}
- */
- template <typename Begin, typename End>
- class IteratorPair
- {
- public:
- /** Constructs a pair from a begin and end iterator.
- Instead of calling this directly, use makeRange().
- */
- constexpr IteratorPair (Begin bIn, End eIn)
- : b (std::move (bIn)), e (std::move (eIn)) {}
-
- /** Returns the begin iterator. */
- constexpr auto begin() const { return b; }
-
- /** Returns the end iterator. */
- constexpr auto end() const { return e; }
-
- private:
- Begin b;
- End e;
- };
-
- /**
- Given two iterators "begin" and "end", returns an IteratorPair with a member
- begin() and end() function. This pair can be used in contexts that expect an
- iterable range, the most significant of which is ranged-for loops.
- This automatically deduces the Begin and End types, so it is more concise to use than
- directly calling the IteratorPair constructor.
- */
- template <typename Begin, typename End = Begin>
- [[nodiscard]] constexpr auto makeRange (Begin begin, End end)
- {
- return IteratorPair<Begin, End> { std::move (begin), std::move (end) };
- }
-
- //==============================================================================
- /**
- Given a range and an optional starting offset, returns an IteratorPair that
- holds EnumerateIterators wrapping the begin() and end() of the range.
-
- This is useful in situations where you need to iterate some range, but also query
- the position of each item in the range.
-
- A simple usage might look like this:
-
- @code
- std::list<int> elements { 10, 20, 30, 40, 50 };
-
- for (const auto pair : enumerate (elements))
- std::cout << pair.index << ' ' << pair.value << ' ';
-
- // output: 0 10 1 20 2 30 3 40 4 50
- @endcode
-
- You can also use structured bindings to concisely destructure each Enumerated instance:
-
- @code
- for (const auto [index, value] : enumerate (elements))
- std::cout << index << ' ' << value << ' ';
- @endcode
-
- Note that the Enumerated instance is returned by value. This is because each Enumerated
- instance is created on-demand when the iterator is dereferenced. As a result, the following
- will result in a dangling reference, and will probably trigger a compiler warning:
-
- @code
- // BAD IDEA: creating a reference to a temporary Enumerated instance
- for (auto& [index, value] : enumerate (elements))
- ...
- @endcode
-
- The 'value' member of Enumerated automatically assumes the same type as dereferencing the
- wrapped iterator, which is normally a reference to an element of a container.
- In the following snippet, the type of '[index, value]' is 'const Enumerated<ptrdiff_t, int&>',
- the type of 'index' is 'ptrdiff_t', and the type of 'value' is 'int&'.
-
- @code
- std::vector<int> elements { 10, 20, 30, 40, 50 };
- for (const auto [index, value] : enumerate (elements))
- ...
- @endcode
-
- By default, the constness of pair.value will match the constness of the range passed to
- enumerate. If you pass a mutable lvalue reference to enumerate, then each value will also
- be mutable. If you pass a constant lvalue reference to enumerate, then each value will be
- const. If you know that you don't need the iterated elements to be mutable, it's good
- practice to wrap the range with std::as_const before passing it to enumerate:
-
- @code
- for (const auto [index, value] : enumerate (std::as_const (elements)))
- {
- // value is immutable here
- }
- @endcode
- */
- template <typename Range, typename Index = detail::withAdlSize::AdlSignedSize<Range>>
- [[nodiscard]] constexpr auto enumerate (Range&& range, Index startingValue = {})
- {
- // This ensures argument-dependent lookup works properly for user-defined non-member begin/end
- using std::begin, std::end;
- return makeRange (EnumerateIterator { begin (range), startingValue },
- EnumerateIterator { end (range), startingValue });
- }
-
- } // namespace juce
|