Add C++ ranges library for C++17 (#1316)

* Add ranges

* Print test names at test time (#1343)

* Fix conflit commit errors

* Cast return value of operator* to value_type

Fixed warning on VS2022

---------

Co-authored-by: John Wellbelove <jwellbelove@users.noreply.github.com>
This commit is contained in:
Roland Reichwein 2026-03-26 09:56:17 +01:00 committed by GitHub
parent add42b6c87
commit 31b87b5419
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 31668 additions and 19 deletions

328
docs/ranges.md Normal file
View File

@ -0,0 +1,328 @@
# ETL C++17 Ranges Implementation
## Overview
The Embedded Template Library provides a C++17-compatible implementation of ranges, inspired by the C++20 ranges library. This implementation enables range-based algorithms and views for embedded and resource-constrained environments where full C++20 support may not be available.
## Features
- **Ranges**: Provides range types and iterator wrappers for composing operations over sequences.
- **Views**: Includes lightweight, composable views such as `filter_view`, `transform_view`, and `subrange`.
- **Algorithms**: Supports range-based algorithms compatible with ETL containers and standard containers.
- **Compatibility**: Designed for C++17, with minimal dependencies and no reliance on C++20 features.
## Getting Started
Include the main header in your project:
```cpp
#include <etl/ranges.h>
```
### Example Usage
#### Using Ranges
```cpp
#include <etl/print.h>
#include <etl/ranges.h>
#include <etl/vector.h>
...
etl::vector<int, 10> data = {6, 1, 3, 3, 2};
etl::ranges::sort(data);
etl::ranges::for_each(data, [](const int& i){etl::print(" {}", i);});
```
Output:
```text
1 2 3 3 6
```
#### Using Views
```cpp
#include <etl/print.h>
#include <etl/ranges.h>
#include <etl/vector.h>
...
etl::vector<int, 10> data = {1, 2, 3, 4, 5};
auto even = [](int v) { return v % 2 == 0; };
auto filtered = etl::ranges::filter_view(data, even);
etl::ranges::for_each(filtered, [](const int& i){etl::print(" {}", i);});
```
Output:
```text
2 4
```
#### Transforming Elements
```cpp
#include <etl/print.h>
#include <etl/ranges.h>
#include <etl/vector.h>
...
etl::vector<int, 10> data = {1, 2, 3, 4, 5};
auto squared = etl::ranges::transform_view(data, [](int v) { return v * v; });
etl::ranges::for_each(squared, [](const int& i){etl::print(" {}", i);});
```
Output:
```text
1 4 9 16 25
```
#### Composition
Views can be composed using the pipe (`|`) operator, allowing you to chain multiple transformations in a readable, left-to-right style:
```cpp
#include <etl/print.h>
#include <etl/ranges.h>
#include <etl/vector.h>
namespace views = etl::ranges::views;
...
etl::vector<int, 10> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto result = data
| views::filter([](const int& v) { return v % 2 == 0; })
| views::transform([](const int& v) { return v * v; });
etl::ranges::for_each(result, [](const int& i){ etl::print(" {}", i); });
```
Output:
```text
4 16 36 64 100
```
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.
## Supported Views
All views are in the `etl::ranges` namespace. Corresponding range adaptor objects are available in `etl::ranges::views`.
### Range Factories
| View | `views::` adaptor | Description |
|---|---|---|
| `empty_view<T>` | `views::empty<T>` | A view with no elements. |
| `single_view` | `views::single` | A view containing exactly one element. |
| `iota_view` | `views::iota` | A view of sequentially increasing values. |
| `repeat_view` | `views::repeat` | A view that repeats a value a given number of times. |
### Range Adaptors
| View | `views::` adaptor | Description |
|---|---|---|
| `ref_view` | `views::ref` | A non-owning view that wraps a reference to a range. |
| `owning_view` | `views::owning` | A view that takes ownership of a range via move. |
| — | `views::all` | Returns the range itself (if already a view), a `ref_view`, or an `owning_view`. |
| `filter_view` | `views::filter` | Filters elements based on a predicate. |
| `transform_view` | `views::transform` | Applies a transformation to each element. |
| `as_rvalue_view` | `views::as_rvalue` | Casts each element to an rvalue reference. |
| `as_const_view` | `views::as_const` | Provides a const view of the elements. |
| `cache_latest_view` | `views::cache_latest` | Caches the most recently accessed element (avoids recomputation). |
| `reverse_view` | `views::reverse` | Reverses the order of elements. |
| `drop_view` | `views::drop` | Skips the first *n* elements. |
| `drop_while_view` | `views::drop_while` | Skips leading elements while a predicate is true. |
| `take_view` | `views::take` | Takes the first *n* elements. |
| `take_while_view` | `views::take_while` | Takes leading elements while a predicate is true. |
| `join_view` | `views::join` | Flattens a range of ranges into a single range. |
| `join_with_view` | `views::join_with` | Flattens a range of ranges, inserting a delimiter between each. |
| `split_view` | `views::split` | Splits a range into subranges around a delimiter pattern. |
| `lazy_split_view` | `views::lazy_split` | Lazily splits a range by a pattern (inner ranges discovered on iteration). |
| — | `views::counted` | Creates a view of *n* elements starting from an iterator. |
| `concat_view` | `views::concat` | Concatenates multiple ranges into a single view. |
| `zip_view` | `views::zip` | Zips multiple ranges into a view of tuples (length of shortest range). |
| `zip_transform_view` | `views::zip_transform` | Zips multiple ranges and applies a function to each tuple of elements. |
| `common_view` | `views::common` | Adapts a view so that its iterator and sentinel types are the same. |
| `enumerate_view` | `views::enumerate` | Pairs each element with its index, producing tuples of (index, value). |
| `elements_view` | `views::elements` | Extracts the *N*-th element from each tuple-like value. |
| `keys_view` | `views::keys` | Alias for `elements_view` with *N*=0 (extracts first element of pairs/tuples). |
| `values_view` | `views::values` | Alias for `elements_view` with *N*=1 (extracts second element of pairs/tuples). |
| `adjacent_view` | `views::adjacent` | Produces a view of tuples of *N* adjacent elements (sliding window of tuples). |
| — | `views::pairwise` | Alias for `views::adjacent<2>`. |
| `adjacent_transform_view` | `views::adjacent_transform` | Applies a function to each group of *N* adjacent elements. |
| — | `views::pairwise_transform` | Alias for `views::adjacent_transform<2>`. |
| `chunk_view` | `views::chunk` | Splits a range into non-overlapping chunks of a given size. |
| `slide_view` | `views::slide` | Produces overlapping subranges (sliding windows) of a given size. |
| `chunk_by_view` | `views::chunk_by` | Splits a range into subranges between adjacent elements where a predicate is false. |
| `stride_view` | `views::stride` | Yields every *N*-th element from the underlying range. |
| `cartesian_product_view` | `views::cartesian_product` | Produces the Cartesian product of multiple ranges as a view of tuples. |
| `to_input_view` | `views::to_input` | Downgrades iterator category to input iterator while preserving elements and order. |
| `subrange` | — | Represents a sub-range defined by an iteratorsentinel pair. |
All views support range-based for-loop iteration and can be composed with the pipe (`|`) operator.
## Supported Algorithms
All algorithms are callable objects in the `etl::ranges` namespace. Each supports both an iterator-pair overload and a range overload (where applicable), and most accept optional projection and comparator arguments.
### Non-modifying Sequence Operations
| Algorithm | Description |
|---|---|
| `for_each` | Applies a function to each element in a range. |
| `for_each_n` | Applies a function to the first *n* elements. |
| `find` | Finds the first element equal to a value. |
| `find_if` | Finds the first element satisfying a predicate. |
| `find_if_not` | Finds the first element not satisfying a predicate. |
| `find_end` | Finds the last occurrence of a subsequence. |
| `find_first_of` | Finds the first element matching any in a second range. |
| `adjacent_find` | Finds the first pair of adjacent equal elements. |
| `count` | Counts elements equal to a value. |
| `count_if` | Counts elements satisfying a predicate. |
| `all_of` | Checks if all elements satisfy a predicate. |
| `any_of` | Checks if any element satisfies a predicate. |
| `none_of` | Checks if no elements satisfy a predicate. |
| `mismatch` | Finds the first position where two ranges differ. |
| `equal` | Checks if two ranges are equal. |
| `is_permutation` | Checks if one range is a permutation of another. |
| `search` | Searches for the first occurrence of a subsequence. |
| `search_n` | Searches for *n* consecutive copies of a value. |
| `starts_with` | Checks if a range starts with another range. |
| `ends_with` | Checks if a range ends with another range. |
| `lexicographical_compare` | Compares two ranges lexicographically. |
### Fold Operations
| Algorithm | Description |
|---|---|
| `fold_left` | Left-folds elements with a binary operation. |
| `fold_left_with_iter` | Left-folds, returning both the result and an iterator. |
| `fold_left_first` | Left-folds using the first element as the initial value. |
| `fold_left_first_with_iter` | Like `fold_left_first`, also returning an iterator. |
| `fold_right` | Right-folds elements with a binary operation. |
| `fold_right_last` | Right-folds using the last element as the initial value. |
### Modifying Sequence Operations
| Algorithm | Description |
|---|---|
| `copy` | Copies elements to a destination range. |
| `copy_n` | Copies *n* elements to a destination range. |
| `copy_if` | Copies elements satisfying a predicate. |
| `copy_backward` | Copies elements backwards to a destination range. |
| `move` | Moves elements to a destination range. |
| `move_backward` | Moves elements backwards to a destination range. |
| `swap_ranges` | Swaps elements between two ranges. |
| `replace` | Replaces elements equal to a value. |
| `replace_if` | Replaces elements satisfying a predicate. |
| `replace_copy` | Copies, replacing elements equal to a value. |
| `replace_copy_if` | Copies, replacing elements satisfying a predicate. |
| `remove` | Removes elements equal to a value. |
| `remove_if` | Removes elements satisfying a predicate. |
| `remove_copy` | Copies, omitting elements equal to a value. |
| `fill` | Fills a range with a value. |
| `fill_n` | Fills *n* elements with a value. |
| `generate` | Assigns each element the result of a generator function. |
| `generate_n` | Assigns *n* elements the result of a generator function. |
| `iota` | Fills a range with sequentially increasing values. |
| `unique` | Removes consecutive duplicate elements. |
| `unique_copy` | Copies, removing consecutive duplicates. |
| `transform` | Applies a transformation to each element. |
| `reverse` | Reverses the order of elements. |
| `reverse_copy` | Copies elements in reverse order. |
| `rotate` | Rotates elements in a range. |
| `rotate_copy` | Copies elements with rotation. |
| `shift_left` | Shifts elements to the left. |
| `shift_right` | Shifts elements to the right. |
| `shuffle` | Randomly reorders elements. |
| `sample` | Selects *n* random elements from a range. |
### Sorting Operations
| Algorithm | Description |
|---|---|
| `sort` | Sorts elements in a range. |
| `stable_sort` | Sorts elements preserving relative order of equivalent elements. |
| `partial_sort` | Partially sorts a range so that the first *n* elements are sorted. |
| `partial_sort_copy` | Copies and partially sorts elements. |
| `nth_element` | Partially sorts so that the *n*-th element is in its sorted position. |
| `is_sorted` | Checks if a range is sorted. |
| `is_sorted_until` | Finds the first unsorted element. |
### Partitioning Operations
| Algorithm | Description |
|---|---|
| `partition` | Partitions elements by a predicate. |
| `stable_partition` | Partitions elements, preserving relative order. |
| `is_partitioned` | Checks if a range is partitioned. |
| `partition_copy` | Copies elements into two ranges based on a predicate. |
| `partition_point` | Finds the partition point. |
### Binary Search (on sorted ranges)
| Algorithm | Description |
|---|---|
| `lower_bound` | Finds the first element not less than a value. |
| `upper_bound` | Finds the first element greater than a value. |
| `equal_range` | Returns the range of elements equal to a value. |
| `binary_search` | Checks if a sorted range contains a value. |
### Set Operations (on sorted ranges)
| Algorithm | Description |
|---|---|
| `includes` | Checks if one sorted range includes another. |
| `merge` | Merges two sorted ranges. |
| `inplace_merge` | Merges two consecutive sorted sub-ranges in place. |
| `set_union` | Computes the union of two sorted ranges. |
| `set_intersection` | Computes the intersection of two sorted ranges. |
| `set_difference` | Computes the difference of two sorted ranges. |
| `set_symmetric_difference` | Computes the symmetric difference of two sorted ranges. |
### Heap Operations
| Algorithm | Description |
|---|---|
| `make_heap` | Creates a heap from a range. |
| `push_heap` | Pushes an element onto a heap. |
| `pop_heap` | Pops the top element from a heap. |
| `sort_heap` | Sorts a heap into a sorted range. |
| `is_heap` | Checks if a range is a heap. |
| `is_heap_until` | Finds the first element that breaks the heap property. |
### Min/Max Operations
| Algorithm | Description |
|---|---|
| `min` | Returns the smaller of two values or the smallest in an initializer list. |
| `min_element` | Finds the smallest element in a range. |
| `max` | Returns the larger of two values or the largest in an initializer list. |
| `max_element` | Finds the largest element in a range. |
| `minmax` | Returns the smaller and larger of two values. |
| `minmax_element` | Finds both the smallest and largest elements in a range. |
| `clamp` | Clamps a value between a minimum and maximum. |
### Permutation Operations
| Algorithm | Description |
|---|---|
| `next_permutation` | Generates the next lexicographic permutation. |
| `prev_permutation` | Generates the previous lexicographic permutation. |
## Reference
For reference to the STD implementation, see also:
- Algorithms: https://en.cppreference.com/w/cpp/algorithm.html
- Ranges/Views: https://en.cppreference.com/w/cpp/ranges.html
## Limitations
- Not all C++20 range features are available due to limitation to C++17. Especially C++20 concepts are not used.
- Designed for ETL containers but can work with standard containers if compatible with ETL's iterator requirements.

File diff suppressed because it is too large Load Diff

View File

@ -37,7 +37,6 @@ SOFTWARE.
#include "error_handler.h"
#include "exception.h"
#include "utility.h"
#include "algorithm.h"
#include <stdint.h>

View File

@ -41,6 +41,7 @@ SOFTWARE.
#include "initializer_list.h"
#include "type_traits.h"
#include "invoke.h"
#include "memory.h"
namespace etl
{

View File

@ -79,6 +79,17 @@ namespace etl
return *t;
}
#if ETL_USING_CPP11
// implementation without etl::invoke, which would add a circular dependency
template <typename... TArgs>
ETL_CONSTEXPR20 auto operator()(TArgs&&... args) const
noexcept(noexcept(etl::declval<T&>()(etl::declval<TArgs>()...)))
-> decltype(etl::declval<T&>()(etl::declval<TArgs>()...))
{
return get()(etl::forward<TArgs>(args)...);
}
#endif
private:
T* t;
@ -646,7 +657,41 @@ namespace etl
return private_functional::const_mem_fn_impl<TReturnType, TClassType, TArgs...>(member_function);
}
#endif
#if ETL_USING_CPP14
struct identity
{
template<class T>
constexpr T&& operator()(T&& t) const noexcept
{
return etl::forward<T>(t);
}
};
#endif
#if ETL_USING_CPP17
namespace ranges
{
struct equal_to
{
template <typename T, typename U>
constexpr auto operator()(T&& t, U&& u) const -> decltype(static_cast<T&&>(t) == static_cast<U&&>(u))
{
return static_cast<T&&>(t) == static_cast<U&&>(u);
}
};
struct less
{
template <typename T, typename U>
constexpr auto operator()(T&& t, U&& u) const -> decltype(static_cast<T&&>(t) < static_cast<U&&>(u))
{
return static_cast<T&&>(t) < static_cast<U&&>(u);
}
};
}
#endif
}
#endif

