mirror of
https://github.com/ETLCPP/etl.git
synced 2026-07-30 16:26:17 +08:00
Fix ranges (#1475)
* Fix etl::get to preserve reference members for tuple rvalues The rvalue-qualified overloads of etl::get (both the index-based and type-based forms) unconditionally applied etl::move to the stored element. For a tuple holding a reference member (e.g. etl::tuple<int&>), this cast the referenced object to an rvalue instead of returning the stored lvalue reference, diverging from std::get and failing to compile when the result was bound to a non-const lvalue reference. Forward the element with static_cast<element_type&&> instead, so that reference members collapse to an lvalue reference while value members are still moved. This is required to support tuples of references such as those produced by the zip, enumerate and adjacent views. * Make ranges views return non-const references to make them mutable * Support type-changing transform_view * Fix join_with_iterator reference type to match operator* join_with_iterator declared reference = inner_trait::reference (e.g. int&) while operator* returns value_type by value. For prvalue-yielding inner ranges (e.g. repeat_view from a type-changing transform) the declared reference was inconsistent with the actual dereference. Derive value_type from the inner iterator's dereference and set reference = value_type, preserving by-value semantics. Add regression test for transform|join_with with prvalue inner ranges. * Make join_with conform to [range.join.with.iterator] using inner/pattern common reference * Clang-format * Fix owning_view const begin()/end() by making _r mutable so chunk_view compiles over rvalue ranges
This commit is contained in:
parent
1d5e9ea67f
commit
05010aebf8
@ -585,6 +585,12 @@ namespace etl
|
||||
|
||||
using iterator_category = ETL_OR_STD::random_access_iterator_tag;
|
||||
|
||||
constexpr repeat_iterator()
|
||||
: _value{}
|
||||
, _i{}
|
||||
{
|
||||
}
|
||||
|
||||
constexpr explicit repeat_iterator(T value, B i = etl::numeric_limits<B>::max())
|
||||
: _value{value}
|
||||
, _i{i}
|
||||
@ -901,7 +907,11 @@ namespace etl
|
||||
|
||||
private:
|
||||
|
||||
Range _r;
|
||||
// 'mutable' so that the const begin()/end() observe a non-const '_r' and
|
||||
// therefore call the non-const ETL_OR_STD::begin/end overloads, yielding
|
||||
// the (mutable) 'iterator' rather than a 'const_iterator' that cannot be
|
||||
// converted back to 'iterator'.
|
||||
mutable Range _r;
|
||||
};
|
||||
|
||||
template <class Range>
|
||||
@ -989,12 +999,15 @@ namespace etl
|
||||
using const_iterator = typename trait::const_iterator;
|
||||
using value_type = typename trait::value_type;
|
||||
using difference_type = typename trait::difference_type;
|
||||
using pointer = typename trait::pointer;
|
||||
using reference = typename trait::reference;
|
||||
// Derive the reference from the mutable iterator so filtered elements
|
||||
// remain mutable when the underlying range is non-const, as required for
|
||||
// std::ranges::filter_view.
|
||||
using reference = decltype(*etl::declval<iterator&>());
|
||||
using pointer = etl::remove_reference_t<reference>*;
|
||||
|
||||
using iterator_category = ETL_OR_STD::bidirectional_iterator_tag;
|
||||
|
||||
filter_iterator(const_iterator it, const_iterator it_end, const Pred& p)
|
||||
filter_iterator(iterator it, iterator it_end, const Pred& p)
|
||||
: _it{it}
|
||||
, _it_begin{it}
|
||||
, _it_end{it_end}
|
||||
@ -1099,7 +1112,7 @@ namespace etl
|
||||
return *this;
|
||||
}
|
||||
|
||||
value_type operator*()
|
||||
reference operator*()
|
||||
{
|
||||
return *_it;
|
||||
}
|
||||
@ -1116,10 +1129,10 @@ namespace etl
|
||||
|
||||
private:
|
||||
|
||||
const_iterator _it;
|
||||
const_iterator _it_begin;
|
||||
const_iterator _it_end;
|
||||
const Pred& _p;
|
||||
iterator _it;
|
||||
iterator _it_begin;
|
||||
iterator _it_end;
|
||||
const Pred& _p;
|
||||
};
|
||||
|
||||
template <class Range, class Pred>
|
||||
@ -1162,18 +1175,18 @@ namespace etl
|
||||
|
||||
constexpr const_iterator begin() const
|
||||
{
|
||||
return const_iterator(ETL_OR_STD::cbegin(_r), ETL_OR_STD::cend(_r), _pred);
|
||||
return const_iterator(ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r), _pred);
|
||||
}
|
||||
|
||||
constexpr const_iterator end() const
|
||||
{
|
||||
return const_iterator(ETL_OR_STD::cend(_r), ETL_OR_STD::cend(_r), _pred);
|
||||
return const_iterator(ETL_OR_STD::end(_r), ETL_OR_STD::end(_r), _pred);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
const Pred _pred;
|
||||
Range _r;
|
||||
const Pred _pred;
|
||||
mutable Range _r;
|
||||
};
|
||||
|
||||
template <class Range, typename Pred>
|
||||
@ -1231,10 +1244,14 @@ namespace etl
|
||||
|
||||
using iterator = typename trait::iterator;
|
||||
using const_iterator = typename trait::const_iterator;
|
||||
using value_type = typename trait::value_type;
|
||||
using difference_type = typename trait::difference_type;
|
||||
using pointer = typename trait::pointer;
|
||||
using reference = typename trait::reference;
|
||||
|
||||
// transform_view is type-changing: the element type is the result of applying
|
||||
// the transform function to the underlying element, not the underlying range's
|
||||
// element type (as specified for std::ranges::transform_view).
|
||||
using reference = decltype(etl::declval<const Fun&>()(*etl::declval<const_iterator&>()));
|
||||
using value_type = etl::remove_cvref_t<reference>;
|
||||
using pointer = void;
|
||||
|
||||
using iterator_category = ETL_OR_STD::forward_iterator_tag;
|
||||
|
||||
@ -1270,9 +1287,9 @@ namespace etl
|
||||
return *this;
|
||||
}
|
||||
|
||||
value_type operator*()
|
||||
reference operator*()
|
||||
{
|
||||
return static_cast<value_type>(_f(*_it));
|
||||
return _f(*_it);
|
||||
}
|
||||
|
||||
bool operator==(const transform_iterator& other) const
|
||||
@ -2269,9 +2286,12 @@ namespace etl
|
||||
using inner_trait = typename etl::ranges::private_ranges::iterator_trait<InnerRange>;
|
||||
using inner_iterator = typename inner_trait::iterator;
|
||||
|
||||
using value_type = typename inner_trait::value_type;
|
||||
// Derive the reference type from the inner iterator's actual dereference, so that
|
||||
// inner ranges whose iterators yield prvalues (e.g. repeat_view) are supported,
|
||||
// matching std::ranges::join_view (reference = range_reference_t<InnerRange>).
|
||||
using reference = decltype(*etl::declval<const inner_iterator&>());
|
||||
using value_type = etl::remove_cvref_t<reference>;
|
||||
using pointer = typename inner_trait::pointer;
|
||||
using reference = typename inner_trait::reference;
|
||||
|
||||
join_iterator(iterator it, iterator it_end)
|
||||
: _it(it)
|
||||
@ -2430,6 +2450,31 @@ namespace etl
|
||||
inline constexpr private_views::join join;
|
||||
} // namespace views
|
||||
|
||||
namespace private_ranges
|
||||
{
|
||||
//*********************************************************************
|
||||
/// Computes the element reference type for join_with, following
|
||||
/// [range.join.with.iterator]: common_reference of the inner range's and
|
||||
/// the pattern's reference types. etl::common_reference_t is only available
|
||||
/// in C++20, so for the common case (inner and pattern share an underlying
|
||||
/// value type) this yields the const-combined lvalue reference - preserving
|
||||
/// writability when neither side is const - and degrades to a prvalue value
|
||||
/// type when either side yields a prvalue or the underlying types differ.
|
||||
//*********************************************************************
|
||||
template <typename InnerRef, typename PatternRef>
|
||||
struct join_with_reference
|
||||
{
|
||||
using value_type = etl::common_type_t<etl::remove_cvref_t<InnerRef>, etl::remove_cvref_t<PatternRef>>;
|
||||
|
||||
static constexpr bool same_underlying = etl::is_same<etl::remove_cvref_t<InnerRef>, etl::remove_cvref_t<PatternRef>>::value;
|
||||
static constexpr bool both_lvalue = etl::is_lvalue_reference<InnerRef>::value && etl::is_lvalue_reference<PatternRef>::value;
|
||||
static constexpr bool any_const =
|
||||
etl::is_const<etl::remove_reference_t<InnerRef>>::value || etl::is_const<etl::remove_reference_t<PatternRef>>::value;
|
||||
|
||||
using type = etl::conditional_t<same_underlying && both_lvalue, etl::conditional_t<any_const, const value_type&, value_type&>, value_type>;
|
||||
};
|
||||
} // namespace private_ranges
|
||||
|
||||
template <class Range, class Pattern>
|
||||
class join_with_iterator
|
||||
{
|
||||
@ -2447,13 +2492,21 @@ namespace etl
|
||||
using inner_trait = typename etl::ranges::private_ranges::iterator_trait<InnerRange>;
|
||||
using inner_iterator = typename inner_trait::iterator;
|
||||
|
||||
using value_type = typename inner_trait::value_type;
|
||||
using pointer = typename inner_trait::pointer;
|
||||
using reference = typename inner_trait::reference;
|
||||
// Deduce the pattern iterator from iterating the (const) pattern view directly,
|
||||
// so reference-backed patterns (e.g. ref_view) stay mutable while value-backed
|
||||
// patterns (e.g. single_view) are read as const.
|
||||
using pattern_iterator = decltype(ETL_OR_STD::begin(etl::declval<const Pattern&>()));
|
||||
|
||||
using pattern_trait = typename etl::ranges::private_ranges::iterator_trait<Pattern>;
|
||||
using pattern_iterator = typename pattern_trait::iterator;
|
||||
using pattern_const_iterator = typename pattern_trait::const_iterator;
|
||||
// Element type follows [range.join.with.iterator]: the common type / common
|
||||
// reference of the inner range's and the pattern's elements. This keeps the
|
||||
// declared reference consistent with operator* and supports inner ranges whose
|
||||
// iterators yield prvalues (e.g. repeat_view).
|
||||
using inner_reference = decltype(*etl::declval<const inner_iterator&>());
|
||||
using pattern_reference = decltype(*etl::declval<const pattern_iterator&>());
|
||||
|
||||
using value_type = etl::common_type_t<etl::remove_cvref_t<inner_reference>, etl::remove_cvref_t<pattern_reference>>;
|
||||
using reference = typename etl::ranges::private_ranges::join_with_reference<inner_reference, pattern_reference>::type;
|
||||
using pointer = typename inner_trait::pointer;
|
||||
|
||||
join_with_iterator(iterator it, iterator it_end, const Pattern& pattern)
|
||||
: _it(it)
|
||||
@ -2461,8 +2514,8 @@ namespace etl
|
||||
, _inner_it(it != it_end ? ETL_OR_STD::begin(*it) : inner_iterator{})
|
||||
, _inner_it_end(it != it_end ? ETL_OR_STD::end(*it) : inner_iterator{})
|
||||
, _pattern(pattern)
|
||||
, _pattern_it(pattern.cend())
|
||||
, _pattern_it_end(pattern.cend())
|
||||
, _pattern_it(ETL_OR_STD::end(pattern))
|
||||
, _pattern_it_end(ETL_OR_STD::end(pattern))
|
||||
{
|
||||
adjust_iterator();
|
||||
}
|
||||
@ -2517,7 +2570,7 @@ namespace etl
|
||||
return *this;
|
||||
}
|
||||
|
||||
value_type operator*() const
|
||||
reference operator*() const
|
||||
{
|
||||
if (_pattern_it != _pattern_it_end)
|
||||
{
|
||||
@ -2545,21 +2598,21 @@ namespace etl
|
||||
++_it;
|
||||
if (_it != _it_end)
|
||||
{
|
||||
_pattern_it = ETL_OR_STD::cbegin(_pattern);
|
||||
_pattern_it_end = ETL_OR_STD::cend(_pattern);
|
||||
_pattern_it = ETL_OR_STD::begin(_pattern);
|
||||
_pattern_it_end = ETL_OR_STD::end(_pattern);
|
||||
_inner_it = ETL_OR_STD::begin(*_it);
|
||||
_inner_it_end = ETL_OR_STD::end(*_it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iterator _it;
|
||||
iterator _it_end;
|
||||
inner_iterator _inner_it;
|
||||
inner_iterator _inner_it_end;
|
||||
const Pattern& _pattern;
|
||||
pattern_const_iterator _pattern_it;
|
||||
pattern_const_iterator _pattern_it_end;
|
||||
iterator _it;
|
||||
iterator _it_end;
|
||||
inner_iterator _inner_it;
|
||||
inner_iterator _inner_it_end;
|
||||
const Pattern& _pattern;
|
||||
pattern_iterator _pattern_it;
|
||||
pattern_iterator _pattern_it_end;
|
||||
};
|
||||
|
||||
template <class Range, class Pattern>
|
||||
@ -2695,11 +2748,11 @@ namespace etl
|
||||
using pattern_trait = typename etl::ranges::private_ranges::iterator_trait<Pattern>;
|
||||
using pattern_const_iterator = typename pattern_trait::const_iterator;
|
||||
|
||||
using value_type = etl::ranges::subrange<const_iterator>;
|
||||
using value_type = etl::ranges::subrange<iterator>;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type;
|
||||
|
||||
split_iterator(const_iterator it, const_iterator it_end, const Pattern& pattern, bool is_end = false)
|
||||
split_iterator(iterator it, iterator it_end, const Pattern& pattern, bool is_end = false)
|
||||
: _it(it)
|
||||
, _it_end(it_end)
|
||||
, _pattern(pattern)
|
||||
@ -2776,7 +2829,7 @@ namespace etl
|
||||
|
||||
private:
|
||||
|
||||
const_iterator find_next() const
|
||||
iterator find_next() const
|
||||
{
|
||||
auto pat_begin = ETL_OR_STD::cbegin(_pattern);
|
||||
auto pat_end = ETL_OR_STD::cend(_pattern);
|
||||
@ -2818,10 +2871,10 @@ namespace etl
|
||||
return _it_end;
|
||||
}
|
||||
|
||||
const_iterator _it;
|
||||
const_iterator _it_end;
|
||||
iterator _it;
|
||||
iterator _it_end;
|
||||
const Pattern& _pattern;
|
||||
const_iterator _next;
|
||||
iterator _next;
|
||||
// there is still one empty segment to emit after the last delimiter if
|
||||
// the last delimiter is at the end of the range
|
||||
bool _trailing_empty;
|
||||
@ -2956,6 +3009,7 @@ namespace etl
|
||||
public:
|
||||
|
||||
using trait = typename etl::ranges::private_ranges::iterator_trait<Range>;
|
||||
using iterator_type = typename trait::iterator;
|
||||
using const_iterator_type = typename trait::const_iterator;
|
||||
using value_type = typename trait::value_type;
|
||||
|
||||
@ -2968,20 +3022,20 @@ namespace etl
|
||||
|
||||
using value_type = typename trait::value_type;
|
||||
using difference_type = typename trait::difference_type;
|
||||
using pointer = const value_type*;
|
||||
using reference = const value_type&;
|
||||
using reference = decltype(*etl::declval<iterator_type&>());
|
||||
using pointer = etl::remove_reference_t<reference>*;
|
||||
using iterator_category = ETL_OR_STD::forward_iterator_tag;
|
||||
|
||||
iterator() = default;
|
||||
|
||||
iterator(const_iterator_type current, const_iterator_type segment_end, bool is_end)
|
||||
iterator(iterator_type current, iterator_type segment_end, bool is_end)
|
||||
: _current_it(current)
|
||||
, _segment_end(segment_end)
|
||||
, _is_end(is_end || (current == segment_end))
|
||||
{
|
||||
}
|
||||
|
||||
reference operator*() const
|
||||
constexpr decltype(auto) operator*() const
|
||||
{
|
||||
return *_current_it;
|
||||
}
|
||||
@ -3028,14 +3082,14 @@ namespace etl
|
||||
|
||||
private:
|
||||
|
||||
const_iterator_type _current_it{};
|
||||
const_iterator_type _segment_end{};
|
||||
bool _is_end = true;
|
||||
iterator_type _current_it{};
|
||||
iterator_type _segment_end{};
|
||||
bool _is_end = true;
|
||||
};
|
||||
|
||||
using const_iterator = iterator;
|
||||
|
||||
lazy_split_inner_range(const_iterator_type segment_begin, const_iterator_type segment_end)
|
||||
lazy_split_inner_range(iterator_type segment_begin, iterator_type segment_end)
|
||||
: _segment_begin(segment_begin)
|
||||
, _segment_end(segment_end)
|
||||
{
|
||||
@ -3058,8 +3112,8 @@ namespace etl
|
||||
|
||||
private:
|
||||
|
||||
const_iterator_type _segment_begin;
|
||||
const_iterator_type _segment_end;
|
||||
iterator_type _segment_begin;
|
||||
iterator_type _segment_end;
|
||||
};
|
||||
|
||||
/// Outer iterator for lazy_split_view.
|
||||
@ -3085,7 +3139,7 @@ namespace etl
|
||||
using pointer = value_type*;
|
||||
using reference = value_type;
|
||||
|
||||
lazy_split_iterator(const_iterator it, const_iterator it_end, const Pattern& pattern, bool is_end = false)
|
||||
lazy_split_iterator(source_iterator it, source_iterator it_end, const Pattern& pattern, bool is_end = false)
|
||||
: _it(it)
|
||||
, _it_end(it_end)
|
||||
, _pattern(pattern)
|
||||
@ -3166,7 +3220,7 @@ namespace etl
|
||||
|
||||
/// Scans forward from _it looking for the pattern; returns the
|
||||
/// position of the first match (i.e. the end of the current segment).
|
||||
const_iterator find_next() const
|
||||
source_iterator find_next() const
|
||||
{
|
||||
auto pat_begin = ETL_OR_STD::cbegin(_pattern);
|
||||
auto pat_end = ETL_OR_STD::cend(_pattern);
|
||||
@ -3208,11 +3262,11 @@ namespace etl
|
||||
return _it_end;
|
||||
}
|
||||
|
||||
const_iterator _it;
|
||||
const_iterator _it_end;
|
||||
const Pattern& _pattern;
|
||||
const_iterator _next;
|
||||
bool _trailing_empty;
|
||||
source_iterator _it;
|
||||
source_iterator _it_end;
|
||||
const Pattern& _pattern;
|
||||
source_iterator _next;
|
||||
bool _trailing_empty;
|
||||
};
|
||||
|
||||
template <class Range, class Pattern>
|
||||
@ -3668,11 +3722,14 @@ namespace etl
|
||||
|
||||
public:
|
||||
|
||||
using iterators_type = etl::tuple< typename etl::ranges::private_ranges::iterator_trait< Ranges>::const_iterator...>;
|
||||
using iterators_type = etl::tuple< typename etl::ranges::private_ranges::iterator_trait< Ranges>::iterator...>;
|
||||
using value_type = etl::tuple<typename etl::ranges::private_ranges::iterator_trait< Ranges>::value_type...>;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = const value_type*;
|
||||
using reference = value_type;
|
||||
// Each tuple element is a reference into the corresponding underlying
|
||||
// range, so zipped elements remain mutable when the underlying ranges
|
||||
// are non-const, as required for std::ranges::zip_view.
|
||||
using reference = etl::tuple<decltype(*etl::declval< typename etl::ranges::private_ranges::iterator_trait<Ranges>::iterator&>())...>;
|
||||
using pointer = value_type*;
|
||||
|
||||
using iterator_category = ETL_OR_STD::forward_iterator_tag;
|
||||
|
||||
@ -3698,7 +3755,7 @@ namespace etl
|
||||
return tmp;
|
||||
}
|
||||
|
||||
constexpr value_type operator*() const
|
||||
constexpr reference operator*() const
|
||||
{
|
||||
return deref(etl::make_index_sequence<sizeof...(Ranges)>{});
|
||||
}
|
||||
@ -3722,9 +3779,9 @@ namespace etl
|
||||
}
|
||||
|
||||
template <size_t... Is>
|
||||
constexpr value_type deref(etl::index_sequence<Is...>) const
|
||||
constexpr reference deref(etl::index_sequence<Is...>) const
|
||||
{
|
||||
return value_type(*etl::get<Is>(_iters)...);
|
||||
return reference(*etl::get<Is>(_iters)...);
|
||||
}
|
||||
|
||||
// zip terminates when ANY iterator reaches its end (shortest range
|
||||
@ -4275,12 +4332,16 @@ namespace etl
|
||||
|
||||
using trait = typename etl::ranges::private_ranges::iterator_trait<Range>;
|
||||
|
||||
using base_iterator = typename trait::const_iterator;
|
||||
using base_iterator = typename trait::iterator;
|
||||
using base_value_type = typename trait::value_type;
|
||||
using base_reference = decltype(*etl::declval<base_iterator&>());
|
||||
using value_type = etl::tuple<size_t, base_value_type>;
|
||||
using difference_type = typename trait::difference_type;
|
||||
using pointer = const value_type*;
|
||||
using reference = value_type;
|
||||
// The second tuple element is a reference into the underlying range, so
|
||||
// elements remain mutable when the underlying range is non-const, as
|
||||
// required for std::ranges::enumerate_view.
|
||||
using reference = etl::tuple<size_t, base_reference>;
|
||||
using pointer = value_type*;
|
||||
|
||||
using iterator_category = ETL_OR_STD::forward_iterator_tag;
|
||||
|
||||
@ -4318,9 +4379,9 @@ namespace etl
|
||||
return *this;
|
||||
}
|
||||
|
||||
value_type operator*() const
|
||||
reference operator*() const
|
||||
{
|
||||
return value_type(_index, *_it);
|
||||
return reference(_index, *_it);
|
||||
}
|
||||
|
||||
bool operator==(const enumerate_iterator& other) const
|
||||
@ -4435,12 +4496,27 @@ namespace etl
|
||||
|
||||
using trait = typename etl::ranges::private_ranges::iterator_trait<Range>;
|
||||
|
||||
using base_iterator = typename trait::const_iterator;
|
||||
using base_iterator = typename trait::iterator;
|
||||
using base_value_type = typename trait::value_type;
|
||||
using value_type = etl::tuple_element_t<N, base_value_type>;
|
||||
using difference_type = typename trait::difference_type;
|
||||
using pointer = const value_type*;
|
||||
using reference = const value_type&;
|
||||
|
||||
private:
|
||||
|
||||
// Computes the dereference type via ADL get, so that std::pair and
|
||||
// etl::tuple both resolve correctly. When the underlying range is
|
||||
// non-const this yields a mutable reference, as required for
|
||||
// std::ranges::elements_view.
|
||||
static decltype(auto) deref_element(const base_iterator& it)
|
||||
{
|
||||
using etl::get;
|
||||
return get<N>(*it);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
using reference = decltype(deref_element(etl::declval<const base_iterator&>()));
|
||||
using pointer = etl::remove_reference_t<reference>*;
|
||||
|
||||
using iterator_category = ETL_OR_STD::forward_iterator_tag;
|
||||
|
||||
@ -4475,8 +4551,7 @@ namespace etl
|
||||
|
||||
decltype(auto) operator*() const
|
||||
{
|
||||
using etl::get;
|
||||
return get<N>(*_it);
|
||||
return deref_element(_it);
|
||||
}
|
||||
|
||||
bool operator==(const elements_iterator& other) const
|
||||
@ -4645,12 +4720,16 @@ namespace etl
|
||||
|
||||
using trait = typename etl::ranges::private_ranges::iterator_trait<Range>;
|
||||
|
||||
using base_iterator = typename trait::const_iterator;
|
||||
using base_iterator = typename trait::iterator;
|
||||
using base_value_type = typename trait::value_type;
|
||||
using base_reference = decltype(*etl::declval<base_iterator&>());
|
||||
using value_type = private_ranges::repeat_tuple_t<base_value_type, N>;
|
||||
using difference_type = typename trait::difference_type;
|
||||
using pointer = const value_type*;
|
||||
using reference = value_type;
|
||||
// Each tuple element is a reference into the underlying range, so the
|
||||
// window elements remain mutable when the underlying range is non-const,
|
||||
// as required for std::ranges::adjacent_view.
|
||||
using reference = private_ranges::repeat_tuple_t<base_reference, N>;
|
||||
using pointer = value_type*;
|
||||
|
||||
using iterator_category = ETL_OR_STD::forward_iterator_tag;
|
||||
|
||||
@ -4679,7 +4758,7 @@ namespace etl
|
||||
return tmp;
|
||||
}
|
||||
|
||||
constexpr value_type operator*() const
|
||||
constexpr reference operator*() const
|
||||
{
|
||||
return deref(etl::make_index_sequence<N>{});
|
||||
}
|
||||
@ -4714,9 +4793,9 @@ namespace etl
|
||||
}
|
||||
|
||||
template <size_t... Is>
|
||||
constexpr value_type deref(etl::index_sequence<Is...>) const
|
||||
constexpr reference deref(etl::index_sequence<Is...>) const
|
||||
{
|
||||
return value_type(*_iters[Is]...);
|
||||
return reference(*_iters[Is]...);
|
||||
}
|
||||
|
||||
base_iterator _iters[N];
|
||||
@ -5038,11 +5117,11 @@ namespace etl
|
||||
|
||||
using iterator_category = ETL_OR_STD::forward_iterator_tag;
|
||||
|
||||
using value_type = etl::ranges::subrange<const_inner_iterator>;
|
||||
using value_type = etl::ranges::subrange<inner_iterator>;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type;
|
||||
|
||||
chunk_iterator(const_inner_iterator it, const_inner_iterator it_end, difference_type chunk_size)
|
||||
chunk_iterator(inner_iterator it, inner_iterator it_end, difference_type chunk_size)
|
||||
: _it(it)
|
||||
, _it_end(it_end)
|
||||
, _chunk_size(chunk_size)
|
||||
@ -5070,9 +5149,9 @@ namespace etl
|
||||
|
||||
value_type operator*() const
|
||||
{
|
||||
difference_type remaining = etl::distance(_it, _it_end);
|
||||
difference_type step = (_chunk_size < remaining) ? _chunk_size : remaining;
|
||||
const_inner_iterator chunk_end = _it;
|
||||
difference_type remaining = etl::distance(_it, _it_end);
|
||||
difference_type step = (_chunk_size < remaining) ? _chunk_size : remaining;
|
||||
inner_iterator chunk_end = _it;
|
||||
etl::advance(chunk_end, step);
|
||||
return value_type(_it, chunk_end);
|
||||
}
|
||||
@ -5089,9 +5168,9 @@ namespace etl
|
||||
|
||||
private:
|
||||
|
||||
const_inner_iterator _it;
|
||||
const_inner_iterator _it_end;
|
||||
difference_type _chunk_size;
|
||||
inner_iterator _it;
|
||||
inner_iterator _it_end;
|
||||
difference_type _chunk_size;
|
||||
};
|
||||
|
||||
//*************************************************************************
|
||||
@ -5200,11 +5279,11 @@ namespace etl
|
||||
|
||||
using iterator_category = ETL_OR_STD::forward_iterator_tag;
|
||||
|
||||
using value_type = etl::ranges::subrange<const_inner_iterator>;
|
||||
using value_type = etl::ranges::subrange<inner_iterator>;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type;
|
||||
|
||||
slide_iterator(const_inner_iterator it, const_inner_iterator it_end, difference_type window_size)
|
||||
slide_iterator(inner_iterator it, inner_iterator it_end, difference_type window_size)
|
||||
: _it(it)
|
||||
, _it_end(it_end)
|
||||
, _window_size(window_size)
|
||||
@ -5230,7 +5309,7 @@ namespace etl
|
||||
|
||||
value_type operator*() const
|
||||
{
|
||||
const_inner_iterator window_end = _it;
|
||||
inner_iterator window_end = _it;
|
||||
etl::advance(window_end, _window_size);
|
||||
return value_type(_it, window_end);
|
||||
}
|
||||
@ -5247,9 +5326,9 @@ namespace etl
|
||||
|
||||
private:
|
||||
|
||||
const_inner_iterator _it;
|
||||
const_inner_iterator _it_end;
|
||||
difference_type _window_size;
|
||||
inner_iterator _it;
|
||||
inner_iterator _it_end;
|
||||
difference_type _window_size;
|
||||
};
|
||||
|
||||
//*************************************************************************
|
||||
@ -5376,11 +5455,11 @@ namespace etl
|
||||
|
||||
using iterator_category = ETL_OR_STD::forward_iterator_tag;
|
||||
|
||||
using value_type = etl::ranges::subrange<const_inner_iterator>;
|
||||
using value_type = etl::ranges::subrange<inner_iterator>;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type;
|
||||
|
||||
chunk_by_iterator(const_inner_iterator it, const_inner_iterator it_end, const Pred& pred)
|
||||
chunk_by_iterator(inner_iterator it, inner_iterator it_end, const Pred& pred)
|
||||
: _it(it)
|
||||
, _it_end(it_end)
|
||||
, _pred(pred)
|
||||
@ -5423,15 +5502,15 @@ namespace etl
|
||||
|
||||
private:
|
||||
|
||||
const_inner_iterator find_next_chunk_end() const
|
||||
inner_iterator find_next_chunk_end() const
|
||||
{
|
||||
if (_it == _it_end)
|
||||
{
|
||||
return _it_end;
|
||||
}
|
||||
|
||||
const_inner_iterator it_prev = _it;
|
||||
const_inner_iterator it_curr = _it;
|
||||
inner_iterator it_prev = _it;
|
||||
inner_iterator it_curr = _it;
|
||||
++it_curr;
|
||||
|
||||
while (it_curr != _it_end)
|
||||
@ -5447,10 +5526,10 @@ namespace etl
|
||||
return _it_end;
|
||||
}
|
||||
|
||||
const_inner_iterator _it;
|
||||
const_inner_iterator _it_end;
|
||||
const_inner_iterator _chunk_end;
|
||||
Pred _pred;
|
||||
inner_iterator _it;
|
||||
inner_iterator _it_end;
|
||||
inner_iterator _chunk_end;
|
||||
Pred _pred;
|
||||
};
|
||||
|
||||
//*************************************************************************
|
||||
@ -5565,10 +5644,10 @@ namespace etl
|
||||
using iterator_category = ETL_OR_STD::forward_iterator_tag;
|
||||
|
||||
using value_type = typename trait::value_type;
|
||||
using pointer = typename trait::pointer;
|
||||
using reference = typename trait::reference;
|
||||
using reference = decltype(*etl::declval<inner_iterator&>());
|
||||
using pointer = etl::remove_reference_t<reference>*;
|
||||
|
||||
constexpr stride_iterator(const_inner_iterator it, const_inner_iterator it_end, difference_type stride_n)
|
||||
constexpr stride_iterator(inner_iterator it, inner_iterator it_end, difference_type stride_n)
|
||||
: _it(it)
|
||||
, _it_end(it_end)
|
||||
, _stride_n(stride_n)
|
||||
@ -5594,12 +5673,12 @@ namespace etl
|
||||
return tmp;
|
||||
}
|
||||
|
||||
constexpr auto operator*() const
|
||||
constexpr decltype(auto) operator*() const
|
||||
{
|
||||
return *_it;
|
||||
}
|
||||
|
||||
constexpr auto operator->() const
|
||||
constexpr pointer operator->() const
|
||||
{
|
||||
return &(*_it);
|
||||
}
|
||||
@ -5616,9 +5695,9 @@ namespace etl
|
||||
|
||||
private:
|
||||
|
||||
mutable const_inner_iterator _it;
|
||||
const_inner_iterator _it_end;
|
||||
difference_type _stride_n;
|
||||
mutable inner_iterator _it;
|
||||
inner_iterator _it_end;
|
||||
difference_type _stride_n;
|
||||
};
|
||||
|
||||
//*************************************************************************
|
||||
@ -5950,7 +6029,7 @@ namespace etl
|
||||
|
||||
to_input_iterator() = default;
|
||||
|
||||
to_input_iterator(const_iterator it)
|
||||
to_input_iterator(iterator it)
|
||||
: _it(it)
|
||||
{
|
||||
}
|
||||
@ -5972,7 +6051,7 @@ namespace etl
|
||||
return tmp;
|
||||
}
|
||||
|
||||
reference operator*() const
|
||||
decltype(auto) operator*() const
|
||||
{
|
||||
return *_it;
|
||||
}
|
||||
@ -5994,7 +6073,7 @@ namespace etl
|
||||
|
||||
private:
|
||||
|
||||
mutable const_iterator _it;
|
||||
mutable iterator _it;
|
||||
};
|
||||
|
||||
//*************************************************************************
|
||||
|
||||
@ -771,10 +771,13 @@ namespace etl
|
||||
ETL_STATIC_ASSERT(Index < sizeof...(TTypes), "etl::get<Index> - Index out of range");
|
||||
|
||||
// Get the type at this index.
|
||||
using tuple_type = etl::nth_base_t<Index, tuple<TTypes...>>&&;
|
||||
using tuple_type = etl::nth_base_t<Index, tuple<TTypes...>>&&;
|
||||
using element_type = etl::tuple_element_t<Index, etl::tuple<TTypes...>>;
|
||||
|
||||
// Cast the tuple to the selected type and get the value.
|
||||
return etl::move(static_cast<tuple_type>(t).get_value());
|
||||
// Forward the element. A reference member must not be turned into an
|
||||
// rvalue, so cast to element_type&& (which collapses to a reference type
|
||||
// when element_type is itself a reference).
|
||||
return static_cast<element_type&&>(static_cast<tuple_type>(t).get_value());
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
@ -788,10 +791,13 @@ namespace etl
|
||||
ETL_STATIC_ASSERT(Index < sizeof...(TTypes), "etl::get<Index> - Index out of range");
|
||||
|
||||
// Get the type at this index.
|
||||
using tuple_type = const etl::nth_base_t<Index, etl::tuple<TTypes...>>&&;
|
||||
using tuple_type = const etl::nth_base_t<Index, etl::tuple<TTypes...>>&&;
|
||||
using element_type = etl::tuple_element_t<Index, etl::tuple<TTypes...>>;
|
||||
|
||||
// Cast the tuple to the selected type and get the value.
|
||||
return etl::move(static_cast<tuple_type>(t).get_value());
|
||||
// Forward the element. A reference member must not be turned into an
|
||||
// rvalue, so cast to const element_type&& (which collapses to a reference
|
||||
// type when element_type is itself a reference).
|
||||
return static_cast<const element_type&&>(static_cast<tuple_type>(t).get_value());
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
@ -844,8 +850,10 @@ namespace etl
|
||||
// Get the tuple base type that contains a T
|
||||
using tuple_type = etl::private_tuple::tuple_type_base_t<T, tuple<TTypes...>>&&;
|
||||
|
||||
// Cast the tuple to the selected type and get the value.
|
||||
return etl::move(static_cast<tuple_type>(t).get_value());
|
||||
// Forward the element. A reference type T must not be turned into an
|
||||
// rvalue, so cast to T&& (which collapses to a reference when T is a
|
||||
// reference type).
|
||||
return static_cast<T&&>(static_cast<tuple_type>(t).get_value());
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
@ -862,8 +870,10 @@ namespace etl
|
||||
// Get the tuple base type that contains a T
|
||||
using tuple_type = const etl::private_tuple::tuple_type_base_t<T, tuple<TTypes...>>&&;
|
||||
|
||||
// Cast the tuple to the selected type and get the value.
|
||||
return etl::move(static_cast<tuple_type>(t).get_value());
|
||||
// Forward the element. A reference type T must not be turned into an
|
||||
// rvalue, so cast to const T&& (which collapses to a reference when T is a
|
||||
// reference type).
|
||||
return static_cast<const T&&>(static_cast<tuple_type>(t).get_value());
|
||||
}
|
||||
|
||||
#if ETL_USING_CPP17
|
||||
|
||||
@ -28,6 +28,8 @@ SOFTWARE.
|
||||
|
||||
#include "unit_test_framework.h"
|
||||
|
||||
#include "etl/algorithm.h"
|
||||
#include "etl/array.h"
|
||||
#include "etl/ranges.h"
|
||||
#include "etl/vector.h"
|
||||
|
||||
@ -1564,6 +1566,53 @@ namespace
|
||||
CHECK(it == tv.end());
|
||||
}
|
||||
|
||||
TEST(test_ranges_transform_view_element_type_is_function_result)
|
||||
{
|
||||
// transform_view must be type-changing: the element type is the result of the
|
||||
// transform function, not the underlying range's element type.
|
||||
auto to_double = [](int i) -> double
|
||||
{
|
||||
return i * 1.5;
|
||||
};
|
||||
|
||||
etl::vector<int, 4> v_in{1, 2, 3, 4};
|
||||
|
||||
auto tv = v_in | etl::views::transform(to_double);
|
||||
|
||||
using element_type = etl::remove_cvref_t<decltype(*tv.begin())>;
|
||||
static_assert(etl::is_same<double, element_type>::value, "transform element should be the function result type");
|
||||
|
||||
// Fractional results must survive (they would be truncated to int by the old behaviour).
|
||||
etl::vector<double, 4> v_out;
|
||||
for (auto x : tv)
|
||||
{
|
||||
v_out.push_back(x);
|
||||
}
|
||||
|
||||
CHECK_CLOSE(1.5, v_out[0], 0.001);
|
||||
CHECK_CLOSE(3.0, v_out[1], 0.001);
|
||||
CHECK_CLOSE(4.5, v_out[2], 0.001);
|
||||
CHECK_CLOSE(6.0, v_out[3], 0.001);
|
||||
}
|
||||
|
||||
TEST(test_ranges_transform_view_yields_range_then_join)
|
||||
{
|
||||
// A transform that returns a range, joined together, only compiles when the
|
||||
// transform element type is the range returned by the function.
|
||||
etl::array<int, 4> samples = {10, 20, 30, 40};
|
||||
etl::array<int, 8> dst = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
auto stereo = samples | etl::views::transform([](int s) { return etl::views::repeat(s, 2); }) | etl::views::join;
|
||||
|
||||
etl::copy(stereo.begin(), stereo.end(), dst.begin());
|
||||
|
||||
etl::array<int, 8> expected = {10, 10, 20, 20, 30, 30, 40, 40};
|
||||
for (size_t i = 0; i < dst.size(); ++i)
|
||||
{
|
||||
CHECK_EQUAL(expected[i], dst[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(test_ranges_transform_view_iterator_increment)
|
||||
{
|
||||
auto square = [](int i) -> int
|
||||
@ -2177,6 +2226,36 @@ namespace
|
||||
CHECK_EQUAL(v_out_expected, v_out);
|
||||
}
|
||||
|
||||
TEST(test_ranges_filter_view_non_const_reference_type)
|
||||
{
|
||||
// filter must yield mutable element references when the underlying range
|
||||
// is non-const, matching std::ranges::filter_view.
|
||||
std::vector<int> v = {1, 2, 3, 4};
|
||||
auto fv = v | etl::views::filter([](int i) { return i % 2 == 0; });
|
||||
|
||||
using element_ref = decltype(*fv.begin());
|
||||
|
||||
static_assert(etl::is_same<element_ref, int&>::value, "filter element should be a non-const reference");
|
||||
static_assert(!etl::is_const_v<etl::remove_reference_t<element_ref>>, "filter element should be mutable");
|
||||
|
||||
*fv.begin() = 99; // first even element is v[1]
|
||||
CHECK_EQUAL(99, v[1]);
|
||||
}
|
||||
|
||||
TEST(test_ranges_filter_view_modify_elements)
|
||||
{
|
||||
// Double every even element in place; odd elements are left untouched.
|
||||
std::vector<int> v = {1, 2, 3, 4, 5, 6};
|
||||
|
||||
for (auto& element : v | etl::views::filter([](int i) { return i % 2 == 0; }))
|
||||
{
|
||||
element *= 10;
|
||||
}
|
||||
|
||||
std::vector<int> expected{1, 20, 3, 40, 5, 60};
|
||||
CHECK_EQUAL(expected, v);
|
||||
}
|
||||
|
||||
TEST(test_ranges_join_functional)
|
||||
{
|
||||
using range_type = etl::vector<int, 3>;
|
||||
@ -2432,6 +2511,101 @@ namespace
|
||||
CHECK_EQUAL(result, v_expected);
|
||||
}
|
||||
|
||||
TEST(test_ranges_join_with_view_yields_range_then_join_with)
|
||||
{
|
||||
// join_with must support inner ranges whose iterators yield prvalues
|
||||
// (e.g. repeat_view produced by a type-changing transform), and its declared
|
||||
// reference type must stay consistent with what operator* actually yields.
|
||||
etl::array<int, 3> samples = {10, 20, 30};
|
||||
etl::array<int, 8> dst = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
auto stereo = samples | etl::views::transform([](int s) { return etl::views::repeat(s, 2); }) | etl::views::join_with(0);
|
||||
|
||||
using iterator_type = decltype(stereo.begin());
|
||||
using declared_reference = typename iterator_type::reference;
|
||||
using actual_deref = decltype(*etl::declval<iterator_type&>());
|
||||
static_assert(etl::is_same<declared_reference, actual_deref>::value, "join_with_iterator::reference must match operator*");
|
||||
|
||||
etl::copy(stereo.begin(), stereo.end(), dst.begin());
|
||||
|
||||
etl::array<int, 8> expected = {10, 10, 0, 20, 20, 0, 30, 30};
|
||||
for (size_t i = 0; i < dst.size(); ++i)
|
||||
{
|
||||
CHECK_EQUAL(expected[i], dst[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(test_ranges_join_with_writable_reference)
|
||||
{
|
||||
// For a mutable range pattern, join_with must yield mutable references and be
|
||||
// writable, matching std::ranges::join_with_view (reference is the common
|
||||
// reference of the inner range's and the pattern's references: int&).
|
||||
etl::vector<etl::vector<int, 3>, 2> v{{1, 2, 3}, {4, 5, 6}};
|
||||
etl::vector<int, 2> pattern{0};
|
||||
|
||||
auto jv = etl::views::join_with(v, pattern);
|
||||
|
||||
using element_ref = decltype(*jv.begin());
|
||||
static_assert(etl::is_same<element_ref, int&>::value, "join_with element should be a non-const reference for a mutable range pattern");
|
||||
static_assert(!etl::is_const_v<etl::remove_reference_t<element_ref>>, "join_with element should be mutable");
|
||||
|
||||
// Sequence: 1, 2, 3, [0], 4, 5, 6
|
||||
auto it = jv.begin();
|
||||
*it = 99; // first inner element -> v[0][0]
|
||||
CHECK_EQUAL(99, v[0][0]);
|
||||
|
||||
++it; // 2
|
||||
++it; // 3
|
||||
++it; // pattern separator
|
||||
*it = 77;
|
||||
CHECK_EQUAL(77, pattern[0]);
|
||||
}
|
||||
|
||||
TEST(test_ranges_join_with_single_value_reference_is_const)
|
||||
{
|
||||
// A single-value pattern is backed by a value (single_view) and is therefore
|
||||
// read as const, so the common reference degrades to const int&. (This is a
|
||||
// minor deviation from std::ranges, where a single-element pattern is writable.)
|
||||
etl::vector<etl::vector<int, 3>, 2> v{{1, 2, 3}, {4, 5, 6}};
|
||||
|
||||
auto jv = etl::views::join_with(v, 0);
|
||||
|
||||
using element_ref = decltype(*jv.begin());
|
||||
static_assert(etl::is_same<element_ref, const int&>::value, "single-value join_with pattern should be read as const");
|
||||
|
||||
etl::vector<int, 10> out;
|
||||
for (auto x : jv)
|
||||
{
|
||||
out.push_back(x);
|
||||
}
|
||||
|
||||
etl::vector<int, 10> expected{1, 2, 3, 0, 4, 5, 6};
|
||||
CHECK_EQUAL(expected, out);
|
||||
}
|
||||
|
||||
TEST(test_ranges_join_with_common_value_type)
|
||||
{
|
||||
// When the inner range and the pattern have different (but common-convertible)
|
||||
// element types, the element type is their common type, per
|
||||
// [range.join.with.iterator].
|
||||
etl::vector<etl::vector<int, 3>, 2> v{{1, 2, 3}, {4, 5, 6}};
|
||||
etl::vector<char, 1> pattern{char(0)};
|
||||
|
||||
auto jv = etl::views::join_with(v, pattern);
|
||||
|
||||
using element_type = etl::remove_cvref_t<decltype(*jv.begin())>;
|
||||
static_assert(etl::is_same<int, element_type>::value, "join_with element type should be the common type of inner and pattern");
|
||||
|
||||
etl::vector<int, 10> out;
|
||||
for (auto x : jv)
|
||||
{
|
||||
out.push_back(x);
|
||||
}
|
||||
|
||||
etl::vector<int, 10> expected{1, 2, 3, 0, 4, 5, 6};
|
||||
CHECK_EQUAL(expected, out);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// split_view and views::split tests
|
||||
//*************************************************************************
|
||||
@ -2597,6 +2771,44 @@ namespace
|
||||
CHECK_EQUAL(idx, expected.size());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_split_view_non_const_reference_type)
|
||||
{
|
||||
// split subranges must expose mutable element references when the
|
||||
// underlying range is non-const, matching std::ranges::split_view.
|
||||
std::vector<int> v_in{1, 0, 2};
|
||||
auto sv = etl::ranges::split_view(v_in, 0);
|
||||
|
||||
auto first_seg = *sv.begin();
|
||||
using element_ref = decltype(*first_seg.begin());
|
||||
|
||||
static_assert(etl::is_same<element_ref, int&>::value, "split element should be a non-const reference");
|
||||
static_assert(!etl::is_const_v<etl::remove_reference_t<element_ref>>, "split element should be mutable");
|
||||
|
||||
*first_seg.begin() = 99;
|
||||
CHECK_EQUAL(99, v_in[0]);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_split_view_modify_elements)
|
||||
{
|
||||
// Split on 0, then negate every element of every segment in place.
|
||||
std::vector<int> v_in{1, 2, 0, 3, 0, 4, 5};
|
||||
auto sv = etl::ranges::split_view(v_in, 0);
|
||||
|
||||
for (auto seg : sv)
|
||||
{
|
||||
for (auto& element : seg)
|
||||
{
|
||||
element = -element;
|
||||
}
|
||||
}
|
||||
|
||||
// Delimiters (the zeros) are not part of any segment and stay untouched.
|
||||
std::vector<int> expected{-1, -2, 0, -3, 0, -4, -5};
|
||||
CHECK_EQUAL(expected, v_in);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// lazy_split_view and views::lazy_split tests
|
||||
//*************************************************************************
|
||||
@ -2814,6 +3026,42 @@ namespace
|
||||
CHECK(seg2.empty());
|
||||
}
|
||||
|
||||
TEST(test_ranges_lazy_split_view_non_const_reference_type)
|
||||
{
|
||||
// lazy_split inner-range elements must be mutable references when the
|
||||
// underlying range is non-const, matching std::ranges::lazy_split_view.
|
||||
std::vector<int> v_in{1, 2, 0, 3};
|
||||
auto sv = etl::ranges::lazy_split_view(v_in, 0);
|
||||
|
||||
auto first_seg = *sv.begin();
|
||||
using element_ref = decltype(*first_seg.begin());
|
||||
|
||||
static_assert(etl::is_same<element_ref, int&>::value, "lazy_split element should be a non-const reference");
|
||||
static_assert(!etl::is_const_v<etl::remove_reference_t<element_ref>>, "lazy_split element should be mutable");
|
||||
|
||||
*first_seg.begin() = 99;
|
||||
CHECK_EQUAL(99, v_in[0]);
|
||||
}
|
||||
|
||||
TEST(test_ranges_lazy_split_view_modify_elements)
|
||||
{
|
||||
// Split lazily on 0, then double every element of every segment in place.
|
||||
std::vector<int> v_in{1, 2, 0, 3, 0, 4};
|
||||
auto sv = etl::ranges::lazy_split_view(v_in, 0);
|
||||
|
||||
for (auto seg : sv)
|
||||
{
|
||||
for (auto& element : seg)
|
||||
{
|
||||
element *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Delimiters (the zeros) are skipped and remain unchanged.
|
||||
std::vector<int> expected{2, 4, 0, 6, 0, 8};
|
||||
CHECK_EQUAL(expected, v_in);
|
||||
}
|
||||
|
||||
TEST(test_counted)
|
||||
{
|
||||
{
|
||||
@ -3527,6 +3775,39 @@ namespace
|
||||
CHECK(it2 == ev2.end());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_elements_view_non_const_reference_type)
|
||||
{
|
||||
// elements must yield a mutable reference to the selected tuple element
|
||||
// when the underlying range is non-const, matching std::ranges::elements_view.
|
||||
std::vector<std::pair<int, double>> v = {{1, 1.1}, {2, 2.2}};
|
||||
auto ev = v | etl::views::keys;
|
||||
|
||||
using element_ref = decltype(*ev.begin());
|
||||
|
||||
static_assert(etl::is_same<element_ref, int&>::value, "elements element should be a non-const reference");
|
||||
static_assert(!etl::is_const_v<etl::remove_reference_t<element_ref>>, "elements element should be mutable");
|
||||
|
||||
*ev.begin() = 99;
|
||||
CHECK_EQUAL(99, v[0].first);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_elements_view_modify_elements)
|
||||
{
|
||||
// Write through the values (second element) of each pair in place.
|
||||
std::vector<std::pair<int, int>> v = {{1, 10}, {2, 20}, {3, 30}};
|
||||
|
||||
for (auto& value : v | etl::views::values)
|
||||
{
|
||||
value += 1;
|
||||
}
|
||||
|
||||
CHECK_EQUAL(11, v[0].second);
|
||||
CHECK_EQUAL(21, v[1].second);
|
||||
CHECK_EQUAL(31, v[2].second);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_enumerate_view_basic)
|
||||
{
|
||||
@ -3635,6 +3916,38 @@ namespace
|
||||
CHECK(it == ev.end());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_enumerate_view_non_const_reference_type)
|
||||
{
|
||||
// The value element of the tuple must be a mutable reference when the
|
||||
// underlying range is non-const, matching std::ranges::enumerate_view.
|
||||
std::vector<int> v = {10, 20, 30};
|
||||
auto ev = v | etl::views::enumerate;
|
||||
|
||||
using value_ref = decltype(etl::get<1>(*ev.begin()));
|
||||
|
||||
static_assert(etl::is_same<value_ref, int&>::value, "enumerate value element should be a non-const reference");
|
||||
static_assert(!etl::is_const_v<etl::remove_reference_t<value_ref>>, "enumerate value element should be mutable");
|
||||
|
||||
etl::get<1>(*ev.begin()) = 99;
|
||||
CHECK_EQUAL(99, v[0]);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_enumerate_view_modify_elements)
|
||||
{
|
||||
// Add the index to each element in place via structured bindings.
|
||||
std::vector<int> v = {10, 20, 30};
|
||||
|
||||
for (auto&& [index, value] : v | etl::views::enumerate)
|
||||
{
|
||||
value += static_cast<int>(index);
|
||||
}
|
||||
|
||||
std::vector<int> expected{10, 21, 32};
|
||||
CHECK_EQUAL(expected, v);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_zip_view_basic)
|
||||
{
|
||||
@ -3800,6 +4113,46 @@ namespace
|
||||
CHECK(it == zv.end());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_zip_view_non_const_reference_type)
|
||||
{
|
||||
// Each zipped element must be a mutable reference into its source range
|
||||
// when that range is non-const, matching std::ranges::zip_view.
|
||||
std::vector<int> v1 = {1, 2, 3};
|
||||
std::vector<char> v2 = {'a', 'b', 'c'};
|
||||
auto zv = etl::views::zip(v1, v2);
|
||||
|
||||
using first_ref = decltype(etl::get<0>(*zv.begin()));
|
||||
using second_ref = decltype(etl::get<1>(*zv.begin()));
|
||||
|
||||
static_assert(etl::is_same<first_ref, int&>::value, "zip element 0 should be a non-const reference");
|
||||
static_assert(etl::is_same<second_ref, char&>::value, "zip element 1 should be a non-const reference");
|
||||
|
||||
etl::get<0>(*zv.begin()) = 99;
|
||||
etl::get<1>(*zv.begin()) = 'Z';
|
||||
CHECK_EQUAL(99, v1[0]);
|
||||
CHECK_EQUAL('Z', v2[0]);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_zip_view_modify_elements)
|
||||
{
|
||||
// Write through both zipped ranges in place.
|
||||
std::vector<int> v1 = {1, 2, 3};
|
||||
std::vector<int> v2 = {10, 20, 30};
|
||||
|
||||
for (auto&& [a, b] : etl::views::zip(v1, v2))
|
||||
{
|
||||
a += 1;
|
||||
b *= 2;
|
||||
}
|
||||
|
||||
std::vector<int> expected1{2, 3, 4};
|
||||
std::vector<int> expected2{20, 40, 60};
|
||||
CHECK_EQUAL(expected1, v1);
|
||||
CHECK_EQUAL(expected2, v2);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_zip_transform_view_basic)
|
||||
{
|
||||
@ -4225,6 +4578,41 @@ namespace
|
||||
CHECK(it == av.end());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_adjacent_view_non_const_reference_type)
|
||||
{
|
||||
// Each window element must be a mutable reference into the underlying
|
||||
// range when it is non-const, matching std::ranges::adjacent_view.
|
||||
std::vector<int> v = {1, 2, 3};
|
||||
auto av = etl::views::adjacent<2>(v);
|
||||
|
||||
using first_ref = decltype(etl::get<0>(*av.begin()));
|
||||
using second_ref = decltype(etl::get<1>(*av.begin()));
|
||||
|
||||
static_assert(etl::is_same<first_ref, int&>::value, "adjacent element 0 should be a non-const reference");
|
||||
static_assert(etl::is_same<second_ref, int&>::value, "adjacent element 1 should be a non-const reference");
|
||||
|
||||
etl::get<0>(*av.begin()) = 99;
|
||||
CHECK_EQUAL(99, v[0]);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_adjacent_view_modify_elements)
|
||||
{
|
||||
// Negate the first element of every adjacent pair in place. Each element
|
||||
// 0..n-2 is the start of exactly one window; the last element is only a
|
||||
// second member, so it is left unchanged.
|
||||
std::vector<int> v = {1, 2, 3, 4};
|
||||
|
||||
for (auto&& window : etl::views::adjacent<2>(v))
|
||||
{
|
||||
etl::get<0>(window) = -etl::get<0>(window);
|
||||
}
|
||||
|
||||
std::vector<int> expected{-1, -2, -3, 4};
|
||||
CHECK_EQUAL(expected, v);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_adjacent_transform_view_basic_sum)
|
||||
{
|
||||
@ -4470,6 +4858,25 @@ namespace
|
||||
CHECK_EQUAL(expected.size(), idx);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_chunk_view_rvalue_range_owning_view)
|
||||
{
|
||||
// Chunking an rvalue range stores it in an owning_view (by value).
|
||||
// The owning_view's const begin()/end() must still yield the mutable
|
||||
// 'iterator' rather than a 'const_iterator', otherwise instantiation fails.
|
||||
auto cv = etl::ranges::views::chunk(std::vector<int>{1, 2, 3, 4, 5, 6, 7}, 3);
|
||||
|
||||
std::vector<std::vector<int>> expected{{1, 2, 3}, {4, 5, 6}, {7}};
|
||||
size_t idx = 0;
|
||||
for (auto chunk : cv)
|
||||
{
|
||||
std::vector<int> actual(chunk.begin(), chunk.end());
|
||||
CHECK_EQUAL(expected[idx], actual);
|
||||
++idx;
|
||||
}
|
||||
CHECK_EQUAL(expected.size(), idx);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_chunk_view_remainder)
|
||||
{
|
||||
@ -4658,6 +5065,81 @@ namespace
|
||||
CHECK_EQUAL(expected.size(), idx);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_chunk_view_non_const_reference_type)
|
||||
{
|
||||
// The chunk elements must expose a non-const iterator/reference when the
|
||||
// underlying range is non-const, matching std::ranges::chunk_view.
|
||||
std::vector<int> v = {1, 2, 3, 4};
|
||||
auto cv = etl::ranges::chunk_view(v, 2);
|
||||
|
||||
auto first_chunk = *cv.begin();
|
||||
using element_ref = decltype(*first_chunk.begin());
|
||||
|
||||
static_assert(etl::is_same<element_ref, int&>::value, "chunk element should be a non-const reference");
|
||||
static_assert(!etl::is_const_v<etl::remove_reference_t<element_ref>>, "chunk element should be mutable");
|
||||
|
||||
// Prove it at runtime by writing through the chunk.
|
||||
*first_chunk.begin() = 99;
|
||||
CHECK_EQUAL(99, v[0]);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_chunk_view_modify_elements)
|
||||
{
|
||||
// Write through the chunks and verify the underlying range is mutated.
|
||||
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7};
|
||||
auto cv = etl::ranges::chunk_view(v, 3);
|
||||
|
||||
for (auto chunk : cv)
|
||||
{
|
||||
for (auto& element : chunk)
|
||||
{
|
||||
element *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> expected{2, 4, 6, 8, 10, 12, 14};
|
||||
CHECK_EQUAL(expected, v);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_chunk_view_modify_elements_pipe)
|
||||
{
|
||||
// Write through chunks produced via the pipe syntax.
|
||||
std::vector<int> v = {10, 20, 30, 40, 50};
|
||||
|
||||
for (auto chunk : v | etl::views::chunk(2))
|
||||
{
|
||||
for (auto& element : chunk)
|
||||
{
|
||||
element += 1;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> expected{11, 21, 31, 41, 51};
|
||||
CHECK_EQUAL(expected, v);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_chunk_view_modify_elements_etl_vector)
|
||||
{
|
||||
// Non-const access also works with ETL containers.
|
||||
etl::vector<int, 10> v = {1, 2, 3, 4, 5, 6};
|
||||
auto cv = etl::ranges::chunk_view(v, 2);
|
||||
|
||||
for (auto chunk : cv)
|
||||
{
|
||||
for (auto& element : chunk)
|
||||
{
|
||||
element = -element;
|
||||
}
|
||||
}
|
||||
|
||||
etl::vector<int, 10> expected = {-1, -2, -3, -4, -5, -6};
|
||||
CHECK(expected == v);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// slide_view tests
|
||||
//*************************************************************************
|
||||
@ -4858,6 +5340,43 @@ namespace
|
||||
CHECK_EQUAL(expected.size(), idx);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_slide_view_non_const_reference_type)
|
||||
{
|
||||
// A slide window must expose mutable element references when the
|
||||
// underlying range is non-const, matching std::ranges::slide_view.
|
||||
std::vector<int> v = {1, 2, 3, 4};
|
||||
auto sv = etl::ranges::slide_view(v, 2);
|
||||
|
||||
auto first_window = *sv.begin();
|
||||
using element_ref = decltype(*first_window.begin());
|
||||
|
||||
static_assert(etl::is_same<element_ref, int&>::value, "slide window element should be a non-const reference");
|
||||
static_assert(!etl::is_const_v<etl::remove_reference_t<element_ref>>, "slide window element should be mutable");
|
||||
|
||||
*first_window.begin() = 99;
|
||||
CHECK_EQUAL(99, v[0]);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_slide_view_modify_elements)
|
||||
{
|
||||
// Each underlying element is the start of exactly one window, so writing
|
||||
// through the first element of every window doubles all but the tail.
|
||||
std::vector<int> v = {1, 2, 3, 4, 5};
|
||||
auto sv = etl::ranges::slide_view(v, 2);
|
||||
|
||||
for (auto window : sv)
|
||||
{
|
||||
auto it = window.begin();
|
||||
*it *= 2;
|
||||
}
|
||||
|
||||
// Windows start at indices 0..3, so the final element is untouched.
|
||||
std::vector<int> expected{2, 4, 6, 8, 5};
|
||||
CHECK_EQUAL(expected, v);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// chunk_by_view tests
|
||||
//*************************************************************************
|
||||
@ -5070,6 +5589,45 @@ namespace
|
||||
CHECK_EQUAL(expected.size(), idx);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_chunk_by_view_non_const_reference_type)
|
||||
{
|
||||
// chunk_by chunks must expose mutable element references when the
|
||||
// underlying range is non-const, matching std::ranges::chunk_by_view.
|
||||
std::vector<int> v = {1, 1, 2};
|
||||
auto cv = etl::ranges::chunk_by_view(v, [](int a, int b) { return a == b; });
|
||||
|
||||
auto first_chunk = *cv.begin();
|
||||
using element_ref = decltype(*first_chunk.begin());
|
||||
|
||||
static_assert(etl::is_same<element_ref, int&>::value, "chunk_by element should be a non-const reference");
|
||||
static_assert(!etl::is_const_v<etl::remove_reference_t<element_ref>>, "chunk_by element should be mutable");
|
||||
|
||||
*first_chunk.begin() = 99;
|
||||
CHECK_EQUAL(99, v[0]);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_chunk_by_view_modify_elements)
|
||||
{
|
||||
// Group consecutive equal elements, then negate every element in place.
|
||||
// Each chunk boundary is computed from the chunk start forwards before
|
||||
// that chunk's elements are modified, so the grouping stays correct.
|
||||
std::vector<int> v = {1, 1, 2, 2, 2, 3};
|
||||
auto cv = etl::ranges::chunk_by_view(v, [](int a, int b) { return a == b; });
|
||||
|
||||
for (auto chunk : cv)
|
||||
{
|
||||
for (auto& element : chunk)
|
||||
{
|
||||
element = -element;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> expected{-1, -1, -2, -2, -2, -3};
|
||||
CHECK_EQUAL(expected, v);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// stride_view tests
|
||||
//*************************************************************************
|
||||
@ -5341,6 +5899,40 @@ namespace
|
||||
CHECK_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_stride_view_non_const_reference_type)
|
||||
{
|
||||
// stride must yield mutable element references when the underlying range
|
||||
// is non-const, matching std::ranges::stride_view.
|
||||
std::vector<int> v = {1, 2, 3, 4};
|
||||
auto sv = etl::ranges::stride_view(v, 2);
|
||||
|
||||
using element_ref = decltype(*sv.begin());
|
||||
|
||||
static_assert(etl::is_same<element_ref, int&>::value, "stride element should be a non-const reference");
|
||||
static_assert(!etl::is_const_v<etl::remove_reference_t<element_ref>>, "stride element should be mutable");
|
||||
|
||||
*sv.begin() = 99;
|
||||
CHECK_EQUAL(99, v[0]);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_stride_view_modify_elements)
|
||||
{
|
||||
// Stride by 2 and double every visited element in place.
|
||||
std::vector<int> v = {1, 2, 3, 4, 5, 6};
|
||||
auto sv = etl::ranges::stride_view(v, 2);
|
||||
|
||||
for (auto& element : sv)
|
||||
{
|
||||
element *= 2;
|
||||
}
|
||||
|
||||
// Elements at indices 0, 2, 4 are visited (1, 3, 5 -> 2, 6, 10).
|
||||
std::vector<int> expected{2, 2, 6, 4, 10, 6};
|
||||
CHECK_EQUAL(expected, v);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_ranges_cartesian_product_view_basic)
|
||||
{
|
||||
@ -5776,6 +6368,37 @@ namespace
|
||||
CHECK_EQUAL(20, v_out[1]);
|
||||
CHECK_EQUAL(30, v_out[2]);
|
||||
}
|
||||
|
||||
TEST(test_ranges_to_input_view_non_const_reference_type)
|
||||
{
|
||||
// to_input_view must preserve the underlying range's mutable references,
|
||||
// matching std::ranges::as_input_view.
|
||||
std::vector<int> v_in{1, 2, 3};
|
||||
auto iv = etl::ranges::to_input_view(v_in);
|
||||
|
||||
using element_ref = decltype(*iv.begin());
|
||||
|
||||
static_assert(etl::is_same<element_ref, int&>::value, "to_input element should be a non-const reference");
|
||||
static_assert(!etl::is_const_v<etl::remove_reference_t<element_ref>>, "to_input element should be mutable");
|
||||
|
||||
*iv.begin() = 99;
|
||||
CHECK_EQUAL(99, v_in[0]);
|
||||
}
|
||||
|
||||
TEST(test_ranges_to_input_view_modify_elements)
|
||||
{
|
||||
// Write through every element of the input view in place.
|
||||
std::vector<int> v_in{1, 2, 3, 4};
|
||||
auto iv = etl::ranges::to_input_view(v_in);
|
||||
|
||||
for (auto& element : iv)
|
||||
{
|
||||
element *= 10;
|
||||
}
|
||||
|
||||
std::vector<int> expected{10, 20, 30, 40};
|
||||
CHECK_EQUAL(expected, v_in);
|
||||
}
|
||||
}
|
||||
#include "etl/private/diagnostic_pop.h"
|
||||
} // namespace
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user