/* ============================================================================== 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 constexpr auto canPreDecrement = false; template constexpr auto canPreDecrement())>> = true; template constexpr auto canAddAssign = false; template constexpr auto canAddAssign() += std::declval())>> = true; template constexpr auto canSubAssign = false; template constexpr auto canSubAssign() -= std::declval())>> = true; template constexpr auto canAdd = false; template constexpr auto canAdd() + std::declval())>> = true; template constexpr auto canSub = false; template constexpr auto canSub() - std::declval())>> = true; template constexpr auto canLessThan = false; template constexpr auto canLessThan() < std::declval())>> = true; template constexpr auto canLessThanEqual = false; template constexpr auto canLessThanEqual() <= std::declval())>> = true; template constexpr auto canGreaterThan = false; template constexpr auto canGreaterThan() > std::declval())>> = true; template constexpr auto canGreaterThanEqual = false; template constexpr auto canGreaterThanEqual() >= std::declval())>> = true; namespace withAdlSize { using std::size; template using AdlSize = decltype (size (std::declval())); template using AdlSignedSize = std::common_type_t>>; } } // 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 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 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 [[nodiscard]] constexpr bool operator== (const EnumerateIterator& other) const { return iterator == other.iterator; } /** @see operator==() */ template [[nodiscard]] constexpr bool operator!= (const EnumerateIterator& 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())> 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 , 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 , 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 , int> = 0> constexpr EnumerateIterator& operator+= (I diff) { iterator += diff; index += static_cast (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 , int> = 0> constexpr EnumerateIterator& operator-= (I diff) { iterator -= diff; index -= static_cast (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 , int> = 0> [[nodiscard]] constexpr auto operator- (const EnumerateIterator& 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 , 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 , int> = 0> [[nodiscard]] constexpr bool operator< (const EnumerateIterator& 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 , int> = 0> [[nodiscard]] constexpr bool operator<= (const EnumerateIterator& 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 , int> = 0> [[nodiscard]] constexpr bool operator> (const EnumerateIterator& 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 , int> = 0> [[nodiscard]] constexpr bool operator>= (const EnumerateIterator& 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 , 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 , 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 , 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 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 [[nodiscard]] constexpr auto makeRange (Begin begin, End end) { return IteratorPair { 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 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', the type of 'index' is 'ptrdiff_t', and the type of 'value' is 'int&'. @code std::vector 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 > [[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