mirror of
https://github.com/ETLCPP/etl.git
synced 2026-04-30 19:09:10 +08:00
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:
parent
add42b6c87
commit
31b87b5419
328
docs/ranges.md
Normal file
328
docs/ranges.md
Normal 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 iterator–sentinel 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
@ -37,7 +37,6 @@ SOFTWARE.
|
||||
#include "error_handler.h"
|
||||
#include "exception.h"
|
||||
#include "utility.h"
|
||||
#include "algorithm.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
@ -41,6 +41,7 @@ SOFTWARE.
|
||||
#include "initializer_list.h"
|
||||
#include "type_traits.h"
|
||||
#include "invoke.h"
|
||||
#include "memory.h"
|
||||
|
||||
namespace etl
|
||||
{
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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>>;
|
||||
}
|
||||
|
||||
//****************************************************************************
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
412
include/etl/private/ranges_mini_variant.h
Normal file
412
include/etl/private/ranges_mini_variant.h
Normal 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[] = { ©_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
|
||||
@ -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
5927
include/etl/ranges.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -31,7 +31,6 @@ SOFTWARE.
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#include "algorithm.h"
|
||||
#include "index_of_type.h"
|
||||
#include "integral_limits.h"
|
||||
#include "static_assert.h"
|
||||
|
||||
@ -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
|
||||
|
||||
11921
test/test_algorithm.cpp
11921
test/test_algorithm.cpp
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
1061
test/test_memory.cpp
1061
test/test_memory.cpp
File diff suppressed because it is too large
Load Diff
5516
test/test_ranges.cpp
Normal file
5516
test/test_ranges.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -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" />
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user