View File

@ -110,7 +110,7 @@ namespace etl
//****************************************************************************
/// Pointer to member object + object (or derived) reference
template <typename TFunction,
template <typename TFunction,
typename TObject,
typename = etl::enable_if_t<etl::is_member_object_pointer<etl::decay_t<TFunction>>::value &&
!etl::is_pointer<etl::decay_t<TObject>>::value &&
@ -121,11 +121,37 @@ namespace etl
return etl::forward<TObject>(obj).*f;
}
//****************************************************************************
/// reference_wrapper callable (unwrap and call directly)
template <typename TFunction,
typename... TArgs,
typename = etl::enable_if_t<etl::is_reference_wrapper<etl::decay_t<TFunction>>::value &&
!etl::is_member_pointer<etl::decay_t<decltype(etl::declval<TFunction>().get())>>::value>>
ETL_CONSTEXPR auto invoke(TFunction&& f, TArgs&&... args)
-> decltype(f.get()(etl::forward<TArgs>(args)...))
{
return f.get()(etl::forward<TArgs>(args)...);
}
//****************************************************************************
/// reference_wrapper callable wrapping a member pointer (unwrap and re-invoke)
template <typename TFunction,
typename... TArgs,
typename = etl::enable_if_t<etl::is_reference_wrapper<etl::decay_t<TFunction>>::value &&
etl::is_member_pointer<etl::decay_t<decltype(etl::declval<TFunction>().get())>>::value>,
typename = void>
ETL_CONSTEXPR auto invoke(TFunction&& f, TArgs&&... args)
-> decltype(etl::invoke(f.get(), etl::forward<TArgs>(args)...))
{
return etl::invoke(f.get(), etl::forward<TArgs>(args)...);
}
//****************************************************************************
/// General callable (function object / lambda / function pointer)
template <typename TFunction,
typename... TArgs,
typename = etl::enable_if_t<!etl::is_member_pointer<etl::decay_t<TFunction>>::value>>
typename = etl::enable_if_t<!etl::is_member_pointer<etl::decay_t<TFunction>>::value &&
!etl::is_reference_wrapper<etl::decay_t<TFunction>>::value>>
ETL_CONSTEXPR auto invoke(TFunction&& f, TArgs&&... args)
-> decltype(etl::forward<TFunction>(f)(etl::forward<TArgs>(args)...))
{
@ -204,11 +230,30 @@ namespace etl
using invoke_result_impl_t = typename invoke_result_impl<TFunction, TArgs...>::type;
//*******************************************
// Map raw function type to pointer.
// Unwrap reference_wrapper<T> to its underlying type T&,
// forwarding to etl::unwrap_ref_decay for reference_wrapper detection.
template <typename TFunction, bool = etl::is_reference_wrapper<etl::decay_t<TFunction>>::value>
struct unwrap_ref_callable
{
using type = TFunction;
};
template <typename TFunction>
struct unwrap_ref_callable<TFunction, true>
{
using type = etl::unwrap_ref_decay_t<TFunction>;
};
template <typename TFunction>
using unwrap_ref_callable_t = typename unwrap_ref_callable<TFunction>::type;
//*******************************************
// Map raw function type to pointer, and unwrap reference_wrapper
// so that function_traits sees the actual callable type.
template <typename TFunction>
using effective_callable_t = etl::conditional_t<etl::is_function<etl::remove_reference_t<TFunction>>::value,
etl::add_pointer_t<etl::remove_reference_t<TFunction>>,
TFunction>;
unwrap_ref_callable_t<TFunction>>;
}
//****************************************************************************

View File

