Fix etl::rotate (#1327)

Per the C++ standard, std::rotate returns first + (last - middle):

* When first == middle, return last
* When middle == last, return first
This commit is contained in:
Roland Reichwein 2026-03-06 11:11:46 +01:00 committed by GitHub
parent 7bac1d02f7
commit a8ebe338f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 117 additions and 4 deletions

View File

@ -1193,7 +1193,12 @@ namespace etl
typename etl::enable_if<etl::is_random_access_iterator<TIterator>::value, TIterator>::type
rotate_general(TIterator first, TIterator middle, TIterator last)
{
if (first == middle || middle == last)
if (first == middle)
{
return last;
}
if (middle == last)
{
return first;
}
@ -1242,7 +1247,12 @@ namespace etl
typename etl::enable_if<etl::is_random_access_iterator<TIterator>::value, TIterator>::type
rotate_general(TIterator first, TIterator middle, TIterator last)
{
if (first == middle || middle == last)
if (first == middle)
{
return last;
}
if (middle == last)
{
return first;
}
@ -1292,7 +1302,12 @@ namespace etl
typename etl::enable_if<etl::is_bidirectional_iterator<TIterator>::value, TIterator>::type
rotate_general(TIterator first, TIterator middle, TIterator last)
{
if (first == middle || middle == last)
if (first == middle)
{
return last;
}
if (middle == last)
{
return first;
}
@ -1314,7 +1329,12 @@ namespace etl
typename etl::enable_if<etl::is_forward_iterator<TIterator>::value, TIterator>::type
rotate_general(TIterator first, TIterator middle, TIterator last)
{
if (first == middle || middle == last)
if (first == middle)
{
return last;
}
if (middle == last)
{
return first;
}

View File

@ -113,6 +113,12 @@ struct non_random_iterator : public etl::iterator<ETL_OR_STD::bidirectional_iter
T* ptr;
};
template <typename T>
bool operator ==(const non_random_iterator<T>& lhs, const non_random_iterator<T>& rhs)
{
return lhs.ptr == rhs.ptr;
}
template <typename T>
bool operator !=(const non_random_iterator<T>& lhs, const non_random_iterator<T>& rhs)
{

View File

@ -1236,6 +1236,93 @@ namespace
}
}
//*************************************************************************
TEST(rotate_return_value)
{
// Verify that etl::rotate returns the same iterator as std::rotate
// in all cases, including the degenerate first==middle and middle==last cases.
std::vector<int> initial_data = { 1, 2, 3, 4, 5 };
for (size_t i = 0UL; i <= initial_data.size(); ++i)
{
std::vector<int> data1(initial_data);
std::vector<int> data2(initial_data);
auto std_result = std::rotate(data1.data(), data1.data() + i, data1.data() + data1.size());
auto etl_result = etl::rotate(data2.data(), data2.data() + i, data2.data() + data2.size());
// Check that the return value offset matches
ptrdiff_t std_offset = std_result - data1.data();
ptrdiff_t etl_offset = etl_result - data2.data();
CHECK_EQUAL(std_offset, etl_offset);
}
// Explicitly test first == middle (empty left half): should return last
{
std::vector<int> data = { 1, 2, 3 };
auto result = etl::rotate(data.data(), data.data(), data.data() + data.size());
CHECK(result == data.data() + data.size());
}
// Explicitly test middle == last (empty right half): should return first
{
std::vector<int> data = { 1, 2, 3 };
auto result = etl::rotate(data.data(), data.data() + data.size(), data.data() + data.size());
CHECK(result == data.data());
}
}
//*************************************************************************
TEST(rotate_return_value_non_random_iterator)
{
// Verify that etl::rotate returns the correct iterator when called with
// non-random (bidirectional) iterators, exercising rotate_general for
// bidirectional iterators rather than the random-access overload.
std::vector<int> initial_data = { 1, 2, 3, 4, 5 };
for (size_t i = 0UL; i <= initial_data.size(); ++i)
{
std::vector<int> data1(initial_data);
std::vector<int> data2(initial_data);
auto std_result = std::rotate(data1.data(), data1.data() + i, data1.data() + data1.size());
non_random_iterator<int> nr_first(data2.data());
non_random_iterator<int> nr_middle(data2.data() + i);
non_random_iterator<int> nr_last(data2.data() + data2.size());
auto etl_result = etl::rotate(nr_first, nr_middle, nr_last);
// Check that the data was rotated correctly
bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2));
CHECK(isEqual);
// Check that the return value offset matches
ptrdiff_t std_offset = std_result - data1.data();
ptrdiff_t etl_offset = etl_result.ptr - data2.data();
CHECK_EQUAL(std_offset, etl_offset);
}
// Explicitly test first == middle (empty left half): should return last
{
std::vector<int> data = { 1, 2, 3 };
non_random_iterator<int> nr_first(data.data());
non_random_iterator<int> nr_middle(data.data());
non_random_iterator<int> nr_last(data.data() + data.size());
auto result = etl::rotate(nr_first, nr_middle, nr_last);
CHECK(result.ptr == data.data() + data.size());
}
// Explicitly test middle == last (empty right half): should return first
{
std::vector<int> data = { 1, 2, 3 };
non_random_iterator<int> nr_first(data.data());
non_random_iterator<int> nr_middle(data.data() + data.size());
non_random_iterator<int> nr_last(data.data() + data.size());
auto result = etl::rotate(nr_first, nr_middle, nr_last);
CHECK(result.ptr == data.data());
}
}
//*************************************************************************
TEST(any_of)
{