From 86c3dad1e93627b63af33798b00ca134459fa2ad Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Wed, 15 Jul 2026 08:24:36 +0100 Subject: [PATCH 1/7] Remove the documentation page for type_id, as it doesn't exist in the ETL. (#1502) Co-authored-by: John Wellbelove --- docs/utilities/type-id.md | 112 -------------------------------------- 1 file changed, 112 deletions(-) delete mode 100644 docs/utilities/type-id.md diff --git a/docs/utilities/type-id.md b/docs/utilities/type-id.md deleted file mode 100644 index 1686c1bd..00000000 --- a/docs/utilities/type-id.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -title: "type_id" ---- - -{{< callout type="info">}} - Header: `type_id.h` - Since: `TBC` -{{< /callout >}} - -Below is a concise, technical overview of the `etl::type_id` facility, suitable for design documentation or code review context. - -## Overview: -`etl::type_id` is a lightweight, RTTI-free mechanism for uniquely identifying C++ types at runtime. It is designed for environments where standard RTTI (`typeid`, `std::type_info`) is unavailable, undesirable, or too costly—such as embedded systems or performance-constrained platforms. - -The implementation guarantees uniqueness per type within a program image while remaining constexpr-friendly and requiring no dynamic allocation. - -## Design Concept - -The core idea is to associate each distinct type `T` with the address of a unique static object. That address serves as the type’s identifier. - -This is achieved through: - -```cpp -template -struct type_id_anchor -{ - static char value; -}; -``` - -Each instantiation of `type_id_anchor` owns its own `static char value`, and the address of that variable is guaranteed to be unique for each distinct `T`. The address is stable for the lifetime of the program. - -## Key Components - -### Type Normalization - -When requesting a type ID via `type_id::get()`, the type is first normalised: - -```cpp -using type = typename etl::remove_cvref::type; -``` - -This ensures that: - -* `T`, `const T`, `T&`, and `T&&` all map to the same `type_id` -* Only the underlying type identity matters - -### Type ID Generation - -```cpp -template -static ETL_CONSTEXPR type_id get() ETL_NOEXCEPT; -``` - -* Returns a `type_id` that uniquely represents `T` -* Internally stores the address of `type_id_anchor::value` -* Requires no RTTI, no dynamic memory, and no runtime registration - -### Invalid Type ID - -```cpp -static ETL_CONSTEXPR type_id invalid_id() ETL_NOEXCEPT; -``` - -* Represents an invalid or uninitialised type ID -* Implemented as a null pointer (`id == nullptr`) -* Default constructor produces this state - -## `type_id` Class Semantics - -### Storage - -```cpp -const void* id; -``` - -* The identifier is stored as a pointer -* Comparison is purely pointer comparison (fast and deterministic) - -### Supported Operations - -| Operation | Behavior | -| ------------------ | -------------------------------------------------- | -| Equality (`==`) | True if both type IDs refer to the same type | -| Inequality (`!=`) | Logical negation of equality | -| Ordering (`<`) | Pointer comparison (useful for ordered containers) | -| Boolean conversion | `true` if valid, `false` if invalid | -| Integer conversion | Explicit conversion to `intptr_t` / `uintptr_t` | - -### Copy and Assignment - -* Copyable and assignable -* Copy semantics preserve identity (pointer value) - -## Guarantees and Properties - -* **Uniqueness**: Each distinct type has exactly one unique `type_id` -* **Stability**: Type IDs remain valid for the entire program lifetime -* **Zero overhead**: No dynamic allocation, no lookup tables -* **RTTI-free**: Suitable for builds with RTTI disabled -* **Comparable**: Can be used as keys in associative containers - -## Typical Use Cases - -* Type-safe containers (e.g., variant, any, message buses) -* Plugin or component systems -* Compile-time / runtime type discrimination in embedded systems -* Replacement for `std::type_index` in restricted environments - -## Summary - -`etl::type_id` provides a robust, minimal, and efficient alternative to C++ RTTI by encoding type identity as the address of a unique static object. Its design is particularly well-suited to embedded and low-level systems where determinism, performance, and binary size are critical. From ea8eda014772828e4f239659134b9003d66e1a17 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Wed, 15 Jul 2026 09:26:50 +0200 Subject: [PATCH 2/7] Remove redundant reinterpret_cast (#1508) --- include/etl/string.h | 20 ++++++++++---------- include/etl/u16string.h | 20 ++++++++++---------- include/etl/u32string.h | 20 ++++++++++---------- include/etl/u8string.h | 20 ++++++++++---------- include/etl/wstring.h | 20 ++++++++++---------- 5 files changed, 50 insertions(+), 50 deletions(-) diff --git a/include/etl/string.h b/include/etl/string.h index 33e3d2d8..3f46a815 100644 --- a/include/etl/string.h +++ b/include/etl/string.h @@ -80,7 +80,7 @@ namespace etl /// Constructor. //************************************************************************* string() - : istring(reinterpret_cast(&buffer), MAX_SIZE) + : istring(buffer, MAX_SIZE) { this->initialise(); } @@ -90,7 +90,7 @@ namespace etl ///\param other The other string. //************************************************************************* string(const etl::string& other) - : istring(reinterpret_cast(&buffer), MAX_SIZE) + : istring(buffer, MAX_SIZE) { this->assign(other); } @@ -100,7 +100,7 @@ namespace etl ///\param other The other istring. //************************************************************************* string(const etl::istring& other) - : istring(reinterpret_cast(&buffer), MAX_SIZE) + : istring(buffer, MAX_SIZE) { this->assign(other); } @@ -112,7 +112,7 @@ namespace etl ///\param length The number of characters. Default = npos. //************************************************************************* string(const etl::istring& other, size_t position, size_t length = npos) - : istring(reinterpret_cast(&buffer), MAX_SIZE) + : istring(buffer, MAX_SIZE) { ETL_ASSERT(position < other.size(), ETL_ERROR(string_out_of_bounds)); @@ -124,7 +124,7 @@ namespace etl ///\param text The initial text of the string. //************************************************************************* ETL_EXPLICIT_STRING_FROM_CHAR string(const value_type* text) - : istring(reinterpret_cast(&buffer), MAX_SIZE) + : istring(buffer, MAX_SIZE) { this->assign(text); } @@ -135,7 +135,7 @@ namespace etl ///\param count The number of characters to copy. //************************************************************************* string(const value_type* text, size_t count) - : istring(reinterpret_cast(&buffer), MAX_SIZE) + : istring(buffer, MAX_SIZE) { this->assign(text, text + count); } @@ -146,7 +146,7 @@ namespace etl ///\param value The value to fill the string with. //************************************************************************* string(size_type count, value_type c) - : istring(reinterpret_cast(&buffer), MAX_SIZE) + : istring(buffer, MAX_SIZE) { this->initialise(); this->resize(count, c); @@ -160,7 +160,7 @@ namespace etl //************************************************************************* template string(TIterator first, TIterator last, typename etl::enable_if::value, int>::type = 0) - : istring(reinterpret_cast(&buffer), MAX_SIZE) + : istring(buffer, MAX_SIZE) { this->assign(first, last); } @@ -170,7 +170,7 @@ namespace etl /// Construct from initializer_list. //************************************************************************* string(std::initializer_list init) - : istring(reinterpret_cast(&buffer), MAX_SIZE) + : istring(buffer, MAX_SIZE) { this->assign(init.begin(), init.end()); } @@ -181,7 +181,7 @@ namespace etl ///\param view The string_view. //************************************************************************* explicit string(const etl::string_view& view) - : istring(reinterpret_cast(&buffer), MAX_SIZE) + : istring(buffer, MAX_SIZE) { this->assign(view.begin(), view.end()); } diff --git a/include/etl/u16string.h b/include/etl/u16string.h index 741a61c2..03810b7d 100644 --- a/include/etl/u16string.h +++ b/include/etl/u16string.h @@ -77,7 +77,7 @@ namespace etl /// Constructor. //************************************************************************* u16string() - : iu16string(reinterpret_cast(&buffer), MAX_SIZE) + : iu16string(buffer, MAX_SIZE) { this->initialise(); } @@ -87,7 +87,7 @@ namespace etl ///\param other The other string. //************************************************************************* u16string(const etl::u16string& other) - : iu16string(reinterpret_cast(&buffer), MAX_SIZE) + : iu16string(buffer, MAX_SIZE) { this->assign(other); } @@ -97,7 +97,7 @@ namespace etl ///\param other The other iu16string. //************************************************************************* u16string(const etl::iu16string& other) - : iu16string(reinterpret_cast(&buffer), MAX_SIZE) + : iu16string(buffer, MAX_SIZE) { this->assign(other); } @@ -109,7 +109,7 @@ namespace etl ///\param length The number of characters. Default = npos. //************************************************************************* u16string(const etl::iu16string& other, size_type position, size_type length = npos) - : iu16string(reinterpret_cast(&buffer), MAX_SIZE) + : iu16string(buffer, MAX_SIZE) { ETL_ASSERT(position < other.size(), ETL_ERROR(string_out_of_bounds)); @@ -121,7 +121,7 @@ namespace etl ///\param text The initial text of the u16string. //************************************************************************* ETL_EXPLICIT_STRING_FROM_CHAR u16string(const value_type* text) - : iu16string(reinterpret_cast(&buffer), MAX_SIZE) + : iu16string(buffer, MAX_SIZE) { this->assign(text); } @@ -132,7 +132,7 @@ namespace etl ///\param count The number of characters to copy. //************************************************************************* u16string(const value_type* text, size_type count) - : iu16string(reinterpret_cast(&buffer), MAX_SIZE) + : iu16string(buffer, MAX_SIZE) { this->assign(text, text + count); } @@ -143,7 +143,7 @@ namespace etl ///\param value The value to fill the u16string with. //************************************************************************* u16string(size_type count, value_type c) - : iu16string(reinterpret_cast(&buffer), MAX_SIZE) + : iu16string(buffer, MAX_SIZE) { this->initialise(); this->resize(count, c); @@ -157,7 +157,7 @@ namespace etl //************************************************************************* template u16string(TIterator first, TIterator last, typename etl::enable_if::value, int>::type = 0) - : iu16string(reinterpret_cast(&buffer), MAX_SIZE) + : iu16string(buffer, MAX_SIZE) { this->assign(first, last); } @@ -167,7 +167,7 @@ namespace etl /// Construct from initializer_list. //************************************************************************* u16string(std::initializer_list init) - : iu16string(reinterpret_cast(&buffer), MAX_SIZE) + : iu16string(buffer, MAX_SIZE) { this->assign(init.begin(), init.end()); } @@ -178,7 +178,7 @@ namespace etl ///\param view The string_view. //************************************************************************* explicit u16string(const etl::u16string_view& view) - : iu16string(reinterpret_cast(&buffer), MAX_SIZE) + : iu16string(buffer, MAX_SIZE) { this->assign(view.begin(), view.end()); } diff --git a/include/etl/u32string.h b/include/etl/u32string.h index 87327cf8..418bd514 100644 --- a/include/etl/u32string.h +++ b/include/etl/u32string.h @@ -77,7 +77,7 @@ namespace etl /// Constructor. //************************************************************************* u32string() - : iu32string(reinterpret_cast(&buffer), MAX_SIZE) + : iu32string(buffer, MAX_SIZE) { this->initialise(); } @@ -87,7 +87,7 @@ namespace etl ///\param other The other string. //************************************************************************* u32string(const etl::u32string& other) - : iu32string(reinterpret_cast(&buffer), MAX_SIZE) + : iu32string(buffer, MAX_SIZE) { this->assign(other); } @@ -97,7 +97,7 @@ namespace etl ///\param other The other iu32string. //************************************************************************* u32string(const etl::iu32string& other) - : iu32string(reinterpret_cast(&buffer), MAX_SIZE) + : iu32string(buffer, MAX_SIZE) { this->assign(other); } @@ -109,7 +109,7 @@ namespace etl ///\param length The number of characters. Default = npos. //************************************************************************* u32string(const etl::iu32string& other, size_type position, size_type length = npos) - : iu32string(reinterpret_cast(&buffer), MAX_SIZE) + : iu32string(buffer, MAX_SIZE) { ETL_ASSERT(position < other.size(), ETL_ERROR(string_out_of_bounds)); @@ -121,7 +121,7 @@ namespace etl ///\param text The initial text of the u32string. //************************************************************************* ETL_EXPLICIT_STRING_FROM_CHAR u32string(const value_type* text) - : iu32string(reinterpret_cast(&buffer), MAX_SIZE) + : iu32string(buffer, MAX_SIZE) { this->assign(text); } @@ -132,7 +132,7 @@ namespace etl ///\param count The number of characters to copy. //************************************************************************* u32string(const value_type* text, size_type count) - : iu32string(reinterpret_cast(&buffer), MAX_SIZE) + : iu32string(buffer, MAX_SIZE) { this->assign(text, text + count); } @@ -143,7 +143,7 @@ namespace etl ///\param value The value to fill the u32string with. //************************************************************************* u32string(size_type count, value_type c) - : iu32string(reinterpret_cast(&buffer), MAX_SIZE) + : iu32string(buffer, MAX_SIZE) { this->initialise(); this->resize(count, c); @@ -157,7 +157,7 @@ namespace etl //************************************************************************* template u32string(TIterator first, TIterator last, typename etl::enable_if::value, int>::type = 0) - : iu32string(reinterpret_cast(&buffer), MAX_SIZE) + : iu32string(buffer, MAX_SIZE) { this->assign(first, last); } @@ -167,7 +167,7 @@ namespace etl /// Construct from initializer_list. //************************************************************************* u32string(std::initializer_list init) - : iu32string(reinterpret_cast(&buffer), MAX_SIZE) + : iu32string(buffer, MAX_SIZE) { this->assign(init.begin(), init.end()); } @@ -178,7 +178,7 @@ namespace etl ///\param view The string_view. //************************************************************************* explicit u32string(const etl::u32string_view& view) - : iu32string(reinterpret_cast(&buffer), MAX_SIZE) + : iu32string(buffer, MAX_SIZE) { this->assign(view.begin(), view.end()); } diff --git a/include/etl/u8string.h b/include/etl/u8string.h index 4561f58c..ffea798f 100644 --- a/include/etl/u8string.h +++ b/include/etl/u8string.h @@ -80,7 +80,7 @@ namespace etl /// Constructor. //************************************************************************* u8string() - : iu8string(reinterpret_cast(&buffer), MAX_SIZE) + : iu8string(buffer, MAX_SIZE) { this->initialise(); } @@ -90,7 +90,7 @@ namespace etl ///\param other The other u8string. //************************************************************************* u8string(const etl::u8string& other) - : iu8string(reinterpret_cast(&buffer), MAX_SIZE) + : iu8string(buffer, MAX_SIZE) { this->assign(other); } @@ -100,7 +100,7 @@ namespace etl ///\param other The other iu8string. //************************************************************************* u8string(const etl::iu8string& other) - : iu8string(reinterpret_cast(&buffer), MAX_SIZE) + : iu8string(buffer, MAX_SIZE) { this->assign(other); } @@ -112,7 +112,7 @@ namespace etl ///\param length The number of characters. Default = npos. //************************************************************************* u8string(const etl::iu8string& other, size_t position, size_t length = npos) - : iu8string(reinterpret_cast(&buffer), MAX_SIZE) + : iu8string(buffer, MAX_SIZE) { ETL_ASSERT(position < other.size(), ETL_ERROR(string_out_of_bounds)); @@ -124,7 +124,7 @@ namespace etl ///\param text The initial text of the u8string. //************************************************************************* ETL_EXPLICIT_STRING_FROM_CHAR u8string(const value_type* text) - : iu8string(reinterpret_cast(&buffer), MAX_SIZE) + : iu8string(buffer, MAX_SIZE) { this->assign(text); } @@ -135,7 +135,7 @@ namespace etl ///\param count The number of characters to copy. //************************************************************************* u8string(const value_type* text, size_t count) - : iu8string(reinterpret_cast(&buffer), MAX_SIZE) + : iu8string(buffer, MAX_SIZE) { this->assign(text, text + count); } @@ -146,7 +146,7 @@ namespace etl ///\param value The value to fill the u8string with. //************************************************************************* u8string(size_type count, value_type c) - : iu8string(reinterpret_cast(&buffer), MAX_SIZE) + : iu8string(buffer, MAX_SIZE) { this->initialise(); this->resize(count, c); @@ -160,7 +160,7 @@ namespace etl //************************************************************************* template u8string(TIterator first, TIterator last, typename etl::enable_if::value, int>::type = 0) - : iu8string(reinterpret_cast(&buffer), MAX_SIZE) + : iu8string(buffer, MAX_SIZE) { this->assign(first, last); } @@ -170,7 +170,7 @@ namespace etl /// Construct from initializer_list. //************************************************************************* u8string(std::initializer_list init) - : iu8string(reinterpret_cast(&buffer), MAX_SIZE) + : iu8string(buffer, MAX_SIZE) { this->assign(init.begin(), init.end()); } @@ -181,7 +181,7 @@ namespace etl ///\param view The string_view. //************************************************************************* explicit u8string(const etl::u8string_view& view) - : iu8string(reinterpret_cast(&buffer), MAX_SIZE) + : iu8string(buffer, MAX_SIZE) { this->assign(view.begin(), view.end()); } diff --git a/include/etl/wstring.h b/include/etl/wstring.h index 0f9e9567..4d7cebde 100644 --- a/include/etl/wstring.h +++ b/include/etl/wstring.h @@ -77,7 +77,7 @@ namespace etl /// Constructor. //************************************************************************* wstring() - : iwstring(reinterpret_cast(&buffer), MAX_SIZE) + : iwstring(buffer, MAX_SIZE) { this->initialise(); } @@ -87,7 +87,7 @@ namespace etl ///\param other The other string. //************************************************************************* wstring(const etl::wstring& other) - : iwstring(reinterpret_cast(&buffer), MAX_SIZE) + : iwstring(buffer, MAX_SIZE) { this->assign(other); } @@ -97,7 +97,7 @@ namespace etl ///\param other The other iwstring. //************************************************************************* wstring(const etl::iwstring& other) - : iwstring(reinterpret_cast(&buffer), MAX_SIZE) + : iwstring(buffer, MAX_SIZE) { this->assign(other); } @@ -109,7 +109,7 @@ namespace etl ///\param length The number of characters. Default = npos. //************************************************************************* wstring(const etl::iwstring& other, size_type position, size_type length = npos) - : iwstring(reinterpret_cast(&buffer), MAX_SIZE) + : iwstring(buffer, MAX_SIZE) { ETL_ASSERT(position < other.size(), ETL_ERROR(string_out_of_bounds)); @@ -121,7 +121,7 @@ namespace etl ///\param text The initial text of the wstring. //************************************************************************* ETL_EXPLICIT_STRING_FROM_CHAR wstring(const value_type* text) - : iwstring(reinterpret_cast(&buffer), MAX_SIZE) + : iwstring(buffer, MAX_SIZE) { this->assign(text); } @@ -132,7 +132,7 @@ namespace etl ///\param count The number of characters to copy. //************************************************************************* wstring(const value_type* text, size_type count) - : iwstring(reinterpret_cast(&buffer), MAX_SIZE) + : iwstring(buffer, MAX_SIZE) { this->assign(text, text + count); } @@ -143,7 +143,7 @@ namespace etl ///\param value The value to fill the wstring with. //************************************************************************* wstring(size_type count, value_type c) - : iwstring(reinterpret_cast(&buffer), MAX_SIZE) + : iwstring(buffer, MAX_SIZE) { this->initialise(); this->resize(count, c); @@ -157,7 +157,7 @@ namespace etl //************************************************************************* template wstring(TIterator first, TIterator last, typename etl::enable_if::value, int>::type = 0) - : iwstring(reinterpret_cast(&buffer), MAX_SIZE) + : iwstring(buffer, MAX_SIZE) { this->assign(first, last); } @@ -167,7 +167,7 @@ namespace etl /// Construct from initializer_list. //************************************************************************* wstring(std::initializer_list init) - : iwstring(reinterpret_cast(&buffer), MAX_SIZE) + : iwstring(buffer, MAX_SIZE) { this->assign(init.begin(), init.end()); } @@ -178,7 +178,7 @@ namespace etl ///\param view The string_view. //************************************************************************* explicit wstring(const etl::wstring_view& view) - : iwstring(reinterpret_cast(&buffer), MAX_SIZE) + : iwstring(buffer, MAX_SIZE) { this->assign(view.begin(), view.end()); } From 38f7318a85136b73a461c9f86c5136fc309b316b Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Thu, 16 Jul 2026 10:15:24 +0200 Subject: [PATCH 3/7] Add etl::apply (#1509) Fixes: #1272 Co-authored-by: John Wellbelove --- docs/types/tuple.md | 18 ++++++++++ include/etl/tuple.h | 30 +++++++++++++++++ test/test_tuple.cpp | 82 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+) diff --git a/docs/types/tuple.md b/docs/types/tuple.md index 5981e34c..666719fa 100644 --- a/docs/types/tuple.md +++ b/docs/types/tuple.md @@ -249,3 +249,21 @@ template ETL_CONSTEXPR14 const T&& get(const tuple&&); ``` + +--- + +```cpp +template +ETL_CONSTEXPR14 +auto apply(TFunction&& f, TTuple&& t); +``` + +Invokes the callable object `f` (via `etl::invoke`) with the elements of the tuple `t` expanded as its arguments. Equivalent to `std::apply`. + +```cpp +int add(int a, int b, int c) { return a + b + c; } + +auto t = etl::make_tuple(1, 2, 3); +int sum = etl::apply(add, t); // sum == 6 +``` + diff --git a/include/etl/tuple.h b/include/etl/tuple.h index 5b7257ec..a20def79 100644 --- a/include/etl/tuple.h +++ b/include/etl/tuple.h @@ -39,6 +39,7 @@ SOFTWARE. #endif #include "functional.h" + #include "invoke.h" #include "nth_type.h" #include "type_list.h" #include "type_traits.h" @@ -934,6 +935,35 @@ namespace etl return tuple(etl::forward(args)...); } + namespace private_tuple + { + //********************************** + // Helper for apply. + // Invokes the callable with the tuple elements expanded via the index sequence. + // Uses an unqualified 'get' so that argument-dependent lookup finds the + // appropriate overload for etl::tuple, etl::array, std::tuple, std::pair, etc. + //********************************** + template + ETL_CONSTEXPR14 auto apply_impl(TFunction&& f, TTuple&& t, etl::index_sequence) + -> decltype(etl::invoke(etl::forward(f), get(etl::forward(t))...)) + { + return etl::invoke(etl::forward(f), get(etl::forward(t))...); + } + } // namespace private_tuple + + //*************************************************************************** + /// Invokes the callable object 'f' with the elements of the tuple 't' as + /// arguments. Equivalent to std::apply. + //*************************************************************************** + template + ETL_CONSTEXPR14 auto apply(TFunction&& f, TTuple&& t) + -> decltype(private_tuple::apply_impl(etl::forward(f), etl::forward(t), + etl::make_index_sequence>::value>{})) + { + return private_tuple::apply_impl(etl::forward(f), etl::forward(t), + etl::make_index_sequence>::value>{}); + } + namespace private_tuple { //********************************** diff --git a/test/test_tuple.cpp b/test/test_tuple.cpp index d3816d6f..18b10bdf 100644 --- a/test/test_tuple.cpp +++ b/test/test_tuple.cpp @@ -76,6 +76,32 @@ namespace int i; }; + //********************************* + int apply_sum(int a, int b, int c) + { + return a + b + c; + } + + //********************************* + struct Summer + { + int operator()(int a, int b) const + { + return a + b; + } + }; + + //********************************* + struct Accumulator + { + int total; + + void add(int a, int b) + { + total = a + b; + } + }; + SUITE(test_tuple) { //************************************************************************* @@ -930,5 +956,61 @@ namespace CHECK_EQUAL(1, i); CHECK_EQUAL(std::string("2"), d.value); } + + //************************************************************************* + TEST(test_apply_free_function) + { + etl::tuple t(1, 2, 3); + + CHECK_EQUAL(6, etl::apply(apply_sum, t)); + } + + //************************************************************************* + TEST(test_apply_function_object) + { + etl::tuple t(10, 20); + + CHECK_EQUAL(30, etl::apply(Summer(), t)); + } + + //************************************************************************* +#if ETL_USING_CPP11 + TEST(test_apply_lambda) + { + etl::tuple t(2, 3, 4); + + int result = etl::apply([](int a, int b, int c) { return a * b * c; }, t); + + CHECK_EQUAL(24, result); + } +#endif + + //************************************************************************* + TEST(test_apply_member_function) + { + Accumulator acc = {0}; + + etl::apply(&Accumulator::add, etl::make_tuple(&acc, 5, 7)); + + CHECK_EQUAL(12, acc.total); + } + + //************************************************************************* + TEST(test_apply_rvalue_tuple) + { + CHECK_EQUAL(6, etl::apply(apply_sum, etl::make_tuple(1, 2, 3))); + } + + //************************************************************************* +#if ETL_USING_CPP11 + TEST(test_apply_empty_tuple) + { + etl::tuple<> t; + + int result = etl::apply([]() { return 42; }, t); + + CHECK_EQUAL(42, result); + } +#endif } } // namespace From f84d2e4af2d8fb231dace319a1a75349ff1cc003 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Thu, 16 Jul 2026 11:10:00 +0200 Subject: [PATCH 4/7] Add difference_type to span (#1503) Aligns with std. Generic code can depend on it. Co-authored-by: John Wellbelove --- include/etl/span.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/etl/span.h b/include/etl/span.h index 1724a87d..3c903772 100644 --- a/include/etl/span.h +++ b/include/etl/span.h @@ -211,6 +211,7 @@ namespace etl typedef T element_type; typedef typename etl::remove_cv::type value_type; typedef size_t size_type; + typedef ptrdiff_t difference_type; typedef T& reference; typedef const T& const_reference; typedef T* pointer; @@ -788,6 +789,7 @@ namespace etl typedef T element_type; typedef typename etl::remove_cv::type value_type; typedef size_t size_type; + typedef ptrdiff_t difference_type; typedef T& reference; typedef const T& const_reference; typedef T* pointer; From 875c9782d779c48648d6419126ffcd5eda5ed933 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Thu, 16 Jul 2026 11:55:01 +0200 Subject: [PATCH 5/7] Add missing array get<> overloads, and noexcept/constexpr (#1504) Co-authored-by: John Wellbelove --- include/etl/array.h | 48 +++++++++++++++++++++++----- test/test_array.cpp | 76 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 8 deletions(-) diff --git a/include/etl/array.h b/include/etl/array.h index ffa3a307..3a6757f1 100644 --- a/include/etl/array.h +++ b/include/etl/array.h @@ -709,7 +709,7 @@ namespace etl //************************************************************************* /// Returns a const reference to the last element. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR const_reference back() const + ETL_NODISCARD ETL_CONSTEXPR const_reference back() const ETL_NOEXCEPT { return *data(); } @@ -1063,7 +1063,7 @@ namespace etl ///\param rhs The second array. //************************************************************************* template - void swap(etl::array& lhs, etl::array& rhs) + ETL_CONSTEXPR14 void swap(etl::array& lhs, etl::array& rhs) ETL_NOEXCEPT_FROM(ETL_OR_STD::swap(etl::declval(), etl::declval())) { lhs.swap(rhs); } @@ -1100,7 +1100,7 @@ namespace etl /// second, otherwise false //************************************************************************* template - bool operator<(const etl::array& lhs, const etl::array& rhs) + ETL_CONSTEXPR14 bool operator<(const etl::array& lhs, const etl::array& rhs) { return etl::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend()); } @@ -1114,7 +1114,7 @@ namespace etl ///< b>false //************************************************************************* template - bool operator<=(const etl::array& lhs, const etl::array& rhs) + ETL_CONSTEXPR14 bool operator<=(const etl::array& lhs, const etl::array& rhs) { return !(lhs > rhs); } @@ -1127,7 +1127,7 @@ namespace etl /// the second, otherwise false template //************************************************************************* - bool operator>(const etl::array& lhs, const etl::array& rhs) + ETL_CONSTEXPR14 bool operator>(const etl::array& lhs, const etl::array& rhs) { return (rhs < lhs); } @@ -1141,7 +1141,7 @@ namespace etl ///< b>false //************************************************************************* template - bool operator>=(const etl::array& lhs, const etl::array& rhs) + ETL_CONSTEXPR14 bool operator>=(const etl::array& lhs, const etl::array& rhs) { return !(lhs < rhs); } @@ -1155,7 +1155,7 @@ namespace etl ///\return A reference to the element //************************************************************************* template - inline T& get(array& a) + ETL_NODISCARD ETL_CONSTEXPR14 T& get(array& a) ETL_NOEXCEPT { ETL_STATIC_ASSERT(Index < Size, "Index out of bounds"); return a[Index]; @@ -1170,11 +1170,43 @@ namespace etl ///\return A const reference to the element //************************************************************************* template - inline const T& get(const array& a) + ETL_NODISCARD ETL_CONSTEXPR14 const T& get(const array& a) ETL_NOEXCEPT { ETL_STATIC_ASSERT(Index < Size, "Index out of bounds"); return a[Index]; } + +#if ETL_USING_CPP11 + //************************************************************************* + /// Gets an rvalue reference to an element in the array. + ///\tparam Index The index. + ///\tparam T The type. + ///\tparam Size The array size. + ///\param a The array. + ///\return An rvalue reference to the element + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 T&& get(array&& a) ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(Index < Size, "Index out of bounds"); + return static_cast(a[Index]); + } + + //************************************************************************* + /// Gets a const rvalue reference to an element in the array. + ///\tparam Index The index. + ///\tparam T The type. + ///\tparam Size The array size. + ///\param a The array. + ///\return A const rvalue reference to the element + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 const T&& get(const array&& a) ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(Index < Size, "Index out of bounds"); + return static_cast(a[Index]); + } +#endif } // namespace etl #endif diff --git a/test/test_array.cpp b/test/test_array.cpp index 7a5d0130..d1af66cb 100644 --- a/test/test_array.cpp +++ b/test/test_array.cpp @@ -400,6 +400,42 @@ namespace // int i = etl::get<11>(data2); } +#if ETL_USING_CPP11 + //************************************************************************* + TEST(test_get_rvalue) + { + Data data1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + const Data data2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + int&& r0 = etl::get<3>(std::move(data1)); + const int&& r1 = etl::get<3>(std::move(data2)); + + CHECK_EQUAL(3, r0); + CHECK_EQUAL(3, r1); + + // The rvalue overloads must be selected and return rvalue references. + CHECK((std::is_same(std::move(data1)))>::value)); + CHECK((std::is_same(std::move(data2)))>::value)); + + // Moving out of an rvalue array element actually moves. + etl::array data3 = {Moveable(1), Moveable(2)}; + + Moveable moved(etl::get<0>(std::move(data3))); + + CHECK_EQUAL(1, moved.value); + CHECK(!data3[0].valid); + } +#endif + + //************************************************************************* + TEST(test_get_constexpr) + { + ETL_CONSTEXPR14 Data data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + ETL_CONSTEXPR14 int result = etl::get<3>(data); + CHECK_EQUAL(3, result); + } + //************************************************************************* TEST(test_assign) { @@ -691,6 +727,16 @@ namespace CHECK(!(greater < data)); } + //************************************************************************* + TEST(test_less_than_constexpr) + { + ETL_CONSTEXPR14 Data lesser = {0, 1, 2, 3, 4, 4, 6, 7, 8, 9}; + ETL_CONSTEXPR14 Data data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + ETL_CONSTEXPR14 bool result = (lesser < data); + CHECK(result); + } + //************************************************************************* TEST(test_less_than_equal) { @@ -703,6 +749,16 @@ namespace CHECK(!(greater <= data)); } + //************************************************************************* + TEST(test_less_than_equal_constexpr) + { + ETL_CONSTEXPR14 Data lesser = {0, 1, 2, 3, 4, 4, 6, 7, 8, 9}; + ETL_CONSTEXPR14 Data data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + ETL_CONSTEXPR14 bool result = (lesser <= data); + CHECK(result); + } + //************************************************************************* TEST(test_greater_than) { @@ -715,6 +771,16 @@ namespace CHECK(!(lesser > data)); } + //************************************************************************* + TEST(test_greater_than_constexpr) + { + ETL_CONSTEXPR14 Data greater = {0, 1, 2, 3, 5, 5, 6, 7, 8, 9}; + ETL_CONSTEXPR14 Data data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + ETL_CONSTEXPR14 bool result = (greater > data); + CHECK(result); + } + //************************************************************************* TEST(test_greater_than_equal) { @@ -727,6 +793,16 @@ namespace CHECK(!(lesser >= data)); } + //************************************************************************* + TEST(test_greater_than_equal_constexpr) + { + ETL_CONSTEXPR14 Data greater = {0, 1, 2, 3, 5, 5, 6, 7, 8, 9}; + ETL_CONSTEXPR14 Data data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + ETL_CONSTEXPR14 bool result = (greater >= data); + CHECK(result); + } + //************************************************************************* #if ETL_USING_CPP17 && ETL_HAS_INITIALIZER_LIST && !defined(ETL_TEMPLATE_DEDUCTION_GUIDE_TESTS_DISABLED) TEST(test_array_template_deduction) From 66f904b1a368ce216407e4180b9fa9f06484d47b Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Thu, 16 Jul 2026 21:21:19 +0200 Subject: [PATCH 6/7] Support structured bindings for array (#1505) --- docs/containers/arrays/array.md | 39 +++++++++++++++++++ include/etl/array.h | 66 +++++++++++++++++++++++++++++++++ test/test_array.cpp | 57 ++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+) diff --git a/docs/containers/arrays/array.md b/docs/containers/arrays/array.md index e2cda55d..1b848dac 100644 --- a/docs/containers/arrays/array.md +++ b/docs/containers/arrays/array.md @@ -314,3 +314,42 @@ operator >= ``` **Description** `true` if the contents of the lhs are lexicographically greater than or equal to the contents of the rhs, otherwise `false`. + +--- + +```cpp +template +T& get(etl::array& a) + +template +const T& get(const etl::array& a) +``` +**Description** +Gets a reference to the element at `Index` in the array. + +## Structured bindings +`etl::array` provides the tuple-like interface required for C++17 structured bindings. + +```cpp +etl::array a = { 1, 2, 3 }; + +auto& [x, y, z] = a; // x, y, z are references to a[0], a[1], a[2] +``` + +```cpp +template +struct tuple_size> +``` +**Description** +Gets the number of elements in the array. +Specialised in namespaces `etl` and `std` (the `std` specialisation allows the use of C++ structured bindings). + +--- + +```cpp +template +struct tuple_element> +``` +**Description** +Gets the type of the element at `Index` in the array. +Specialised in namespaces `etl` and `std` (the `std` specialisation allows the use of C++ structured bindings). diff --git a/include/etl/array.h b/include/etl/array.h index 3a6757f1..82ad5846 100644 --- a/include/etl/array.h +++ b/include/etl/array.h @@ -40,6 +40,9 @@ SOFTWARE. #include "static_assert.h" #include "type_traits.h" +#include "private/tuple_element.h" +#include "private/tuple_size.h" + #include ///\defgroup array array @@ -1206,7 +1209,70 @@ namespace etl ETL_STATIC_ASSERT(Index < Size, "Index out of bounds"); return static_cast(a[Index]); } + + //************************************************************************* + /// Gets the number of elements in an array. + ///\tparam T The type. + ///\tparam Size The array size. + //************************************************************************* + template + struct tuple_size > : etl::integral_constant + { + }; + + //************************************************************************* + /// Gets the type of the element at Index in an array. + ///\tparam Index The index. + ///\tparam T The type. + ///\tparam Size The array size. + //************************************************************************* + template + struct tuple_element > + { + ETL_STATIC_ASSERT(Index < Size, "Index out of bounds"); + + using type = T; + }; #endif } // namespace etl +#if ETL_USING_CPP11 +namespace std +{ + // libc++ already declares std::tuple_size / std::tuple_element in its inline + // namespace (std::__1), so re-declaring them here would make the name + // ambiguous. Detect libc++ via _LIBCPP_VERSION and skip the forward + // declarations in that case, even when not using the STL. + #if ETL_NOT_USING_STL && !defined(_LIBCPP_VERSION) \ + && !((defined(ETL_DEVELOPMENT_OS_APPLE) || (ETL_COMPILER_FULL_VERSION >= 190000) && (ETL_COMPILER_FULL_VERSION < 210000)) \ + && defined(ETL_COMPILER_CLANG)) + template + struct tuple_size; + + template + struct tuple_element; + #endif + + //*************************************************************************** + /// Specialisation of tuple_size to allow the use of C++ structured bindings. + //*************************************************************************** + template + struct tuple_size > : etl::integral_constant + { + }; + + //*************************************************************************** + /// Specialisation of tuple_element to allow the use of C++ structured + /// bindings. + //*************************************************************************** + template + struct tuple_element > + { + ETL_STATIC_ASSERT(Index < Size, "Index out of bounds"); + + using type = T; + }; +} // namespace std +#endif + #endif diff --git a/test/test_array.cpp b/test/test_array.cpp index d1af66cb..f76e617f 100644 --- a/test/test_array.cpp +++ b/test/test_array.cpp @@ -35,6 +35,9 @@ SOFTWARE. #include #include #include +#if ETL_USING_CPP11 + #include +#endif #include #include "etl/integral_limits.h" @@ -436,6 +439,60 @@ namespace CHECK_EQUAL(3, result); } +#if ETL_USING_CPP11 + //************************************************************************* + TEST(test_tuple_size) + { + CHECK_EQUAL(SIZE, (etl::tuple_size::value)); + CHECK_EQUAL(SIZE, (std::tuple_size::value)); + + #if ETL_USING_CPP17 + CHECK_EQUAL(SIZE, (etl::tuple_size_v)); + CHECK_EQUAL(SIZE, (std::tuple_size_v)); + #endif + } + + //************************************************************************* + TEST(test_tuple_element) + { + CHECK_TRUE((std::is_same>::value)); + CHECK_TRUE((std::is_same>::value)); + + CHECK_TRUE((std::is_same::type>::value)); + CHECK_TRUE((std::is_same::type>::value)); + } +#endif + +#if ETL_USING_CPP17 + //************************************************************************* + TEST(test_structured_bindings) + { + etl::array data = {1, 2, 3}; + + // Bind by reference and modify. + auto& [a, b, c] = data; + + CHECK_EQUAL(1, a); + CHECK_EQUAL(2, b); + CHECK_EQUAL(3, c); + + a = 10; + b = 20; + c = 30; + + CHECK_EQUAL(10, data[0]); + CHECK_EQUAL(20, data[1]); + CHECK_EQUAL(30, data[2]); + + // Bind by const reference. + const auto& [x, y, z] = data; + + CHECK_EQUAL(10, x); + CHECK_EQUAL(20, y); + CHECK_EQUAL(30, z); + } +#endif + //************************************************************************* TEST(test_assign) { From 49f690a921e1c264dc183a372feaab61dfe5acd0 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Sat, 18 Jul 2026 16:37:53 +0200 Subject: [PATCH 7/7] Add safe integer comparisons (#1506) Adding cmp_equal, cmp_not_equal, cmp_less, cmp_greater, cmp_less_equal, cmp_greater_equal and in_range. Co-authored-by: John Wellbelove --- docs/utilities/utility.md | 76 +++++++++++++++++ include/etl/utility.h | 168 ++++++++++++++++++++++++++++++++++++++ test/test_utility.cpp | 155 +++++++++++++++++++++++++++++++++++ 3 files changed, 399 insertions(+) diff --git a/docs/utilities/utility.md b/docs/utilities/utility.md index 3e36bc7a..c4f76c5f 100644 --- a/docs/utilities/utility.md +++ b/docs/utilities/utility.md @@ -71,6 +71,82 @@ T exchange(T& object, const U& new_value) Copies the new value to object and returns the old value. Note: This is not an atomic operation. +## Integer comparison functions + +Safe comparison of integers of different signedness. Unlike the built-in +comparison operators, negative signed integers always compare less than (and +never equal to) unsigned integers, so the result is never affected by +value-changing implicit conversions. +The arguments must be standard signed or unsigned integer types, including +`signed char` and `unsigned char`. `bool`, `char` and the extended character +types are not permitted. +When the STL is available and the compiler provides +`__cpp_lib_integer_comparison_functions` (C++20), the `std` versions are used. +Since: `20.49.0` + +```cpp +template +constexpr bool cmp_equal(T t, U u) noexcept +``` +**Description** +Returns `true` if `t` is equal to `u`. + +--- + +```cpp +template +constexpr bool cmp_not_equal(T t, U u) noexcept +``` +**Description** +Returns `true` if `t` is not equal to `u`. + +--- + +```cpp +template +constexpr bool cmp_less(T t, U u) noexcept +``` +**Description** +Returns `true` if `t` is less than `u`. + +--- + +```cpp +template +constexpr bool cmp_greater(T t, U u) noexcept +``` +**Description** +Returns `true` if `t` is greater than `u`. + +--- + +```cpp +template +constexpr bool cmp_less_equal(T t, U u) noexcept +``` +**Description** +Returns `true` if `t` is less than or equal to `u`. + +--- + +```cpp +template +constexpr bool cmp_greater_equal(T t, U u) noexcept +``` +**Description** +Returns `true` if `t` is greater than or equal to `u`. + +--- + +```cpp +template +constexpr bool in_range(T t) noexcept +``` +**Description** +Returns `true` if the value of `t` is in the range of values that can be +represented by `R`, that is, if `t` can be converted to `R` without loss of its +value. + ## add_const ```cpp diff --git a/include/etl/utility.h b/include/etl/utility.h index addd7ca1..0040dd7a 100644 --- a/include/etl/utility.h +++ b/include/etl/utility.h @@ -32,6 +32,7 @@ SOFTWARE. #define ETL_UTILITY_INCLUDED #include "platform.h" +#include "limits.h" #include "type_traits.h" #include "private/tuple_element.h" @@ -142,6 +143,173 @@ namespace etl return static_cast::type>(value); } +#if ETL_USING_STL && ETL_USING_CPP20 && defined(__cpp_lib_integer_comparison_functions) + //*************************************************************************** + // Safe integer comparison functions. + //*************************************************************************** + using std::cmp_equal; + using std::cmp_greater; + using std::cmp_greater_equal; + using std::cmp_less; + using std::cmp_less_equal; + using std::cmp_not_equal; + using std::in_range; +#else + namespace private_utility + { + //*********************************** + /// Determines whether T is one of the standard integer types permitted as + /// an argument to the safe integer comparison functions. The permitted + /// types are the signed and unsigned standard integer types, including + /// signed char and unsigned char. bool, char and the extended character + /// types are excluded, matching the C++ standard's constraints on + /// etl::cmp_* and etl::in_range. + //*********************************** + template + struct is_standard_integer : etl::false_type + { + }; + + template <> + struct is_standard_integer : etl::true_type + { + }; + template <> + struct is_standard_integer : etl::true_type + { + }; + template <> + struct is_standard_integer : etl::true_type + { + }; + template <> + struct is_standard_integer : etl::true_type + { + }; + template <> + struct is_standard_integer : etl::true_type + { + }; + template <> + struct is_standard_integer : etl::true_type + { + }; + template <> + struct is_standard_integer : etl::true_type + { + }; + template <> + struct is_standard_integer : etl::true_type + { + }; + template <> + struct is_standard_integer : etl::true_type + { + }; + template <> + struct is_standard_integer : etl::true_type + { + }; + + template + struct is_standard_integer : is_standard_integer + { + }; + template + struct is_standard_integer : is_standard_integer + { + }; + template + struct is_standard_integer : is_standard_integer + { + }; + + //*********************************** + /// Enabled when both T and U are permitted comparison types. + //*********************************** + template + struct enable_if_comparable : etl::enable_if::value && is_standard_integer::value, TReturn> + { + }; + } // namespace private_utility + + //*************************************************************************** + /// Compares the values of two integers. + /// Unlike the built-in comparison operators, negative signed integers always + /// compare less than (and not equal to) unsigned integers. + /// The comparison is safe against value-changing implicit conversions. + /// https://en.cppreference.com/w/cpp/utility/intcmp + ///\ingroup utility + //*************************************************************************** + template + ETL_NODISCARD ETL_CONSTEXPR typename private_utility::enable_if_comparable::type cmp_equal(T t, U u) ETL_NOEXCEPT + { + return (etl::is_signed::value == etl::is_signed::value) ? (t == u) + : etl::is_signed::value ? ((t >= T(0)) && (typename etl::make_unsigned::type(t) == u)) + : ((u >= U(0)) && (t == typename etl::make_unsigned::type(u))); + } + + //*************************************************************************** + /// See @ref cmp_equal. + //*************************************************************************** + template + ETL_NODISCARD ETL_CONSTEXPR typename private_utility::enable_if_comparable::type cmp_not_equal(T t, U u) ETL_NOEXCEPT + { + return !etl::cmp_equal(t, u); + } + + //*************************************************************************** + /// See @ref cmp_equal. + //*************************************************************************** + template + ETL_NODISCARD ETL_CONSTEXPR typename private_utility::enable_if_comparable::type cmp_less(T t, U u) ETL_NOEXCEPT + { + return (etl::is_signed::value == etl::is_signed::value) ? (t < u) + : etl::is_signed::value ? ((t < T(0)) || (typename etl::make_unsigned::type(t) < u)) + : ((u >= U(0)) && (t < typename etl::make_unsigned::type(u))); + } + + //*************************************************************************** + /// See @ref cmp_equal. + //*************************************************************************** + template + ETL_NODISCARD ETL_CONSTEXPR typename private_utility::enable_if_comparable::type cmp_greater(T t, U u) ETL_NOEXCEPT + { + return etl::cmp_less(u, t); + } + + //*************************************************************************** + /// See @ref cmp_equal. + //*************************************************************************** + template + ETL_NODISCARD ETL_CONSTEXPR typename private_utility::enable_if_comparable::type cmp_less_equal(T t, U u) ETL_NOEXCEPT + { + return !etl::cmp_less(u, t); + } + + //*************************************************************************** + /// See @ref cmp_equal. + //*************************************************************************** + template + ETL_NODISCARD ETL_CONSTEXPR typename private_utility::enable_if_comparable::type cmp_greater_equal(T t, U u) ETL_NOEXCEPT + { + return !etl::cmp_less(t, u); + } + + //*************************************************************************** + /// Returns whether the value of t is in the range of values that can be + /// represented by R, that is, whether t can be converted to R without loss + /// of its value. + /// https://en.cppreference.com/w/cpp/utility/in_range + ///\ingroup utility + //*************************************************************************** + template + ETL_NODISCARD ETL_CONSTEXPR typename private_utility::enable_if_comparable::type in_range(T t) ETL_NOEXCEPT + { + return etl::cmp_greater_equal(t, etl::numeric_limits::min()) && etl::cmp_less_equal(t, etl::numeric_limits::max()); + } +#endif + // We can't have std::swap and etl::swap templates coexisting in the unit // tests as the compiler will be unable to decide which one to use, due to // ADL. diff --git a/test/test_utility.cpp b/test/test_utility.cpp index 5b824aa3..2093a355 100644 --- a/test/test_utility.cpp +++ b/test/test_utility.cpp @@ -785,6 +785,161 @@ namespace } #endif + //********************************* + TEST(test_cmp_equal) + { + // Same signedness. + CHECK_TRUE(etl::cmp_equal(1, 1)); + CHECK_FALSE(etl::cmp_equal(1, 2)); + CHECK_TRUE(etl::cmp_equal(1U, 1U)); + CHECK_FALSE(etl::cmp_equal(1U, 2U)); + + // Mixed signedness where the built-in operator would give the wrong result. + // -1 as unsigned would be a large value, so a naive (a == b) would be false anyway, + // but the classic trap is comparing a negative signed with an equal-bit-pattern unsigned. + CHECK_FALSE(etl::cmp_equal(-1, static_cast(-1))); + CHECK_FALSE(etl::cmp_equal(static_cast(-1), -1)); + + // Non-negative signed compared with unsigned. + CHECK_TRUE(etl::cmp_equal(5, 5U)); + CHECK_TRUE(etl::cmp_equal(5U, 5)); + CHECK_FALSE(etl::cmp_equal(-5, 5U)); + CHECK_FALSE(etl::cmp_equal(5U, -5)); + + // Different widths. + CHECK_TRUE(etl::cmp_equal(int8_t(100), int64_t(100))); + CHECK_FALSE(etl::cmp_equal(int8_t(-1), uint64_t(0xFFFFFFFFFFFFFFFFULL))); + } + + //********************************* + TEST(test_cmp_not_equal) + { + CHECK_FALSE(etl::cmp_not_equal(1, 1)); + CHECK_TRUE(etl::cmp_not_equal(1, 2)); + CHECK_TRUE(etl::cmp_not_equal(-1, static_cast(-1))); + CHECK_FALSE(etl::cmp_not_equal(5, 5U)); + CHECK_TRUE(etl::cmp_not_equal(-5, 5U)); + } + + //********************************* + TEST(test_cmp_less) + { + // Same signedness. + CHECK_TRUE(etl::cmp_less(1, 2)); + CHECK_FALSE(etl::cmp_less(2, 1)); + CHECK_FALSE(etl::cmp_less(1, 1)); + CHECK_TRUE(etl::cmp_less(1U, 2U)); + CHECK_FALSE(etl::cmp_less(2U, 1U)); + + // A negative signed value is always less than any unsigned value. + CHECK_TRUE(etl::cmp_less(-1, 0U)); + CHECK_TRUE(etl::cmp_less(-1, static_cast(-1))); + CHECK_FALSE(etl::cmp_less(static_cast(-1), -1)); + + // Non-negative signed with unsigned. + CHECK_TRUE(etl::cmp_less(5, 6U)); + CHECK_FALSE(etl::cmp_less(6, 5U)); + CHECK_TRUE(etl::cmp_less(5U, 6)); + CHECK_FALSE(etl::cmp_less(6U, 5)); + + // Large unsigned is not less than a small signed. + CHECK_FALSE(etl::cmp_less(0xFFFFFFFFU, 1)); + CHECK_TRUE(etl::cmp_less(1, 0xFFFFFFFFU)); + } + + //********************************* + TEST(test_cmp_greater) + { + CHECK_TRUE(etl::cmp_greater(2, 1)); + CHECK_FALSE(etl::cmp_greater(1, 2)); + CHECK_FALSE(etl::cmp_greater(1, 1)); + + CHECK_FALSE(etl::cmp_greater(-1, 0U)); + CHECK_TRUE(etl::cmp_greater(static_cast(-1), -1)); + CHECK_TRUE(etl::cmp_greater(0xFFFFFFFFU, 1)); + CHECK_FALSE(etl::cmp_greater(1, 0xFFFFFFFFU)); + } + + //********************************* + TEST(test_cmp_less_equal) + { + CHECK_TRUE(etl::cmp_less_equal(1, 1)); + CHECK_TRUE(etl::cmp_less_equal(1, 2)); + CHECK_FALSE(etl::cmp_less_equal(2, 1)); + + CHECK_TRUE(etl::cmp_less_equal(-1, static_cast(-1))); + CHECK_FALSE(etl::cmp_less_equal(static_cast(-1), -1)); + CHECK_TRUE(etl::cmp_less_equal(5U, 5)); + CHECK_FALSE(etl::cmp_less_equal(6U, 5)); + } + + //********************************* + TEST(test_cmp_greater_equal) + { + CHECK_TRUE(etl::cmp_greater_equal(1, 1)); + CHECK_TRUE(etl::cmp_greater_equal(2, 1)); + CHECK_FALSE(etl::cmp_greater_equal(1, 2)); + + CHECK_FALSE(etl::cmp_greater_equal(-1, static_cast(-1))); + CHECK_TRUE(etl::cmp_greater_equal(static_cast(-1), -1)); + CHECK_TRUE(etl::cmp_greater_equal(5, 5U)); + CHECK_FALSE(etl::cmp_greater_equal(-5, 5U)); + } + + //********************************* + TEST(test_in_range) + { + // Signed target type. + CHECK_TRUE(etl::in_range(0)); + CHECK_TRUE(etl::in_range(127)); + CHECK_TRUE(etl::in_range(-128)); + CHECK_FALSE(etl::in_range(128)); + CHECK_FALSE(etl::in_range(-129)); + CHECK_FALSE(etl::in_range(200)); + + // Unsigned target type. + CHECK_TRUE(etl::in_range(0)); + CHECK_TRUE(etl::in_range(255)); + CHECK_FALSE(etl::in_range(256)); + CHECK_FALSE(etl::in_range(-1)); + + // Unsigned source that would be negative if reinterpreted as signed. + CHECK_FALSE(etl::in_range(static_cast(-1))); + CHECK_TRUE(etl::in_range(static_cast(-1))); + + // Wider target always contains a narrower value. + CHECK_TRUE(etl::in_range(int8_t(-1))); + CHECK_TRUE(etl::in_range(uint32_t(0xFFFFFFFFU))); + } + +#if ETL_USING_CPP14 + //********************************* + TEST(test_cmp_and_in_range_constexpr) + { + constexpr bool ce0 = etl::cmp_equal(1, 1); + constexpr bool ce1 = etl::cmp_not_equal(-1, static_cast(-1)); + constexpr bool ce2 = etl::cmp_less(-1, 0U); + constexpr bool ce3 = etl::cmp_greater(static_cast(-1), -1); + constexpr bool ce4 = etl::cmp_less_equal(-1, static_cast(-1)); + constexpr bool ce5 = etl::cmp_greater_equal(static_cast(-1), -1); + constexpr bool ce6 = etl::in_range(100); + constexpr bool ce7 = etl::in_range(200); + + static_assert(ce0, "cmp_equal constexpr"); + static_assert(ce1, "cmp_not_equal constexpr"); + static_assert(ce2, "cmp_less constexpr"); + static_assert(ce3, "cmp_greater constexpr"); + static_assert(ce4, "cmp_less_equal constexpr"); + static_assert(ce5, "cmp_greater_equal constexpr"); + static_assert(ce6, "in_range true constexpr"); + static_assert(!ce7, "in_range false constexpr"); + + CHECK_TRUE(ce0); + CHECK_TRUE(ce6); + CHECK_FALSE(ce7); + } +#endif + #if ETL_HAS_PACKED //********************************* TEST(test_packed)