@ -34,6 +34,7 @@ SOFTWARE.
#include "platform.h"
#include "type_traits.h"
#include "utility.h"
#include "invoke.h"
#include "private/addressof.h"
#if ETL_USING_STL || defined(ETL_IN_UNIT_TEST)
@ -92,10 +93,19 @@ namespace etl
//***************************************************************************
// advance
template <typename TIterator, typename TDistance>
ETL_CONSTEXPR14 void advance_helper(TIterator& itr, TDistance n, ETL_OR_STD::input_iterator_tag)
{
while (n-- > 0)
{
++itr;
}
}
template <typename TIterator, typename TDistance>
ETL_CONSTEXPR14 void advance_helper(TIterator& itr, TDistance n, ETL_OR_STD::output_iterator_tag)
{
while (n--)
while (n-- > 0)
{
++itr;
}
@ -104,7 +114,7 @@ namespace etl
template <typename TIterator, typename TDistance>
ETL_CONSTEXPR14 void advance_helper(TIterator& itr, TDistance n, ETL_OR_STD::forward_iterator_tag)
{
while (n--)
while (n-- > 0)
{
++itr;
}
@ -1212,6 +1222,725 @@ namespace etl
#define ETL_ARRAY_SIZE(a) sizeof(etl::array_size(a))
#if ETL_USING_CPP17
template<class T>
using iter_value_t = typename etl::iterator_traits<etl::remove_cvref_t<T>>::value_type;
template<class T>
using iter_reference_t = decltype(*etl::declval<T&>());
#if ETL_USING_CPP20
template<class T>
using iter_const_reference_t = typename etl::common_reference_t<const etl::iter_value_t<T>&&, etl::iter_reference_t<T>>;
#endif
template<class T>
using iter_difference_t = typename etl::iterator_traits<etl::remove_cvref_t<T>>::difference_type;
template<class I, class Proj>
using projected_value_t = etl::remove_cvref_t<etl::invoke_result_t<Proj&, etl::iter_reference_t<I>>>;
namespace ranges
{
namespace private_ranges
{
struct begin
{
template<class T>
constexpr auto operator()(T& t) const
{
return ETL_OR_STD::begin(t);
}
};
struct end
{
template<class T>
constexpr auto operator()(T& t) const
{
return ETL_OR_STD::end(t);
}
};
struct cbegin
{
template<class T>
constexpr auto operator()(T& t) const
{
return ETL_OR_STD::cbegin(t);
}
};
struct cend
{
template<class T>
constexpr auto operator()(T& t) const
{
return ETL_OR_STD::cend(t);
}
};
struct rbegin
{
template<class T>
constexpr auto operator()(T& t) const
{
return ETL_OR_STD::rbegin(t);
}
};
struct rend
{
template<class T>
constexpr auto operator()(T& t) const
{
return ETL_OR_STD::rend(t);
}
};
struct crbegin
{
template<class T>
constexpr auto operator()(T& t) const
{
return ETL_OR_STD::crbegin(t);
}
};
struct crend
{
template<class T>
constexpr auto operator()(T& t) const
{
return ETL_OR_STD::crend(t);
}
};
template<class T, class = void>
struct has_size_member : etl::false_type {};
template<class T>
struct has_size_member<T, etl::void_t<decltype(etl::declval<const T&>().size())>> : etl::true_type {};
template<class T, class = void>
struct has_empty_member : etl::false_type {};
template<class T>
struct has_empty_member<T, etl::void_t<decltype(etl::declval<const T&>().empty())>> : etl::true_type {};
struct distance
{
// Overload for common ranges (iterator == sentinel type)
template<typename I, typename = etl::enable_if_t<etl::is_input_iterator_concept<I>::value || etl::is_output_iterator_concept<I>::value>>
constexpr etl::iter_difference_t<I> operator()(I first, I last) const
{
if constexpr (etl::is_random_access_iterator_concept<I>::value)
{
return last - first;
}
else
{
etl::iter_difference_t<I> n = 0;
while (!(first == last))
{
++first;
++n;
}
return n;
}
}
// Overload for non-common ranges (iterator != sentinel type)
template<typename I, typename S, typename = etl::enable_if_t<
(etl::is_input_iterator_concept<I>::value || etl::is_output_iterator_concept<I>::value) &&
!etl::is_same<I, S>::value>>
constexpr etl::iter_difference_t<I> operator()(I first, S last) const
{
etl::iter_difference_t<I> n = 0;
while (!(first == last))
{
++first;
++n;
}
return n;
}
};
struct size
{
template<class T>
constexpr size_t operator()(T&& t) const
{
using U = etl::remove_cvref_t<T>;
if constexpr (has_size_member<U>::value)
{
return static_cast<size_t>(t.size());
}
else
{
using iter_type = decltype(ETL_OR_STD::begin(t));
static_assert(etl::is_forward_iterator_concept<iter_type>::value,
"ranges::size requires a sized range or at least a forward range; "
"single-pass input ranges are not supported");
return static_cast<size_t>(distance{}(ETL_OR_STD::begin(t), ETL_OR_STD::end(t)));
}
}
};
struct ssize
{
template<class T>
constexpr auto operator()(T&& t) const
{
using U = etl::remove_cvref_t<T>;
if constexpr (has_size_member<U>::value)
{
return static_cast<ptrdiff_t>(t.size());
}
else
{
using iter_type = decltype(ETL_OR_STD::begin(t));
static_assert(etl::is_forward_iterator_concept<iter_type>::value,
"ranges::ssize requires a sized range or at least a forward range; "
"single-pass input ranges are not supported");
return static_cast<ptrdiff_t>(distance{}(ETL_OR_STD::begin(t), ETL_OR_STD::end(t)));
}
}
};
struct empty
{
template<class T>
constexpr auto operator()(T&& t) const
{
using U = etl::remove_cvref_t<T>;
if constexpr (has_empty_member<U>::value)
{
return t.empty();
}
else
{
return ETL_OR_STD::cbegin(t) == ETL_OR_STD::cend(t);
}
}
};
struct data
{
template<class T>
constexpr auto operator()(T& t) const
{
return ETL_OR_STD::data(t);
}
};
struct cdata
{
template<class T>
constexpr etl::add_pointer_t<etl::add_const_t<etl::remove_pointer_t<decltype(ETL_OR_STD::data(etl::declval<T&>()))>>> operator()(T& t) const
{
return ETL_OR_STD::data(t);
}
};
}
inline constexpr private_ranges::begin begin;
inline constexpr private_ranges::end end;
inline constexpr private_ranges::cbegin cbegin;
inline constexpr private_ranges::cend cend;
inline constexpr private_ranges::rbegin rbegin;
inline constexpr private_ranges::rend rend;
inline constexpr private_ranges::crbegin crbegin;
inline constexpr private_ranges::crend crend;
inline constexpr private_ranges::size size;
inline constexpr private_ranges::ssize ssize;
inline constexpr private_ranges::empty empty;
inline constexpr private_ranges::data data;
inline constexpr private_ranges::cdata cdata;
inline constexpr private_ranges::distance distance;
//*************************************************************************
/// Range primitives.
//*************************************************************************
template<class T>
using iterator_t = decltype(etl::ranges::begin(etl::declval<T&>()));
template<class T>
using const_iterator_t = decltype(etl::ranges::cbegin(etl::declval<T&>()));
template<class T>
using sentinel_t = decltype(etl::ranges::end(etl::declval<T&>()));
template<class T>
using const_sentinel_t = decltype(etl::ranges::cend(etl::declval<T&>()));
template<class T>
using range_size_t = decltype(etl::ranges::size(etl::declval<T&>()));
template<class T>
using range_difference_t = etl::iter_difference_t<etl::ranges::iterator_t<T>>;
template<class T>
using range_value_t = etl::iter_value_t<etl::ranges::iterator_t<T>>;
template<class T>
using range_reference_t = etl::iter_reference_t<ranges::iterator_t<T>>;
struct advance_fn
{
template<typename I, typename = etl::enable_if_t<(etl::is_input_iterator_concept<I>::value || etl::is_output_iterator_concept<I>::value) && etl::is_integral<etl::iter_difference_t<I>>::value>>
constexpr void operator()(I& i, etl::iter_difference_t<I> n) const
{
if constexpr (etl::is_random_access_iterator_concept<I>::value)
{
i += n;
}
else
{
while (n > 0)
{
--n;
++i;
}
if constexpr (etl::is_bidirectional_iterator_concept<I>::value)
{
while (n < 0)
{
++n;
--i;
}
}
}
}
template<typename I, typename S, typename = etl::enable_if_t<(etl::is_input_iterator_concept<I>::value || etl::is_output_iterator_concept<I>::value) && !etl::is_integral<S>::value>>
constexpr void operator()(I& i, S bound) const
{
if constexpr (etl::is_assignable_v<I&, S>)
{
i = etl::move(bound);
}
else if constexpr (etl::is_same_v<S, I> && etl::is_random_access_iterator_concept<I>::value)
{
(*this)(i, bound - i);
}
else
{
while (!(i == bound))
{
++i;
}
}
}
template<typename I, typename S, typename = etl::enable_if_t<etl::is_input_iterator_concept<I>::value || etl::is_output_iterator_concept<I>::value>>
constexpr etl::iter_difference_t<I>
operator()(I& i, etl::iter_difference_t<I> n, S bound) const
{
if constexpr (etl::is_same_v<S, I> && etl::is_random_access_iterator_concept<I>::value)
{
const auto dist = bound - i;
if ((n >= 0 && dist >= 0 && n >= dist) ||
(n <= 0 && dist <= 0 && n <= dist))
{
(*this)(i, bound);
return n - dist;
}
(*this)(i, n);
return 0;
}
else
{
while (n > 0 && !(i == bound))
{
--n;
++i;
}
if constexpr (etl::is_bidirectional_iterator_concept<I>::value)
{
while (n < 0 && !(i == bound))
{
++n;
--i;
}
}
return n;
}
}
};
inline constexpr auto advance = advance_fn();
struct prev_fn
{
template<typename I, typename = etl::enable_if_t<etl::is_bidirectional_iterator_concept<I>::value>>
constexpr I operator()(I i) const
{
--i;
return i;
}
template<typename I, typename = etl::enable_if_t<etl::is_bidirectional_iterator_concept<I>::value>>
constexpr I operator()(I i, etl::iter_difference_t<I> n) const
{
ranges::advance(i, -n);
return i;
}
template<typename I, typename = etl::enable_if_t<etl::is_bidirectional_iterator_concept<I>::value>>
constexpr I operator()(I i, etl::iter_difference_t<I> n, I bound) const
{
ranges::advance(i, -n, bound);
return i;
}
};
inline constexpr auto prev = prev_fn();
struct next_fn
{
template<typename I, typename = etl::enable_if_t<etl::is_input_iterator_concept<I>::value || etl::is_output_iterator_concept<I>::value>>
constexpr I operator()(I i) const
{
++i;
return i;
}
template<typename I, typename = etl::enable_if_t<(etl::is_input_iterator_concept<I>::value || etl::is_output_iterator_concept<I>::value) && etl::is_integral<etl::iter_difference_t<I>>::value>>
constexpr I operator()(I i, etl::iter_difference_t<I> n) const
{
ranges::advance(i, n);
return i;
}
template<typename I, typename S, typename = etl::enable_if_t<(etl::is_input_iterator_concept<I>::value || etl::is_output_iterator_concept<I>::value) && !etl::is_integral<S>::value>>
constexpr I operator()(I i, S bound) const
{
ranges::advance(i, bound);
return i;
}
template<typename I, typename S, typename = etl::enable_if_t<(etl::is_input_iterator_concept<I>::value || etl::is_output_iterator_concept<I>::value) && !etl::is_integral<S>::value>>
constexpr I operator()(I i, etl::iter_difference_t<I> n, S bound) const
{
ranges::advance(i, n, bound);
return i;
}
};
inline constexpr auto next = next_fn();
}
struct unreachable_sentinel_t
{
};
inline constexpr unreachable_sentinel_t unreachable_sentinel{};
template<typename I>
constexpr bool operator==(unreachable_sentinel_t, const I&) noexcept
{
return false;
}
template<typename I>
constexpr bool operator==(const I&, unreachable_sentinel_t) noexcept
{
return false;
}
template<typename I>
constexpr bool operator!=(unreachable_sentinel_t, const I& i) noexcept
{
return !(unreachable_sentinel_t{} == i);
}
template<typename I>
constexpr bool operator!=(const I& i, unreachable_sentinel_t) noexcept
{
return !(i == unreachable_sentinel_t{});
}
struct default_sentinel_t
{
};
inline constexpr default_sentinel_t default_sentinel{};
namespace private_iterator
{
template<typename T, typename = void>
struct has_arrow_operator : etl::false_type {};
template<typename T>
struct has_arrow_operator<T, etl::void_t<decltype(etl::declval<const T&>().operator->())>> : etl::true_type {};
//***********************************
/// Proxy that owns a copy of the dereferenced value so that operator->
/// can safely return a pointer to it. Used when the wrapped iterator
/// has no member operator-> and is not a raw pointer (i.e. *it may
/// yield a prvalue / proxy whose address would otherwise dangle).
//***********************************
template<typename TValue>
struct arrow_proxy
{
TValue stored;
constexpr arrow_proxy(TValue value) : stored(etl::move(value)) {}
constexpr const TValue* operator->() const noexcept { return etl::addressof(stored); }
};
}
template<class I>
class counted_iterator
{
template<class> friend class counted_iterator;
public:
using iterator_type = I;
using value_type = etl::iter_value_t<I>;
using difference_type = etl::iter_difference_t<I>;
using iterator_category = typename etl::iterator_traits<iterator_type>::iterator_category;
using pointer = typename etl::iterator_traits<iterator_type>::pointer;
using reference = typename etl::iterator_traits<iterator_type>::reference;
constexpr counted_iterator() = default;
constexpr counted_iterator(I x, etl::iter_difference_t<I> n): current(etl::move(x)), length(n)
{
}
template<class I2>
constexpr counted_iterator(const counted_iterator<I2>& other): current(other.current), length(other.length)
{
}
template<class I2>
constexpr counted_iterator& operator=(const counted_iterator<I2>& other)
{
current = other.current;
length = other.length;
return *this;
}
constexpr const I& base() const& noexcept
{
return current;
}
constexpr I base() &&
{
return etl::move(current);
}
constexpr etl::iter_difference_t<I> count() const noexcept
{
return length;
}
constexpr decltype(auto) operator*() const
{
return *current;
}
// operator-> for iterator types that provide a member operator->
template<typename J = I, etl::enable_if_t<
(etl::is_input_iterator_concept<J>::value || etl::is_output_iterator_concept<J>::value) &&
private_iterator::has_arrow_operator<J>::value, int> = 0>
constexpr auto operator->() const noexcept
{
return current.operator->();
}
// operator-> fallback for raw-pointer iterators (addressof is always safe)
template<typename J = I, etl::enable_if_t<
(etl::is_input_iterator_concept<J>::value || etl::is_output_iterator_concept<J>::value) &&
!private_iterator::has_arrow_operator<J>::value &&
etl::is_pointer<J>::value, int> = 0>
constexpr auto operator->() const noexcept
{
return current;
}
// operator-> fallback for class-type iterators without member operator->
// When *current yields an lvalue reference, just take its address.
template<typename J = I, etl::enable_if_t<
(etl::is_input_iterator_concept<J>::value || etl::is_output_iterator_concept<J>::value) &&
!private_iterator::has_arrow_operator<J>::value &&
!etl::is_pointer<J>::value &&
etl::is_lvalue_reference<decltype(*etl::declval<const J&>())>::value, int> = 0>
constexpr auto operator->() const noexcept
{
return etl::addressof(*current);
}
// operator-> fallback for class-type iterators without member operator->
// When *current yields a prvalue / proxy, use an owning proxy so the
// address remains valid.
template<typename J = I, etl::enable_if_t<
(etl::is_input_iterator_concept<J>::value || etl::is_output_iterator_concept<J>::value) &&
!private_iterator::has_arrow_operator<J>::value &&
!etl::is_pointer<J>::value &&
!etl::is_lvalue_reference<decltype(*etl::declval<const J&>())>::value, int> = 0>
constexpr auto operator->() const
{
return private_iterator::arrow_proxy<value_type>{*current};
}
template<typename J = I, etl::enable_if_t<etl::is_random_access_iterator<J>::value, int> = 0>
constexpr decltype(auto) operator[](etl::iter_difference_t<I> n) const
{
return current[n];
}
constexpr counted_iterator& operator++()
{
++current;
--length;
return *this;
}
constexpr counted_iterator operator++(int)
{
counted_iterator tmp = *this;
current++;
length--;
return tmp;
}
template<typename J = I, etl::enable_if_t<etl::is_random_access_iterator<J>::value, int> = 0>
constexpr counted_iterator& operator+=(etl::iter_difference_t<I> n)
{
current += n;
length -= n;
return *this;
}
template<typename J = I, etl::enable_if_t<etl::is_random_access_iterator<J>::value, int> = 0>
constexpr counted_iterator operator+(etl::iter_difference_t<I> n) const
{
counted_iterator result{*this};
result += n;
return result;
}
constexpr counted_iterator& operator--()
{
--current;
++length;
return *this;
}
constexpr counted_iterator operator--(int)
{
counted_iterator tmp = *this;
current--;
length++;
return tmp;
}
template<typename J = I, etl::enable_if_t<etl::is_random_access_iterator<J>::value, int> = 0>
constexpr counted_iterator& operator-=(etl::iter_difference_t<I> n)
{
current -= n;
length += n;
return *this;
}
template<typename J = I, etl::enable_if_t<etl::is_random_access_iterator<J>::value, int> = 0>
constexpr counted_iterator operator-(etl::iter_difference_t<I> n) const
{
counted_iterator result{*this};
result -= n;
return result;
}
friend constexpr bool operator==(const counted_iterator& x, const counted_iterator& y)
{
return x.length == y.length;
}
friend constexpr bool operator==(const counted_iterator& x, etl::default_sentinel_t)
{
return x.count() == 0;
}
friend constexpr bool operator==(etl::default_sentinel_t, const counted_iterator& x)
{
return x.count() == 0;
}
friend constexpr bool operator!=(const counted_iterator& x, etl::default_sentinel_t)
{
return x.count() != 0;
}
friend constexpr bool operator!=(etl::default_sentinel_t, const counted_iterator& y)
{
return y.count() != 0;
}
template<typename J = I, etl::enable_if_t<etl::is_random_access_iterator<J>::value, int> = 0>
friend constexpr counted_iterator operator+(etl::iter_difference_t<I> n, const counted_iterator& x)
{
return counted_iterator(x.current + n, x.length - n);
}
friend constexpr etl::iter_difference_t<I> operator-(const counted_iterator& x, const counted_iterator& y)
{
return y.length - x.length;
}
friend constexpr etl::iter_difference_t<I> operator-(const counted_iterator& x, etl::default_sentinel_t)
{
return -x.length;
}
friend constexpr etl::iter_difference_t<I> operator-(etl::default_sentinel_t, const counted_iterator& y)
{
return y.length;
}
private:
I current{};
difference_type length{};
};
template<typename TIterator, typename = etl::enable_if_t<etl::is_base_of<etl::counted_iterator<typename TIterator::iterator_type>, TIterator>::value>>
constexpr typename etl::iterator_traits<TIterator>::difference_type distance(TIterator first, etl::default_sentinel_t)
{
return first.count();
}
#endif
#if ETL_USING_CPP14
template <class T, typename = void>
struct is_range: etl::false_type
{
};
template <class T>
struct is_range<T,
etl::void_t<decltype(ETL_OR_STD::begin(etl::declval<T&>())),
decltype(ETL_OR_STD::end(etl::declval<T&>()))>>: etl::true_type
{
};
#if ETL_USING_CPP17
template <typename T>
inline constexpr bool is_range_v = is_range<T>::value;
#endif
#endif
#if ETL_NOT_USING_STL || ETL_CPP17_NOT_SUPPORTED
//**************************************************************************
/// Returns a pointer to the block of memory containing the elements of the range.
@ -1256,4 +1985,3 @@ namespace etl
}
#endif

View File

@ -322,6 +322,190 @@ namespace etl
}
#endif
#if ETL_USING_CPP17
namespace ranges {
//*****************************************************************************
/// Copies a range of objects to uninitialised memory.
/// https://en.cppreference.com/w/cpp/memory/ranges/uninitialized_copy
///\ingroup memory
//*****************************************************************************
struct uninitialized_copy_fn
{
template<class I, class S1, class O, class S2, typename = etl::enable_if_t<!etl::is_range_v<I>>>
ranges::uninitialized_copy_result<I, O>
operator()(I ifirst, S1 ilast, O ofirst, S2 olast) const
{
using value_type = typename etl::iterator_traits<O>::value_type;
O ofirst_original = ofirst;
#if ETL_USING_EXCEPTIONS
try
{
#endif
for (; ifirst != ilast && ofirst != olast; ++ifirst, ++ofirst)
{
::new (static_cast<void*>(etl::to_address(ofirst)))
value_type(*ifirst);
}
return {etl::move(ifirst), etl::move(ofirst)};
#if ETL_USING_EXCEPTIONS
}
catch (...)
{
for (; ofirst_original != ofirst; ++ofirst_original)
{
etl::to_address(ofirst_original)->~value_type();
}
throw;
}
#endif
}
template<class IR, class OR, typename = etl::enable_if_t<etl::is_range_v<IR>>>
ranges::uninitialized_copy_result<ranges::borrowed_iterator_t<IR>, ranges::borrowed_iterator_t<OR>>
operator()(IR&& in_range, OR&& out_range) const
{
return (*this)(ranges::begin(in_range), ranges::end(in_range),
ranges::begin(out_range), ranges::end(out_range));
}
};
inline constexpr uninitialized_copy_fn uninitialized_copy {};
//*****************************************************************************
/// Copies N objects to uninitialised memory.
/// https://en.cppreference.com/w/cpp/memory/ranges/uninitialized_copy_n
///\ingroup memory
//*****************************************************************************
struct uninitialized_copy_n_fn
{
template<class I, class O, class S, typename = etl::enable_if_t<!etl::is_range_v<I>>>
ranges::uninitialized_copy_n_result<I, O>
operator()(I ifirst, etl::iter_difference_t<I> n, O ofirst, S olast) const
{
using value_type = typename etl::iterator_traits<O>::value_type;
O ofirst_original = ofirst;
#if ETL_USING_EXCEPTIONS
try
{
#endif
for (; n > 0 && ofirst != olast; ++ifirst, ++ofirst, --n)
{
::new (static_cast<void*>(etl::to_address(ofirst)))
value_type(*ifirst);
}
return {etl::move(ifirst), etl::move(ofirst)};
#if ETL_USING_EXCEPTIONS
}
catch (...)
{
for (; ofirst_original != ofirst; ++ofirst_original)
{
etl::to_address(ofirst_original)->~value_type();
}
throw;
}
#endif
}
};
inline constexpr uninitialized_copy_n_fn uninitialized_copy_n {};
//*****************************************************************************
/// Fills uninitialised memory range with a value.
/// https://en.cppreference.com/w/cpp/memory/ranges/uninitialized_fill
///\ingroup memory
//*****************************************************************************
struct uninitialized_fill_fn
{
template<class I, class S, class T, typename = etl::enable_if_t<!etl::is_range_v<I>>>
I operator()(I first, S last, const T& value) const
{
using value_type = typename etl::iterator_traits<I>::value_type;
I current = first;
#if ETL_USING_EXCEPTIONS
try
{
#endif
for (; current != last; ++current)
{
::new (static_cast<void*>(etl::to_address(current)))
value_type(value);
}
return current;
#if ETL_USING_EXCEPTIONS
}
catch (...)
{
for (; first != current; ++first)
{
etl::to_address(first)->~value_type();
}
throw;
}
#endif
}
template<class R, class T, typename = etl::enable_if_t<etl::is_range_v<R>>>
ranges::borrowed_iterator_t<R> operator()(R&& r, const T& value) const
{
return (*this)(ranges::begin(r), ranges::end(r), value);
}
};
inline constexpr uninitialized_fill_fn uninitialized_fill {};
//*****************************************************************************
/// Fills uninitialised memory with N copies of a value.
/// https://en.cppreference.com/w/cpp/memory/ranges/uninitialized_fill_n
///\ingroup memory
//*****************************************************************************
struct uninitialized_fill_n_fn
{
template<class I, class T>
I operator()(I first, etl::iter_difference_t<I> n, const T& value) const
{
using value_type = typename etl::iterator_traits<I>::value_type;
I current = first;
#if ETL_USING_EXCEPTIONS
try
{
#endif
for (; n > 0; ++current, --n)
{
::new (static_cast<void*>(etl::to_address(current)))
value_type(value);
}
return current;
#if ETL_USING_EXCEPTIONS
}
catch (...)
{
for (; first != current; ++first)
{
etl::to_address(first)->~value_type();
}
throw;
}
#endif
}
};
inline constexpr uninitialized_fill_n_fn uninitialized_fill_n {};
}
#endif
#if ETL_USING_STL && ETL_USING_CPP11
//*****************************************************************************
/// Copies N objects to uninitialised memory.
@ -638,6 +822,102 @@ namespace etl
}
#endif
#if ETL_USING_CPP17
namespace ranges {
//*****************************************************************************
/// Moves a range of objects to uninitialised memory.
/// https://en.cppreference.com/w/cpp/memory/ranges/uninitialized_move
///\ingroup memory
//*****************************************************************************
struct uninitialized_move_fn
{
template<class I, class S1, class O, class S2, typename = etl::enable_if_t<!etl::is_range_v<I>>>
ranges::uninitialized_move_result<I, O>
operator()(I ifirst, S1 ilast, O ofirst, S2 olast) const
{
using value_type = typename etl::iterator_traits<O>::value_type;
O ofirst_original = ofirst;
#if ETL_USING_EXCEPTIONS
try
{
#endif
for (; ifirst != ilast && ofirst != olast; ++ifirst, ++ofirst)
{
::new (static_cast<void*>(etl::to_address(ofirst)))
value_type(etl::move(*ifirst));
}
return {etl::move(ifirst), etl::move(ofirst)};
#if ETL_USING_EXCEPTIONS
}
catch (...)
{
for (; ofirst_original != ofirst; ++ofirst_original)
{
etl::to_address(ofirst_original)->~value_type();
}
throw;
}
#endif
}
template<class IR, class OR, typename = etl::enable_if_t<etl::is_range_v<IR>>>
ranges::uninitialized_move_result<ranges::borrowed_iterator_t<IR>, ranges::borrowed_iterator_t<OR>>
operator()(IR&& in_range, OR&& out_range) const
{
return (*this)(ranges::begin(in_range), ranges::end(in_range),
ranges::begin(out_range), ranges::end(out_range));
}
};
inline constexpr uninitialized_move_fn uninitialized_move {};
//*****************************************************************************
/// Moves N objects to uninitialised memory.
/// https://en.cppreference.com/w/cpp/memory/ranges/uninitialized_move_n
///\ingroup memory
//*****************************************************************************
struct uninitialized_move_n_fn
{
template<class I, class O, class S, typename = etl::enable_if_t<!etl::is_range_v<I>>>
ranges::uninitialized_move_n_result<I, O>
operator()(I ifirst, etl::iter_difference_t<I> n, O ofirst, S olast) const
{
using value_type = typename etl::iterator_traits<O>::value_type;
O ofirst_original = ofirst;
#if ETL_USING_EXCEPTIONS
try
{
#endif
for (; n > 0 && ofirst != olast; ++ifirst, ++ofirst, --n)
{
::new (static_cast<void*>(etl::to_address(ofirst)))
value_type(etl::move(*ifirst));
}
return {etl::move(ifirst), etl::move(ofirst)};
#if ETL_USING_EXCEPTIONS
}
catch (...)
{
for (; ofirst_original != ofirst; ++ofirst_original)
{
etl::to_address(ofirst_original)->~value_type();
}
throw;
}
#endif
}
};
inline constexpr uninitialized_move_n_fn uninitialized_move_n {};
}
#endif
#if ETL_USING_STL && ETL_USING_CPP17
//*****************************************************************************
/// Default initialises a range of objects to uninitialised memory.
@ -818,6 +1098,98 @@ namespace etl
}
#endif
#if ETL_USING_CPP17
namespace ranges {
//*****************************************************************************
/// Default constructs objects in uninitialised memory range.
/// https://en.cppreference.com/w/cpp/memory/ranges/uninitialized_default_construct
///\ingroup memory
//*****************************************************************************
struct uninitialized_default_construct_fn
{
template<class I, class S, typename = etl::enable_if_t<!etl::is_range_v<I>>>
I operator()(I first, S last) const
{
using value_type = typename etl::iterator_traits<I>::value_type;
I current = first;
#if ETL_USING_EXCEPTIONS
try
{
#endif
for (; current != last; ++current)
{
::new (static_cast<void*>(etl::to_address(current)))
value_type;
}
return current;
#if ETL_USING_EXCEPTIONS
}
catch (...)
{
for (; first != current; ++first)
{
etl::to_address(first)->~value_type();
}
throw;
}
#endif
}
template<class R, typename = etl::enable_if_t<etl::is_range_v<R>>>
ranges::borrowed_iterator_t<R> operator()(R&& r) const
{
return (*this)(ranges::begin(r), ranges::end(r));
}
};
inline constexpr uninitialized_default_construct_fn uninitialized_default_construct {};
//*****************************************************************************
/// Default constructs N objects in uninitialised memory.
/// https://en.cppreference.com/w/cpp/memory/ranges/uninitialized_default_construct_n
///\ingroup memory
//*****************************************************************************
struct uninitialized_default_construct_n_fn
{
template<class I>
I operator()(I first, etl::iter_difference_t<I> n) const
{
using value_type = typename etl::iterator_traits<I>::value_type;
I current = first;
#if ETL_USING_EXCEPTIONS
try
{
#endif
for (; n > 0; ++current, --n)
{
::new (static_cast<void*>(etl::to_address(current)))
value_type;
}
return current;
#if ETL_USING_EXCEPTIONS
}
catch (...)
{
for (; first != current; ++first)
{
etl::to_address(first)->~value_type();
}
throw;
}
#endif
}
};
inline constexpr uninitialized_default_construct_n_fn uninitialized_default_construct_n {};
}
#endif
#if ETL_USING_STL && ETL_USING_CPP17
//*****************************************************************************
/// Default initialises a range of objects to uninitialised memory.
@ -951,6 +1323,98 @@ namespace etl
}
#endif
#if ETL_USING_CPP17
namespace ranges {
//*****************************************************************************
/// Value constructs objects in uninitialised memory range.
/// https://en.cppreference.com/w/cpp/memory/ranges/uninitialized_value_construct
///\ingroup memory
//*****************************************************************************
struct uninitialized_value_construct_fn
{
template<class I, class S, typename = etl::enable_if_t<!etl::is_range_v<I>>>
I operator()(I first, S last) const
{
using value_type = typename etl::iterator_traits<I>::value_type;
I current = first;
#if ETL_USING_EXCEPTIONS
try
{
#endif
for (; current != last; ++current)
{
::new (static_cast<void*>(etl::to_address(current)))
value_type();
}
return current;
#if ETL_USING_EXCEPTIONS
}
catch (...)
{
for (; first != current; ++first)
{
etl::to_address(first)->~value_type();
}
throw;
}
#endif
}
template<class R, typename = etl::enable_if_t<etl::is_range_v<R>>>
ranges::borrowed_iterator_t<R> operator()(R&& r) const
{
return (*this)(ranges::begin(r), ranges::end(r));
}
};
inline constexpr uninitialized_value_construct_fn uninitialized_value_construct {};
//*****************************************************************************
/// Value constructs N objects in uninitialised memory.
/// https://en.cppreference.com/w/cpp/memory/ranges/uninitialized_value_construct_n
///\ingroup memory
//*****************************************************************************
struct uninitialized_value_construct_n_fn
{
template<class I>
I operator()(I first, etl::iter_difference_t<I> n) const
{
using value_type = typename etl::iterator_traits<I>::value_type;
I current = first;
#if ETL_USING_EXCEPTIONS
try
{
#endif
for (; n > 0; ++current, --n)
{
::new (static_cast<void*>(etl::to_address(current)))
value_type();
}
return current;
#if ETL_USING_EXCEPTIONS
}
catch (...)
{
for (; first != current; ++first)
{
etl::to_address(first)->~value_type();
}
throw;
}
#endif
}
};
inline constexpr uninitialized_value_construct_n_fn uninitialized_value_construct_n {};
}
#endif
#if ETL_USING_STL && ETL_USING_CPP20
//*****************************************************************************
/// Constructs an item at address p with value constructed from 'args'.
@ -996,6 +1460,26 @@ namespace etl
}
#endif
#if ETL_USING_CPP17
namespace ranges {
//*****************************************************************************
/// Constructs an item at address p with value constructed from 'args'.
/// https://en.cppreference.com/w/cpp/memory/ranges/construct_at
///\ingroup memory
//*****************************************************************************
struct construct_at_fn
{
template<class T, class... Args>
constexpr T* operator()(T* p, Args&&... args) const
{
return etl::construct_at(p, etl::forward<Args>(args)...);
}
};
inline constexpr construct_at_fn construct_at {};
}
#endif
#if ETL_USING_STL && ETL_USING_CPP20
//*****************************************************************************
/// Destroys an item at address p.
@ -1254,6 +1738,74 @@ namespace etl
}
#endif
#if ETL_USING_CPP17
namespace ranges {
//*****************************************************************************
/// Destroys an item at address p.
/// https://en.cppreference.com/w/cpp/memory/ranges/destroy_at
///\ingroup memory
//*****************************************************************************
struct destroy_at_fn
{
template<class T>
constexpr void operator()(T* p) const
{
etl::destroy_at(p);
}
};
inline constexpr destroy_at_fn destroy_at {};
//*****************************************************************************
/// Destroys a range of items.
/// https://en.cppreference.com/w/cpp/memory/ranges/destroy
///\ingroup memory
//*****************************************************************************
struct destroy_fn
{
template<class I, class S, typename = etl::enable_if_t<!etl::is_range_v<I>>>
I operator()(I first, S last) const
{
for (; first != last; ++first)
{
etl::destroy_at(etl::to_address(first));
}
return first;
}
template<class R, typename = etl::enable_if_t<etl::is_range_v<R>>>
ranges::borrowed_iterator_t<R> operator()(R&& r) const
{
return (*this)(ranges::begin(r), ranges::end(r));
}
};
inline constexpr destroy_fn destroy {};
//*****************************************************************************
/// Destroys a number of items.
/// https://en.cppreference.com/w/cpp/memory/ranges/destroy_n
///\ingroup memory
//*****************************************************************************
struct destroy_n_fn
{
template<class I>
I operator()(I first, etl::iter_difference_t<I> n) const
{
for (; n > 0; ++first, --n)
{
etl::destroy_at(etl::to_address(first));
}
return first;
}
};
inline constexpr destroy_n_fn destroy_n {};
}
#endif
//*****************************************************************************
/// Default deleter.
///\tparam T The pointed to type type.

