From c6c1ee1b2830a722804ec9f9398091502abc0222 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Wed, 26 Aug 2026 21:16:54 +0200 Subject: [PATCH] Add missing etl::ranges::distance overload (#1569) * Update _index.md * Update _index.md * Cherry pick the Docker fixes for PowerPC (#1563) Co-authored-by: John Wellbelove * Add missing etl::ranges::distance overload --------- Co-authored-by: John Wellbelove Co-authored-by: John Wellbelove --- docs/iterators/Iterator.md | 2 ++ docs/ranges/ranges.md | 57 ++++++++++++++++++++++++++++++++++++++ include/etl/iterator.h | 16 +++++++++++ test/test_iterator.cpp | 50 +++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+) diff --git a/docs/iterators/Iterator.md b/docs/iterators/Iterator.md index c95017b7..c6028f8c 100644 --- a/docs/iterators/Iterator.md +++ b/docs/iterators/Iterator.md @@ -133,6 +133,8 @@ ETL_CONSTEXPR17 typename std::iterator_traits::difference_type distance(TIterator first, TIterator last) ``` +See [Ranges](../../ranges/ranges) for the `etl::ranges` range access primitives, including `etl::ranges::distance`. + ## iterator A base class provided to simplify definitions of the required types for iterators. https://en.cppreference.com/w/cpp/iterator/iterator diff --git a/docs/ranges/ranges.md b/docs/ranges/ranges.md index 48b9b495..32cffcd8 100644 --- a/docs/ranges/ranges.md +++ b/docs/ranges/ranges.md @@ -119,6 +119,63 @@ Output: This first filters the even numbers and then squares them. Each `|` passes the result of the previous stage as input to the next view adaptor. +## Range Access and Primitives + +These are declared in the `etl::ranges` namespace in `iterator.h`, which is included by `ranges.h`. + +### Range access + +Function objects, equivalent to their `std::ranges` counterparts. + +| Name | Description | +|---|---| +| `begin`, `end` | The iterator to the first element, and the sentinel. | +| `cbegin`, `cend` | The const iterator to the first element, and the const sentinel. | +| `rbegin`, `rend` | The reverse iterator to the last element in forward order, and the past-the-end reverse iterator. | +| `crbegin`, `crend` | The const reverse iterator to the last element in forward order, and the const past-the-end reverse iterator. | +| `size` | The number of elements, as a `size_t`. | +| `ssize` | The number of elements, as a `ptrdiff_t`. | +| `empty` | Whether the range has no elements. | +| `data`, `cdata` | A pointer to the first element. | +| `distance` | The number of elements between two iterators, or in a range. | +| `advance` | Advances an iterator by a given distance and/or to a bound. | +| `next`, `prev` | An iterator advanced forwards or backwards by a given distance and/or to a bound. | + +`size` and `ssize` use the range's `size()` member if it has one; otherwise they count the elements by traversing the range, which requires at least a forward range. + +#### distance + +```cpp +// Iterator and sentinel of the same type. +template +constexpr etl::iter_difference_t distance(I first, I last) + +// Iterator and sentinel of different types. +template +constexpr etl::iter_difference_t distance(I first, S last) + +// Range. +template +constexpr etl::iter_difference_t> distance(R&& r) +``` + +The range overload uses the range's `size()` member if it has one; otherwise it counts the elements by traversing from `begin()` to `end()`. + +### Range primitives + +Alias templates that give the associated types of a range. + +| Name | Description | +|---|---| +| `iterator_t` | The iterator type. | +| `const_iterator_t` | The const iterator type. | +| `sentinel_t` | The sentinel type. | +| `const_sentinel_t` | The const sentinel type. | +| `range_size_t` | The size type. | +| `range_difference_t` | The difference type. | +| `range_value_t` | The element value type. | +| `range_reference_t` | The element reference type. | + ## Supported Views All views are in the `etl::ranges` namespace. Corresponding range adaptor objects are available in `etl::ranges::views`. diff --git a/include/etl/iterator.h b/include/etl/iterator.h index a990fb6c..01de257a 100644 --- a/include/etl/iterator.h +++ b/include/etl/iterator.h @@ -1656,6 +1656,22 @@ namespace etl } return n; } + + // Overload for ranges + template ()))> + constexpr etl::iter_difference_t operator()(R&& r) const + { + using U = etl::remove_cvref_t; + + if constexpr (has_size_member::value) + { + return static_cast>(r.size()); + } + else + { + return (*this)(ETL_OR_STD::begin(r), ETL_OR_STD::end(r)); + } + } }; struct size diff --git a/test/test_iterator.cpp b/test/test_iterator.cpp index db018c40..ed808582 100644 --- a/test/test_iterator.cpp +++ b/test/test_iterator.cpp @@ -29,6 +29,7 @@ SOFTWARE. #include "unit_test_framework.h" #include +#include #include #include #include @@ -1420,6 +1421,55 @@ namespace CHECK_EQUAL(1, *result); } + //************************************************************************* + // etl::ranges::distance(first, last) + //************************************************************************* + TEST(ranges_distance_iterator_sentinel) + { + int data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + CHECK_EQUAL(10, etl::ranges::distance(&data[0], &data[10])); + CHECK_EQUAL(4, etl::ranges::distance(&data[3], &data[7])); + CHECK_EQUAL(0, etl::ranges::distance(&data[3], &data[3])); + + non_random_iterator first(&data[0]); + non_random_sentinel last(&data[6]); + CHECK_EQUAL(6, etl::ranges::distance(first, last)); + } + + //************************************************************************* + // etl::ranges::distance(range) + //************************************************************************* + TEST(ranges_distance_range_with_size_member) + { + std::vector data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + CHECK_EQUAL(10, etl::ranges::distance(data)); + + const std::vector& cdata = data; + CHECK_EQUAL(10, etl::ranges::distance(cdata)); + + std::vector empty_data; + CHECK_EQUAL(0, etl::ranges::distance(empty_data)); + } + + //************************************************************************* + TEST(ranges_distance_c_array) + { + int data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + CHECK_EQUAL(10, etl::ranges::distance(data)); + } + + //************************************************************************* + TEST(ranges_distance_range_without_size_member) + { + std::forward_list data = {0, 1, 2, 3, 4}; + + CHECK_EQUAL(5, etl::ranges::distance(data)); + CHECK_EQUAL(etl::ranges::distance(data.begin(), data.end()), etl::ranges::distance(data)); + } + TEST(test_is_range) { std::vector vec;