From a8ebe338f80ee25fcc79d53cdbc8140bf208fae1 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Fri, 6 Mar 2026 11:11:46 +0100 Subject: [PATCH 1/5] Fix etl::rotate (#1327) Per the C++ standard, std::rotate returns first + (last - middle): * When first == middle, return last * When middle == last, return first --- include/etl/algorithm.h | 28 +++++++++-- test/iterators_for_unit_tests.h | 6 +++ test/test_algorithm.cpp | 87 +++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 4 deletions(-) diff --git a/include/etl/algorithm.h b/include/etl/algorithm.h index 74fbb8ed..8ee795a8 100644 --- a/include/etl/algorithm.h +++ b/include/etl/algorithm.h @@ -1193,7 +1193,12 @@ namespace etl typename etl::enable_if::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::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::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::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; } diff --git a/test/iterators_for_unit_tests.h b/test/iterators_for_unit_tests.h index 16085f87..82d28c6c 100644 --- a/test/iterators_for_unit_tests.h +++ b/test/iterators_for_unit_tests.h @@ -113,6 +113,12 @@ struct non_random_iterator : public etl::iterator +bool operator ==(const non_random_iterator& lhs, const non_random_iterator& rhs) +{ + return lhs.ptr == rhs.ptr; +} + template bool operator !=(const non_random_iterator& lhs, const non_random_iterator& rhs) { diff --git a/test/test_algorithm.cpp b/test/test_algorithm.cpp index abdab9d5..4dd01e9f 100644 --- a/test/test_algorithm.cpp +++ b/test/test_algorithm.cpp @@ -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 initial_data = { 1, 2, 3, 4, 5 }; + + for (size_t i = 0UL; i <= initial_data.size(); ++i) + { + std::vector data1(initial_data); + std::vector 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 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 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 initial_data = { 1, 2, 3, 4, 5 }; + + for (size_t i = 0UL; i <= initial_data.size(); ++i) + { + std::vector data1(initial_data); + std::vector data2(initial_data); + + auto std_result = std::rotate(data1.data(), data1.data() + i, data1.data() + data1.size()); + + non_random_iterator nr_first(data2.data()); + non_random_iterator nr_middle(data2.data() + i); + non_random_iterator 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 data = { 1, 2, 3 }; + non_random_iterator nr_first(data.data()); + non_random_iterator nr_middle(data.data()); + non_random_iterator 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 data = { 1, 2, 3 }; + non_random_iterator nr_first(data.data()); + non_random_iterator nr_middle(data.data() + data.size()); + non_random_iterator nr_last(data.data() + data.size()); + auto result = etl::rotate(nr_first, nr_middle, nr_last); + CHECK(result.ptr == data.data()); + } + } + //************************************************************************* TEST(any_of) { From f96510bd79dca27c7a9445b56d3625d85e219843 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Mon, 2 Mar 2026 09:59:08 +0000 Subject: [PATCH 2/5] Added missing files from VS2022 project --- test/vs2022/etl.vcxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/vs2022/etl.vcxproj b/test/vs2022/etl.vcxproj index 6396b761..2b069673 100644 --- a/test/vs2022/etl.vcxproj +++ b/test/vs2022/etl.vcxproj @@ -3552,6 +3552,7 @@ + @@ -10362,6 +10363,7 @@ + From 4ad6126ec5fdd7c27ed359ee09de2482e922bebb Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Fri, 6 Mar 2026 21:08:31 +0100 Subject: [PATCH 3/5] Fix greater_equal and less_equal (#1331) --- include/etl/functional.h | 10 +++--- test/test_functional.cpp | 70 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/include/etl/functional.h b/include/etl/functional.h index 0f0172ec..73b1685b 100644 --- a/include/etl/functional.h +++ b/include/etl/functional.h @@ -224,9 +224,9 @@ namespace etl typedef int is_transparent; template - constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(static_cast(lhs) < static_cast(rhs)) + constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(!(static_cast(rhs) < static_cast(lhs))) { - return !(static_cast(lhs) < static_cast(rhs)); + return !(static_cast(rhs) < static_cast(lhs)); } }; #endif @@ -250,7 +250,7 @@ namespace etl typedef int is_transparent; template - constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(static_cast(lhs) < static_cast(rhs)) + constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(static_cast(rhs) < static_cast(lhs)) { return static_cast(rhs) < static_cast(lhs); } @@ -276,9 +276,9 @@ namespace etl typedef int is_transparent; template - constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(static_cast(lhs) < static_cast(rhs)) + constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(!(static_cast(lhs) < static_cast(rhs))) { - return static_cast(rhs) < static_cast(lhs); + return !(static_cast(lhs) < static_cast(rhs)); } }; #endif diff --git a/test/test_functional.cpp b/test/test_functional.cpp index 1204d330..5bbe6beb 100644 --- a/test/test_functional.cpp +++ b/test/test_functional.cpp @@ -85,6 +85,24 @@ namespace mutable std::string result; }; +#if ETL_USING_CPP11 + // Lightweight type used to verify transparent heterogeneous comparison. + // Only operator<(int, Wrapper) is defined; operator<(Wrapper, int) is + // intentionally absent. less_equal is implemented as + // !(rhs < lhs), so less_equal{}(Wrapper, int) needs + // operator<(int, Wrapper) which IS provided. + struct Wrapper + { + int value; + constexpr explicit Wrapper(int v) : value(v) {} + }; + + // int < Wrapper -- defined + constexpr bool operator<(int lhs, const Wrapper& rhs) { return lhs < rhs.value; } + + // Wrapper < int -- intentionally NOT defined +#endif + SUITE(test_functional) { //************************************************************************* @@ -101,6 +119,32 @@ namespace CHECK((compare>(1, 2))); CHECK(!(compare>(2, 1))); CHECK((compare>(1, 1))); + +#if ETL_USING_CPP11 + CHECK((compare>(1, 2))); + CHECK(!(compare>(2, 1))); + CHECK((compare>(1, 1))); +#endif + } + + //************************************************************************* + TEST(test_less_equal_void_heterogeneous) + { +#if ETL_USING_CPP11 + // less_equal{}(lhs, rhs) is !(rhs < lhs). + // With only operator<(int, Wrapper) defined, we can call + // less_equal{}(Wrapper, int) because the implementation + // evaluates !(int < Wrapper). + + // Wrapper(1) <= 2 → !(2 < Wrapper(1)) → !(2 < 1) → !false → true + CHECK((etl::less_equal{}(Wrapper(1), 2))); + + // Wrapper(2) <= 1 → !(1 < Wrapper(2)) → !(1 < 2) → !true → false + CHECK(!(etl::less_equal{}(Wrapper(2), 1))); + + // Wrapper(3) <= 3 → !(3 < Wrapper(3)) → !(3 < 3) → !false → true + CHECK((etl::less_equal{}(Wrapper(3), 3))); +#endif } //************************************************************************* @@ -117,6 +161,32 @@ namespace CHECK(!(compare>(1, 2))); CHECK((compare>(2, 1))); CHECK((compare>(1, 1))); + +#if ETL_USING_CPP11 + CHECK(!(compare>(1, 2))); + CHECK((compare>(2, 1))); + CHECK((compare>(1, 1))); +#endif + } + + //************************************************************************* + TEST(test_greater_equal_void_heterogeneous) + { +#if ETL_USING_CPP11 + // greater_equal{}(lhs, rhs) is !(lhs < rhs). + // With only operator<(int, Wrapper) defined, we can call + // greater_equal{}(int, Wrapper) because the implementation + // evaluates !(int < Wrapper). + + // 2 >= Wrapper(1) → !(2 < Wrapper(1)) → !(2 < 1) → !false → true + CHECK((etl::greater_equal{}(2, Wrapper(1)))); + + // 1 >= Wrapper(2) → !(1 < Wrapper(2)) → !(1 < 2) → !true → false + CHECK(!(etl::greater_equal{}(1, Wrapper(2)))); + + // 3 >= Wrapper(3) → !(3 < Wrapper(3)) → !(3 < 3) → !false → true + CHECK((etl::greater_equal{}(3, Wrapper(3)))); +#endif } //************************************************************************* From 7329efc7dfde1116de974899558be5345ec0e5c2 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Fri, 6 Mar 2026 21:29:58 +0100 Subject: [PATCH 4/5] Align comparison operators (#1330) In functional.h, the comparison operators for equal_to and not_equal_to mismatch between the actual comparison execution and the type inference for the return type. This change adjusts it by using the same operator==() in the return type inference as used in the comparison execution. Co-authored-by: John Wellbelove --- include/etl/functional.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/etl/functional.h b/include/etl/functional.h index 73b1685b..3c1c55b4 100644 --- a/include/etl/functional.h +++ b/include/etl/functional.h @@ -303,7 +303,7 @@ namespace etl typedef int is_transparent; template - constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(static_cast(lhs) < static_cast(rhs)) + constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(static_cast(lhs) == static_cast(rhs)) { return static_cast(lhs) == static_cast(rhs); } @@ -329,7 +329,7 @@ namespace etl typedef int is_transparent; template - constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(static_cast(lhs) < static_cast(rhs)) + constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(!(static_cast(lhs) == static_cast(rhs))) { return !(static_cast(lhs) == static_cast(rhs)); } From 67ecc8e11e2be245c15f03b231108b1adbd5882e Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Fri, 6 Mar 2026 22:34:54 +0100 Subject: [PATCH 5/5] Add missing tests (#1321) * Add missing tests * Typo fixes --------- Co-authored-by: John Wellbelove --- include/etl/algorithm.h | 2 +- include/etl/array.h | 6 +++--- include/etl/functional.h | 2 +- include/etl/numeric.h | 2 +- test/CMakeLists.txt | 9 +++++++++ test/test_etl_assert.cpp | 2 ++ 6 files changed, 17 insertions(+), 6 deletions(-) diff --git a/include/etl/algorithm.h b/include/etl/algorithm.h index 8ee795a8..7317889c 100644 --- a/include/etl/algorithm.h +++ b/include/etl/algorithm.h @@ -34,7 +34,7 @@ SOFTWARE. #define ETL_ALGORITHM_INCLUDED ///\defgroup algorithm algorithm -/// Including reverse engineered algorithms from C++ 0x11, 0x14, 0x17 +/// Including reverse engineered algorithms from C++11, 14, 17 /// Additional new variants of certain algorithms. ///\ingroup utilities diff --git a/include/etl/array.h b/include/etl/array.h index f1624209..1f2f5044 100644 --- a/include/etl/array.h +++ b/include/etl/array.h @@ -46,7 +46,7 @@ SOFTWARE. #include ///\defgroup array array -/// A replacement for std::array if you haven't got C++0x11. +/// A replacement for std::array if you haven't got C++11. ///\ingroup containers namespace etl @@ -81,7 +81,7 @@ namespace etl //*************************************************************************** ///\ingroup array - /// A replacement for std::array if you haven't got C++0x11. + /// A replacement for std::array if you haven't got C++11. //*************************************************************************** template class array @@ -646,7 +646,7 @@ namespace etl //*************************************************************************** ///\ingroup array - /// A replacement for std::array if you haven't got C++0x11. + /// A replacement for std::array if you haven't got C++11. /// Specialisation for zero sized array. //*************************************************************************** template diff --git a/include/etl/functional.h b/include/etl/functional.h index 3c1c55b4..e313325e 100644 --- a/include/etl/functional.h +++ b/include/etl/functional.h @@ -43,7 +43,7 @@ SOFTWARE. namespace etl { //*************************************************************************** - /// A definition of reference_wrapper for those that don't have C++ 0x11 support. + /// A definition of reference_wrapper for those that don't have C++11 support. ///\ingroup reference //*************************************************************************** template diff --git a/include/etl/numeric.h b/include/etl/numeric.h index dbb43696..3283b338 100644 --- a/include/etl/numeric.h +++ b/include/etl/numeric.h @@ -47,7 +47,7 @@ namespace etl { //*************************************************************************** /// iota - /// Reverse engineered version of std::iota for non C++ 0x11 compilers. + /// Reverse engineered version of std::iota for non C++11 compilers. /// Fills a range of elements with sequentially increasing values starting with value. ///\param first An iterator to the first position to fill. ///\param last An iterator to the last + 1 position. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2fdfc71c..2bec7847 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -86,6 +86,7 @@ add_executable(etl_tests test_closure.cpp test_closure_constexpr.cpp test_compare.cpp + test_concepts.cpp test_constant.cpp test_const_map.cpp test_const_map_constexpr.cpp @@ -172,6 +173,7 @@ add_executable(etl_tests test_endian.cpp test_enum_type.cpp test_error_handler.cpp + test_etl_assert.cpp test_etl_traits.cpp test_exception.cpp test_expected.cpp @@ -183,6 +185,7 @@ add_executable(etl_tests test_flat_multiset.cpp test_flat_set.cpp test_fnv_1.cpp + test_format.cpp test_format_spec.cpp test_forward_list.cpp test_forward_list_shared_pool.cpp @@ -194,10 +197,12 @@ add_executable(etl_tests test_hash.cpp test_hfsm.cpp test_hfsm_recurse_to_inner_state_on_start.cpp + test_hfsm_transition_on_enter.cpp test_histogram.cpp test_index_of_type.cpp test_indirect_vector.cpp test_indirect_vector_external_buffer.cpp + test_inplace_function.cpp test_instance_count.cpp test_integral_limits.cpp test_intrusive_forward_list.cpp @@ -206,7 +211,9 @@ add_executable(etl_tests test_intrusive_queue.cpp test_intrusive_stack.cpp test_invert.cpp + test_invoke.cpp test_io_port.cpp + test_is_invocable.cpp test_iterator.cpp test_jenkins.cpp test_largest.cpp @@ -259,6 +266,7 @@ add_executable(etl_tests test_pool.cpp test_pool_external_buffer.cpp test_priority_queue.cpp + test_print.cpp test_pseudo_moving_average.cpp test_quantize.cpp test_queue.cpp @@ -286,6 +294,7 @@ add_executable(etl_tests test_scaled_rounding.cpp test_set.cpp test_shared_message.cpp + test_signal.cpp test_singleton.cpp test_singleton_base.cpp test_smallest.cpp diff --git a/test/test_etl_assert.cpp b/test/test_etl_assert.cpp index e927a0b2..26fcced4 100644 --- a/test/test_etl_assert.cpp +++ b/test/test_etl_assert.cpp @@ -34,6 +34,8 @@ SOFTWARE. #include "etl/error_handler.h" #include "etl/exception.h" +#include + namespace { class TestException1 : public etl::exception