View File

@ -0,0 +1,412 @@
///\file
/******************************************************************************
The MIT License(MIT)
Embedded Template Library.
https://github.com/ETLCPP/etl
https://www.etlcpp.com
Copyright(c) 2026 BMW AG
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
******************************************************************************/
#ifndef ETL_RANGES_MINI_VARIANT_INCLUDED
#define ETL_RANGES_MINI_VARIANT_INCLUDED
#include "../platform.h"
#include "../error_handler.h"
#include "../utility.h"
#if ETL_USING_CPP17
namespace etl
{
namespace ranges
{
namespace private_ranges
{
//*********************************************************************
/// mini_variant
/// A minimal, self-contained variant used internally by ranges,
/// to prevent cyclic dependencies from algorithm.h and ranges.h.
//*********************************************************************
// Helper: get the I-th type from a parameter pack.
template<size_t I, typename... Ts>
struct type_at_index;
template<size_t I, typename Head, typename... Tail>
struct type_at_index<I, Head, Tail...> : type_at_index<I - 1, Tail...> {};
template<typename Head, typename... Tail>
struct type_at_index<0, Head, Tail...>
{
using type = Head;
};
template<size_t I, typename... Ts>
using type_at_index_t = typename type_at_index<I, Ts...>::type;
// Helper: maximum of sizeof... values
template<typename... Ts>
struct max_size;
template<typename T>
struct max_size<T>
{
static constexpr size_t value = sizeof(T);
};
template<typename T, typename... Ts>
struct max_size<T, Ts...>
{
static constexpr size_t value = (sizeof(T) > max_size<Ts...>::value) ? sizeof(T) : max_size<Ts...>::value;
};
// Helper: maximum of alignof... values
template<typename... Ts>
struct max_align;
template<typename T>
struct max_align<T>
{
static constexpr size_t value = alignof(T);
};
template<typename T, typename... Ts>
struct max_align<T, Ts...>
{
static constexpr size_t value = (alignof(T) > max_align<Ts...>::value) ? alignof(T) : max_align<Ts...>::value;
};
// Index value representing "no active alternative"
inline constexpr size_t mini_variant_npos = ~size_t(0);
// Detection trait: is a single type equality-comparable?
template<typename T, typename = void>
struct is_equality_comparable : etl::false_type {};
template<typename T>
struct is_equality_comparable<T, etl::void_t<decltype(etl::declval<const T&>() == etl::declval<const T&>())>>
: etl::true_type {};
// Conjunction: all types in the pack are equality-comparable
template<typename... Ts>
struct all_equality_comparable : etl::bool_constant<(is_equality_comparable<Ts>::value && ...)> {};
// Detection trait: is a single type nothrow-move-constructible?
template<typename T>
struct is_nothrow_move_constructible
{
private:
template<typename U>
static auto test(int) -> etl::bool_constant<noexcept(U(etl::declval<U&&>()))>;
template<typename>
static etl::false_type test(...);
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
// Conjunction: all types in the pack are nothrow-move-constructible
template<typename... Ts>
struct all_nothrow_move_constructible : etl::bool_constant<(is_nothrow_move_constructible<Ts>::value && ...)> {};
// Detection trait: is a single type nothrow-destructible?
template<typename T>
struct is_nothrow_destructible
{
private:
template<typename U>
static auto test(int) -> etl::bool_constant<noexcept(etl::declval<U&>().~U())>;
template<typename>
static etl::false_type test(...);
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
// Conjunction: all types in the pack are nothrow-destructible
template<typename... Ts>
struct all_nothrow_destructible : etl::bool_constant<(is_nothrow_destructible<Ts>::value && ...)> {};
template<typename... Ts>
class mini_variant
{
static_assert(sizeof...(Ts) > 0, "mini_variant requires at least one type");
static constexpr size_t storage_size = max_size<Ts...>::value;
static constexpr size_t storage_align = max_align<Ts...>::value;
alignas(storage_align) unsigned char _storage[storage_size];
size_t _index;
// ---- Destruction dispatch table ----
using destroy_fn = void(*)(void*);
template<size_t I>
static void destroy_impl(void* ptr)
{
using T = type_at_index_t<I, Ts...>;
static_cast<T*>(ptr)->~T();
}
template<size_t... Is>
static const destroy_fn* make_destroy_table(etl::index_sequence<Is...>)
{
static const destroy_fn table[] = { &destroy_impl<Is>... };
return table;
}
static const destroy_fn* destroy_table()
{
static const destroy_fn* t = make_destroy_table(etl::make_index_sequence<sizeof...(Ts)>{});
return t;
}
// ---- Copy dispatch table ----
using copy_fn = void(*)(void* /*dst*/, const void* /*src*/);
template<size_t I>
static void copy_impl(void* dst, const void* src)
{
using T = type_at_index_t<I, Ts...>;
::new (dst) T(*static_cast<const T*>(src));
}
template<size_t... Is>
static const copy_fn* make_copy_table(etl::index_sequence<Is...>)
{
static const copy_fn table[] = { &copy_impl<Is>... };
return table;
}
static const copy_fn* copy_table()
{
static const copy_fn* t = make_copy_table(etl::make_index_sequence<sizeof...(Ts)>{});
return t;
}
// ---- Move dispatch table ----
using move_fn = void(*)(void* /*dst*/, void* /*src*/);
template<size_t I>
static void move_impl(void* dst, void* src)
{
using T = type_at_index_t<I, Ts...>;
::new (dst) T(etl::move(*static_cast<T*>(src)));
}
template<size_t... Is>
static const move_fn* make_move_table(etl::index_sequence<Is...>)
{
static const move_fn table[] = { &move_impl<Is>... };
return table;
}
static const move_fn* move_table()
{
static const move_fn* t = make_move_table(etl::make_index_sequence<sizeof...(Ts)>{});
return t;
}
// ---- Equality dispatch table ----
using equal_fn = bool(*)(const void* /*lhs*/, const void* /*rhs*/);
template<size_t I>
static bool equal_impl(const void* lhs, const void* rhs)
{
using T = type_at_index_t<I, Ts...>;
return *static_cast<const T*>(lhs) == *static_cast<const T*>(rhs);
}
template<size_t... Is>
static const equal_fn* make_equal_table(etl::index_sequence<Is...>)
{
static const equal_fn table[] = { &equal_impl<Is>... };
return table;
}
static const equal_fn* equal_table()
{
static const equal_fn* t = make_equal_table(etl::make_index_sequence<sizeof...(Ts)>{});
return t;
}
void destroy_current()
{
if (_index != mini_variant_npos)
{
destroy_table()[_index](&_storage);
_index = mini_variant_npos;
}
}
public:
mini_variant() : _index{mini_variant_npos}
{
}
mini_variant(const mini_variant& other) : _index{mini_variant_npos}
{
if (other._index != mini_variant_npos)
{
copy_table()[other._index](&_storage, &other._storage);
_index = other._index;
}
}
mini_variant& operator=(const mini_variant& other)
{
if (this != &other)
{
destroy_current();
if (other._index != mini_variant_npos)
{
copy_table()[other._index](&_storage, &other._storage);
_index = other._index;
}
}
return *this;
}
mini_variant(mini_variant&& other) noexcept(all_nothrow_move_constructible<Ts...>::value && all_nothrow_destructible<Ts...>::value)
: _index{mini_variant_npos}
{
if (other._index != mini_variant_npos)
{
move_table()[other._index](&_storage, &other._storage);
_index = other._index;
other.destroy_current();
}
}
mini_variant& operator=(mini_variant&& other) noexcept(all_nothrow_move_constructible<Ts...>::value && all_nothrow_destructible<Ts...>::value)
{
if (this != &other)
{
destroy_current();
if (other._index != mini_variant_npos)
{
move_table()[other._index](&_storage, &other._storage);
_index = other._index;
other.destroy_current();
}
}
return *this;
}
~mini_variant() noexcept(all_nothrow_destructible<Ts...>::value)
{
destroy_current();
}
template<size_t I, typename... Args>
void emplace(Args&&... args)
{
static_assert(I < sizeof...(Ts), "Index out of range");
using T = type_at_index_t<I, Ts...>;
destroy_current();
::new (&_storage) T(etl::forward<Args>(args)...);
_index = I;
}
constexpr size_t index() const
{
return _index;
}
template<size_t I>
type_at_index_t<I, Ts...>& get_ref()
{
static_assert(I < sizeof...(Ts), "Index out of range");
ETL_ASSERT(_index == I, ETL_ERROR_GENERIC("mini_variant: bad index"));
using T = type_at_index_t<I, Ts...>;
return *reinterpret_cast<T*>(&_storage);
}
template<size_t I>
const type_at_index_t<I, Ts...>& get_ref() const
{
static_assert(I < sizeof...(Ts), "Index out of range");
ETL_ASSERT(_index == I, ETL_ERROR_GENERIC("mini_variant: bad index"));
using T = type_at_index_t<I, Ts...>;
return *reinterpret_cast<const T*>(&_storage);
}
template<bool B = all_equality_comparable<Ts...>::value, etl::enable_if_t<B, int> = 0>
friend bool operator==(const mini_variant& lhs, const mini_variant& rhs)
{
if (lhs._index != rhs._index)
{
return false;
}
if (lhs._index == mini_variant_npos)
{
return true;
}
return equal_table()[lhs._index](&lhs._storage, &rhs._storage);
}
template<bool B = all_equality_comparable<Ts...>::value, etl::enable_if_t<B, int> = 0>
friend bool operator!=(const mini_variant& lhs, const mini_variant& rhs)
{
return !(lhs == rhs);
}
};
} // namespace private_ranges
} // namespace ranges (temporarily close to define get<> in etl namespace)
template<size_t I, typename... Ts>
typename ranges::private_ranges::type_at_index_t<I, Ts...>&
get(ranges::private_ranges::mini_variant<Ts...>& v)
{
return v.template get_ref<I>();
}
template<size_t I, typename... Ts>
const typename ranges::private_ranges::type_at_index_t<I, Ts...>&
get(const ranges::private_ranges::mini_variant<Ts...>& v)
{
return v.template get_ref<I>();
}
template<size_t I, typename... Ts>
typename ranges::private_ranges::type_at_index_t<I, Ts...>&&
get(ranges::private_ranges::mini_variant<Ts...>&& v)
{
return etl::move(v.template get_ref<I>());
}
template<size_t I, typename... Ts>
const typename ranges::private_ranges::type_at_index_t<I, Ts...>&&
get(const ranges::private_ranges::mini_variant<Ts...>&& v)
{
return etl::move(v.template get_ref<I>());
}
} // namespace etl
#endif // ETL_USING_CPP17
#endif

View File

@ -30,7 +30,6 @@ SOFTWARE.
#include "../platform.h"
#include "../utility.h"
#include "../array.h"
#include "../largest.h"
#include "../exception.h"
#include "../type_traits.h"
@ -39,6 +38,7 @@ SOFTWARE.
#include "../alignment.h"
#include "../error_handler.h"
#include "../null_type.h"
#include "../parameter_type.h"
#include "../placement_new.h"
#include "../monostate.h"

5927
include/etl/ranges.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -31,7 +31,6 @@ SOFTWARE.
#include "platform.h"
#include "algorithm.h"
#include "index_of_type.h"
#include "integral_limits.h"
#include "static_assert.h"

View File

@ -282,6 +282,7 @@ add_executable(etl_tests
test_queue_spsc_locked.cpp
test_queue_spsc_locked_small.cpp
test_random.cpp
test_ranges.cpp
test_ratio.cpp
test_reference_flat_map.cpp
test_reference_flat_multimap.cpp

File diff suppressed because it is too large Load Diff

View File

@ -274,6 +274,43 @@ namespace
CHECK_EQUAL(1, ra);
}
#if ETL_USING_CPP11
//*************************************************************************
TEST(test_reference_wrapper_call_operator_no_args)
{
struct functor
{
int operator()() const { return 42; }
};
functor f;
etl::reference_wrapper<functor> rw(f);
CHECK_EQUAL(42, rw());
}
//*************************************************************************
TEST(test_reference_wrapper_call_operator_with_args)
{
struct adder
{
int operator()(int a, int b) const { return a + b; }
};
adder f;
etl::reference_wrapper<adder> rw(f);
CHECK_EQUAL(7, rw(3, 4));
}
//*************************************************************************
TEST(test_reference_wrapper_call_operator_function_pointer)
{
int (*fn)(int, int) = [](int a, int b) { return a * b; };
etl::reference_wrapper<int(*)(int, int)> rw(fn);
CHECK_EQUAL(12, rw(3, 4));
}
#endif
//*************************************************************************
TEST(test_plus)
{
@ -449,5 +486,96 @@ namespace
result = f3(mft, "Arg1", " : Arg2");
CHECK_EQUAL("Function3_Const: Arg1 : Arg2", result);
}
#if ETL_USING_CPP14
//*************************************************************************
TEST(test_identity)
{
etl::identity i;
std::string s{"abc"};
CHECK_EQUAL(s, i(s));
CHECK_EQUAL(&s, &i(s));
CHECK_EQUAL(&s, i(&s));
}
#endif
#if ETL_USING_CPP17
//*************************************************************************
TEST(test_ranges_equal_to_same_type)
{
etl::ranges::equal_to eq;
CHECK(eq(1, 1));
CHECK(!eq(1, 2));
CHECK(!eq(2, 1));
}
//*************************************************************************
TEST(test_ranges_equal_to_mixed_types)
{
etl::ranges::equal_to eq;
// int vs long
CHECK(eq(1, 1L));
CHECK(!eq(1, 2L));
CHECK(eq(2L, 2));
// int vs short
CHECK(eq(42, short(42)));
CHECK(!eq(42, short(43)));
}
//*************************************************************************
TEST(test_ranges_equal_to_constexpr)
{
constexpr etl::ranges::equal_to eq{};
constexpr bool result_equal = eq(5, 5);
constexpr bool result_not_equal = eq(5, 6);
CHECK(result_equal);
CHECK(!result_not_equal);
}
//*************************************************************************
TEST(test_ranges_less_same_type)
{
etl::ranges::less lt;
CHECK(lt(1, 2));
CHECK(!lt(2, 1));
CHECK(!lt(1, 1));
}
//*************************************************************************
TEST(test_ranges_less_mixed_types)
{
etl::ranges::less lt;
// int vs long
CHECK(lt(1, 2L));
CHECK(!lt(2, 1L));
CHECK(!lt(1, 1L));
CHECK(lt(1L, 2));
// int vs short
CHECK(lt(42, short(43)));
CHECK(!lt(42, short(42)));
CHECK(!lt(43, short(42)));
}
//*************************************************************************
TEST(test_ranges_less_constexpr)
{
constexpr etl::ranges::less lt{};
constexpr bool result_less = lt(5, 6);
constexpr bool result_not_less = lt(6, 5);
constexpr bool result_equal = lt(5, 5);
CHECK(result_less);
CHECK(!result_not_less);
CHECK(!result_equal);
}
#endif
}
}

View File

@ -481,4 +481,153 @@ SUITE(test_invoke)
CHECK_EQUAL(1, etl::invoke(&RR::f, etl::move(rr)));
CHECK_EQUAL(2, etl::invoke(&RR::g, rr));
}
//*************************************************************************
// Tests for reference_wrapper as callable (first argument)
//*************************************************************************
//*************************************************************************
TEST(test_reference_wrapper_free_function)
{
// reference_wrapper wrapping a function (decays to function pointer)
auto ref_fn = etl::ref(free_add);
CHECK_EQUAL(3, etl::invoke(ref_fn, 1, 2));
}
//*************************************************************************
TEST(test_reference_wrapper_functor)
{
Functor functor(10);
auto ref_functor = etl::ref(functor);
CHECK_EQUAL(12, etl::invoke(ref_functor, 2));
}
//*************************************************************************
TEST(test_reference_wrapper_const_functor)
{
const ConstFunctor functor(10);
auto ref_functor = etl::cref(functor);
CHECK_EQUAL(12, etl::invoke(ref_functor, 2));
}
//*************************************************************************
TEST(test_reference_wrapper_lambda)
{
int capture_value = 5;
auto lambda = [capture_value](int a) { return a + capture_value; };
auto ref_lambda = etl::ref(lambda);
CHECK_EQUAL(6, etl::invoke(ref_lambda, 1));
}
//*************************************************************************
TEST(test_reference_wrapper_nothrow_functor)
{
NoThrowFunctor nothrow_functor;
auto ref_functor = etl::ref(nothrow_functor);
CHECK_EQUAL(12, etl::invoke(ref_functor, 10));
}
//*************************************************************************
TEST(test_reference_wrapper_throwing_functor)
{
ThrowingFunctor throwing_functor;
auto ref_functor = etl::ref(throwing_functor);
CHECK_THROW(etl::invoke(ref_functor, 10), int);
}
//*************************************************************************
TEST(test_reference_wrapper_member_function_pointer)
{
// reference_wrapper wrapping a pointer-to-member-function
auto pmf = &Base::add;
auto ref_pmf = etl::ref(pmf);
Base base(10);
CHECK_EQUAL(11, etl::invoke(ref_pmf, base, 1));
CHECK_EQUAL(12, etl::invoke(ref_pmf, &base, 2));
}
//*************************************************************************
TEST(test_reference_wrapper_const_member_function_pointer)
{
auto pmf = &Base::add_const;
auto ref_pmf = etl::ref(pmf);
const Base const_base(20);
CHECK_EQUAL(24, etl::invoke(ref_pmf, const_base, 3));
CHECK_EQUAL(25, etl::invoke(ref_pmf, &const_base, 4));
}
//*************************************************************************
TEST(test_reference_wrapper_member_object_pointer)
{
// reference_wrapper wrapping a pointer-to-member-data
auto pmd = &MemberObj::i;
auto ref_pmd = etl::ref(pmd);
MemberObj obj(42);
CHECK_EQUAL(42, etl::invoke(ref_pmd, obj));
CHECK_EQUAL(42, etl::invoke(ref_pmd, &obj));
}
//*************************************************************************
TEST(test_reference_wrapper_derived_member_function)
{
// reference_wrapper wrapping member pointer, invoked on derived class
auto pmf = &Base::add;
auto ref_pmf = etl::ref(pmf);
Derived derived(10);
CHECK_EQUAL(11, etl::invoke(ref_pmf, derived, 1));
CHECK_EQUAL(12, etl::invoke(ref_pmf, &derived, 2));
}
#if ETL_USING_CPP14
//*************************************************************************
TEST(test_reference_wrapper_identity)
{
// This is the case that triggered the original compile error:
// reference_wrapper<etl::identity> as a callable
etl::identity id;
auto ref_id = etl::ref(id);
CHECK_EQUAL(42, etl::invoke(ref_id, 42));
CHECK_EQUAL(7, etl::invoke(ref_id, 7));
}
#endif
//*************************************************************************
TEST(test_reference_wrapper_with_reference_wrapper_arg)
{
// reference_wrapper as both callable and second argument (member pointer case)
auto pmf = &Base::add;
auto ref_pmf = etl::ref(pmf);
Base base(10);
auto ref_base = etl::ref(base);
CHECK_EQUAL(11, etl::invoke(ref_pmf, ref_base, 1));
}
//*************************************************************************
TEST(test_reference_wrapper_member_object_with_reference_wrapper_arg)
{
auto pmd = &MemberObj::i;
auto ref_pmd = etl::ref(pmd);
MemberObj obj(99);
auto ref_obj = etl::ref(obj);
CHECK_EQUAL(99, etl::invoke(ref_pmd, ref_obj));
}
}

View File

@ -32,6 +32,7 @@ SOFTWARE.
#include <list>
#include <queue>
#include <algorithm>
#include <vector>
#include "etl/iterator.h"
@ -70,6 +71,40 @@ namespace
typedef int* pointer;
typedef const int* const_pointer;
// A sentinel type for non_random_iterator, used to test the
// advance(i, n, bound) overload where I != S (different types).
template <typename T>
struct non_random_sentinel
{
non_random_sentinel() : ptr(nullptr) {}
non_random_sentinel(T* v) : ptr(v) {}
T* ptr;
};
template <typename T>
bool operator==(const non_random_iterator<T>& lhs, const non_random_sentinel<T>& rhs)
{
return lhs.ptr == rhs.ptr;
}
template <typename T>
bool operator!=(const non_random_iterator<T>& lhs, const non_random_sentinel<T>& rhs)
{
return lhs.ptr != rhs.ptr;
}
template <typename T>
bool operator==(const non_random_sentinel<T>& lhs, const non_random_iterator<T>& rhs)
{
return rhs == lhs;
}
template <typename T>
bool operator!=(const non_random_sentinel<T>& lhs, const non_random_iterator<T>& rhs)
{
return rhs != lhs;
}
const size_t SIZE = 10UL;
int dataA[SIZE] = { 2, 1, 4, 3, 6, 5, 8, 7, 10, 9 };
@ -580,5 +615,780 @@ namespace
// CHECK_EQUAL(expected.size(), output.size());
// CHECK(std::equal(output.begin(), output.end(), expected.begin()));
//}
#if ETL_USING_CPP17
//*************************************************************************
TEST(test_counted_iterator)
{
std::vector<int> v{1, 2, 3, 4};
etl::counted_iterator<std::vector<int>::iterator> ci0{};
CHECK_EQUAL(0, ci0.count());
etl::counted_iterator ci1{v.begin(), 3};
etl::counted_iterator ci2{v.begin(), 4};
CHECK_EQUAL(3, ci1.count());
CHECK_NOT_EQUAL(ci1.count(), ci2.count());
CHECK(!(ci1 == ci2));
ci2 = ci1;
CHECK_EQUAL(ci1.count(), ci2.count());
CHECK(ci1 == ci2);
CHECK(ci1.base() == v.begin());
CHECK(ci1.count() == 3);
ci1++;
CHECK(ci1.count() == 2);
++ci1;
CHECK(ci1.count() == 1);
ci1--;
CHECK(ci1.count() == 2);
--ci1;
CHECK(ci1.count() == 3);
ci1 += 2;
CHECK(ci1.count() == 1);
ci1 -= 2;
CHECK(ci1.count() == 3);
CHECK_EQUAL(ci1[0], 1);
CHECK_EQUAL(ci1[1], 2);
CHECK_EQUAL(ci1[2], 3);
auto ci3 = ci1 + 3;
CHECK(ci1.count() == 3);
CHECK_EQUAL(ci3.count(), 0);
auto ci4 = ci3 - 3;
CHECK(ci1.count() == 3);
CHECK_EQUAL(ci3.count(), 0);
CHECK(ci4.count() == 3);
}
//*************************************************************************
// etl::ranges::advance(i, n) with random access iterator
//*************************************************************************
TEST(ranges_advance_n_random_access_forward)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[0];
etl::ranges::advance(itr, 4);
CHECK_EQUAL(4, *itr);
}
//*************************************************************************
TEST(ranges_advance_n_random_access_backward)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[7];
etl::ranges::advance(itr, -3);
CHECK_EQUAL(4, *itr);
}
//*************************************************************************
TEST(ranges_advance_n_random_access_zero)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[5];
etl::ranges::advance(itr, 0);
CHECK_EQUAL(5, *itr);
}
//*************************************************************************
// etl::ranges::advance(i, n) with bidirectional (non-random) iterator
//*************************************************************************
TEST(ranges_advance_n_bidirectional_forward)
{
std::list<int> data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto itr = data.begin();
etl::ranges::advance(itr, 4);
CHECK_EQUAL(4, *itr);
}
//*************************************************************************
TEST(ranges_advance_n_bidirectional_backward)
{
std::list<int> data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto itr = data.end();
etl::ranges::advance(itr, -3);
CHECK_EQUAL(7, *itr);
}
//*************************************************************************
TEST(ranges_advance_n_bidirectional_zero)
{
std::list<int> data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto itr = data.begin();
etl::ranges::advance(itr, 5);
etl::ranges::advance(itr, 0);
CHECK_EQUAL(5, *itr);
}
//*************************************************************************
TEST(ranges_advance_n_non_random_iterator_forward)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[0]);
etl::ranges::advance(itr, 6);
CHECK_EQUAL(6, *itr);
}
//*************************************************************************
TEST(ranges_advance_n_non_random_iterator_backward)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[8]);
etl::ranges::advance(itr, -3);
CHECK_EQUAL(5, *itr);
}
//*************************************************************************
// etl::ranges::advance(i, bound) — advance to sentinel
//*************************************************************************
TEST(ranges_advance_to_bound_random_access)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[0];
int* bound = &data[7];
etl::ranges::advance(itr, bound);
CHECK_EQUAL(7, *itr);
CHECK(itr == bound);
}
//*************************************************************************
TEST(ranges_advance_to_bound_bidirectional)
{
std::list<int> data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto itr = data.begin();
auto bound = data.end();
etl::ranges::advance(itr, bound);
CHECK(itr == bound);
}
//*************************************************************************
TEST(ranges_advance_to_bound_non_random)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[0]);
non_random_iterator<int> bound(&data[5]);
etl::ranges::advance(itr, bound);
CHECK_EQUAL(5, *itr);
}
//*************************************************************************
TEST(ranges_advance_to_bound_already_at_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[3];
int* bound = &data[3];
etl::ranges::advance(itr, bound);
CHECK(itr == bound);
CHECK_EQUAL(3, *itr);
}
//*************************************************************************
// etl::ranges::advance(i, n, bound) — advance with bound, returns remainder
//*************************************************************************
TEST(ranges_advance_n_bound_random_access_not_reaching_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[0];
int* bound = &data[9];
auto remaining = etl::ranges::advance(itr, 4, bound);
CHECK_EQUAL(4, *itr);
CHECK_EQUAL(0, remaining);
}
//*************************************************************************
TEST(ranges_advance_n_bound_random_access_reaching_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[0];
int* bound = &data[3];
auto remaining = etl::ranges::advance(itr, 7, bound);
CHECK(itr == bound);
CHECK_EQUAL(3, *itr);
CHECK_EQUAL(4, remaining);
}
//*************************************************************************
TEST(ranges_advance_n_bound_random_access_exact_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[0];
int* bound = &data[5];
auto remaining = etl::ranges::advance(itr, 5, bound);
CHECK(itr == bound);
CHECK_EQUAL(5, *itr);
CHECK_EQUAL(0, remaining);
}
//*************************************************************************
TEST(ranges_advance_n_bound_random_access_backward_not_reaching_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[9];
int* bound = &data[0];
auto remaining = etl::ranges::advance(itr, -4, bound);
CHECK_EQUAL(5, *itr);
CHECK_EQUAL(0, remaining);
}
//*************************************************************************
TEST(ranges_advance_n_bound_random_access_backward_reaching_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[5];
int* bound = &data[2];
auto remaining = etl::ranges::advance(itr, -7, bound);
CHECK(itr == bound);
CHECK_EQUAL(2, *itr);
CHECK_EQUAL(-4, remaining);
}
//*************************************************************************
TEST(ranges_advance_n_bound_non_random_forward_not_reaching_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[0]);
non_random_sentinel<int> bound(&data[9]);
auto remaining = etl::ranges::advance(itr, 4, bound);
CHECK_EQUAL(4, *itr);
CHECK_EQUAL(0, remaining);
}
//*************************************************************************
TEST(ranges_advance_n_bound_non_random_forward_reaching_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[0]);
non_random_sentinel<int> bound(&data[3]);
auto remaining = etl::ranges::advance(itr, 7, bound);
CHECK_EQUAL(3, *itr);
CHECK_EQUAL(4, remaining);
}
//*************************************************************************
TEST(ranges_advance_n_bound_non_random_backward_not_reaching_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[9]);
non_random_sentinel<int> bound(&data[0]);
auto remaining = etl::ranges::advance(itr, -3, bound);
CHECK_EQUAL(6, *itr);
CHECK_EQUAL(0, remaining);
}
//*************************************************************************
TEST(ranges_advance_n_bound_non_random_backward_reaching_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[3]);
non_random_sentinel<int> bound(&data[1]);
auto remaining = etl::ranges::advance(itr, -7, bound);
CHECK_EQUAL(1, *itr);
CHECK_EQUAL(-5, remaining);
}
//*************************************************************************
TEST(ranges_advance_n_bound_zero_steps)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[5];
int* bound = &data[8];
auto remaining = etl::ranges::advance(itr, 0, bound);
CHECK_EQUAL(5, *itr);
CHECK_EQUAL(0, remaining);
}
//*************************************************************************
// etl::ranges::prev(i) with random access iterator
//*************************************************************************
TEST(ranges_prev_random_access)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[5];
int* result = etl::ranges::prev(itr);
CHECK_EQUAL(4, *result);
CHECK_EQUAL(5, *itr); // original unchanged
}
//*************************************************************************
// etl::ranges::prev(i) with bidirectional (non-random) iterator
//*************************************************************************
TEST(ranges_prev_non_random)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[5]);
non_random_iterator<int> result = etl::ranges::prev(itr);
CHECK_EQUAL(4, *result);
CHECK_EQUAL(5, *itr); // original unchanged
}
//*************************************************************************
// etl::ranges::prev(i, n) with random access iterator — positive n
//*************************************************************************
TEST(ranges_prev_n_random_access_positive)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[7];
int* result = etl::ranges::prev(itr, 3);
CHECK_EQUAL(4, *result);
CHECK_EQUAL(7, *itr); // original unchanged
}
//*************************************************************************
// etl::ranges::prev(i, n) with random access iterator — zero n
//*************************************************************************
TEST(ranges_prev_n_random_access_zero)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[5];
int* result = etl::ranges::prev(itr, 0);
CHECK_EQUAL(5, *result);
}
//*************************************************************************
// etl::ranges::prev(i, n) with random access iterator — negative n (moves forward)
//*************************************************************************
TEST(ranges_prev_n_random_access_negative)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[3];
int* result = etl::ranges::prev(itr, -2);
CHECK_EQUAL(5, *result);
CHECK_EQUAL(3, *itr); // original unchanged
}
//*************************************************************************
// etl::ranges::prev(i, n) with bidirectional (non-random) iterator — positive n
//*************************************************************************
TEST(ranges_prev_n_non_random_positive)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[7]);
non_random_iterator<int> result = etl::ranges::prev(itr, 4);
CHECK_EQUAL(3, *result);
CHECK_EQUAL(7, *itr); // original unchanged
}
//*************************************************************************
// etl::ranges::prev(i, n) with bidirectional (non-random) iterator — zero n
//*************************************************************************
TEST(ranges_prev_n_non_random_zero)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[5]);
non_random_iterator<int> result = etl::ranges::prev(itr, 0);
CHECK_EQUAL(5, *result);
}
//*************************************************************************
// etl::ranges::prev(i, n) with bidirectional (non-random) iterator — negative n (moves forward)
//*************************************************************************
TEST(ranges_prev_n_non_random_negative)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[3]);
non_random_iterator<int> result = etl::ranges::prev(itr, -2);
CHECK_EQUAL(5, *result);
CHECK_EQUAL(3, *itr); // original unchanged
}
//*************************************************************************
// etl::ranges::prev(i, n, bound) with random access iterator — not reaching bound
//*************************************************************************
TEST(ranges_prev_n_bound_random_access_not_reaching_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[8];
int* bound = &data[2];
int* result = etl::ranges::prev(itr, 4, bound);
CHECK_EQUAL(4, *result);
}
//*************************************************************************
// etl::ranges::prev(i, n, bound) with random access iterator — reaching bound
//*************************************************************************
TEST(ranges_prev_n_bound_random_access_reaching_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[5];
int* bound = &data[3];
int* result = etl::ranges::prev(itr, 7, bound);
CHECK_EQUAL(3, *result);
CHECK(result == bound);
}
//*************************************************************************
// etl::ranges::prev(i, n, bound) with random access iterator — zero steps
//*************************************************************************
TEST(ranges_prev_n_bound_random_access_zero_steps)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[5];
int* bound = &data[2];
int* result = etl::ranges::prev(itr, 0, bound);
CHECK_EQUAL(5, *result);
}
//*************************************************************************
// etl::ranges::prev(i, n, bound) with bidirectional (non-random) iterator — not reaching bound
//*************************************************************************
TEST(ranges_prev_n_bound_non_random_not_reaching_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[8]);
non_random_iterator<int> bound(&data[2]);
non_random_iterator<int> result = etl::ranges::prev(itr, 3, bound);
CHECK_EQUAL(5, *result);
}
//*************************************************************************
// etl::ranges::prev(i, n, bound) with bidirectional (non-random) iterator — reaching bound
//*************************************************************************
TEST(ranges_prev_n_bound_non_random_reaching_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[5]);
non_random_iterator<int> bound(&data[3]);
non_random_iterator<int> result = etl::ranges::prev(itr, 7, bound);
CHECK_EQUAL(3, *result);
CHECK(result.ptr == bound.ptr);
}
//*************************************************************************
// etl::ranges::next(i) with random access iterator
//*************************************************************************
TEST(ranges_next_random_access)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[5];
int* result = etl::ranges::next(itr);
CHECK_EQUAL(6, *result);
CHECK_EQUAL(5, *itr); // original unchanged
}
//*************************************************************************
// etl::ranges::next(i) with bidirectional (non-random) iterator
//*************************************************************************
TEST(ranges_next_non_random)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[5]);
non_random_iterator<int> result = etl::ranges::next(itr);
CHECK_EQUAL(6, *result);
CHECK_EQUAL(5, *itr); // original unchanged
}
//*************************************************************************
// etl::ranges::next(i, n) with random access iterator — positive n
//*************************************************************************
TEST(ranges_next_n_random_access_positive)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[2];
int* result = etl::ranges::next(itr, 5);
CHECK_EQUAL(7, *result);
CHECK_EQUAL(2, *itr); // original unchanged
}
//*************************************************************************
// etl::ranges::next(i, n) with random access iterator — zero n
//*************************************************************************
TEST(ranges_next_n_random_access_zero)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[5];
int* result = etl::ranges::next(itr, 0);
CHECK_EQUAL(5, *result);
}
//*************************************************************************
// etl::ranges::next(i, n) with random access iterator — negative n (moves backward)
//*************************************************************************
TEST(ranges_next_n_random_access_negative)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[7];
int* result = etl::ranges::next(itr, -3);
CHECK_EQUAL(4, *result);
CHECK_EQUAL(7, *itr); // original unchanged
}
//*************************************************************************
// etl::ranges::next(i, n) with bidirectional (non-random) iterator — positive n
//*************************************************************************
TEST(ranges_next_n_non_random_positive)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[1]);
non_random_iterator<int> result = etl::ranges::next(itr, 4);
CHECK_EQUAL(5, *result);
CHECK_EQUAL(1, *itr); // original unchanged
}
//*************************************************************************
// etl::ranges::next(i, n) with bidirectional (non-random) iterator — zero n
//*************************************************************************
TEST(ranges_next_n_non_random_zero)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[5]);
non_random_iterator<int> result = etl::ranges::next(itr, 0);
CHECK_EQUAL(5, *result);
}
//*************************************************************************
// etl::ranges::next(i, n) with bidirectional (non-random) iterator — negative n (moves backward)
//*************************************************************************
TEST(ranges_next_n_non_random_negative)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[7]);
non_random_iterator<int> result = etl::ranges::next(itr, -3);
CHECK_EQUAL(4, *result);
CHECK_EQUAL(7, *itr); // original unchanged
}
//*************************************************************************
// etl::ranges::next(i, bound) with random access iterator
//*************************************************************************
TEST(ranges_next_bound_random_access)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[0];
int* bound = &data[7];
int* result = etl::ranges::next(itr, bound);
CHECK_EQUAL(7, *result);
CHECK(result == bound);
CHECK_EQUAL(0, *itr); // original unchanged
}
//*************************************************************************
// etl::ranges::next(i, bound) with non-random iterator
//*************************************************************************
TEST(ranges_next_bound_non_random)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[0]);
non_random_sentinel<int> bound(&data[5]);
non_random_iterator<int> result = etl::ranges::next(itr, bound);
CHECK_EQUAL(5, *result);
CHECK_EQUAL(0, *itr); // original unchanged
}
//*************************************************************************
// etl::ranges::next(i, bound) already at bound
//*************************************************************************
TEST(ranges_next_bound_already_at_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[4];
int* bound = &data[4];
int* result = etl::ranges::next(itr, bound);
CHECK(result == bound);
CHECK_EQUAL(4, *result);
}
//*************************************************************************
// etl::ranges::next(i, n, bound) with random access iterator — not reaching bound
//*************************************************************************
TEST(ranges_next_n_bound_random_access_not_reaching_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[0];
int* bound = &data[9];
int* result = etl::ranges::next(itr, 4, bound);
CHECK_EQUAL(4, *result);
CHECK_EQUAL(0, *itr); // original unchanged
}
//*************************************************************************
// etl::ranges::next(i, n, bound) with random access iterator — reaching bound
//*************************************************************************
TEST(ranges_next_n_bound_random_access_reaching_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[0];
int* bound = &data[3];
int* result = etl::ranges::next(itr, 7, bound);
CHECK(result == bound);
CHECK_EQUAL(3, *result);
}
//*************************************************************************
// etl::ranges::next(i, n, bound) with random access iterator — exact bound
//*************************************************************************
TEST(ranges_next_n_bound_random_access_exact_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[0];
int* bound = &data[5];
int* result = etl::ranges::next(itr, 5, bound);
CHECK(result == bound);
CHECK_EQUAL(5, *result);
}
//*************************************************************************
// etl::ranges::next(i, n, bound) with random access iterator — backward not reaching bound
//*************************************************************************
TEST(ranges_next_n_bound_random_access_backward_not_reaching_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[9];
int* bound = &data[0];
int* result = etl::ranges::next(itr, -4, bound);
CHECK_EQUAL(5, *result);
}
//*************************************************************************
// etl::ranges::next(i, n, bound) with random access iterator — backward reaching bound
//*************************************************************************
TEST(ranges_next_n_bound_random_access_backward_reaching_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[5];
int* bound = &data[2];
int* result = etl::ranges::next(itr, -7, bound);
CHECK(result == bound);
CHECK_EQUAL(2, *result);
}
//*************************************************************************
// etl::ranges::next(i, n, bound) with random access iterator — zero steps
//*************************************************************************
TEST(ranges_next_n_bound_random_access_zero_steps)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* itr = &data[5];
int* bound = &data[8];
int* result = etl::ranges::next(itr, 0, bound);
CHECK_EQUAL(5, *result);
}
//*************************************************************************
// etl::ranges::next(i, n, bound) with non-random iterator — forward not reaching bound
//*************************************************************************
TEST(ranges_next_n_bound_non_random_forward_not_reaching_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[0]);
non_random_sentinel<int> bound(&data[9]);
non_random_iterator<int> result = etl::ranges::next(itr, 4, bound);
CHECK_EQUAL(4, *result);
}
//*************************************************************************
// etl::ranges::next(i, n, bound) with non-random iterator — forward reaching bound
//*************************************************************************
TEST(ranges_next_n_bound_non_random_forward_reaching_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[0]);
non_random_sentinel<int> bound(&data[3]);
non_random_iterator<int> result = etl::ranges::next(itr, 7, bound);
CHECK_EQUAL(3, *result);
}
//*************************************************************************
// etl::ranges::next(i, n, bound) with non-random iterator — backward not reaching bound
//*************************************************************************
TEST(ranges_next_n_bound_non_random_backward_not_reaching_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[9]);
non_random_sentinel<int> bound(&data[0]);
non_random_iterator<int> result = etl::ranges::next(itr, -3, bound);
CHECK_EQUAL(6, *result);
}
//*************************************************************************
// etl::ranges::next(i, n, bound) with non-random iterator — backward reaching bound
//*************************************************************************
TEST(ranges_next_n_bound_non_random_backward_reaching_bound)
{
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
non_random_iterator<int> itr(&data[3]);
non_random_sentinel<int> bound(&data[1]);
non_random_iterator<int> result = etl::ranges::next(itr, -7, bound);
CHECK_EQUAL(1, *result);
}
TEST(test_is_range)
{
std::vector<int> vec;
int arr[3]{};
int i{};
static_assert(etl::is_range_v<decltype(vec)> == true, "Expected range");
static_assert(etl::is_range_v<decltype(arr)> == true, "Expected range");
static_assert(etl::is_range_v<decltype(i)> == false, "Expected non range");
}
#endif
}
}

File diff suppressed because it is too large Load Diff

5516
test/test_ranges.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -3642,6 +3642,7 @@
<ClInclude Include="..\..\include\etl\private\crc_parameters.h" />
<ClInclude Include="..\..\include\etl\private\delegate_cpp03.h" />
<ClInclude Include="..\..\include\etl\private\delegate_cpp11.h" />
<ClInclude Include="..\..\include\etl\private\ranges_mini_variant.h" />
<ClInclude Include="..\..\include\etl\private\to_string_helper.h" />
<ClInclude Include="..\..\include\etl\private\variant_legacy.h" />
<ClInclude Include="..\..\include\etl\private\variant_variadic.h" />
@ -3813,6 +3814,7 @@
<ClInclude Include="..\..\include\etl\queue.h" />
<ClInclude Include="..\..\include\etl\radix.h" />
<ClInclude Include="..\..\include\etl\random.h" />
<ClInclude Include="..\..\include\etl\ranges.h" />
<ClInclude Include="..\..\include\etl\reference_flat_map.h" />
<ClInclude Include="..\..\include\etl\reference_flat_multimap.h" />
<ClInclude Include="..\..\include\etl\reference_flat_multiset.h" />
@ -11347,6 +11349,7 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\test_ranges.cpp" />
<ClCompile Include="..\test_reference_flat_map.cpp" />
<ClCompile Include="..\test_reference_flat_multimap.cpp" />
<ClCompile Include="..\test_reference_flat_multiset.cpp" />

View File

@ -2381,6 +2381,9 @@
<ClCompile Include="..\test_random.cpp">
<Filter>Tests\Maths</Filter>
</ClCompile>
<ClCompile Include="..\test_ranges.cpp">
<Filter>Tests\Misc</Filter>
</ClCompile>
<ClCompile Include="..\test_error_handler.cpp">
<Filter>Tests\Errors</Filter>
</ClCompile>