From 86c3dad1e93627b63af33798b00ca134459fa2ad Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Wed, 15 Jul 2026 08:24:36 +0100 Subject: [PATCH 01/38] 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 02/38] 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 03/38] 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 04/38] 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 05/38] 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 06/38] 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 07/38] 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) From 0e01c812ea049b9df3e1e88355718e6a56d91afa Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Sun, 19 Jul 2026 13:00:41 +0200 Subject: [PATCH 08/38] Fix variant copy operations enabled with non-copyable alternatives (#1514) etl::variant was copy-constructible and copy-assignable even when an alternative was not copyable, and copying a non-copyable active alternative silently copied only the index, leaving the variant in an invalid state. Move the copy/move construct and assign logic into variant_base and add a variant_copy_move_control mixin that conditionally deletes the copy and move special members based on whether every alternative is copy/move constructible. The variant's special members are now defaulted, so they are deleted unless all alternatives support the operation. Fixes: #1512 Further, same-type-assignment optimization: Previously, assignment was destroy-and-reconstruct. Now, copy/move-assign are enabled if the types allow for it. Co-authored-by: John Wellbelove --- include/etl/private/variant_variadic.h | 470 ++++++++++++++++++++----- include/etl/type_traits.h | 21 ++ test/test_variant_variadic.cpp | 283 +++++++++++++++ 3 files changed, 682 insertions(+), 92 deletions(-) diff --git a/include/etl/private/variant_variadic.h b/include/etl/private/variant_variadic.h index 545fd5e8..b76ba7a6 100644 --- a/include/etl/private/variant_variadic.h +++ b/include/etl/private/variant_variadic.h @@ -86,6 +86,8 @@ namespace etl static void destroy(char*, size_t) {} static void copy(char*, const char*, size_t) {} static void move(char*, const char*, size_t) {} + static void copy_assign(char*, const char*, size_t) {} + static void move_assign(char*, const char*, size_t) {} }; // Recursive case. @@ -128,6 +130,30 @@ namespace etl } } + static void copy_assign(char* dst, const char* src, size_t type_id) + { + if (type_id == Index) + { + copy_assign_impl(dst, src, etl::integral_constant::value>{}); + } + else + { + variant_operations::copy_assign(dst, src, type_id); + } + } + + static void move_assign(char* dst, const char* src, size_t type_id) + { + if (type_id == Index) + { + move_assign_impl(dst, src, etl::integral_constant::value>{}); + } + else + { + variant_operations::move_assign(dst, src, type_id); + } + } + private: static void copy_impl(char* dst, const char* src, etl::true_type) @@ -143,6 +169,20 @@ namespace etl } static void move_impl(char*, const char*, etl::false_type) {} + + static void copy_assign_impl(char* dst, const char* src, etl::true_type) + { + *reinterpret_cast(dst) = *reinterpret_cast(src); + } + + static void copy_assign_impl(char*, const char*, etl::false_type) {} + + static void move_assign_impl(char* dst, const char* src, etl::true_type) + { + *reinterpret_cast(dst) = etl::move(*reinterpret_cast(const_cast(src))); + } + + static void move_assign_impl(char*, const char*, etl::false_type) {} }; //******************************************* @@ -431,6 +471,80 @@ namespace etl { } + variant_base(const variant_base& other) noexcept(etl::conjunction...>::value) + : type_id(other.type_id) + { + if (other.type_id != variant_npos) + { + variant_operations<0, TTypes...>::copy(data, other.data, other.type_id); + } + } + + variant_base(variant_base&& other) noexcept(etl::conjunction...>::value) + : type_id(other.type_id) + { + if (other.type_id != variant_npos) + { + variant_operations<0, TTypes...>::move(data, other.data, other.type_id); + } + } + + variant_base& operator=(const variant_base& other) noexcept(etl::conjunction...>::value + && etl::conjunction...>::value) + { + if (this != &other) + { + if ((type_id != variant_npos) && (type_id == other.type_id)) + { + variant_operations<0, TTypes...>::copy_assign(data, other.data, type_id); + } + else + { + if (type_id != variant_npos) + { + variant_operations<0, TTypes...>::destroy(data, type_id); + type_id = variant_npos; + } + + if (other.type_id != variant_npos) + { + variant_operations<0, TTypes...>::copy(data, other.data, other.type_id); + type_id = other.type_id; + } + } + } + + return *this; + } + + variant_base& operator=(variant_base&& other) noexcept(etl::conjunction...>::value + && etl::conjunction...>::value) + { + if (this != &other) + { + if ((type_id != variant_npos) && (type_id == other.type_id)) + { + variant_operations<0, TTypes...>::move_assign(data, other.data, type_id); + } + else + { + if (type_id != variant_npos) + { + variant_operations<0, TTypes...>::destroy(data, type_id); + type_id = variant_npos; + } + + if (other.type_id != variant_npos) + { + variant_operations<0, TTypes...>::move(data, other.data, other.type_id); + type_id = other.type_id; + } + } + } + + return *this; + } + ~variant_base() { if (type_id != variant_npos) @@ -474,7 +588,259 @@ namespace etl { } + variant_base(const variant_base& other) noexcept(etl::conjunction...>::value) + : data() + , type_id(other.type_id) + { + copy_construct_from(other, is_all_trivially_copyable{}); + } + + variant_base(variant_base&& other) noexcept(etl::conjunction...>::value) + : data() + , type_id(other.type_id) + { + move_construct_from(other, is_all_trivially_copyable{}); + } + + variant_base& operator=(const variant_base& other) noexcept(etl::conjunction...>::value + && etl::conjunction...>::value) + { + if (this != &other) + { + copy_assign_from(other, is_all_trivially_copyable{}); + } + return *this; + } + + variant_base& operator=(variant_base&& other) noexcept(etl::conjunction...>::value + && etl::conjunction...>::value) + { + if (this != &other) + { + move_assign_from(other, is_all_trivially_copyable{}); + } + return *this; + } + ~variant_base() = default; + + private: + + // All alternatives trivially copyable => a raw-byte copy correctly + // reproduces both copy and move construction. + typedef etl::integral_constant...>::value> is_all_trivially_copyable; + + //******************************************* + // Trivially copyable fast path: raw-byte copy. + //******************************************* + void copy_construct_from(const variant_base& other, etl::true_type) noexcept + { + memcpy(static_cast(&data), static_cast(&other.data), sizeof(data)); + } + + void move_construct_from(const variant_base& other, etl::true_type) noexcept + { + memcpy(static_cast(&data), static_cast(&other.data), sizeof(data)); + } + + void copy_assign_from(const variant_base& other, etl::true_type) noexcept + { + type_id = other.type_id; + memcpy(static_cast(&data), static_cast(&other.data), sizeof(data)); + } + + void move_assign_from(const variant_base& other, etl::true_type) noexcept + { + type_id = other.type_id; + memcpy(static_cast(&data), static_cast(&other.data), sizeof(data)); + } + + //******************************************* + // Non-trivial path: dispatch to the active alternative's own + // copy/move constructor. The active union member shares its address + // with the union, so the raw-pointer dispatch places the new object + // in the correct storage. + //******************************************* + void copy_construct_from(const variant_base& other, etl::false_type) + { + if (other.type_id != variant_npos) + { + variant_operations<0, TTypes...>::copy(reinterpret_cast(&data), reinterpret_cast(&other.data), other.type_id); + } + } + + void move_construct_from(variant_base& other, etl::false_type) + { + if (other.type_id != variant_npos) + { + variant_operations<0, TTypes...>::move(reinterpret_cast(&data), reinterpret_cast(&other.data), other.type_id); + } + } + + void copy_assign_from(const variant_base& other, etl::false_type) + { + if ((type_id != variant_npos) && (type_id == other.type_id)) + { + variant_operations<0, TTypes...>::copy_assign(reinterpret_cast(&data), reinterpret_cast(&other.data), type_id); + return; + } + + if (type_id != variant_npos) + { + variant_operations<0, TTypes...>::destroy(reinterpret_cast(&data), type_id); + type_id = variant_npos; + } + + if (other.type_id != variant_npos) + { + variant_operations<0, TTypes...>::copy(reinterpret_cast(&data), reinterpret_cast(&other.data), other.type_id); + type_id = other.type_id; + } + } + + void move_assign_from(variant_base& other, etl::false_type) + { + if ((type_id != variant_npos) && (type_id == other.type_id)) + { + variant_operations<0, TTypes...>::move_assign(reinterpret_cast(&data), reinterpret_cast(&other.data), type_id); + return; + } + + if (type_id != variant_npos) + { + variant_operations<0, TTypes...>::destroy(reinterpret_cast(&data), type_id); + type_id = variant_npos; + } + + if (other.type_id != variant_npos) + { + variant_operations<0, TTypes...>::move(reinterpret_cast(&data), reinterpret_cast(&other.data), other.type_id); + type_id = other.type_id; + } + } + }; + + //******************************************* + /// Trait: are all types copy constructible? + //******************************************* + template + struct are_all_copy_constructible : etl::conjunction...> + { + }; + + //******************************************* + /// Trait: are all types move constructible? + //******************************************* + template + struct are_all_move_constructible : etl::conjunction...> + { + }; + + //******************************************* + /// Trait: are all types copy assignable? + //******************************************* + template + struct are_all_copy_assignable : etl::conjunction...> + { + }; + + //******************************************* + /// Trait: are all types move assignable? + //******************************************* + template + struct are_all_move_assignable : etl::conjunction...> + { + }; + + //******************************************* + /// Flat base that conditionally deletes the copy and move CONSTRUCTORS. + /// Every special member is declared directly (no intermediate bases) so + /// that MSVC does not spuriously delete the defaulted move members when a + /// sibling/base subobject has a deleted special member. + //******************************************* + template + struct variant_constructor_control + { + variant_constructor_control() = default; + variant_constructor_control(const variant_constructor_control&) = default; + variant_constructor_control(variant_constructor_control&&) = default; + variant_constructor_control& operator=(const variant_constructor_control&) = default; + variant_constructor_control& operator=(variant_constructor_control&&) = default; + }; + + template <> + struct variant_constructor_control + { + variant_constructor_control() = default; + variant_constructor_control(const variant_constructor_control&) = delete; + variant_constructor_control(variant_constructor_control&&) = default; + variant_constructor_control& operator=(const variant_constructor_control&) = default; + variant_constructor_control& operator=(variant_constructor_control&&) = default; + }; + + template <> + struct variant_constructor_control + { + variant_constructor_control() = default; + variant_constructor_control(const variant_constructor_control&) = default; + variant_constructor_control(variant_constructor_control&&) = delete; + variant_constructor_control& operator=(const variant_constructor_control&) = default; + variant_constructor_control& operator=(variant_constructor_control&&) = default; + }; + + template <> + struct variant_constructor_control + { + variant_constructor_control() = default; + variant_constructor_control(const variant_constructor_control&) = delete; + variant_constructor_control(variant_constructor_control&&) = delete; + variant_constructor_control& operator=(const variant_constructor_control&) = default; + variant_constructor_control& operator=(variant_constructor_control&&) = default; + }; + + //******************************************* + /// Flat base that conditionally deletes the copy and move ASSIGNMENT + /// operators. Every special member is declared directly (see the note on + /// variant_constructor_control above). + //******************************************* + template + struct variant_assignment_control + { + variant_assignment_control() = default; + variant_assignment_control(const variant_assignment_control&) = default; + variant_assignment_control(variant_assignment_control&&) = default; + variant_assignment_control& operator=(const variant_assignment_control&) = default; + variant_assignment_control& operator=(variant_assignment_control&&) = default; + }; + + template <> + struct variant_assignment_control + { + variant_assignment_control() = default; + variant_assignment_control(const variant_assignment_control&) = default; + variant_assignment_control(variant_assignment_control&&) = default; + variant_assignment_control& operator=(const variant_assignment_control&) = delete; + variant_assignment_control& operator=(variant_assignment_control&&) = default; + }; + + template <> + struct variant_assignment_control + { + variant_assignment_control() = default; + variant_assignment_control(const variant_assignment_control&) = default; + variant_assignment_control(variant_assignment_control&&) = default; + variant_assignment_control& operator=(const variant_assignment_control&) = default; + variant_assignment_control& operator=(variant_assignment_control&&) = delete; + }; + + template <> + struct variant_assignment_control + { + variant_assignment_control() = default; + variant_assignment_control(const variant_assignment_control&) = default; + variant_assignment_control(variant_assignment_control&&) = default; + variant_assignment_control& operator=(const variant_assignment_control&) = delete; + variant_assignment_control& operator=(variant_assignment_control&&) = delete; }; } // namespace private_variant @@ -483,7 +849,13 @@ namespace etl ///\ingroup variant //*************************************************************************** template - class variant : private private_variant::variant_base::value, TTypes...> + class variant + : private private_variant::variant_base::value, TTypes...> + , private private_variant::variant_constructor_control< private_variant::are_all_copy_constructible::value, + private_variant::are_all_move_constructible::value> + , private private_variant::variant_assignment_control< + private_variant::are_all_copy_constructible::value && private_variant::are_all_copy_assignable::value, + private_variant::are_all_move_constructible::value && private_variant::are_all_move_assignable::value> { using base_type = private_variant::variant_base::value, TTypes...>; @@ -671,31 +1043,13 @@ namespace etl /// Copy constructor. ///\param other The other variant object to copy. //*************************************************************************** - #include "diagnostic_uninitialized_push.h" - ETL_CONSTEXPR14 variant(const variant& other) noexcept(etl::conjunction...>::value) - : base_type(other.type_id) - { - if (other.index() != variant_npos) - { - do_copy_from(other, etl::integral_constant{}); - } - } - #include "diagnostic_pop.h" + variant(const variant& other) = default; //*************************************************************************** /// Move constructor. ///\param other The other variant object to copy. //*************************************************************************** - #include "diagnostic_uninitialized_push.h" - ETL_CONSTEXPR14 variant(variant&& other) noexcept(etl::conjunction...>::value) - : base_type(other.type_id) - { - if (other.index() != variant_npos) - { - do_move_from(other, etl::integral_constant{}); - } - } - #include "diagnostic_pop.h" + variant(variant&& other) = default; //*************************************************************************** /// Destructor. @@ -806,51 +1160,13 @@ namespace etl /// Assignment operator for variant type. ///\param other The variant to assign. //*************************************************************************** - variant& operator=(const variant& other) ETL_NOEXCEPT_IF((etl::conjunction...>::value)) - { - if (this != &other) - { - if (other.index() == variant_npos) - { - type_id = variant_npos; - } - else - { - do_destroy(); - - do_copy_from(other, etl::integral_constant{}); - - type_id = other.type_id; - } - } - - return *this; - } + variant& operator=(const variant& other) = default; //*************************************************************************** - /// Assignment operator for variant type. + /// Move assignment operator for variant type. ///\param other The variant to assign. //*************************************************************************** - variant& operator=(variant&& other) ETL_NOEXCEPT_IF((etl::conjunction...>::value)) - { - if (this != &other) - { - if (other.index() == variant_npos) - { - type_id = variant_npos; - } - else - { - do_destroy(); - - do_move_from(other, etl::integral_constant{}); - - type_id = other.type_id; - } - } - - return *this; - } + variant& operator=(variant&& other) = default; //*************************************************************************** /// Checks whether the variant doesn't contain a valid value. @@ -1149,36 +1465,6 @@ namespace etl ::new (static_cast(data)) T(etl::forward(value)); } - //*************************************************************************** - /// Copy from another variant. - //*************************************************************************** - void do_copy_from(const variant& other, etl::integral_constant) - { - // Trivially destructible: copy via memcpy to avoid issues with non-trivial copy assignment in union. - memcpy(static_cast(&data), static_cast(&other.data), sizeof(data)); - } - - void do_copy_from(const variant& other, etl::integral_constant) - { - // Non-trivially destructible: use switch-based dispatch. - private_variant::variant_operations<0, TTypes...>::copy(data, other.data, other.type_id); - } - - //*************************************************************************** - /// Move from another variant. - //*************************************************************************** - void do_move_from(variant& other, etl::integral_constant) - { - // Trivially destructible: copy via memcpy (trivial move == copy). - memcpy(static_cast(&data), static_cast(&other.data), sizeof(data)); - } - - void do_move_from(variant& other, etl::integral_constant) - { - // Non-trivially destructible: use switch-based dispatch. - private_variant::variant_operations<0, TTypes...>::move(data, other.data, other.type_id); - } - #if ETL_USING_CPP17 && !defined(ETL_VARIANT_FORCE_CPP11) //*************************************************************************** /// Call the relevant visitor by attempting each one. diff --git a/include/etl/type_traits.h b/include/etl/type_traits.h index 52fd9859..a4751fa3 100644 --- a/include/etl/type_traits.h +++ b/include/etl/type_traits.h @@ -3323,9 +3323,30 @@ namespace etl //********************************************* // is_assignable + #if !ETL_USING_BUILTIN_IS_ASSIGNABLE && ETL_USING_CPP11 + namespace private_type_traits + { + template + struct is_assignable_ : etl::false_type + { + }; + + template + struct is_assignable_() = etl::declval())>, T1, T2> : etl::true_type + { + }; + } // namespace private_type_traits + #endif + template #if ETL_USING_BUILTIN_IS_ASSIGNABLE struct is_assignable : public etl::bool_constant<__is_assignable(T1, T2)> + #elif ETL_USING_CPP11 + // Builtin-free assignability detection. Used when the __is_assignable builtin + // is unavailable (e.g. MSVC, where determine_builtin_support.h disables the + // __has_builtin block). Mirrors the void_t-based is_constructible above so + // that class types are correctly reported as (copy/move) assignable. + struct is_assignable : public private_type_traits::is_assignable_, T1, T2> #else struct is_assignable : public etl::bool_constant< (etl::is_arithmetic::value || etl::is_pointer::value) diff --git a/test/test_variant_variadic.cpp b/test/test_variant_variadic.cpp index 5b558e05..11cef693 100644 --- a/test/test_variant_variadic.cpp +++ b/test/test_variant_variadic.cpp @@ -321,6 +321,26 @@ namespace bool copied_to; }; + //********************************************* + // Copy constructible, but copy assignment is deleted. + struct CopyConstructibleNonCopyAssignable + { + CopyConstructibleNonCopyAssignable() = default; + CopyConstructibleNonCopyAssignable(const CopyConstructibleNonCopyAssignable&) = default; + CopyConstructibleNonCopyAssignable& operator=(const CopyConstructibleNonCopyAssignable&) = delete; + }; + + //********************************************* + // Move constructible, but move assignment is deleted. + struct MoveConstructibleNonMoveAssignable + { + MoveConstructibleNonMoveAssignable() = default; + MoveConstructibleNonMoveAssignable(MoveConstructibleNonMoveAssignable&&) = default; + MoveConstructibleNonMoveAssignable& operator=(MoveConstructibleNonMoveAssignable&&) = delete; + MoveConstructibleNonMoveAssignable(const MoveConstructibleNonMoveAssignable&) = delete; + MoveConstructibleNonMoveAssignable& operator=(const MoveConstructibleNonMoveAssignable&) = delete; + }; + //********************************************* // Trivially destructible, but non-copyable and non-movable. // Exercises the trivially-destructible (variadic_union) emplace path. @@ -420,6 +440,109 @@ namespace int value; }; + + //********************************************* + // Non-trivially destructible type whose copy/move ASSIGNMENT has a + // distinguishable effect from copy/move CONSTRUCTION. Used to verify that + // same-type variant assignment dispatches through the alternative's own + // assignment operator (no destroy + reconstruct). Exercises the + // uninitialized_buffer (non-trivial) path. + struct AssignTracker + { + AssignTracker(int value_) + : value(value_) + , constructed(true) + , copy_assigned(false) + , move_assigned(false) + { + } + + AssignTracker(const AssignTracker& other) + : value(other.value) + , constructed(true) + , copy_assigned(false) + , move_assigned(false) + { + } + + AssignTracker(AssignTracker&& other) + : value(other.value) + , constructed(true) + , copy_assigned(false) + , move_assigned(false) + { + } + + ~AssignTracker() {} + + AssignTracker& operator=(const AssignTracker& rhs) + { + value = rhs.value; + copy_assigned = true; + move_assigned = false; + return *this; + } + + AssignTracker& operator=(AssignTracker&& rhs) + { + value = rhs.value; + copy_assigned = false; + move_assigned = true; + return *this; + } + + int value; + bool constructed; + bool copy_assigned; + bool move_assigned; + }; + + //********************************************* + // Trivially destructible variant of AssignTracker (no user destructor) that + // exercises the trivially-destructible (variadic_union) path. + struct TrivialAssignTracker + { + TrivialAssignTracker(int value_) + : value(value_) + , copy_assigned(false) + , move_assigned(false) + { + } + + TrivialAssignTracker(const TrivialAssignTracker& other) + : value(other.value) + , copy_assigned(false) + , move_assigned(false) + { + } + + TrivialAssignTracker(TrivialAssignTracker&& other) + : value(other.value) + , copy_assigned(false) + , move_assigned(false) + { + } + + TrivialAssignTracker& operator=(const TrivialAssignTracker& rhs) + { + value = rhs.value; + copy_assigned = true; + move_assigned = false; + return *this; + } + + TrivialAssignTracker& operator=(TrivialAssignTracker&& rhs) + { + value = rhs.value; + copy_assigned = false; + move_assigned = true; + return *this; + } + + int value; + bool copy_assigned; + bool move_assigned; + }; } // namespace // Moved from the top of the file otherwise clang has issues with @@ -560,6 +683,26 @@ struct etl::is_move_constructible : public etl::false_type template <> struct etl::is_move_constructible : public etl::true_type { +}; + +template <> +struct etl::is_copy_constructible : public etl::true_type +{ +}; + +template <> +struct etl::is_move_constructible : public etl::true_type +{ +}; + +template <> +struct etl::is_copy_constructible : public etl::true_type +{ +}; + +template <> +struct etl::is_move_constructible : public etl::true_type +{ }; #endif @@ -999,6 +1142,146 @@ namespace CHECK_EQUAL(variant_1_etl.index(), variant_2_etl.index()); } + //************************************************************************* + // Issue #1512: a variant must only be copy constructible / copy assignable + // when every alternative is copy constructible. Moveable is move-only + // (its copy operations are deleted), so a variant containing it must not + // be copyable, while remaining move constructible / move assignable. + TEST(test_copy_disabled_with_non_copyable_alternative) + { + typedef etl::variant non_copyable_variant; + + CHECK(!etl::is_copy_constructible::value); + CHECK(!etl::is_copy_assignable::value); + CHECK(etl::is_move_constructible::value); + CHECK(etl::is_move_assignable::value); + + // A variant of purely copyable alternatives must remain copyable. + typedef etl::variant copyable_variant; + + CHECK(etl::is_copy_constructible::value); + CHECK(etl::is_copy_assignable::value); + } + + //************************************************************************* + // A non-copy-assignable alternative must disable the variant's copy + // assignment operator, and a non-move-assignable alternative must disable + // the variant's move assignment operator, while leaving the corresponding + // constructors available. + TEST(test_assignment_disabled_with_non_assignable_alternative) + { + typedef etl::variant copy_non_assignable_variant; + + CHECK(etl::is_copy_constructible::value); + CHECK(!etl::is_copy_assignable::value); + + typedef etl::variant move_non_assignable_variant; + + CHECK(etl::is_move_constructible::value); + CHECK(!etl::is_move_assignable::value); + } + + //************************************************************************* + // When source and destination hold the SAME alternative, assignment must + // dispatch through that alternative's own assignment operator rather than + // destroying and reconstructing it. Non-trivially-destructible path. + TEST(test_copy_assign_same_type_uses_alternative_assignment) + { + typedef etl::variant tracker_variant; + + tracker_variant variant_1(etl::in_place_type_t{}, 1); + tracker_variant variant_2(etl::in_place_type_t{}, 2); + + variant_1 = variant_2; + + CHECK_EQUAL(1U, variant_1.index()); + CHECK_EQUAL(2, etl::get(variant_1).value); + CHECK(etl::get(variant_1).copy_assigned); + CHECK(!etl::get(variant_1).move_assigned); + } + + //************************************************************************* + TEST(test_move_assign_same_type_uses_alternative_assignment) + { + typedef etl::variant tracker_variant; + + tracker_variant variant_1(etl::in_place_type_t{}, 1); + tracker_variant variant_2(etl::in_place_type_t{}, 2); + + variant_1 = etl::move(variant_2); + + CHECK_EQUAL(1U, variant_1.index()); + CHECK_EQUAL(2, etl::get(variant_1).value); + CHECK(!etl::get(variant_1).copy_assigned); + CHECK(etl::get(variant_1).move_assigned); + } + + //************************************************************************* + // When source and destination hold DIFFERENT alternatives, assignment must + // still destroy and reconstruct. + TEST(test_copy_assign_different_type_reconstructs) + { + typedef etl::variant tracker_variant; + + tracker_variant variant_1(etl::in_place_type_t{}, 5); + tracker_variant variant_2(etl::in_place_type_t{}, 2); + + variant_1 = variant_2; + + CHECK_EQUAL(1U, variant_1.index()); + CHECK_EQUAL(2, etl::get(variant_1).value); + CHECK(etl::get(variant_1).constructed); + CHECK(!etl::get(variant_1).copy_assigned); + CHECK(!etl::get(variant_1).move_assigned); + } + + //************************************************************************* + // Same as above but for the trivially-destructible (variadic_union) path. + TEST(test_assign_same_type_uses_alternative_assignment_trivial) + { + typedef etl::variant tracker_variant; + + tracker_variant variant_1(etl::in_place_type_t{}, 1); + tracker_variant variant_2(etl::in_place_type_t{}, 2); + + variant_1 = variant_2; + + CHECK_EQUAL(1U, variant_1.index()); + CHECK_EQUAL(2, etl::get(variant_1).value); + CHECK(etl::get(variant_1).copy_assigned); + CHECK(!etl::get(variant_1).move_assigned); + + tracker_variant variant_3(etl::in_place_type_t{}, 3); + variant_1 = etl::move(variant_3); + + CHECK_EQUAL(1U, variant_1.index()); + CHECK_EQUAL(3, etl::get(variant_1).value); + CHECK(etl::get(variant_1).move_assigned); + } + + //************************************************************************* + // Issue #1512: even though the copy operations are disabled at compile + // time, a move-only variant must still move construct / move assign + // correctly, preserving the active alternative. + TEST(test_move_only_variant_moves_correctly) + { + typedef etl::variant non_copyable_variant; + + non_copyable_variant variant_1(etl::in_place_type_t{}); + CHECK_EQUAL(1U, variant_1.index()); + + // Move construction preserves the active alternative. + non_copyable_variant variant_2(etl::move(variant_1)); + CHECK_EQUAL(1U, variant_2.index()); + CHECK(etl::get(variant_2).moved_to); + + // Move assignment preserves the active alternative. + non_copyable_variant variant_3; + variant_3 = etl::move(variant_2); + CHECK_EQUAL(1U, variant_3.index()); + CHECK(etl::get(variant_3).moved_to); + } + //************************************************************************* TEST(test_assign_from_value) { From 22686f869eb047b31a195285ce44c5d18948d9ec Mon Sep 17 00:00:00 2001 From: Murat Kilivan Date: Sun, 19 Jul 2026 22:17:48 +0100 Subject: [PATCH 09/38] ci: bump actions/checkout to v6.0.3 (#1517) GitHub Actions runners now force Node.js 24; actions/checkout@v4 still targets Node.js 20 and triggers a deprecation warning. Pin all workflows to v6.0.3, the latest stable release with native Node.js 24 support. --- .github/workflows/bazel-gcc-c++23-no-stl.yml | 2 +- .github/workflows/clang-c++11.yml | 6 +-- .github/workflows/clang-c++14.yml | 6 +-- .github/workflows/clang-c++17.yml | 6 +-- .github/workflows/clang-c++20.yml | 16 +++--- .github/workflows/clang-c++23.yml | 16 +++--- .github/workflows/clang-c++26.yml | 18 +++---- .github/workflows/clang-format.yaml | 2 +- .github/workflows/clang-format_update.yaml | 2 +- .github/workflows/clang-syntax-checks.yml | 52 +++++++++---------- .github/workflows/clang-tidy.yaml | 2 +- .github/workflows/coverage.yml | 2 +- .../workflows/deploy-etl-documentation.yml | 2 +- .github/workflows/gcc-c++11.yml | 4 +- .github/workflows/gcc-c++14.yml | 4 +- .github/workflows/gcc-c++17.yml | 4 +- .github/workflows/gcc-c++20.yml | 8 +-- .github/workflows/gcc-c++23-armhf.yml | 2 +- .github/workflows/gcc-c++23-i386.yml | 2 +- .github/workflows/gcc-c++23-powerpc.yml | 2 +- .github/workflows/gcc-c++23-riscv64.yml | 2 +- .github/workflows/gcc-c++23-s390x.yml | 2 +- .github/workflows/gcc-c++23.yml | 8 +-- .github/workflows/gcc-c++26.yml | 8 +-- .github/workflows/gcc-syntax-checks.yml | 52 +++++++++---------- .github/workflows/generator.yml | 2 +- .github/workflows/meson-gcc-c++23-no-stl.yml | 2 +- .github/workflows/msvc.yml | 8 +-- .github/workflows/platformio-update.yml | 2 +- 29 files changed, 122 insertions(+), 122 deletions(-) diff --git a/.github/workflows/bazel-gcc-c++23-no-stl.yml b/.github/workflows/bazel-gcc-c++23-no-stl.yml index b41765a2..06a94a9f 100644 --- a/.github/workflows/bazel-gcc-c++23-no-stl.yml +++ b/.github/workflows/bazel-gcc-c++23-no-stl.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: bazel build //test:etl_tests --cxxopt=-std=c++23 --copt=-DETL_NO_STL diff --git a/.github/workflows/clang-c++11.yml b/.github/workflows/clang-c++11.yml index edf2f2e5..12fe33b5 100644 --- a/.github/workflows/clang-c++11.yml +++ b/.github/workflows/clang-c++11.yml @@ -16,7 +16,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -38,7 +38,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Install libc++ run: | @@ -67,7 +67,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | diff --git a/.github/workflows/clang-c++14.yml b/.github/workflows/clang-c++14.yml index b11f3407..7735118a 100644 --- a/.github/workflows/clang-c++14.yml +++ b/.github/workflows/clang-c++14.yml @@ -16,7 +16,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -38,7 +38,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Install libc++ run: | @@ -67,7 +67,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | diff --git a/.github/workflows/clang-c++17.yml b/.github/workflows/clang-c++17.yml index 917d8da0..73df80e8 100644 --- a/.github/workflows/clang-c++17.yml +++ b/.github/workflows/clang-c++17.yml @@ -16,7 +16,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -38,7 +38,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Install libc++ run: | @@ -67,7 +67,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | diff --git a/.github/workflows/clang-c++20.yml b/.github/workflows/clang-c++20.yml index 681d03c0..07bfede7 100644 --- a/.github/workflows/clang-c++20.yml +++ b/.github/workflows/clang-c++20.yml @@ -16,7 +16,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 # Temporary fix. See https://github.com/actions/runner-images/issues/8659 - name: Install newer Clang @@ -50,7 +50,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 # Temporary fix. See https://github.com/actions/runner-images/issues/8659 - name: Install newer Clang @@ -79,7 +79,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 # Temporary fix. See https://github.com/actions/runner-images/issues/8659 - name: Install newer Clang @@ -108,7 +108,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 # Temporary fix. See https://github.com/actions/runner-images/issues/8659 - name: Install newer Clang @@ -137,7 +137,7 @@ jobs: os: [macos-15] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -159,7 +159,7 @@ jobs: os: [macos-15] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -181,7 +181,7 @@ jobs: os: [macos-15] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -203,7 +203,7 @@ jobs: os: [macos-15] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | diff --git a/.github/workflows/clang-c++23.yml b/.github/workflows/clang-c++23.yml index 2cdb909d..f2125e8e 100644 --- a/.github/workflows/clang-c++23.yml +++ b/.github/workflows/clang-c++23.yml @@ -16,7 +16,7 @@ jobs: os: [ubuntu-24.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Install libc++ run: | @@ -45,7 +45,7 @@ jobs: os: [ubuntu-24.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -67,7 +67,7 @@ jobs: os: [ubuntu-24.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -89,7 +89,7 @@ jobs: os: [ubuntu-24.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -111,7 +111,7 @@ jobs: os: [macos-15] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -133,7 +133,7 @@ jobs: os: [macos-15] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -155,7 +155,7 @@ jobs: os: [macos-15] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -177,7 +177,7 @@ jobs: os: [macos-15] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | diff --git a/.github/workflows/clang-c++26.yml b/.github/workflows/clang-c++26.yml index 61dc931f..3a6fc8bc 100644 --- a/.github/workflows/clang-c++26.yml +++ b/.github/workflows/clang-c++26.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -38,7 +38,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -60,7 +60,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -82,7 +82,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -104,7 +104,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -129,7 +129,7 @@ jobs: os: [macos-26] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -151,7 +151,7 @@ jobs: os: [macos-26] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -173,7 +173,7 @@ jobs: os: [macos-26] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -195,7 +195,7 @@ jobs: os: [macos-26] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | diff --git a/.github/workflows/clang-format.yaml b/.github/workflows/clang-format.yaml index 083f265f..b4dd04a0 100644 --- a/.github/workflows/clang-format.yaml +++ b/.github/workflows/clang-format.yaml @@ -12,7 +12,7 @@ jobs: name: clang-format runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 with: fetch-depth: 0 diff --git a/.github/workflows/clang-format_update.yaml b/.github/workflows/clang-format_update.yaml index acaa3453..a3d61e92 100644 --- a/.github/workflows/clang-format_update.yaml +++ b/.github/workflows/clang-format_update.yaml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6.0.3 - name: Install clang-format run: | diff --git a/.github/workflows/clang-syntax-checks.yml b/.github/workflows/clang-syntax-checks.yml index 2c6000e7..742be83c 100644 --- a/.github/workflows/clang-syntax-checks.yml +++ b/.github/workflows/clang-syntax-checks.yml @@ -15,7 +15,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -33,7 +33,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -51,7 +51,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -69,7 +69,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -87,7 +87,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -105,7 +105,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -123,7 +123,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -141,7 +141,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -159,7 +159,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -177,7 +177,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -195,7 +195,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -213,7 +213,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -231,7 +231,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -249,7 +249,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -267,7 +267,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -285,7 +285,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -303,7 +303,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -321,7 +321,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -339,7 +339,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -357,7 +357,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -375,7 +375,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -393,7 +393,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -408,7 +408,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -428,7 +428,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -448,7 +448,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -468,7 +468,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index 214d30b2..d580f6ef 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -12,7 +12,7 @@ jobs: name: clang-tidy runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 with: fetch-depth: 0 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 5c05ebef..a8f19c75 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Install dependencies run: | diff --git a/.github/workflows/deploy-etl-documentation.yml b/.github/workflows/deploy-etl-documentation.yml index b7636de1..597bfa9d 100644 --- a/.github/workflows/deploy-etl-documentation.yml +++ b/.github/workflows/deploy-etl-documentation.yml @@ -15,7 +15,7 @@ jobs: steps: # Check out the repository code - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6.0.3 with: submodules: true fetch-depth: 0 diff --git a/.github/workflows/gcc-c++11.yml b/.github/workflows/gcc-c++11.yml index c9706aeb..67ca6a1b 100644 --- a/.github/workflows/gcc-c++11.yml +++ b/.github/workflows/gcc-c++11.yml @@ -16,7 +16,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -39,7 +39,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | diff --git a/.github/workflows/gcc-c++14.yml b/.github/workflows/gcc-c++14.yml index a3277b66..1d875a9e 100644 --- a/.github/workflows/gcc-c++14.yml +++ b/.github/workflows/gcc-c++14.yml @@ -16,7 +16,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -38,7 +38,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | diff --git a/.github/workflows/gcc-c++17.yml b/.github/workflows/gcc-c++17.yml index dbeef7fa..977e3700 100644 --- a/.github/workflows/gcc-c++17.yml +++ b/.github/workflows/gcc-c++17.yml @@ -16,7 +16,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -38,7 +38,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | diff --git a/.github/workflows/gcc-c++20.yml b/.github/workflows/gcc-c++20.yml index be4e6fb2..0439fc7a 100644 --- a/.github/workflows/gcc-c++20.yml +++ b/.github/workflows/gcc-c++20.yml @@ -16,7 +16,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -38,7 +38,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -60,7 +60,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -82,7 +82,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | diff --git a/.github/workflows/gcc-c++23-armhf.yml b/.github/workflows/gcc-c++23-armhf.yml index 1bd2912b..277fcb05 100644 --- a/.github/workflows/gcc-c++23-armhf.yml +++ b/.github/workflows/gcc-c++23-armhf.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-armhf -f .devcontainer/armhf/Dockerfile . diff --git a/.github/workflows/gcc-c++23-i386.yml b/.github/workflows/gcc-c++23-i386.yml index 31bfc331..7fb1f9db 100644 --- a/.github/workflows/gcc-c++23-i386.yml +++ b/.github/workflows/gcc-c++23-i386.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-i386 -f .devcontainer/i386/Dockerfile . diff --git a/.github/workflows/gcc-c++23-powerpc.yml b/.github/workflows/gcc-c++23-powerpc.yml index 7494ae55..4467414f 100644 --- a/.github/workflows/gcc-c++23-powerpc.yml +++ b/.github/workflows/gcc-c++23-powerpc.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-powerpc -f .devcontainer/powerpc/Dockerfile . diff --git a/.github/workflows/gcc-c++23-riscv64.yml b/.github/workflows/gcc-c++23-riscv64.yml index 695056a8..0c3a2d6c 100644 --- a/.github/workflows/gcc-c++23-riscv64.yml +++ b/.github/workflows/gcc-c++23-riscv64.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-riscv64 -f .devcontainer/riscv64/Dockerfile . diff --git a/.github/workflows/gcc-c++23-s390x.yml b/.github/workflows/gcc-c++23-s390x.yml index f6a15aca..8e022473 100644 --- a/.github/workflows/gcc-c++23-s390x.yml +++ b/.github/workflows/gcc-c++23-s390x.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-s390x -f .devcontainer/s390x/Dockerfile . diff --git a/.github/workflows/gcc-c++23.yml b/.github/workflows/gcc-c++23.yml index 842b6152..fd933127 100644 --- a/.github/workflows/gcc-c++23.yml +++ b/.github/workflows/gcc-c++23.yml @@ -16,7 +16,7 @@ jobs: os: [ubuntu-24.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -38,7 +38,7 @@ jobs: os: [ubuntu-24.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -60,7 +60,7 @@ jobs: os: [ubuntu-24.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -82,7 +82,7 @@ jobs: os: [ubuntu-24.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | diff --git a/.github/workflows/gcc-c++26.yml b/.github/workflows/gcc-c++26.yml index b7ff42d3..76a6075f 100644 --- a/.github/workflows/gcc-c++26.yml +++ b/.github/workflows/gcc-c++26.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -35,7 +35,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -57,7 +57,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -79,7 +79,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . diff --git a/.github/workflows/gcc-syntax-checks.yml b/.github/workflows/gcc-syntax-checks.yml index b6b77bad..01f69e55 100644 --- a/.github/workflows/gcc-syntax-checks.yml +++ b/.github/workflows/gcc-syntax-checks.yml @@ -15,7 +15,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -33,7 +33,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -51,7 +51,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -69,7 +69,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -87,7 +87,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -105,7 +105,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -123,7 +123,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -141,7 +141,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -159,7 +159,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -177,7 +177,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -195,7 +195,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -213,7 +213,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -231,7 +231,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -249,7 +249,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -267,7 +267,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -285,7 +285,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -303,7 +303,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -321,7 +321,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -339,7 +339,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -357,7 +357,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -375,7 +375,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -393,7 +393,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -408,7 +408,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -428,7 +428,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -448,7 +448,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -468,7 +468,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . diff --git a/.github/workflows/generator.yml b/.github/workflows/generator.yml index 1829c342..b40dda0a 100644 --- a/.github/workflows/generator.yml +++ b/.github/workflows/generator.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Install dependencies run: | diff --git a/.github/workflows/meson-gcc-c++23-no-stl.yml b/.github/workflows/meson-gcc-c++23-no-stl.yml index d8767317..bf3c2e76 100644 --- a/.github/workflows/meson-gcc-c++23-no-stl.yml +++ b/.github/workflows/meson-gcc-c++23-no-stl.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.3 - name: Install Meson run: sudo apt-get install -y meson diff --git a/.github/workflows/msvc.yml b/.github/workflows/msvc.yml index 32d7aee8..d72f2477 100644 --- a/.github/workflows/msvc.yml +++ b/.github/workflows/msvc.yml @@ -13,7 +13,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6.0.3 with: submodules: recursive @@ -34,7 +34,7 @@ jobs: runs-on: [windows-2022] steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6.0.3 with: submodules: recursive @@ -56,7 +56,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6.0.3 with: submodules: recursive @@ -78,7 +78,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6.0.3 with: submodules: recursive diff --git a/.github/workflows/platformio-update.yml b/.github/workflows/platformio-update.yml index 318cf953..04e64889 100644 --- a/.github/workflows/platformio-update.yml +++ b/.github/workflows/platformio-update.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6.0.3 - name: Set up Python uses: actions/setup-python@v5 From 08b9c371a0272a753bc76ceaf4531f20a0a07550 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Mon, 20 Jul 2026 11:19:41 +0200 Subject: [PATCH 10/38] Various fixes and cleanup (#1507) * Fix histogram test Use different Start index so skipped start_index copy would not get unnoticed in the test. * Fix several test_vector_* for valid data Instead of comparing the moved-from data which is invalid, compare with the valid initial_data. * Fix test_vector to check for the correct iterator position after erase This was done before, but a recent change dropped it. * Fix bit_stream: Guard nbits > 0 to prevent division by zero * Cleanup --------- Co-authored-by: John Wellbelove --- docs/IO/buffer_descriptors.md | 2 +- docs/_index.md | 2 +- .../determining-line-line-intersections.md | 2 +- docs/callbacks/function.md | 2 +- docs/callbacks/signal.md | 2 +- docs/chrono/duration/duration.md | 2 +- docs/chrono/month/month_weekday.md | 20 +++++++++---------- docs/chrono/year/year_month_day_last.md | 2 +- docs/containers/Views/span.md | 2 +- docs/containers/pools/generic-pool.md | 2 +- docs/containers/pools/pool.md | 2 +- docs/containers/sets/const-multiset.md | 2 +- docs/containers/sets/const-set.md | 2 +- docs/containers/variadics/_index.md | 2 +- docs/containers/variadics/packet.md | 2 +- docs/getting-started/contribute.md | 6 +++--- docs/getting-started/macros.md | 12 +++++------ docs/iterators/fixed_iterator.md | 10 +++++----- docs/messaging/message-bus.md | 2 +- docs/releases/_index.md | 12 +++++------ docs/timers/_index.md | 2 +- docs/timers/callback-timer-interrupt.md | 2 +- docs/timers/message-timer-interrupt.md | 2 +- docs/timers/timer.md | 2 +- docs/tutorials/callback-service.md | 4 ++-- docs/tutorials/delegate-service.md | 4 ++-- docs/tutorials/shared-message_tutorial.md | 4 ++-- docs/utilities/bit-stream-reader.md | 6 +++--- docs/utilities/bit-stream-writer.md | 6 +++--- docs/utilities/endian.md | 4 ++-- docs/utilities/error-handler.md | 2 +- docs/utilities/initializer-list.md | 2 +- include/etl/bit_stream.h | 4 +++- include/etl/debounce.h | 5 ++++- include/etl/private/variant_legacy.h | 2 +- .../etl/private/variant_select_do_operator.h | 2 +- .../etl/private/variant_select_do_visitor.h | 2 +- include/etl/type_traits.h | 2 +- include/etl/unaligned_type.h | 4 ++-- test/test_histogram.cpp | 4 ++-- test/test_vector.cpp | 7 +++++-- test/test_vector_function_pointer.cpp | 2 +- ...ector_function_pointer_external_buffer.cpp | 2 +- test/test_vector_member_function_pointer.cpp | 2 +- ...ember_function_pointer_external_buffer.cpp | 2 +- 45 files changed, 89 insertions(+), 81 deletions(-) diff --git a/docs/IO/buffer_descriptors.md b/docs/IO/buffer_descriptors.md index 4e49278e..df7f77b6 100644 --- a/docs/IO/buffer_descriptors.md +++ b/docs/IO/buffer_descriptors.md @@ -188,7 +188,7 @@ A nested class that is sent to the user defined callback function. notification() ``` **Description** -Default contructor. +Default constructor. Initialises to a default constructed descriptor and a count of zero. --- diff --git a/docs/_index.md b/docs/_index.md index 4fbb737c..a1886a77 100644 --- a/docs/_index.md +++ b/docs/_index.md @@ -39,7 +39,7 @@ type: hextra-home
- coverage + coverage Codacy
diff --git a/docs/blog/graphics/determining-line-line-intersections.md b/docs/blog/graphics/determining-line-line-intersections.md index 279103ae..bcc6b12f 100644 --- a/docs/blog/graphics/determining-line-line-intersections.md +++ b/docs/blog/graphics/determining-line-line-intersections.md @@ -87,7 +87,7 @@ This means you can find intersection points relative to a fixed reference line. ![LineCross2](images/LineCross2.png) -## Refection +## Reflection This describes reflecting a point across a reference line. It uses the intersection method described above. diff --git a/docs/callbacks/function.md b/docs/callbacks/function.md index 7785b0f1..42f83b4e 100644 --- a/docs/callbacks/function.md +++ b/docs/callbacks/function.md @@ -18,7 +18,7 @@ This may be used to implement a platform abstraction layer that allows an embedd | Template | Description | | ----------------- | ------------------------------------------------------------------------- | | etl::function | Callbacks to free or member functions taking zero or one parameter.
Function pointer at runtime. | -| etl::function_fv | Callback to a free function taking no parameters. (Functiion Void).
Function pointer at compile time. | +| etl::function_fv | Callback to a free function taking no parameters. (Function Void).
Function pointer at compile time. | | etl::function_fp | Callback to a free function taking one parameter. (Function Parameter).
Function pointer at compile time. | etl::function_mv | Callback to a member function taking no parameters. (Member Void).
Function pointer at compile time. | | etl::function_imv | Callback to a member function taking no parameters. (Instance Member Void ).
Instance reference and function pointer at compile time. | diff --git a/docs/callbacks/signal.md b/docs/callbacks/signal.md index a8c19efc..53d05534 100644 --- a/docs/callbacks/signal.md +++ b/docs/callbacks/signal.md @@ -15,7 +15,7 @@ template ` ## Types diff --git a/docs/chrono/duration/duration.md b/docs/chrono/duration/duration.md index c75c6a7b..7900e8b3 100644 --- a/docs/chrono/duration/duration.md +++ b/docs/chrono/duration/duration.md @@ -112,7 +112,7 @@ ETL_CONSTEXPR14 etl::chrono::duration operator =(const etl::chrono::duration& other) ETL_NOEXCEPT ``` -### Convertion +### Conversion ```cpp ETL_CONSTEXPR14 TRep count() const diff --git a/docs/chrono/month/month_weekday.md b/docs/chrono/month/month_weekday.md index ab73505e..3d7037c6 100644 --- a/docs/chrono/month/month_weekday.md +++ b/docs/chrono/month/month_weekday.md @@ -20,7 +20,7 @@ class month_weekday ETL_CONSTEXPR month_weekday() ``` -**Decription** +**Description** Default constructor. --- @@ -31,7 +31,7 @@ month_weekday(const etl::chrono::month& m, const etl::chrono::weekday_indexed& wd) ETL_NOEXCEPT ``` -**Decription** +**Description** Construct from month and weekday_indexed. ## Access @@ -75,7 +75,7 @@ bool operator ==(const etl::chrono::month_weekday& lhs, const etl::chrono::month_weekday& rhs) ETL_NOEXCEPT ``` -**Decription** +**Description** Equality operator. --- @@ -86,7 +86,7 @@ bool operator !=(const etl::chrono::month_weekday& lhs, const etl::chrono::month_weekday& rhs) ETL_NOEXCEPT ``` -**Decription** +**Description** Inequality operator. --- @@ -97,7 +97,7 @@ bool operator <(const etl::chrono::month_weekday& lhs, const etl::chrono::month_weekday& rhs) ETL_NOEXCEPT ``` -**Decription** +**Description** Less-than operator. --- @@ -108,7 +108,7 @@ bool operator <=(const etl::chrono::month_weekday& lhs, const etl::chrono::month_weekday& rhs) ETL_NOEXCEPT ``` -**Decription** +**Description** Less-than-equal operator. --- @@ -119,7 +119,7 @@ bool operator <(const etl::chrono::month_weekday& lhs, const etl::chrono::month_weekday& rhs) ETL_NOEXCEPT ``` -**Decription** +**Description** Greater-than operator. --- @@ -130,7 +130,7 @@ bool operator <=(const etl::chrono::month_weekday& lhs, const etl::chrono::month_weekday& rhs) ETL_NOEXCEPT ``` -**Decription** +**Description** Greater-than-equal operator. --- @@ -141,7 +141,7 @@ auto operator <=>(const etl::chrono::month_weekday& lhs, const etl::chrono::month_weekday& rhs) ETL_NOEXCEPT ``` -**Decription** +**Description** Spaceship operator. C++20 @@ -151,6 +151,6 @@ C++20 template struct hash ``` -**Decription** +**Description** Hash function for `etl::chrono::month_weekday`. diff --git a/docs/chrono/year/year_month_day_last.md b/docs/chrono/year/year_month_day_last.md index 426a51ab..dc19db52 100644 --- a/docs/chrono/year/year_month_day_last.md +++ b/docs/chrono/year/year_month_day_last.md @@ -56,7 +56,7 @@ The year. --- -```coo +```cpp ETL_NODISCARD ETL_CONSTEXPR14 etl::chrono::month month() const diff --git a/docs/containers/Views/span.md b/docs/containers/Views/span.md index 8fb131f7..9e619b4e 100644 --- a/docs/containers/Views/span.md +++ b/docs/containers/Views/span.md @@ -356,7 +356,7 @@ bool equal(const etl::span& lhs, const etl::span& rhs) ``` **Description** Equality function. -Performs a comparision of the range values. +Performs a comparison of the range values. Returns `true` if one of the following are `true` 1. Both spans are empty. 2. They both point to the same range of data. diff --git a/docs/containers/pools/generic-pool.md b/docs/containers/pools/generic-pool.md index eb400d36..d4dba97b 100644 --- a/docs/containers/pools/generic-pool.md +++ b/docs/containers/pools/generic-pool.md @@ -72,7 +72,7 @@ generic_pool_ext(char* buffer, size_t size) ``` **Description** For `etl::generic_pool_ext`. -Constructs a generic_pool from an external bufffer. +Constructs a generic_pool from an external buffer. No elements are constructed. ## Operations diff --git a/docs/containers/pools/pool.md b/docs/containers/pools/pool.md index 4b1e6fbf..4f6b1c6e 100644 --- a/docs/containers/pools/pool.md +++ b/docs/containers/pools/pool.md @@ -94,7 +94,7 @@ pool_ext(char* buffer, size_t size) ``` **Description** For `etl::pool_ext`. -Constructs a pool from an external bufffer. +Constructs a pool from an external buffer. No elements are constructed. ## Operations diff --git a/docs/containers/sets/const-multiset.md b/docs/containers/sets/const-multiset.md index 8d8fc79a..f4200dad 100644 --- a/docs/containers/sets/const-multiset.md +++ b/docs/containers/sets/const-multiset.md @@ -17,7 +17,7 @@ Uses `etl::less` as the default key comparison method. etl::const_multiset ``` -**Enternal storage** +**Internal storage** ```cpp etl::const_multiset_ext ``` diff --git a/docs/containers/sets/const-set.md b/docs/containers/sets/const-set.md index 706eef0e..100549f7 100644 --- a/docs/containers/sets/const-set.md +++ b/docs/containers/sets/const-set.md @@ -17,7 +17,7 @@ Uses `etl::less` as the default key comparison method. etl::const_set ``` -**Enternal storage** +**Internal storage** ```cpp etl::const_set_ext ``` diff --git a/docs/containers/variadics/_index.md b/docs/containers/variadics/_index.md index 7c051c71..3789c5e4 100644 --- a/docs/containers/variadics/_index.md +++ b/docs/containers/variadics/_index.md @@ -3,4 +3,4 @@ title: "Variadics" weight: 100 --- -Containers tha accept multiple types. \ No newline at end of file +Containers that accept multiple types. \ No newline at end of file diff --git a/docs/containers/variadics/packet.md b/docs/containers/variadics/packet.md index 4533be50..9a420646 100644 --- a/docs/containers/variadics/packet.md +++ b/docs/containers/variadics/packet.md @@ -22,7 +22,7 @@ etl::packet `base_t` = `TBase` -## Contructors +## Constructors **C++03** ```cpp diff --git a/docs/getting-started/contribute.md b/docs/getting-started/contribute.md index 003fbef7..4d75959f 100644 --- a/docs/getting-started/contribute.md +++ b/docs/getting-started/contribute.md @@ -30,7 +30,7 @@ Lower case class and function names, separated by an underscore. `funky_container` `do_something` -### Nameing convension +### Naming convention Use the same naming convention as the STL. If appropriate for the class, a set of `typedef`s should often be present to define types such as... @@ -159,7 +159,7 @@ class my_new_class ``` ### Layout -When possible, try to tabluate the initialisation of multiple variables. It's a lot easier to read. +When possible, try to tabulate the initialisation of multiple variables. It's a lot easier to read. Don't be too strict about it, if it would result in giant amounts of whitespace. ```cpp @@ -240,4 +240,4 @@ When opening a new pull request, ensure that you include the following informati - Branches should be based on the branch `master`. `development` can change quite frequently, so I will rebase the your PR against it before merging. - For formatting help, you can use `clang-format`, or the convenience wrapper `treefmt`. See also [Source formatting]({{% relref "source-formatting.md" %}}). -- Update the relevent documentation markdown file, found in `etl/docs`. \ No newline at end of file +- Update the relevant documentation markdown file, found in `etl/docs`. \ No newline at end of file diff --git a/docs/getting-started/macros.md b/docs/getting-started/macros.md index 3444a3a9..23a6f37d 100644 --- a/docs/getting-started/macros.md +++ b/docs/getting-started/macros.md @@ -83,19 +83,19 @@ These may be defined in the project settings or the user created `etl_profile.h` | `ETL_NODISCARD` | If `ETL_CPP11_SUPPORTED` is defined as `1` then this macro is defined as `[[nodiscard]]`, otherwise defined as blank.| | `ETL_DEPRECATED` | If `ETL_CPP14_SUPPORTED` is defined as `1` then this macro is defined as `[[deprecated]]`, otherwise defined as blank.| | `ETL_DEPRECATED_REASON(reason)` | If `ETL_CPP14_SUPPORTED` is defined as `1` then this macro is defined as `[[deprecated(reason)]]`, otherwise defined as blank.| -| `ETL_FALLTHROUGH` | If `ETL_CPP17_SUPPORTED` is defined as `1` then this macro is defined as `[[falltrough]]`, otherwise defined as blank.| +| `ETL_FALLTHROUGH` | If `ETL_CPP17_SUPPORTED` is defined as `1` then this macro is defined as `[[fallthrough]]`, otherwise defined as blank.| | `ETL_NORETURN` | If `ETL_CPP11_SUPPORTED` is defined as `1` then this macro is defined as `[[noreturn]]`, otherwise defined as blank.| | `ETL_OR_STD` | If ETL_NO_STL is defined and ETL_IN_UNIT_TEST is not then `ETL_OR_STD` is defined as `etl`, otherwise it is defined as `std`.| | `ETL_IN_UNIT_TEST` | If defined, then the code is being compiled in the unit tests.
For internal ETL use only.| | `ETL_HAS_ATOMIC` | This is defined as `1` if the compiler supplies an atomic class, otherwise `0`.| | `ETL_INLINE_VAR` | If `ETL_CPP17_SUPPORTED` is defined as `1` then this macro is defined as `inline`, otherwise defined as blank.| -| `ETL_USING_STL` | This macro will be defined as `0` & `1` dependant of whether `ETL_NO_STL` is defined or not.| +| `ETL_USING_STL` | This macro will be defined as `0` & `1` depending on whether `ETL_NO_STL` is defined or not.| | `ETL_NOT_USING_STL` | Inversion of `ETL_USING_STL`.| -| `ETL_USING_STLPORT` | This macro will be defined as `0` & `1` dependant of whether `ETL_STLPORT` is defined or not.| +| `ETL_USING_STLPORT` | This macro will be defined as `0` & `1` depending on whether `ETL_STLPORT` is defined or not.| | `ETL_NOT_USING_STLPORT` | Inversion of `ETL_USING_STLPORT`.| -| `ETL_USING_8BIT_TYPES` | This macro will be defined as `0` & `1` dependant of whether `CHAR_BIT == 8` or not.| +| `ETL_USING_8BIT_TYPES` | This macro will be defined as `0` & `1` depending on whether `CHAR_BIT == 8` or not.| | `ETL_NOT_USING_8BIT_TYPES` | Inversion of `ETL_USING_8BIT_TYPES`.| -| `ETL_USING_64BIT_TYPES` | This macro will be defined as `0` & `1` dependant of whether `ETL_NO_64BIT_TYPES` is defined or not.| +| `ETL_USING_64BIT_TYPES` | This macro will be defined as `0` & `1` depending on whether `ETL_NO_64BIT_TYPES` is defined or not.| | `ETL_NOT_USING_64BIT_TYPES` | Inversion of `ETL_USING_64BIT_TYPES`.| | `ETL_HAS_ISTRING_REPAIR` | Set to `1` if the repair functionality for `etl::istring` is enabled, otherwise `0`.| | `ETL_HAS_IVECTOR_REPAIR` | Set to `1` if the repair functionality for `etl::ivector` is enabled, otherwise `0`.| @@ -103,7 +103,7 @@ These may be defined in the project settings or the user created `etl_profile.h` | `ETL_IS_DEBUG_BUILD` | Set to `1` if in a debug build, otherwise `0`.| | `ETL_HAS_POLYMORPHIC_MESSAGES` | Set to `1` if messages are polymorphic, otherwise `0`.| | `ETL_HAS_ERROR_ON_STRING_TRUNCATION` | Set to `1` if truncated strings are an error, otherwise `0`.| -| `ETL_USING_LIBC_WCHAR_H` | These macros will be defined as `0` & `1` dependant of whether `ETL_NO_LIBC_WCHAR_H` is defined or not.| +| `ETL_USING_LIBC_WCHAR_H` | These macros will be defined as `0` & `1` depending on whether `ETL_NO_LIBC_WCHAR_H` is defined or not.| | `ETL_NOT_USING_LIBC_WCHAR_H` | Inversion of `ETL_USING_LIBC_WCHAR_H`.| | `ETL_USING_CPP11` | This is defined as `1` if the compiler supports C++11, otherwise `0`.| | `ETL_USING_CPP14` | This is defined as `1` if the compiler supports C++14, otherwise `0`.| diff --git a/docs/iterators/fixed_iterator.md b/docs/iterators/fixed_iterator.md index 207a16d5..033081cc 100644 --- a/docs/iterators/fixed_iterator.md +++ b/docs/iterators/fixed_iterator.md @@ -30,13 +30,13 @@ etl::fixed_iterator(const etl::fixed_iterator&); ```cpp Titerator get() const; ``` -**Decscription** +**Description** Get the internal iterator. ```cpp void get(TIterator it); ``` -**Decscription** +**Description** Set the iterator. ## Operators @@ -44,7 +44,7 @@ Set the iterator. typename etl::iterator_traits::value_type operator *() const typename etl::iterator_traits::value_type operator *() const ``` -**Decscription** +**Description** Dereference operators --- @@ -53,7 +53,7 @@ Dereference operators TIterator operator ->() const TIterator operator ->() const ``` -**Decscription** +**Description** Member dereference operators --- @@ -61,7 +61,7 @@ Member dereference operators ```cpp operator TIterator() const ``` -**Decscription** +**Description** Conversion operator ## Example diff --git a/docs/messaging/message-bus.md b/docs/messaging/message-bus.md index 921e5b86..7277c01d 100644 --- a/docs/messaging/message-bus.md +++ b/docs/messaging/message-bus.md @@ -177,7 +177,7 @@ bus2.subscribe(routerD); bus2.subscribe(routerC); // Subscribe routerE to bus3. -bus3.subscibe(routerE); +bus3.subscribe(routerE); // Assume all routers accept the same messages. diff --git a/docs/releases/_index.md b/docs/releases/_index.md index 15553d90..aa737d96 100644 --- a/docs/releases/_index.md +++ b/docs/releases/_index.md @@ -73,7 +73,7 @@ Click on the links to see the original Github page. [#1432](https://github.com/ETLCPP/etl/pull/1432) Simplify and fix test/run-syntax-checks.sh [#1433](https://github.com/ETLCPP/etl/pull/1433) Fix the case of using STL without pthreads but with mutexes [#1434](https://github.com/ETLCPP/etl/pull/1434) Remove unnecessary includes -[#1437](https://github.com/ETLCPP/etl/pull/1437) ranges: Fix confict with Zephyr preprocessor definition +[#1437](https://github.com/ETLCPP/etl/pull/1437) ranges: Fix conflict with Zephyr preprocessor definition [#1440](https://github.com/ETLCPP/etl/pull/1440) Make etl::variant capable for ROM placement and optimize runtime size [#1441](https://github.com/ETLCPP/etl/pull/1441) Make etl::variant capable for ROM placement and optimize runtime size [#1442](https://github.com/ETLCPP/etl/pull/1442) Fix format: float zero-padding, nested replacement width, octal alter… @@ -152,7 +152,7 @@ Click on the links to see the original Github page. **Issues:** [#1184](https://github.com/ETLCPP/etl/issues/1184) Request: Adding a method to check whether a etl::callback_timer_atomic is currently running -[#1189](https://github.com/ETLCPP/etl/issues/1189) Request: make useage of osMutexRobust in etl::mutex optional +[#1189](https://github.com/ETLCPP/etl/issues/1189) Request: make usage of osMutexRobust in etl::mutex optional [#1207](https://github.com/ETLCPP/etl/issues/1207) Monadic Operations for etl::expected [#1240](https://github.com/ETLCPP/etl/issues/1240) Add redundant semicolon checks to CMakeLists.txt and fix issues [#1241](https://github.com/ETLCPP/etl/issues/1241) Add -pedantic-errors to unit test CMakeLists.txt and fix issues @@ -335,7 +335,7 @@ Removed redundant ETL_USING_WIDE_CHARACTERS Fixes: #1126 to_arithmetic-does-not-compile-on-C++98 #1128 Request: Make Delegate Methods noexcept -#1130 Use of etl::mem_copy for overlaped areas +#1130 Use of etl::mem_copy for overlapped areas #1132 Make type_def Methods Noexcept #1143 v20.41.x, risc-v (esp32-c3) "No user defined specialisation of etl::underlying_type for this type" @@ -510,11 +510,11 @@ Fixes: #942 Warning on pragma c++20-compat #951 Can't get call_if to work because of etl::optional #956 Fix build error (etl::circular_buffer) -#957 Support heterogenous lookup for maps +#957 Support heterogeneous lookup for maps #959 Treat bitset with size_type #972 Template not allowed warning in parameter pack #979 QueuedMessageRouter does not work : message are not sent to queue -#980 Documentation clarification: Add link from endianess to unaligned_type +#980 Documentation clarification: Add link from endianness to unaligned_type #982 Added return to etl::optional emplace, fixed typo #984 Update Base64 Documentation #986 Added const iterators to span @@ -639,7 +639,7 @@ Pull Requests: #905 Added support for type identity Other: -Reenabled address sanitizer for VS2022 +Re-enabled address sanitizer for VS2022 20.38.16 diff --git a/docs/timers/_index.md b/docs/timers/_index.md index 127fda77..a9129c17 100644 --- a/docs/timers/_index.md +++ b/docs/timers/_index.md @@ -3,4 +3,4 @@ title: "Timers" weight: 100 --- -Classes the implement timer based services. +Classes that implement timer-based services. diff --git a/docs/timers/callback-timer-interrupt.md b/docs/timers/callback-timer-interrupt.md index 45834642..54d3b116 100644 --- a/docs/timers/callback-timer-interrupt.md +++ b/docs/timers/callback-timer-interrupt.md @@ -196,7 +196,7 @@ Test test; //*************************************************************************** // The interrupt guard type. -// Saves and disables on contruction. +// Saves and disables interrupts on construction. // Restores on destruction. //*************************************************************************** struct InterruptGuard diff --git a/docs/timers/message-timer-interrupt.md b/docs/timers/message-timer-interrupt.md index 9cbc6d70..c41dc227 100644 --- a/docs/timers/message-timer-interrupt.md +++ b/docs/timers/message-timer-interrupt.md @@ -193,7 +193,7 @@ Message3 message3; //*************************************************************************** // The interrupt guard type. -// Saves and disables on contruction. +// Saves and disables on construction. // Restores on destruction. //*************************************************************************** struct InterruptGuard diff --git a/docs/timers/timer.md b/docs/timers/timer.md index b09785b3..d0d4ed63 100644 --- a/docs/timers/timer.md +++ b/docs/timers/timer.md @@ -15,7 +15,7 @@ struct timer { // Timer modes. struct mode - { + { enum { SINGLE_SHOT = false, diff --git a/docs/tutorials/callback-service.md b/docs/tutorials/callback-service.md index 972e97c1..fccf4a77 100644 --- a/docs/tutorials/callback-service.md +++ b/docs/tutorials/callback-service.md @@ -19,7 +19,7 @@ This code demonstrates using the callback service for five example ARM interrupt `Timer3` has no entry in the callback service and will therefore trigger execution of the unhandled handler. `USART1` and `USART2` interrupts are handled by instances of Uart. -There callbacks are defined withing the class and are initialised in the Uart constructor. +Their callbacks are defined within the class and are initialised in the Uart constructor. ```cpp #include @@ -146,7 +146,7 @@ Uart uart1(0, USART1_IRQ_HANDLER); Uart uart2(1, USART2_IRQ_HANDLER); // Declare a global callback for the timer. -// Uses the most efficient callback type for a class, as everthing is known at compile time. +// Uses the most efficient callback type for a class, as everything is known at compile time. etl::function_imp timer_member_callback; // Declare the callbacks for the free functions. diff --git a/docs/tutorials/delegate-service.md b/docs/tutorials/delegate-service.md index edb85f68..cc7dd612 100644 --- a/docs/tutorials/delegate-service.md +++ b/docs/tutorials/delegate-service.md @@ -13,7 +13,7 @@ This code demonstrates using the delegate service for five example ARM interrupt `Timer3` has no entry in the callback service and will therefore trigger execution of the unhandled handler. `USART1` and `USART2` interrupts are handled by instances of `Uart`. -There callbacks are defined withing the class and are initialised in the `Uart` constructor. +Their callbacks are defined within the class and are initialised in the `Uart` constructor. ```cpp #include @@ -137,7 +137,7 @@ Uart uart1(0, USART1_IRQ_HANDLER); Uart uart2(1, USART2_IRQ_HANDLER); // Declare a global callback for the timer. -// Uses the most efficient callback type for a class, as everthing is known at compile time. +// Uses the most efficient callback type for a class, as everything is known at compile time. etl::delegate timer_member_callback = etl::delegate::create(); diff --git a/docs/tutorials/shared-message_tutorial.md b/docs/tutorials/shared-message_tutorial.md index f94efb1b..e692a8d7 100644 --- a/docs/tutorials/shared-message_tutorial.md +++ b/docs/tutorials/shared-message_tutorial.md @@ -34,7 +34,7 @@ To this end a template alias is defined; `etl::atomic_counted_message`, which us In a muti-threaded or interrupt driven system, a method must be derived to manage access to the pool. All reference counted messages have an owner. -Owners are obects based on `etl::ireference_counted_message_pool`. +Owners are objects based on `etl::ireference_counted_message_pool`. A reference counted message pool will allocate reference counted messages. The storage for these are allocated from a supplied memory block allocator. @@ -256,7 +256,7 @@ private: }; //***************************************************************************** -// A message bus that can accomodate two subscribers. +// A message bus that can accommodate two subscribers. //***************************************************************************** struct Bus : public etl::message_bus<2U> { diff --git a/docs/utilities/bit-stream-reader.md b/docs/utilities/bit-stream-reader.md index bba3e54c..64acaa66 100644 --- a/docs/utilities/bit-stream-reader.md +++ b/docs/utilities/bit-stream-reader.md @@ -248,15 +248,15 @@ namespace etl etl::optional read_unchecked(etl::bit_stream_reader& stream) { char c = stream.read_unchecked(); - short s = stream.read_unchecked(); - int32_t i = stream.read_unchecked(); + short s = stream.read_unchecked(11U); + int32_t i = stream.read_unchecked(25U); return CustomType { c, s, i }; } } std::array storage; // Assume the buffer gets filled with bit stream data. -etl::bit_stream_reader bit_stream(storage.data(), storage.size()); +etl::bit_stream_reader bit_stream(storage.data(), storage.size(), etl::bit_order::msb_first); // Read unchecked values from the stream. char c = etl::read_unchecked(bit_stream, 6U); diff --git a/docs/utilities/bit-stream-writer.md b/docs/utilities/bit-stream-writer.md index 3d83c0e4..5cc3f4a6 100644 --- a/docs/utilities/bit-stream-writer.md +++ b/docs/utilities/bit-stream-writer.md @@ -328,7 +328,7 @@ Sets the delegate to call after every write. callback_type get_callback() const ``` **Description** -Gets the function to call afer every write. +Gets the function to call after every write. --- @@ -397,13 +397,13 @@ namespace etl void write_unchecked(etl::bit_stream_reader& stream) { stream.write_unchecked(); - stream.write_unchecked(); + stream.write_unchecked(); stream.write_unchecked(); } } std::array storage; // Assume the buffer gets filled with bit stream data. -etl::bit_stream_reader bit_stream(storage.data(), storage.size()); +etl::bit_stream_writer bit_stream(storage.data(), storage.size(), etl::bit_order::msb_first); // Write unchecked values to the stream. etl::write_unchecked(bit_stream, 6U); diff --git a/docs/utilities/endian.md b/docs/utilities/endian.md index 1aa2c969..4de8014d 100644 --- a/docs/utilities/endian.md +++ b/docs/utilities/endian.md @@ -7,11 +7,11 @@ title: "endian" Since: `TBC` {{< /callout >}} -Constants & utilities for endianess. +Constants & utilities for endianness. For endian specific integral types, see `unaligned_type`. -The values for endianness are dependant on the platform setup. +The values for endianness are dependent on the platform setup. If `ETL_ENDIAN_NATIVE` is defined by the user, then  `etl::endian::little = 0` diff --git a/docs/utilities/error-handler.md b/docs/utilities/error-handler.md index ab317500..074b199e 100644 --- a/docs/utilities/error-handler.md +++ b/docs/utilities/error-handler.md @@ -16,7 +16,7 @@ You have a choice of: - Error log - No error checking -The type of error handler used is dependant on the compile time macro defined. +The type of error handler used is dependent on the compile time macro defined. Note: This are usually set as a project wide definition. | Macro | Actions | diff --git a/docs/utilities/initializer-list.md b/docs/utilities/initializer-list.md index 48cd46a1..054e1373 100644 --- a/docs/utilities/initializer-list.md +++ b/docs/utilities/initializer-list.md @@ -18,7 +18,7 @@ The `initializer_list` template class is a special case in that it *must* have a This header will either include the standard `` header, or attempt to define a `std::initializer_list` template class that is compatible with the current compiler. -The header will define `ETL_USING_INITIALIZER_LIST` to either `1` or `0` dependant of whether `std::initializer_list` is available or not. +The header will define `ETL_USING_INITIALIZER_LIST` to either `1` or `0` dependent on whether `std::initializer_list` is available or not. The ETL will select the STL version if you are using the STL and `ETL_FORCE_ETL_INITIALIZER_LIST` is not defined or, `ETL_FORCE_STD_INITIALIZER_LIST` is defined. diff --git a/include/etl/bit_stream.h b/include/etl/bit_stream.h index 8248d9ae..349aa12f 100644 --- a/include/etl/bit_stream.h +++ b/include/etl/bit_stream.h @@ -695,7 +695,7 @@ namespace etl template typename etl::enable_if::value, bool>::type write(T value, uint_least8_t nbits = CHAR_BIT * sizeof(T)) { - bool success = (available(nbits) > 0U); + bool success = (nbits > 0U) && (available(nbits) > 0U); if (success) { @@ -914,6 +914,7 @@ namespace etl template void write_data(T value, uint_least8_t nbits) { + ETL_ASSERT(nbits > 0U, ETL_ERROR_GENERIC("bit_stream_writer::write_data: nbits is zero")); ETL_ASSERT(nbits <= (CHAR_BIT * sizeof(T)), ETL_ERROR_GENERIC("bit_stream_writer::write_data: nbits too large")); // Apply the byte order (endianness). @@ -1305,6 +1306,7 @@ namespace etl template T read_value(uint_least8_t nbits, bool is_signed) { + ETL_ASSERT(nbits > 0U, ETL_ERROR_GENERIC("bit_stream_reader::read_value: nbits is zero")); ETL_ASSERT(nbits <= (CHAR_BIT * sizeof(T)), ETL_ERROR_GENERIC("bit_stream_reader::read_value: nbits too large")); T value = 0; diff --git a/include/etl/debounce.h b/include/etl/debounce.h index 2618d5e3..20a13ab5 100644 --- a/include/etl/debounce.h +++ b/include/etl/debounce.h @@ -536,7 +536,10 @@ namespace etl } //************************************************************************* - /// Constructor. + /// Sets the count thresholds. + ///\param valid The count for a valid state. + ///\param hold The count after valid for a hold state. Default = 0. + ///\param repeat The count after hold for a key repeat. Default = 0. //************************************************************************* void set(count_t valid, count_t hold = 0, count_t repeat = 0) { diff --git a/include/etl/private/variant_legacy.h b/include/etl/private/variant_legacy.h index 59d454d4..585f02e1 100644 --- a/include/etl/private/variant_legacy.h +++ b/include/etl/private/variant_legacy.h @@ -468,7 +468,7 @@ namespace etl explicit variant(etl::in_place_index_t, T const& value) : type_id(Index) { - ETL_STATIC_ASSERT(Type_Id_Lookup::type_id == Index, "Missmatched type"); + ETL_STATIC_ASSERT(Type_Id_Lookup::type_id == Index, "Mismatched type"); ::new (static_cast(data)) T(value); type_id = Index; } diff --git a/include/etl/private/variant_select_do_operator.h b/include/etl/private/variant_select_do_operator.h index 1fb1794a..42bcb0d0 100644 --- a/include/etl/private/variant_select_do_operator.h +++ b/include/etl/private/variant_select_do_operator.h @@ -34,7 +34,7 @@ SOFTWARE. namespace private_variant { //*************************************************************************** - // Selects a do_operator inplementation that is configured for the number of + // Selects a do_operator implementation that is configured for the number of // types. //*************************************************************************** template diff --git a/include/etl/private/variant_select_do_visitor.h b/include/etl/private/variant_select_do_visitor.h index e770af68..58a6e2d6 100644 --- a/include/etl/private/variant_select_do_visitor.h +++ b/include/etl/private/variant_select_do_visitor.h @@ -34,7 +34,7 @@ SOFTWARE. namespace private_variant { //*************************************************************************** - // Selects a do_visitor inplementation that is configured for the number of + // Selects a do_visitor implementation that is configured for the number of // types. //*************************************************************************** template diff --git a/include/etl/type_traits.h b/include/etl/type_traits.h index a4751fa3..13d21810 100644 --- a/include/etl/type_traits.h +++ b/include/etl/type_traits.h @@ -3317,7 +3317,7 @@ namespace etl #else //********************************************* - // Assume that anything other than arithmetics + // Assume that anything other than arithmetic // and pointers return false for the traits. //********************************************* diff --git a/include/etl/unaligned_type.h b/include/etl/unaligned_type.h index 504317f7..6f4189dc 100644 --- a/include/etl/unaligned_type.h +++ b/include/etl/unaligned_type.h @@ -666,7 +666,7 @@ namespace etl unaligned_type_ext(unaligned_type_ext && other) : private_unaligned_type::unaligned_type_storage_ext(other.storage) { - // If we're constructing from a different endianess then we need to + // If we're constructing from a different endianness then we need to // reverse the data order. if (Endian != Endian_Other) { @@ -728,7 +728,7 @@ namespace etl { this->storage = other.storage; - // If we're assigning from a different endianess then we need to reverse + // If we're assigning from a different endianness then we need to reverse // the data order. if (Endian != Endian_Other) { diff --git a/test/test_histogram.cpp b/test/test_histogram.cpp index 40f5de20..b0db0263 100644 --- a/test/test_histogram.cpp +++ b/test/test_histogram.cpp @@ -270,7 +270,7 @@ namespace { IntRuntimeOffsetHistogram histogram1(Start, input2.begin(), input2.end()); - IntRuntimeOffsetHistogram histogram2(Start); + IntRuntimeOffsetHistogram histogram2(Start + 1); histogram2 = histogram1; for (int key = Start; key < Start + static_cast(Size); ++key) @@ -300,7 +300,7 @@ namespace IntRuntimeOffsetHistogram histogram1(Start, input2.begin(), input2.end()); IntRuntimeOffsetHistogram histogram_source(Start, input2.begin(), input2.end()); - IntRuntimeOffsetHistogram histogram2(Start); + IntRuntimeOffsetHistogram histogram2(Start + 1); histogram2 = etl::move(histogram_source); for (int key = Start; key < Start + static_cast(Size); ++key) diff --git a/test/test_vector.cpp b/test/test_vector.cpp index b9957e5d..026e2176 100644 --- a/test/test_vector.cpp +++ b/test/test_vector.cpp @@ -1143,9 +1143,12 @@ namespace Compare_Data compare_data(initial_data.begin(), initial_data.end()); Data data(initial_data.begin(), initial_data.end()); - compare_data.erase(compare_data.begin() + 2, compare_data.begin() + 4); + Compare_Data::iterator cdi = compare_data.erase(compare_data.begin() + 2, compare_data.begin() + 4); - data.erase(data.begin() + 2, data.begin() + 4); + Data::iterator di = data.erase(data.begin() + 2, data.begin() + 4); + + CHECK(cdi == compare_data.begin() + 2); + CHECK(di == data.begin() + 2); CHECK_EQUAL(compare_data.size(), data.size()); diff --git a/test/test_vector_function_pointer.cpp b/test/test_vector_function_pointer.cpp index 62b18a71..0f95ae4f 100644 --- a/test/test_vector_function_pointer.cpp +++ b/test/test_vector_function_pointer.cpp @@ -301,7 +301,7 @@ namespace CHECK_EQUAL(0U, data.size()); CHECK_EQUAL(initial_data.size(), other_data.size()); - bool is_equal = std::equal(data.begin(), data.end(), other_data.begin()); + bool is_equal = std::equal(initial_data.begin(), initial_data.end(), other_data.begin()); CHECK(is_equal); } diff --git a/test/test_vector_function_pointer_external_buffer.cpp b/test/test_vector_function_pointer_external_buffer.cpp index 4a6d5a86..70ef97ec 100644 --- a/test/test_vector_function_pointer_external_buffer.cpp +++ b/test/test_vector_function_pointer_external_buffer.cpp @@ -288,7 +288,7 @@ namespace CHECK_EQUAL(0U, data.size()); CHECK_EQUAL(initial_data.size(), other_data.size()); - bool is_equal = std::equal(data.begin(), data.end(), other_data.begin()); + bool is_equal = std::equal(initial_data.begin(), initial_data.end(), other_data.begin()); CHECK(is_equal); } diff --git a/test/test_vector_member_function_pointer.cpp b/test/test_vector_member_function_pointer.cpp index a1828db7..6b8e1512 100644 --- a/test/test_vector_member_function_pointer.cpp +++ b/test/test_vector_member_function_pointer.cpp @@ -311,7 +311,7 @@ namespace CHECK_EQUAL(0U, data.size()); CHECK_EQUAL(initial_data.size(), other_data.size()); - bool is_equal = std::equal(data.begin(), data.end(), other_data.begin()); + bool is_equal = std::equal(initial_data.begin(), initial_data.end(), other_data.begin()); CHECK(is_equal); } diff --git a/test/test_vector_member_function_pointer_external_buffer.cpp b/test/test_vector_member_function_pointer_external_buffer.cpp index 644fbf92..b94358c3 100644 --- a/test/test_vector_member_function_pointer_external_buffer.cpp +++ b/test/test_vector_member_function_pointer_external_buffer.cpp @@ -316,7 +316,7 @@ namespace CHECK_EQUAL(0U, data.size()); CHECK_EQUAL(initial_data.size(), other_data.size()); - bool is_equal = std::equal(data.begin(), data.end(), other_data.begin()); + bool is_equal = std::equal(initial_data.begin(), initial_data.end(), other_data.begin()); CHECK(is_equal); } From a74c33bab9ef674a36e43178645f1f4fb982ae19 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Mon, 20 Jul 2026 12:26:02 +0200 Subject: [PATCH 11/38] Fix variant emplace to be valueless after a throwing construction (#1510) variant::do_destroy() now always resets type_id to variant_npos after destroying the active alternative. emplace()/operator= set the new discriminator only after successful construction, so if the constructor throws the variant is left valueless (index() == variant_npos) instead of reporting the stale old index. Add regression test test_emplace_throwing_is_valueless_by_exception covering both the trivially-destructible (union) and non-trivially- destructible (buffer) storage suites. Co-authored-by: John Wellbelove --- include/etl/private/variant_variadic.h | 2 + test/test_variant_variadic.cpp | 65 ++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/include/etl/private/variant_variadic.h b/include/etl/private/variant_variadic.h index b76ba7a6..b2b8771e 100644 --- a/include/etl/private/variant_variadic.h +++ b/include/etl/private/variant_variadic.h @@ -1400,6 +1400,8 @@ namespace etl void do_destroy() { do_destroy_impl(etl::integral_constant{}); + + type_id = variant_npos; } void do_destroy_impl(etl::integral_constant) diff --git a/test/test_variant_variadic.cpp b/test/test_variant_variadic.cpp index 11cef693..0680f79a 100644 --- a/test/test_variant_variadic.cpp +++ b/test/test_variant_variadic.cpp @@ -1097,6 +1097,71 @@ namespace } #endif + #if ETL_USING_EXCEPTIONS + //************************************************************************* + // A throwing emplace() must leave the variant valueless-after-exception + // (index() == variant_npos) rather than reporting the stale old index. + struct ThrowOnConstruct + { + struct exception + { + }; + + ThrowOnConstruct() = default; + explicit ThrowOnConstruct(int) + { + throw exception(); + } + }; + + TEST(test_emplace_throwing_is_valueless_by_exception) + { + // Trivially destructible suite (variadic_union storage). + { + etl::variant v; + v.emplace(42); + CHECK(!v.valueless_by_exception()); + CHECK_EQUAL(0U, v.index()); + + bool threw = false; + try + { + v.emplace(1); + } + catch (const ThrowOnConstruct::exception&) + { + threw = true; + } + + CHECK(threw); + CHECK(v.valueless_by_exception()); + CHECK_EQUAL(etl::variant_npos, v.index()); + } + + // Non-trivially destructible suite (uninitialized_buffer storage). + { + etl::variant v; + v.emplace("Some Text"); + CHECK(!v.valueless_by_exception()); + CHECK_EQUAL(0U, v.index()); + + bool threw = false; + try + { + v.emplace<1>(1); + } + catch (const ThrowOnConstruct::exception&) + { + threw = true; + } + + CHECK(threw); + CHECK(v.valueless_by_exception()); + CHECK_EQUAL(etl::variant_npos, v.index()); + } + } + #endif + //************************************************************************* TEST(test_copy_constructor) { From 83831216d431e6b625bb6349493572de80e74ee7 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Mon, 20 Jul 2026 13:53:21 +0200 Subject: [PATCH 12/38] Fix deep const-cast in ivector pointer repair for const pointee types (#1511) to_void_pptr() previously stripped const from the pointed-to type via element_type, breaking pointer-to-const element types such as etl::vector. Convert through void* instead, removing only the buffer's top-level const and avoiding reinterpret_cast. Co-authored-by: John Wellbelove --- include/etl/vector.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/etl/vector.h b/include/etl/vector.h index 492247be..2c5b3713 100644 --- a/include/etl/vector.h +++ b/include/etl/vector.h @@ -1212,8 +1212,6 @@ namespace etl typedef pvoidvector base_t; - typedef typename etl::remove_const::type>::type* element_type; - template struct is_compatible_iterator : etl::bool_constant::value_type>::value @@ -1728,7 +1726,10 @@ namespace etl // Convert from pointer to void** static ETL_CONSTEXPR14 void** to_void_pptr(pointer p_buffer) ETL_NOEXCEPT { - return reinterpret_cast(const_cast(p_buffer)); + ETL_STATIC_ASSERT(!etl::is_const::type>::value, + "to_void_pptr must not remove const from the pointed-to element type"); + + return static_cast(const_cast(static_cast(p_buffer))); } }; From 7cdc89a38ab062685ecf83dea980f5f7bc18eb91 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Mon, 20 Jul 2026 13:58:57 +0100 Subject: [PATCH 13/38] Clean up the use of type traits by using C++11 forms wherever possible. (#1515) * Clean up the use of type traits by using C++11 forms wherever possible. * clang-format changes --------- Co-authored-by: John Wellbelove Co-authored-by: Roland Reichwein --- include/etl/expected.h | 100 ++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/include/etl/expected.h b/include/etl/expected.h index 655be8b1..bbfc514b 100644 --- a/include/etl/expected.h +++ b/include/etl/expected.h @@ -315,13 +315,13 @@ namespace etl //******************************************* /// Copy construct from unexpected type. //******************************************* - template ::value, bool>::type = false> + template ::value, bool> = false> ETL_CONSTEXPR14 explicit expected(const etl::unexpected& ue) : storage(etl::in_place_index_t(), ue.error()) { } - template ::value, bool>::type = false> + template ::value, bool> = false> ETL_CONSTEXPR14 expected(const etl::unexpected& ue) : storage(etl::in_place_index_t(), ue.error()) { @@ -338,13 +338,13 @@ namespace etl //******************************************* /// Move construct from unexpected type. //******************************************* - template ::value, bool>::type = false> + template ::value, bool> = false> ETL_CONSTEXPR14 explicit expected(etl::unexpected&& ue) : storage(etl::in_place_index_t(), etl::move(ue.error())) { } - template ::value, bool>::type = false> + template ::value, bool> = false> ETL_CONSTEXPR14 expected(etl::unexpected&& ue) : storage(etl::in_place_index_t(), etl::move(ue.error())) { @@ -807,97 +807,97 @@ namespace etl } #if ETL_USING_CPP11 - template ::type>::type> + template >> auto transform(F&& f) & -> expected { return transform_impl(etl::forward(f), *this); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t< etl::invoke_result_t>> auto transform(F&& f) const& -> expected { return transform_impl(etl::forward(f), *this); } - template ::type>::type> + template >> auto transform(F&& f) && -> expected { return transform_impl(etl::forward(f), etl::move(*this)); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t< etl::invoke_result_t>> auto transform(F&& f) const&& -> expected { return transform_impl(etl::forward(f), etl::move(*this)); } - template ::type>::type> + template >> auto and_then(F&& f) & -> U { return and_then_impl(etl::forward(f), *this); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t< etl::invoke_result_t>> auto and_then(F&& f) const& -> U { return and_then_impl(etl::forward(f), *this); } - template ::type>::type> + template >> auto and_then(F&& f) && -> U { return and_then_impl(etl::forward(f), etl::move(*this)); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t< etl::invoke_result_t>> auto and_then(F&& f) const&& -> U { return and_then_impl(etl::forward(f), etl::move(*this)); } - template ::type>::type> + template >> auto or_else(F&& f) & -> U { return or_else_impl(etl::forward(f), *this); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t< etl::invoke_result_t>> auto or_else(F&& f) const& -> U { return or_else_impl(etl::forward(f), *this); } - template ::type>::type> + template >> auto or_else(F&& f) && -> U { return or_else_impl(etl::forward(f), etl::move(*this)); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t< etl::invoke_result_t>> auto or_else(F&& f) const&& -> U { return or_else_impl(etl::forward(f), etl::move(*this)); } - template ::type>::type> + template >> auto transform_error(F&& f) & -> expected { return transform_error_impl(etl::forward(f), *this); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t< etl::invoke_result_t>> auto transform_error(F&& f) const& -> expected { return transform_error_impl(etl::forward(f), *this); } - template ::type>::type> + template >> auto transform_error(F&& f) && -> expected { return transform_error_impl(etl::forward(f), etl::move(*this)); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t< etl::invoke_result_t>> auto transform_error(F&& f) const&& -> expected { return transform_error_impl(etl::forward(f), etl::move(*this)); @@ -916,7 +916,7 @@ namespace etl storage_type storage; #if ETL_USING_CPP11 - template < typename F, typename TExp, typename TRet, typename TValueRef, typename = typename etl::enable_if::value>::type> + template < typename F, typename TExp, typename TRet, typename TValueRef, typename = etl::enable_if_t::value>> auto transform_impl(F&& f, TExp&& exp) const -> expected { if (exp.has_value()) @@ -929,7 +929,7 @@ namespace etl } } - template < typename F, typename TExp, typename TRet, typename TValueRef, typename = typename etl::enable_if::value>::type> + template < typename F, typename TExp, typename TRet, typename TValueRef, typename = etl::enable_if_t::value>> auto transform_impl(F&& f, TExp&& exp) const -> expected { if (exp.has_value()) @@ -944,8 +944,8 @@ namespace etl } template < typename F, typename TExp, typename TRet, typename TValueRef, - typename = typename etl::enable_if< !etl::is_void::value && etl::is_expected::value - && etl::is_same::value>::type> + typename = etl::enable_if_t::value && etl::is_expected::value + && etl::is_same::value>> auto and_then_impl(F&& f, TExp&& exp) const -> TRet { if (exp.has_value()) @@ -959,8 +959,8 @@ namespace etl } template < typename F, typename TExp, typename TRet, typename TErrorRef, - typename = typename etl::enable_if< !etl::is_void::value && etl::is_expected::value - && etl::is_same::value>::type> + typename = etl::enable_if_t::value && etl::is_expected::value + && etl::is_same::value>> auto or_else_impl(F&& f, TExp&& exp) const -> TRet { if (exp.has_value()) @@ -973,7 +973,7 @@ namespace etl } } - template < typename F, typename TExp, typename TRet, typename TErrorRef, typename = typename etl::enable_if::value>::type> + template < typename F, typename TExp, typename TRet, typename TErrorRef, typename = etl::enable_if_t::value>> auto transform_error_impl(F&& f, TExp&& exp) const -> expected { if (exp.has_value()) @@ -1216,97 +1216,97 @@ namespace etl } #if ETL_USING_CPP11 - template ::type>::type> + template >> auto transform(F&& f) & -> expected { return transform_impl(etl::forward(f), *this); } - template ::type>::type> + template >> auto transform(F&& f) const& -> expected { return transform_impl(etl::forward(f), *this); } - template ::type>::type> + template >> auto transform(F&& f) && -> expected { return transform_impl(etl::forward(f), etl::move(*this)); } - template ::type>::type> + template >> auto transform(F&& f) const&& -> expected { return transform_impl(etl::forward(f), etl::move(*this)); } - template ::type>::type> + template >> auto and_then(F&& f) & -> U { return and_then_impl(etl::forward(f), *this); } - template ::type>::type> + template >> auto and_then(F&& f) const& -> U { return and_then_impl(etl::forward(f), *this); } - template ::type>::type> + template >> auto and_then(F&& f) && -> U { return and_then_impl(etl::forward(f), etl::move(*this)); } - template ::type>::type> + template >> auto and_then(F&& f) const&& -> U { return and_then_impl(etl::forward(f), etl::move(*this)); } - template ::type>::type> + template >> auto or_else(F&& f) & -> U { return or_else_impl(etl::forward(f), *this); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t>> auto or_else(F&& f) const& -> U { return or_else_impl(etl::forward(f), *this); } - template ::type>::type> + template >> auto or_else(F&& f) && -> U { return or_else_impl(etl::forward(f), etl::move(*this)); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t>> auto or_else(F&& f) const&& -> U { return or_else_impl(etl::forward(f), etl::move(*this)); } - template ::type>::type> + template >> auto transform_error(F&& f) & -> expected { return transform_error_impl(etl::forward(f), *this); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t>> auto transform_error(F&& f) const& -> expected { return transform_error_impl(etl::forward(f), *this); } - template ::type>::type> + template >> auto transform_error(F&& f) && -> expected { return transform_error_impl(etl::forward(f), etl::move(*this)); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t>> auto transform_error(F&& f) const&& -> expected { return transform_error_impl(etl::forward(f), etl::move(*this)); @@ -1324,7 +1324,7 @@ namespace etl etl::variant storage; #if ETL_USING_CPP11 - template < typename F, typename TExp, typename TRet, typename = typename etl::enable_if::value>::type> + template < typename F, typename TExp, typename TRet, typename = etl::enable_if_t::value>> auto transform_impl(F&& f, TExp&& exp) const -> expected { if (exp.has_value()) @@ -1337,7 +1337,7 @@ namespace etl } } - template < typename F, typename TExp, typename TRet, typename = typename etl::enable_if::value>::type> + template < typename F, typename TExp, typename TRet, typename = etl::enable_if_t::value>> auto transform_impl(F&& f, TExp&& exp) const -> expected { if (exp.has_value()) @@ -1352,8 +1352,8 @@ namespace etl } template < typename F, typename TExp, typename TRet, - typename = typename etl::enable_if< !etl::is_void::value && etl::is_expected::value - && etl::is_same::value>::type> + typename = etl::enable_if_t< !etl::is_void::value && etl::is_expected::value + && etl::is_same::value>> auto and_then_impl(F&& f, TExp&& exp) const -> TRet { if (exp.has_value()) @@ -1367,8 +1367,8 @@ namespace etl } template ::value && etl::is_expected::value - && etl::is_same::value>::type> + typename = etl::enable_if_t< !etl::is_void::value && etl::is_expected::value + && etl::is_same::value>> auto or_else_impl(F&& f, TExp&& exp) const -> TRet { if (exp.has_value()) @@ -1381,7 +1381,7 @@ namespace etl } } - template < typename F, typename TExp, typename TRet, typename TErrorRef, typename = typename etl::enable_if::value>::type> + template < typename F, typename TExp, typename TRet, typename TErrorRef, typename = etl::enable_if_t::value>> auto transform_error_impl(F&& f, TExp&& exp) const -> expected { if (exp.has_value()) From c1cab856a8fc61e672bd30270cdd3f6814fe2c04 Mon Sep 17 00:00:00 2001 From: Murat Kilivan Date: Tue, 21 Jul 2026 11:45:35 +0100 Subject: [PATCH 14/38] Restore .devcontainer/context directory removed by #1422 (#1516) PR #1422 deleted the only file in .devcontainer/context/ (reinstall-cmake.sh) as unused, which silently removed the directory itself since git doesn't track empty ones. Every devcontainer.json under .devcontainer/ (root and all compiler variants) references this path as its Docker build context, so opening the project in a dev container now fails immediately with: ERROR: failed to build: unable to prepare context: path ".../.devcontainer/context" not found Add a .gitkeep placeholder to keep the directory present in git so the build context resolves again. Co-authored-by: John Wellbelove --- .devcontainer/context/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .devcontainer/context/.gitkeep diff --git a/.devcontainer/context/.gitkeep b/.devcontainer/context/.gitkeep new file mode 100644 index 00000000..e69de29b From 52502734fa80d8ed618746d9d40c3c53806fe445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Sch=C3=A4pers?= Date: Sun, 26 Jul 2026 10:21:53 +0200 Subject: [PATCH 15/38] Fix ODR violation in optional (#1522) * Fix ODR violation in optional Detected by GCC 15, when using in a module context. * Make etl::nullopt constant --- include/etl/optional.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/etl/optional.h b/include/etl/optional.h index 371c9f2a..eeb4b5c7 100644 --- a/include/etl/optional.h +++ b/include/etl/optional.h @@ -59,7 +59,7 @@ namespace etl // Convertible to any type of null non-member pointer. template - operator T*() const + ETL_CONSTEXPR operator T*() const ETL_NOEXCEPT { return 0; } @@ -74,7 +74,7 @@ namespace etl /// A null option. ///\ingroup utilities //***************************************************************************** - const nullopt_t nullopt = {}; + ETL_INLINE_VAR ETL_CONSTANT nullopt_t nullopt = {}; //*************************************************************************** /// Exception for optional. From 7566eceed741bb8615f5253a74622b5f209def64 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Thu, 30 Jul 2026 10:10:52 +0100 Subject: [PATCH 16/38] Fixed signed/unsigned warnings in VS2022 (#1524) Co-authored-by: John Wellbelove --- include/etl/utility.h | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/include/etl/utility.h b/include/etl/utility.h index 0040dd7a..3c7da3a5 100644 --- a/include/etl/utility.h +++ b/include/etl/utility.h @@ -231,6 +231,35 @@ namespace etl struct enable_if_comparable : etl::enable_if::value && is_standard_integer::value, TReturn> { }; + + //*********************************** + /// cmp_less implementation helpers, selected by SFINAE to avoid + /// signed/unsigned comparison warnings in dead code branches. + //*********************************** + + // Both same signedness. + template + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value == etl::is_signed::value, bool>::type cmp_less_impl(T t, U u) + ETL_NOEXCEPT + { + return t < u; + } + + // T is signed, U is unsigned. + template + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value && !etl::is_signed::value, bool>::type cmp_less_impl(T t, U u) + ETL_NOEXCEPT + { + return (t < T(0)) || (typename etl::make_unsigned::type(t) < u); + } + + // T is unsigned, U is signed. + template + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value && etl::is_signed::value, bool>::type cmp_less_impl(T t, U u) + ETL_NOEXCEPT + { + return (u >= U(0)) && (t < typename etl::make_unsigned::type(u)); + } } // namespace private_utility //*************************************************************************** @@ -264,9 +293,7 @@ namespace etl 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))); + return etl::private_utility::cmp_less_impl(t, u); } //*************************************************************************** From 9d92081d1e6387c5ea79371beeabb8181845e5e7 Mon Sep 17 00:00:00 2001 From: taltenbach <92919739+taltenbach@users.noreply.github.com> Date: Tue, 4 Aug 2026 09:01:42 +0200 Subject: [PATCH 17/38] Fix operator[] for const iota_view and repeat_view (#1525) * Fix missing operator[] for iota_iterator and repeat_iterator The iterators of etl::ranges::iota_view and etl::ranges::repeat_view are random access iterators but they did not define any operator[]. This did not cause any issues when using mutable views since the implementation of operator[] provided by view_interface for random access views was hidden by another implementation using etl::advance. However, this was not the case for 'operator[] const', which resulted in a build error when using constant views. This commit ensures that the operator[] implementation for random access views is no longer hidden by another implementation in view_interface, and adds operator[] to both iterators. * Fix sign conversion error on operator[] for some views The operator[] definitions provided by view_interface were accepting size_t arguments. This was causing a compile-time warning (Wsign-conversion) for the random access views using an iterator with a signed difference type. This has been observed on etl::ranges::iota_view and etl::ranges::repeat_view. No test is added since the tests provided by the previous commit already produce the warning, causing a build error. --------- Co-authored-by: John Wellbelove --- include/etl/ranges.h | 17 ++++++++++++++--- test/test_ranges.cpp | 26 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/include/etl/ranges.h b/include/etl/ranges.h index b502f82d..9ab8b1a6 100644 --- a/include/etl/ranges.h +++ b/include/etl/ranges.h @@ -150,7 +150,8 @@ namespace etl return *etl::prev(cend()); } - constexpr decltype(auto) operator[](size_t i) + template < typename D2 = D, etl::enable_if_t().begin())>::value, int> = 0> + constexpr decltype(auto) operator[](range_difference_t i) { auto it{static_cast(this)->begin()}; etl::advance(it, i); @@ -158,13 +159,13 @@ namespace etl } template < typename D2 = D, etl::enable_if_t().begin())>::value, int> = 0> - constexpr decltype(auto) operator[](size_t i) + constexpr decltype(auto) operator[](range_difference_t i) { return static_cast(this)->begin()[i]; } template < typename D2 = D, etl::enable_if_t().begin())>::value, int> = 0> - constexpr decltype(auto) operator[](size_t i) const + constexpr decltype(auto) operator[](range_difference_t i) const { return cbegin()[i]; } @@ -502,6 +503,11 @@ namespace etl return _i; } + constexpr value_type operator[](difference_type n) const + { + return *(*this + n); + } + private: value_type _i; @@ -673,6 +679,11 @@ namespace etl return _value; } + constexpr value_type operator[](difference_type n) const + { + return *(*this + n); + } + private: value_type _value; diff --git a/test/test_ranges.cpp b/test/test_ranges.cpp index efe64148..cab77148 100644 --- a/test/test_ranges.cpp +++ b/test/test_ranges.cpp @@ -473,6 +473,20 @@ namespace CHECK_EQUAL(iv3.empty(), true); } + TEST(test_ranges_iota_view_const) + { + const auto iv = etl::ranges::iota_view(1, 7); + + int compare = 1; + for (auto i : iv) + { + CHECK_EQUAL(i, compare); + ++compare; + } + + CHECK_EQUAL(iv[4], 5); + } + TEST(test_ranges_views_iota) { auto iv = etl::ranges::views::iota(1, 7); @@ -540,6 +554,18 @@ namespace CHECK_EQUAL(iv3.empty(), true); } + TEST(test_ranges_repeat_view_const) + { + const auto rv = etl::ranges::repeat_view(1, 7); + + for (auto i : rv) + { + CHECK_EQUAL(i, 1); + } + + CHECK_EQUAL(rv[4], 1); + } + TEST(test_ranges_views_repeat) { auto iv = etl::ranges::views::repeat(1, 7); From 62925ec1add3aadcb8100251b63523dd43b36173 Mon Sep 17 00:00:00 2001 From: rleigh-electradx Date: Tue, 4 Aug 2026 10:57:25 +0100 Subject: [PATCH 18/38] Changed make_list to take its arguments by forwarding reference (#1528) make_list was the only make_xxx container factory taking its argument pack by value rather than by forwarding reference, costing an extra move per lvalue argument: each lvalue was copy-constructed into the parameter and then moved into the initializer_list backing array, where the forwarding-reference signature copies it there directly. Rvalue prvalue arguments count the same either way in practice, as copy elision constructs them directly into the by-value parameter. Change the pack to T&&..., matching make_vector, make_forward_list and make_indirect_vector. The deduced reference types decay through common_type, so the element type deduction is unchanged. Adds tests for const/non-const lvalues of the element type (guarding against the forward defect fixed for make_array and make_deque), mixed-type common_type deduction, and an instrumented type counting copy/move constructions, which fails with the by-value signature (one extra move per lvalue argument) and passes with the fix. Also corrects the make_list documentation, which described a make_array-style signature with an explicit element type parameter that never matched the implementation. Verified with clang 22 (C++17): all 93 list tests pass; C++14/20 syntax checks pass. Note: mixed-type lvalue arguments are affected by a separate pre-existing defect in etl::common_type (its two-type specialization recurses with decay twice instead of decay, decay), which already affects the other common_type-based factories and is being fixed separately. Co-authored-by: John Wellbelove --- docs/containers/lists/list.md | 8 ++- include/etl/list.h | 2 +- test/test_list.cpp | 105 ++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 4 deletions(-) diff --git a/docs/containers/lists/list.md b/docs/containers/lists/list.md index 8ae13fd3..0ae64e39 100644 --- a/docs/containers/lists/list.md +++ b/docs/containers/lists/list.md @@ -69,13 +69,15 @@ Defines data as an list of `int`, of length `10`, containing the supplied data. ## Make template **C++11 and above** ```cpp -template -constexpr auto make_list(TValues&&... values) +template +constexpr auto make_list(T&&... t) -> etl::list, sizeof...(T)> ``` +The element type is deduced as the common type of the arguments. + ### Example ```cpp -auto data = etl::make_list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); +auto data = etl::make_list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); ``` ## Member types diff --git a/include/etl/list.h b/include/etl/list.h index c2bbdfc2..7be34fcc 100644 --- a/include/etl/list.h +++ b/include/etl/list.h @@ -2219,7 +2219,7 @@ namespace etl //************************************************************************* #if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST template - constexpr auto make_list(T... t) -> etl::list, sizeof...(T)> + constexpr auto make_list(T&&... t) -> etl::list, sizeof...(T)> { return {etl::forward(t)...}; } diff --git a/test/test_list.cpp b/test/test_list.cpp index 00574f24..767166e7 100644 --- a/test/test_list.cpp +++ b/test/test_list.cpp @@ -2272,5 +2272,110 @@ namespace CHECK_EQUAL(ItemNDC("F"), *itr++); } #endif + + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Const-qualified lvalue arguments must be accepted and must not affect the + // deduced element type (the deduced reference types decay via common_type). + TEST(test_make_list_from_const_lvalues_of_element_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_list(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + CHECK((std::is_same, decltype(data)>::value)); + + auto itr = data.begin(); + CHECK_EQUAL(42, *itr++); + CHECK_EQUAL(43, *itr++); + CHECK_EQUAL(44, *itr++); + } +#endif + + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + TEST(test_make_list_common_type_from_mixed_types) + { + auto data = etl::make_list(1, 2L, 3); + + CHECK((std::is_same, decltype(data)>::value)); + + auto itr = data.begin(); + CHECK_EQUAL(1L, *itr++); + CHECK_EQUAL(2L, *itr++); + CHECK_EQUAL(3L, *itr++); + } +#endif + + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + struct CopyMoveCounted + { + explicit CopyMoveCounted(int value_) + : value(value_) + { + } + + CopyMoveCounted(const CopyMoveCounted& other) + : value(other.value) + { + ++copy_count; + } + + CopyMoveCounted(CopyMoveCounted&& other) + : value(other.value) + { + ++move_count; + } + + int value; + + static size_t copy_count; + static size_t move_count; + }; + + size_t CopyMoveCounted::copy_count = 0; + size_t CopyMoveCounted::move_count = 0; + + // The arguments must be forwarded into the initializer list, not taken by + // value: a by-value parameter pack costs an extra move per argument. + // Each element is then copied from the initializer_list backing array into + // its list node, which is why lvalue arguments count two copies. + TEST(test_make_list_argument_copy_and_move_counts) + { + const CopyMoveCounted const_lvalue(1); + CopyMoveCounted mutable_lvalue(2); + + CopyMoveCounted::copy_count = 0; + CopyMoveCounted::move_count = 0; + + auto data = etl::make_list(const_lvalue, mutable_lvalue); + + CHECK_EQUAL(4U, CopyMoveCounted::copy_count); + #if ETL_USING_CPP17 + // The exact move counts rely on guaranteed copy elision. Before C++17 the + // returned list may be move-constructed once more, which moves each element, + // so only the copy counts are checked there. + CHECK_EQUAL(0U, CopyMoveCounted::move_count); + #endif + + CopyMoveCounted::copy_count = 0; + CopyMoveCounted::move_count = 0; + + auto data2 = etl::make_list(CopyMoveCounted(3)); + + CHECK_EQUAL(1U, CopyMoveCounted::copy_count); + #if ETL_USING_CPP17 + CHECK_EQUAL(1U, CopyMoveCounted::move_count); + #endif + + auto itr = data.begin(); + CHECK_EQUAL(1, (*itr++).value); + CHECK_EQUAL(2, (*itr).value); + CHECK_EQUAL(3, data2.begin()->value); + } +#endif } } // namespace From c8bc1bd8965343e6deb5b52111bbed86a8a20910 Mon Sep 17 00:00:00 2001 From: rleigh-electradx Date: Tue, 4 Aug 2026 11:45:49 +0100 Subject: [PATCH 19/38] Correct the documented make_xxx factory signatures and examples (#1530) * Corrected the documented make_xxx factory signatures and examples The Make template sections across the container documentation had drifted from the implementations, largely through copy-paste: - map.md, multimap.md and flat-multimap.md all documented make_flat_map; multiset.md documented make_set; flat-multiset.md documented make_flat_set; unordered-multimap.md documented make_unordered_map. Names and examples now match each container. - Every signature omitted the C++11-required trailing return type despite the sections being labelled C++11 and above; several also used a parameter name (TValues) that did not match any template parameter, and the unordered map headers were missing a comma. - forward-list.md documented a make_array-style signature with an explicit element type; the real function deduces the element type as the common type of the arguments, as documented for make_list. - The flat_set/flat_multiset template headers were missing the variadic parameter pack, and their examples passed a braced list to a variadic function, which does not compile; set.md used TKeyCompare where the implementation uses TCompare. - deque.md and the reference container docs were missing code fences. - Updated the four reference_flat_* function. However these functions cannot be instantiated at all (the reference containers have no initializer_list constructor, since one would dangle by design), so the documented examples have never compiled. Every remaining documented example verified to compile with clang 22 (C++17) against the current headers. * Fixed C++17-only syntax in factory examples and flagged the non-compilable ones Review feedback on the make_xxx documentation examples: - All the map-family factory examples are labelled C++11 and above but constructed their pairs with class template argument deduction (etl::pair{0, 1}), which requires C++17. The pairs in the make_flat_map, make_flat_multimap, make_map, make_multimap, make_unordered_map, make_unordered_multimap, make_reference_flat_map and make_reference_flat_multimap examples now name their template arguments explicitly. The template deduction guide examples are unchanged, as those sections are labelled C++17 and above. Verified with clang 22 in C++14 mode, which, like C++11, has no deduction guides. - The four make_reference_flat_* examples are now preceded by a warning callout stating that they do not currently compile: the functions cannot be instantiated because the reference containers have no initializer_list constructor. The examples themselves are retained, rewritten to pass named lvalues rather than temporaries, since the reference containers store references to external objects and the temporaries would dangle even if the functions compiled. The defect is being reported separately. --------- Co-authored-by: John Wellbelove --- docs/containers/arrays/array.md | 2 +- docs/containers/lists/forward-list.md | 8 +++-- docs/containers/maps/flat-map.md | 6 ++-- docs/containers/maps/flat-multimap.md | 8 ++--- docs/containers/maps/map.md | 6 ++-- docs/containers/maps/multimap.md | 6 ++-- docs/containers/maps/reference-flat-map.md | 29 +++++++++++++----- .../maps/reference-flat-multimap.md | 29 +++++++++++++----- docs/containers/maps/unordered-map.md | 12 ++++---- docs/containers/maps/unordered-multimap.md | 12 ++++---- docs/containers/queues & stacks/deque.md | 6 ++-- docs/containers/sets/flat-multiset.md | 11 +++---- docs/containers/sets/flat-set.md | 9 +++--- docs/containers/sets/multiset.md | 6 ++-- .../sets/reference-flat-multiset.md | 30 ++++++++++++++++--- docs/containers/sets/reference-flat-set.md | 30 ++++++++++++++++--- docs/containers/sets/set.md | 6 ++-- 17 files changed, 146 insertions(+), 70 deletions(-) diff --git a/docs/containers/arrays/array.md b/docs/containers/arrays/array.md index 1b848dac..fcf8af32 100644 --- a/docs/containers/arrays/array.md +++ b/docs/containers/arrays/array.md @@ -39,7 +39,7 @@ Defines data as an array of 'int', of length '10', containing the supplied data. ```cpp template -constexpr auto make_array(TValues&&... values) +constexpr auto make_array(TValues&&... values) -> etl::array ``` ### Example diff --git a/docs/containers/lists/forward-list.md b/docs/containers/lists/forward-list.md index 2be721e0..442d6b51 100644 --- a/docs/containers/lists/forward-list.md +++ b/docs/containers/lists/forward-list.md @@ -65,13 +65,15 @@ Defines data as an `forward_list` of int, of length 10, containing the supplied ## Make template **C++11 and above** ```cpp -template - constexpr auto make_forward_list(TValues&&... values) +template +constexpr auto make_forward_list(T&&... t) -> etl::forward_list, sizeof...(T)> ``` +The element type is deduced as the common type of the arguments. + ### Example ```cpp -auto data = etl::make_forward_list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); +auto data = etl::make_forward_list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); ``` ## Member types diff --git a/docs/containers/maps/flat-map.md b/docs/containers/maps/flat-map.md index dd1d1850..cc3ea886 100644 --- a/docs/containers/maps/flat-map.md +++ b/docs/containers/maps/flat-map.md @@ -40,13 +40,13 @@ template , typename... TPairs> -constexpr auto make_flat_map(TValues&&... values) +constexpr auto make_flat_map(TPairs&&... pairs) -> etl::flat_map ``` ### Example ```cpp -auto data = etl::make_flat_map(etl::pair{0, 1}, etl::pair{2, 3}, - etl::pair{4, 5}, etl::pair{6, 7}); +auto data = etl::make_flat_map(etl::pair{0, 1}, etl::pair{2, 3}, + etl::pair{4, 5}, etl::pair{6, 7}); ``` ## Member types diff --git a/docs/containers/maps/flat-multimap.md b/docs/containers/maps/flat-multimap.md index b6f6832d..58bda46b 100644 --- a/docs/containers/maps/flat-multimap.md +++ b/docs/containers/maps/flat-multimap.md @@ -33,20 +33,20 @@ etl::flat_multimap data{ etl::pair{0, 1}, etl::pair{2, 3}, etl::pair{4, 5}, etl: ``` Defines data as an `flat_multimap` of `int`/`int` pairs, of length 4, containing the supplied data. -## make_flat_map +## make_flat_multimap C++11 and above ```cpp template , typename... TPairs> -constexpr auto make_flat_map(TValues&&... values) +constexpr auto make_flat_multimap(TPairs&&... pairs) -> etl::flat_multimap ``` ### Example ```cpp -auto data = etl::make_flat_map(etl::pair{0, 1}, etl::pair{2, 3}, - etl::pair{4, 5}, etl::pair{6, 7}); +auto data = etl::make_flat_multimap(etl::pair{0, 1}, etl::pair{2, 3}, + etl::pair{4, 5}, etl::pair{6, 7}); ``` ## Member types diff --git a/docs/containers/maps/map.md b/docs/containers/maps/map.md index c2c26396..8b747fb8 100644 --- a/docs/containers/maps/map.md +++ b/docs/containers/maps/map.md @@ -38,13 +38,13 @@ template , typename... TPairs> -constexpr auto make_flat_map(TValues&&... values) +constexpr auto make_map(TPairs&&... pairs) -> etl::map ``` ### Example ```cpp -auto data = etl::make_map(etl::pair{0, 1}, etl::pair{2, 3}, - etl::pair{4, 5}, etl::pair{6, 7}); +auto data = etl::make_map(etl::pair{0, 1}, etl::pair{2, 3}, + etl::pair{4, 5}, etl::pair{6, 7}); ``` ## Member types diff --git a/docs/containers/maps/multimap.md b/docs/containers/maps/multimap.md index 09fda167..67c02108 100644 --- a/docs/containers/maps/multimap.md +++ b/docs/containers/maps/multimap.md @@ -38,13 +38,13 @@ template , typename... TPairs> -constexpr auto make_flat_map(TValues&&... values) +constexpr auto make_multimap(TPairs&&... pairs) -> etl::multimap ``` ### Example ```cpp -auto data = etl::make_multimap(etl::pair{0, 1}, etl::pair{2, 3}, - etl::pair{4, 5}, etl::pair{6, 7}); +auto data = etl::make_multimap(etl::pair{0, 1}, etl::pair{2, 3}, + etl::pair{4, 5}, etl::pair{6, 7}); ``` ## Member types diff --git a/docs/containers/maps/reference-flat-map.md b/docs/containers/maps/reference-flat-map.md index 57f7dda1..baa0a013 100644 --- a/docs/containers/maps/reference-flat-map.md +++ b/docs/containers/maps/reference-flat-map.md @@ -36,19 +36,32 @@ etl::reference_flat_map data{ etl::pair{0, 1}, etl::pair{2, 3}, etl::pair{4, 5}, Defines data as an reference_flat_map of int, of length 4, containing the supplied data. ## Make template -C++11 and above - +**C++11 and above** ```cpp -template , +template , typename... TPairs> -constexpr auto make_reference_flat_map(TValues&&... values) +constexpr auto make_reference_flat_map(TPairs&&... pairs) -> etl::reference_flat_map ``` ### Example -auto data = etl::make_reference_flat_map(etl::pair{0, 1}, etl::pair{2, 3}, - etl::pair{4, 5}, etl::pair{6, 7}); + +{{< callout type="warning" >}} + This example does not currently compile: `make_reference_flat_map` cannot be + instantiated, because the reference containers have no `initializer_list` + constructor for its braced return value to bind to. This is a known defect. +{{< /callout >}} + +```cpp +// The referenced objects must outlive the container. +etl::pair p0{0, 1}; +etl::pair p1{2, 3}; +etl::pair p2{4, 5}; +etl::pair p3{6, 7}; + +auto data = etl::make_reference_flat_map(p0, p1, p2, p3); +``` ## Member types diff --git a/docs/containers/maps/reference-flat-multimap.md b/docs/containers/maps/reference-flat-multimap.md index 9d4ccd64..01bdb556 100644 --- a/docs/containers/maps/reference-flat-multimap.md +++ b/docs/containers/maps/reference-flat-multimap.md @@ -36,19 +36,32 @@ etl::reference_flat_multimap data{ etl::pair{0, 1}, etl::pair{2, 3}, etl::pair{4 Defines data as an reference_flat_multimap of int, of length 4, containing the supplied data. ## Make template -C++11 and above - +**C++11 and above** ```cpp -template , +template , typename... TPairs> -constexpr auto make_reference_flat_map(TValues&&... values) +constexpr auto make_reference_flat_multimap(TPairs&&... pairs) -> etl::reference_flat_multimap ``` ### Example -auto data = etl::make_reference_flat_multimap(etl::pair{0, 1}, etl::pair{2, 3}, - etl::pair{4, 5}, etl::pair{6, 7}); + +{{< callout type="warning" >}} + This example does not currently compile: `make_reference_flat_multimap` cannot be + instantiated, because the reference containers have no `initializer_list` + constructor for its braced return value to bind to. This is a known defect. +{{< /callout >}} + +```cpp +// The referenced objects must outlive the container. +etl::pair p0{0, 1}; +etl::pair p1{2, 3}; +etl::pair p2{4, 5}; +etl::pair p3{6, 7}; + +auto data = etl::make_reference_flat_multimap(p0, p1, p2, p3); +``` ## Member types diff --git a/docs/containers/maps/unordered-map.md b/docs/containers/maps/unordered-map.md index bf2810ad..ba576cd6 100644 --- a/docs/containers/maps/unordered-map.md +++ b/docs/containers/maps/unordered-map.md @@ -44,17 +44,17 @@ C++11 and above ```cpp template , - typename TKeyEqual = etl::equal_to + typename T, + typename THash = etl::hash, + typename TKeyEqual = etl::equal_to, typename... TPairs> -constexpr auto make_unordered_map(TValues&&... values) +constexpr auto make_unordered_map(TPairs&&... pairs) -> etl::unordered_map ``` ### Example ```cpp -auto data = etl::make_unordered_map(etl::pair{0, 1}, etl::pair{2, 3}, - etl::pair{4, 5}, etl::pair{6, 7}); +auto data = etl::make_unordered_map(etl::pair{0, 1}, etl::pair{2, 3}, + etl::pair{4, 5}, etl::pair{6, 7}); ``` ## Member types diff --git a/docs/containers/maps/unordered-multimap.md b/docs/containers/maps/unordered-multimap.md index 5e50cba3..9622f558 100644 --- a/docs/containers/maps/unordered-multimap.md +++ b/docs/containers/maps/unordered-multimap.md @@ -42,17 +42,17 @@ C++11 and above ```cpp template , - typename TKeyEqual = etl::equal_to + typename T, + typename THash = etl::hash, + typename TKeyEqual = etl::equal_to, typename... TPairs> -constexpr auto make_unordered_map(TValues&&... values) +constexpr auto make_unordered_multimap(TPairs&&... pairs) -> etl::unordered_multimap ``` ### Example ```cpp -auto data = etl::make_unordered_map(etl::pair{0, 1}, etl::pair{2, 3}, - etl::pair{4, 5}, etl::pair{6, 7}); +auto data = etl::make_unordered_multimap(etl::pair{0, 1}, etl::pair{2, 3}, + etl::pair{4, 5}, etl::pair{6, 7}); ``` ## Member types diff --git a/docs/containers/queues & stacks/deque.md b/docs/containers/queues & stacks/deque.md index a4960607..f326e308 100644 --- a/docs/containers/queues & stacks/deque.md +++ b/docs/containers/queues & stacks/deque.md @@ -32,9 +32,11 @@ etl::deque data{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; Defines data as an `deque` of `int`, of length `10`, containing the supplied data. ## Make template +**C++11 and above** +```cpp template - constexpr auto make_deque(TValues&&... values) - C++11 and above +constexpr auto make_deque(TValues&&... values) -> etl::deque +``` ### Example ```cpp diff --git a/docs/containers/sets/flat-multiset.md b/docs/containers/sets/flat-multiset.md index 890fcd52..d88e4a17 100644 --- a/docs/containers/sets/flat-multiset.md +++ b/docs/containers/sets/flat-multiset.md @@ -33,17 +33,18 @@ etl::flat_multiset data{ 0, 1, 2, 3 }; ``` Defines data as an `flat_multiset` of `int`, of length 4, containing the supplied data. -## make_flat_set +## make_flat_multiset C++11 and above ```cpp -template > -constexpr auto make_flat_set(TValues&&... values) +template , + typename... T> +constexpr auto make_flat_multiset(T&&... keys) -> etl::flat_multiset ``` ### Example ```cpp -auto data = etl::make_flat_set({0, 1, 2, 3 }); +auto data = etl::make_flat_multiset(0, 1, 2, 3); ``` ## Member types diff --git a/docs/containers/sets/flat-set.md b/docs/containers/sets/flat-set.md index 6f9c0540..e5c08610 100644 --- a/docs/containers/sets/flat-set.md +++ b/docs/containers/sets/flat-set.md @@ -36,14 +36,15 @@ Defines data as an `flat_set` of `int`, of length 4, containing the supplied dat ## make_flat_set C++11 and above ```cpp -template > -constexpr auto make_flat_set(TValues&&... values) +template , + typename... T> +constexpr auto make_flat_set(T&&... keys) -> etl::flat_set ``` ### Example ```cpp -auto data = etl::make_flat_set({0, 1, 2, 3 }); +auto data = etl::make_flat_set(0, 1, 2, 3); ``` ## Member types diff --git a/docs/containers/sets/multiset.md b/docs/containers/sets/multiset.md index 9bf091c8..4e3de78f 100644 --- a/docs/containers/sets/multiset.md +++ b/docs/containers/sets/multiset.md @@ -37,13 +37,13 @@ Defines data as an multiset of `int`, of length 8, containing the supplied data. ```cpp template , - typename... TPairs> -constexpr auto make_set(TValues&&... values) + typename... T> +constexpr auto make_multiset(T&&... keys) -> etl::multiset ``` ### Example ```cpp -auto data = etl::make_set(0, 1, 2, 3, 4, 5, 6, 7); +auto data = etl::make_multiset(0, 1, 2, 3, 4, 5, 6, 7); ``` ## Member types diff --git a/docs/containers/sets/reference-flat-multiset.md b/docs/containers/sets/reference-flat-multiset.md index 9e7dd725..9bb622dc 100644 --- a/docs/containers/sets/reference-flat-multiset.md +++ b/docs/containers/sets/reference-flat-multiset.md @@ -32,13 +32,35 @@ etl::reference_flat_setdata{ 0, 1, 2, 3, 4, 5, 6, 7 }; Defines data as a set of int/int pairs, of length 8, containing the supplied data. ## Make template -C++11 and above -template , typename... T> -constexpr auto make_reference_flat_set(T&&... values) +constexpr auto make_reference_flat_multiset(T&&... keys) -> etl::reference_flat_multiset +``` ### Example -auto data = etl::make_reference_flat_set(0, 1, 2, 3, 4, 5, 6, 7); + +{{< callout type="warning" >}} + This example does not currently compile: `make_reference_flat_multiset` cannot be + instantiated, because the reference containers have no `initializer_list` + constructor for its braced return value to bind to. This is a known defect. +{{< /callout >}} + +```cpp +// The referenced objects must outlive the container. +int k0 = 0; +int k1 = 1; +int k2 = 2; +int k3 = 3; +int k4 = 4; +int k5 = 5; +int k6 = 6; +int k7 = 7; + +auto data = etl::make_reference_flat_multiset(k0, k1, k2, k3, k4, k5, k6, k7); +``` ## Member types diff --git a/docs/containers/sets/reference-flat-set.md b/docs/containers/sets/reference-flat-set.md index da2c9dc9..8ff1e296 100644 --- a/docs/containers/sets/reference-flat-set.md +++ b/docs/containers/sets/reference-flat-set.md @@ -32,13 +32,35 @@ etl::reference_flat_setdata{ 0, 1, 2, 3, 4, 5, 6, 7 }; Defines data as a set of int/int pairs, of length 8, containing the supplied data. ## Make template -C++11 and above -template , typename... T> -constexpr auto make_reference_flat_set(T&&... values) +constexpr auto make_reference_flat_set(T&&... keys) -> etl::reference_flat_set +``` ### Example -auto data = etl::make_reference_flat_set(0, 1, 2, 3, 4, 5, 6, 7); + +{{< callout type="warning" >}} + This example does not currently compile: `make_reference_flat_set` cannot be + instantiated, because the reference containers have no `initializer_list` + constructor for its braced return value to bind to. This is a known defect. +{{< /callout >}} + +```cpp +// The referenced objects must outlive the container. +int k0 = 0; +int k1 = 1; +int k2 = 2; +int k3 = 3; +int k4 = 4; +int k5 = 5; +int k6 = 6; +int k7 = 7; + +auto data = etl::make_reference_flat_set(k0, k1, k2, k3, k4, k5, k6, k7); +``` ## Member types diff --git a/docs/containers/sets/set.md b/docs/containers/sets/set.md index cec118bd..b6da157f 100644 --- a/docs/containers/sets/set.md +++ b/docs/containers/sets/set.md @@ -36,9 +36,9 @@ Defines data as an set of `int`, of length 8, containing the supplied data. ```cpp template , - typename... TPairs> -constexpr auto make_set(TValues&&... values) + typename TCompare = etl::less, + typename... T> +constexpr auto make_set(T&&... keys) -> etl::set ``` ### Example From 77079c8125d1dd9fce21be15909afaa3a380f07d Mon Sep 17 00:00:00 2001 From: rleigh-electradx Date: Tue, 4 Aug 2026 19:04:19 +0100 Subject: [PATCH 20/38] Fix etl::common_type decaying both arguments to T2 (#1529) The two-type etl::common_type specialization recursed with decay twice when either argument was not already decayed, so etl::common_type_t yielded int instead of long. Recurse with decay and decay, matching the cppreference sample implementation, and add regression tests comparing against std::common_type for reference-type arguments. Co-authored-by: John Wellbelove --- include/etl/type_traits.h | 2 +- test/test_type_traits.cpp | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/include/etl/type_traits.h b/include/etl/type_traits.h index 13d21810..6ecf9451 100644 --- a/include/etl/type_traits.h +++ b/include/etl/type_traits.h @@ -3724,7 +3724,7 @@ namespace etl struct common_type : etl::conditional< etl::is_same::type>::value && etl::is_same::type>::value, private_common_type::common_type_2_impl, - common_type::type, typename etl::decay::type>>::type + common_type::type, typename etl::decay::type>>::type { }; diff --git a/test/test_type_traits.cpp b/test/test_type_traits.cpp index 6756d6d9..603c992b 100644 --- a/test/test_type_traits.cpp +++ b/test/test_type_traits.cpp @@ -2268,5 +2268,23 @@ namespace CHECK_TRUE(etl::is_unsigned::value); #endif } + +#if ETL_USING_CPP11 + //************************************************************************* + TEST(test_common_type) + { + // Reference types must decay to the same result as std::common_type. + CHECK((etl::is_same, long>::value)); + CHECK((etl::is_same, std::common_type::type>::value)); + CHECK((etl::is_same, long>::value)); + CHECK((etl::is_same, std::common_type::type>::value)); + CHECK((etl::is_same, char>::value)); + CHECK((etl::is_same, std::common_type::type>::value)); + + // Already decayed types. + CHECK((etl::is_same, long>::value)); + CHECK((etl::is_same, std::common_type::type>::value)); + } +#endif } } // namespace From d9d312f671554bc200bb49daa7a850bcd6139901 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Wed, 5 Aug 2026 16:12:05 +0200 Subject: [PATCH 21/38] Prevent usage of N as template value parameter (#1534) Rename to Index. Collisions with certain other libraries which define N as macro can be prevented this way. Fixes #1452. --- include/etl/priority_queue.h | 8 +- include/etl/ranges.h | 202 +++++++++++++++++------------------ include/etl/span.h | 8 +- 3 files changed, 109 insertions(+), 109 deletions(-) diff --git a/include/etl/priority_queue.h b/include/etl/priority_queue.h index ea9df835..50174501 100644 --- a/include/etl/priority_queue.h +++ b/include/etl/priority_queue.h @@ -121,14 +121,14 @@ namespace etl ETL_STATIC_ASSERT(sizeof(TContainer) == 0, "Unsupported container type for etl::ipriority_queue"); }; - template - struct priority_queue_container_interface > + template + struct priority_queue_container_interface > { typedef etl::ivector type; }; - template - struct priority_queue_container_interface > + template + struct priority_queue_container_interface > { typedef etl::ideque type; }; diff --git a/include/etl/ranges.h b/include/etl/ranges.h index 9ab8b1a6..82ae3502 100644 --- a/include/etl/ranges.h +++ b/include/etl/ranges.h @@ -4500,7 +4500,7 @@ namespace etl /// An iterator adaptor that extracts the Nth element from a tuple-like /// value type using etl::get or std::get (found via ADL). //************************************************************************* - template + template class elements_iterator { public: @@ -4509,7 +4509,7 @@ namespace etl using base_iterator = typename trait::iterator; using base_value_type = typename trait::value_type; - using value_type = etl::tuple_element_t; + using value_type = etl::tuple_element_t; using difference_type = typename trait::difference_type; private: @@ -4521,7 +4521,7 @@ namespace etl static decltype(auto) deref_element(const base_iterator& it) { using etl::get; - return get(*it); + return get(*it); } public: @@ -4585,13 +4585,13 @@ namespace etl /// A range adaptor that takes a view of tuple-like values and produces /// a view of the Nth element of each tuple-like value. //************************************************************************* - template - class elements_view : public etl::ranges::view_interface> + template + class elements_view : public etl::ranges::view_interface> { public: - using iterator = elements_iterator; - using const_iterator = elements_iterator; + using iterator = elements_iterator; + using const_iterator = elements_iterator; elements_view(Range&& r) : _r{etl::move(r)} @@ -4625,29 +4625,29 @@ namespace etl mutable Range _r; }; - template - elements_view(Range&&, etl::integral_constant) -> elements_view, N>; + template + elements_view(Range&&, etl::integral_constant) -> elements_view, Index>; - template - struct elements_range_adapter_closure : public range_adapter_closure> + template + struct elements_range_adapter_closure : public range_adapter_closure> { template - using target_view_type = elements_view; + using target_view_type = elements_view; elements_range_adapter_closure() = default; template constexpr auto operator()(Range&& r) { - return elements_view, N>(views::all(etl::forward(r))); + return elements_view, Index>(views::all(etl::forward(r))); } }; - /// keys_view is an alias for elements_view with N=0. + /// keys_view is an alias for elements_view with Index=0. template using keys_view = elements_view; - /// values_view is an alias for elements_view with N=1. + /// values_view is an alias for elements_view with Index=1. template using values_view = elements_view; @@ -4655,39 +4655,39 @@ namespace etl { namespace private_views { - template + template struct elements_fn : public range_adapter_closure_base { template constexpr auto operator()(Range&& r) const { - return elements_view, N>(views::all(etl::forward(r))); + return elements_view, Index>(views::all(etl::forward(r))); } constexpr auto operator()() const { - return ranges::elements_range_adapter_closure(); + return ranges::elements_range_adapter_closure(); } }; } // namespace private_views - template - inline constexpr private_views::elements_fn elements{}; + template + inline constexpr private_views::elements_fn elements{}; inline constexpr private_views::elements_fn<0> keys{}; inline constexpr private_views::elements_fn<1> values{}; } // namespace views //************************************************************************* - /// Helper: create a tuple type that repeats T exactly N times. + /// Helper: create a tuple type that repeats T exactly Index times. //************************************************************************* namespace private_ranges { - template > + template > struct repeat_tuple; - template - struct repeat_tuple> + template + struct repeat_tuple> { template using always = T; @@ -4695,16 +4695,16 @@ namespace etl using type = etl::tuple...>; }; - template - using repeat_tuple_t = typename repeat_tuple::type; + template + using repeat_tuple_t = typename repeat_tuple::type; - /// Helper: compute invoke_result_t with T repeated N + /// Helper: compute invoke_result_t with T repeated Index /// times. - template > + template > struct repeat_invoke_result; - template - struct repeat_invoke_result> + template + struct repeat_invoke_result> { template using always = T; @@ -4712,20 +4712,20 @@ namespace etl using type = etl::invoke_result_t...>; }; - template - using repeat_invoke_result_t = typename repeat_invoke_result::type; + template + using repeat_invoke_result_t = typename repeat_invoke_result::type; } // namespace private_ranges //************************************************************************* /// adjacent_iterator - /// An iterator adaptor that produces tuples of N consecutive elements - /// from the underlying range. Each increment advances all N internal + /// An iterator adaptor that produces tuples of Index consecutive elements + /// from the underlying range. Each increment advances all Index internal /// iterators by one position. //************************************************************************* - template + template class adjacent_iterator { - static_assert(N > 0, "adjacent window size must be > 0"); + static_assert(Index > 0, "adjacent window size must be > 0"); public: @@ -4734,17 +4734,17 @@ namespace etl using base_iterator = typename trait::iterator; using base_value_type = typename trait::value_type; using base_reference = decltype(*etl::declval()); - using value_type = private_ranges::repeat_tuple_t; + using value_type = private_ranges::repeat_tuple_t; using difference_type = typename trait::difference_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; + using reference = private_ranges::repeat_tuple_t; using pointer = value_type*; using iterator_category = ETL_OR_STD::forward_iterator_tag; - /// Construct from an array of N iterators (the sliding window). + /// Construct from an array of Index iterators (the sliding window). template constexpr adjacent_iterator(base_iterator first, base_iterator last, etl::index_sequence) : _iters{advance_copy(first, last, Is)...} @@ -4758,7 +4758,7 @@ namespace etl constexpr adjacent_iterator& operator++() { - increment(etl::make_index_sequence{}); + increment(etl::make_index_sequence{}); return *this; } @@ -4771,14 +4771,14 @@ namespace etl constexpr reference operator*() const { - return deref(etl::make_index_sequence{}); + return deref(etl::make_index_sequence{}); } friend constexpr bool operator==(const adjacent_iterator& lhs, const adjacent_iterator& rhs) { - // Compare the last iterator in the window (index N-1). + // Compare the last iterator in the window (index Index-1). // When it reaches end, the window is exhausted. - return lhs._iters[N - 1] == rhs._iters[N - 1]; + return lhs._iters[Index - 1] == rhs._iters[Index - 1]; } friend constexpr bool operator!=(const adjacent_iterator& lhs, const adjacent_iterator& rhs) @@ -4809,27 +4809,27 @@ namespace etl return reference(*_iters[Is]...); } - base_iterator _iters[N]; + base_iterator _iters[Index]; base_iterator _end; }; //************************************************************************* /// adjacent_view - /// A range adaptor that takes a range and a compile-time window size N + /// A range adaptor that takes a range and a compile-time window size Index /// and produces a view of tuples, where the i-th tuple contains the - /// elements at positions [i, i+1, ..., i+N-1] from the underlying range. - /// The resulting view has (size - N + 1) elements, or is empty if the - /// underlying range has fewer than N elements. + /// elements at positions [i, i+1, ..., i+Index-1] from the underlying range. + /// The resulting view has (size - Index + 1) elements, or is empty if the + /// underlying range has fewer than Index elements. //************************************************************************* - template - class adjacent_view : public etl::ranges::view_interface> + template + class adjacent_view : public etl::ranges::view_interface> { - static_assert(N > 0, "adjacent window size must be > 0"); + static_assert(Index > 0, "adjacent window size must be > 0"); public: - using iterator = adjacent_iterator; - using const_iterator = adjacent_iterator; + using iterator = adjacent_iterator; + using const_iterator = adjacent_iterator; constexpr adjacent_view(Range&& r) : _r{etl::move(r)} @@ -4845,19 +4845,19 @@ namespace etl constexpr const_iterator begin() const { - return const_iterator(ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r), etl::make_index_sequence{}); + return const_iterator(ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r), etl::make_index_sequence{}); } constexpr const_iterator end() const { - // The end iterator has all N internal iterators at the end position. - return const_iterator(ETL_OR_STD::end(_r), ETL_OR_STD::end(_r), etl::make_index_sequence{}); + // The end iterator has all Index internal iterators at the end position. + return const_iterator(ETL_OR_STD::end(_r), ETL_OR_STD::end(_r), etl::make_index_sequence{}); } constexpr size_t size() const { auto total = static_cast(etl::distance(ETL_OR_STD::cbegin(_r), ETL_OR_STD::cend(_r))); - return (total >= N) ? (total - N + 1) : 0; + return (total >= Index) ? (total - Index + 1) : 0; } private: @@ -4865,21 +4865,21 @@ namespace etl mutable Range _r; }; - template - adjacent_view(Range&&, etl::integral_constant) -> adjacent_view, N>; + template + adjacent_view(Range&&, etl::integral_constant) -> adjacent_view, Index>; - template - struct adjacent_range_adapter_closure : public range_adapter_closure> + template + struct adjacent_range_adapter_closure : public range_adapter_closure> { template - using target_view_type = adjacent_view; + using target_view_type = adjacent_view; adjacent_range_adapter_closure() = default; template constexpr auto operator()(Range&& r) { - return adjacent_view, N>(views::all(etl::forward(r))); + return adjacent_view, Index>(views::all(etl::forward(r))); } }; @@ -4887,24 +4887,24 @@ namespace etl { namespace private_views { - template + template struct adjacent_fn : public range_adapter_closure_base { template constexpr auto operator()(Range&& r) const { - return adjacent_view, N>(views::all(etl::forward(r))); + return adjacent_view, Index>(views::all(etl::forward(r))); } constexpr auto operator()() const { - return ranges::adjacent_range_adapter_closure(); + return ranges::adjacent_range_adapter_closure(); } }; } // namespace private_views - template - inline constexpr private_views::adjacent_fn adjacent{}; + template + inline constexpr private_views::adjacent_fn adjacent{}; /// pairwise is an alias for adjacent<2>. inline constexpr private_views::adjacent_fn<2> pairwise{}; @@ -4912,14 +4912,14 @@ namespace etl //************************************************************************* /// adjacent_transform_iterator - /// An iterator adaptor that takes a sliding window of N consecutive + /// An iterator adaptor that takes a sliding window of Index consecutive /// elements from the underlying range and applies a transformation /// function to them, producing a single value per window position. //************************************************************************* - template + template class adjacent_transform_iterator { - static_assert(N > 0, "adjacent window size must be > 0"); + static_assert(Index > 0, "adjacent window size must be > 0"); public: @@ -4927,7 +4927,7 @@ namespace etl using base_iterator = typename trait::const_iterator; using base_value_type = typename trait::value_type; - using value_type = private_ranges::repeat_invoke_result_t; + using value_type = private_ranges::repeat_invoke_result_t; using difference_type = typename trait::difference_type; using pointer = const value_type*; using reference = value_type; @@ -4950,7 +4950,7 @@ namespace etl constexpr adjacent_transform_iterator& operator++() { - increment(etl::make_index_sequence{}); + increment(etl::make_index_sequence{}); return *this; } @@ -4963,14 +4963,14 @@ namespace etl constexpr value_type operator*() const { - return deref(etl::make_index_sequence{}); + return deref(etl::make_index_sequence{}); } friend constexpr bool operator==(const adjacent_transform_iterator& lhs, const adjacent_transform_iterator& rhs) { - // Compare the last iterator in the window (index N-1). + // Compare the last iterator in the window (index Index-1). // When it reaches end, the window is exhausted. - return lhs._iters[N - 1] == rhs._iters[N - 1]; + return lhs._iters[Index - 1] == rhs._iters[Index - 1]; } friend constexpr bool operator!=(const adjacent_transform_iterator& lhs, const adjacent_transform_iterator& rhs) @@ -5002,28 +5002,28 @@ namespace etl } Fun _f; - base_iterator _iters[N]; + base_iterator _iters[Index]; base_iterator _end; }; //************************************************************************* /// adjacent_transform_view - /// A range adaptor that takes a range, a compile-time window size N, and + /// A range adaptor that takes a range, a compile-time window size Index, and /// a transformation function, and produces a view whose elements are the - /// result of applying the function to each sliding window of N consecutive + /// result of applying the function to each sliding window of Index consecutive /// elements from the underlying range. - /// The resulting view has (size - N + 1) elements, or is empty if the - /// underlying range has fewer than N elements. + /// The resulting view has (size - Index + 1) elements, or is empty if the + /// underlying range has fewer than Index elements. //************************************************************************* - template - class adjacent_transform_view : public etl::ranges::view_interface< adjacent_transform_view> + template + class adjacent_transform_view : public etl::ranges::view_interface< adjacent_transform_view> { - static_assert(N > 0, "adjacent window size must be > 0"); + static_assert(Index > 0, "adjacent window size must be > 0"); public: - using iterator = adjacent_transform_iterator; - using const_iterator = adjacent_transform_iterator; + using iterator = adjacent_transform_iterator; + using const_iterator = adjacent_transform_iterator; constexpr adjacent_transform_view(Fun f, Range&& r) : _f{f} @@ -5040,19 +5040,19 @@ namespace etl constexpr const_iterator begin() const { - return const_iterator(_f, ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r), etl::make_index_sequence{}); + return const_iterator(_f, ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r), etl::make_index_sequence{}); } constexpr const_iterator end() const { - // The end iterator has all N internal iterators at the end position. - return const_iterator(_f, ETL_OR_STD::end(_r), ETL_OR_STD::end(_r), etl::make_index_sequence{}); + // The end iterator has all Index internal iterators at the end position. + return const_iterator(_f, ETL_OR_STD::end(_r), ETL_OR_STD::end(_r), etl::make_index_sequence{}); } constexpr size_t size() const { auto total = static_cast(etl::distance(ETL_OR_STD::cbegin(_r), ETL_OR_STD::cend(_r))); - return (total >= N) ? (total - N + 1) : 0; + return (total >= Index) ? (total - Index + 1) : 0; } private: @@ -5061,14 +5061,14 @@ namespace etl mutable Range _r; }; - template - adjacent_transform_view(Fun, Range&&, etl::integral_constant) -> adjacent_transform_view, Fun, N>; + template + adjacent_transform_view(Fun, Range&&, etl::integral_constant) -> adjacent_transform_view, Fun, Index>; - template - struct adjacent_transform_range_adapter_closure : public range_adapter_closure< adjacent_transform_range_adapter_closure> + template + struct adjacent_transform_range_adapter_closure : public range_adapter_closure< adjacent_transform_range_adapter_closure> { template - using target_view_type = adjacent_transform_view; + using target_view_type = adjacent_transform_view; adjacent_transform_range_adapter_closure(Fun f) : _f{f} @@ -5078,7 +5078,7 @@ namespace etl template constexpr auto operator()(Range&& r) { - return adjacent_transform_view, Fun, N>(_f, views::all(etl::forward(r))); + return adjacent_transform_view, Fun, Index>(_f, views::all(etl::forward(r))); } Fun _f; @@ -5088,25 +5088,25 @@ namespace etl { namespace private_views { - template + template struct adjacent_transform_fn { template constexpr auto operator()(Range&& r, Fun&& f) const { - return adjacent_transform_view, etl::decay_t, N>(etl::forward(f), views::all(etl::forward(r))); + return adjacent_transform_view, etl::decay_t, Index>(etl::forward(f), views::all(etl::forward(r))); } template constexpr auto operator()(Fun&& f) const { - return ranges::adjacent_transform_range_adapter_closure< N, etl::decay_t>(etl::forward(f)); + return ranges::adjacent_transform_range_adapter_closure< Index, etl::decay_t>(etl::forward(f)); } }; } // namespace private_views - template - inline constexpr private_views::adjacent_transform_fn adjacent_transform{}; + template + inline constexpr private_views::adjacent_transform_fn adjacent_transform{}; /// pairwise_transform is an alias for adjacent_transform<2>. inline constexpr private_views::adjacent_transform_fn<2> pairwise_transform{}; diff --git a/include/etl/span.h b/include/etl/span.h index 3c903772..3d5da1c2 100644 --- a/include/etl/span.h +++ b/include/etl/span.h @@ -66,8 +66,8 @@ namespace etl { }; #if ETL_USING_STL && ETL_USING_CPP11 - template - struct is_std_array> : etl::true_type + template + struct is_std_array> : etl::true_type { }; #endif @@ -88,8 +88,8 @@ namespace etl struct is_etl_array : etl::false_type { }; - template - struct is_etl_array > : etl::true_type + template + struct is_etl_array > : etl::true_type { }; template From 1fd302e168ea51ffe9610734034225e526ae4a24 Mon Sep 17 00:00:00 2001 From: Mike Bloom <91038685+mike919192@users.noreply.github.com> Date: Mon, 10 Aug 2026 05:07:58 -0400 Subject: [PATCH 22/38] Remove redundant noexcept expr (#1542) Co-authored-by: John Wellbelove --- include/etl/alignment.h | 28 ++++++++++++++-------------- include/etl/array.h | 4 ++-- include/etl/not_null.h | 12 ++++++------ include/etl/signal.h | 6 +++--- include/etl/span.h | 6 +++--- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/include/etl/alignment.h b/include/etl/alignment.h index 023acac2..89412503 100644 --- a/include/etl/alignment.h +++ b/include/etl/alignment.h @@ -384,7 +384,7 @@ namespace etl /// constructor. //*************************************************************************** template - typed_storage(TArgs&&... args) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + typed_storage(TArgs&&... args) : valid(false) { create(etl::forward(args)...); @@ -456,7 +456,7 @@ namespace etl /// the internal byte array. //*************************************************************************** template - reference create(TArgs&&... args) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + reference create(TArgs&&... args) { ETL_ASSERT(!has_value(), ETL_ERROR(etl::typed_storage_error)); pointer p = ::new (&storage.value) value_type(etl::forward(args)...); @@ -536,7 +536,7 @@ namespace etl //*************************************************************************** /// \returns a pointer of type T and asserts if has_value() is false. //*************************************************************************** - pointer operator->() ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + pointer operator->() { ETL_ASSERT(has_value(), ETL_ERROR(etl::typed_storage_error)); @@ -546,7 +546,7 @@ namespace etl //*************************************************************************** /// \returns a const pointer of type T and asserts if has_value() is false. //*************************************************************************** - const_pointer operator->() const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + const_pointer operator->() const { ETL_ASSERT(has_value(), ETL_ERROR(etl::typed_storage_error)); @@ -556,7 +556,7 @@ namespace etl //*************************************************************************** /// \returns reference of type T and asserts if has_value() is false. //*************************************************************************** - reference operator*() ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + reference operator*() { return *operator->(); } @@ -564,7 +564,7 @@ namespace etl //*************************************************************************** /// \returns const_reference of type T and asserts if has_value() is false. //*************************************************************************** - const_reference operator*() const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + const_reference operator*() const { return *operator->(); } @@ -620,7 +620,7 @@ namespace etl //*************************************************************************** /// Constructor. //*************************************************************************** - typed_storage_ext(void* pbuffer_) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + typed_storage_ext(void* pbuffer_) : pbuffer(reinterpret_cast(pbuffer_)) , valid(false) { @@ -633,7 +633,7 @@ namespace etl /// constructor. //*************************************************************************** template - typed_storage_ext(void* pbuffer_, TArgs&&... args) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + typed_storage_ext(void* pbuffer_, TArgs&&... args) : pbuffer(reinterpret_cast(pbuffer_)) , valid(false) { @@ -645,7 +645,7 @@ namespace etl /// Move constructor. /// Transfers ownership of the buffer from \p other to this. //*************************************************************************** - typed_storage_ext(typed_storage_ext&& other) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + typed_storage_ext(typed_storage_ext&& other) : pbuffer(other.pbuffer) , valid(other.valid) { @@ -727,7 +727,7 @@ namespace etl /// the external buffer. //*************************************************************************** template - reference create(TArgs&&... args) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + reference create(TArgs&&... args) { ETL_ASSERT(!has_value(), ETL_ERROR(etl::typed_storage_error)); pointer p = ::new (pbuffer) value_type(etl::forward(args)...); @@ -807,7 +807,7 @@ namespace etl //*************************************************************************** /// \returns a pointer of type T and asserts if has_value() is false. //*************************************************************************** - pointer operator->() ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + pointer operator->() { ETL_ASSERT(has_value(), ETL_ERROR(etl::typed_storage_error)); @@ -817,7 +817,7 @@ namespace etl //*************************************************************************** /// \returns a const pointer of type T and asserts if has_value() is false. //*************************************************************************** - const_pointer operator->() const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + const_pointer operator->() const { ETL_ASSERT(has_value(), ETL_ERROR(etl::typed_storage_error)); @@ -827,7 +827,7 @@ namespace etl //*************************************************************************** /// \returns reference of type T and asserts if has_value() is false. //*************************************************************************** - reference operator*() ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + reference operator*() { return *operator->(); } @@ -835,7 +835,7 @@ namespace etl //*************************************************************************** /// \returns const_reference of type T and asserts if has_value() is false. //*************************************************************************** - const_reference operator*() const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + const_reference operator*() const { return *operator->(); } diff --git a/include/etl/array.h b/include/etl/array.h index 82ad5846..1662419b 100644 --- a/include/etl/array.h +++ b/include/etl/array.h @@ -114,7 +114,7 @@ namespace etl /// Returns a reference to the value at index 'i'. ///\param i The index of the element to access. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR14 reference at(size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + ETL_NODISCARD ETL_CONSTEXPR14 reference at(size_t i) { ETL_ASSERT(i < SIZE, ETL_ERROR(array_out_of_range)); @@ -125,7 +125,7 @@ namespace etl /// Returns a const reference to the value at index 'i'. ///\param i The index of the element to access. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR14 const_reference at(size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + ETL_NODISCARD ETL_CONSTEXPR14 const_reference at(size_t i) const { ETL_ASSERT(i < SIZE, ETL_ERROR(array_out_of_range)); diff --git a/include/etl/not_null.h b/include/etl/not_null.h index 25823d68..dc1ada2f 100644 --- a/include/etl/not_null.h +++ b/include/etl/not_null.h @@ -46,7 +46,7 @@ namespace etl { public: - not_null_exception(string_type reason_, string_type file_name_, numeric_type line_number_) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + not_null_exception(string_type reason_, string_type file_name_, numeric_type line_number_) : exception(reason_, file_name_, line_number_) { } @@ -59,7 +59,7 @@ namespace etl { public: - not_null_contains_null(string_type file_name_, numeric_type line_number_) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + not_null_contains_null(string_type file_name_, numeric_type line_number_) : not_null_exception(ETL_ERROR_TEXT("not_null:contains null", ETL_NOT_NULL_FILE_ID"A"), file_name_, line_number_) { } @@ -92,7 +92,7 @@ namespace etl /// Constructs a not_null from a pointer. /// Asserts if the pointer is null. //********************************* - ETL_CONSTEXPR14 explicit not_null(underlying_type ptr_) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 explicit not_null(underlying_type ptr_) : ptr(ptr_) { ETL_ASSERT(ptr_ != ETL_NULLPTR, ETL_ERROR(not_null_contains_null)); @@ -110,7 +110,7 @@ namespace etl /// Assignment from a pointer. /// Asserts if the pointer is null. //********************************* - ETL_CONSTEXPR14 not_null& operator=(underlying_type rhs) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 not_null& operator=(underlying_type rhs) { ETL_ASSERT_OR_RETURN_VALUE(rhs != ETL_NULLPTR, ETL_ERROR(not_null_contains_null), *this); @@ -193,7 +193,7 @@ namespace etl /// Asserts if the unique_ptr contains null. /// Moves from the unique_ptr. //********************************* - ETL_CONSTEXPR14 explicit not_null(underlying_type&& u_ptr_) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 explicit not_null(underlying_type&& u_ptr_) : u_ptr(etl::move(u_ptr_)) { ETL_ASSERT(u_ptr.get() != ETL_NULLPTR, ETL_ERROR(not_null_contains_null)); @@ -204,7 +204,7 @@ namespace etl /// Asserts if the unique_ptr contains null. /// Moves from the unique_ptr. //********************************* - ETL_CONSTEXPR14 not_null& operator=(underlying_type&& rhs) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 not_null& operator=(underlying_type&& rhs) { ETL_ASSERT_OR_RETURN_VALUE(rhs.get() != ETL_NULLPTR, ETL_ERROR(not_null_contains_null), *this); diff --git a/include/etl/signal.h b/include/etl/signal.h index b78e7e58..695bd0c1 100644 --- a/include/etl/signal.h +++ b/include/etl/signal.h @@ -185,7 +185,7 @@ namespace etl ///\param slot: The slot to connect. ///\return false if not all slots could be connected. //************************************************************************* - bool connect(const slot_type& slot) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + bool connect(const slot_type& slot) { if (!connected(slot)) { @@ -204,7 +204,7 @@ namespace etl ///\param slots: std::initializer_list of slots to connect. ///\return false if not all slots could be connected. //************************************************************************* - bool connect(std::initializer_list slots) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + bool connect(std::initializer_list slots) { for (const slot_type& slot : slots) { @@ -226,7 +226,7 @@ namespace etl ///\param slots: etl::span of slots to connect. ///\return false if not all slots could be connected. //************************************************************************* - bool connect(const span_type slots) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + bool connect(const span_type slots) { for (const slot_type& slot : slots) { diff --git a/include/etl/span.h b/include/etl/span.h index 3d5da1c2..b0f7aaf8 100644 --- a/include/etl/span.h +++ b/include/etl/span.h @@ -249,7 +249,7 @@ namespace etl /// Construct from iterator + size //************************************************************************* template - explicit ETL_CONSTEXPR14 span(const TIterator begin_, const size_t size_) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + explicit ETL_CONSTEXPR14 span(const TIterator begin_, const size_t size_) : pbegin(etl::to_address(begin_)) { ETL_ASSERT(size_ == Extent, ETL_ERROR(span_size_mismatch)); @@ -261,7 +261,7 @@ namespace etl //************************************************************************* template ETL_CONSTEXPR14 span(const TIteratorBegin begin_, const TIteratorEnd end_, - typename etl::enable_if< !etl::is_integral::value, void>::type* = 0) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + typename etl::enable_if< !etl::is_integral::value, void>::type* = 0) : pbegin(etl::to_address(begin_)) { ETL_ASSERT(etl::distance(begin_, end_) == Extent, ETL_ERROR(span_size_mismatch)); @@ -293,7 +293,7 @@ namespace etl && etl::is_convertible< decltype(etl::declval< typename etl::remove_reference::type&>().data()), pointer>::value && etl::is_same< typename etl::remove_cv::type, typename etl::remove_cv::type::value_type>::type>::value, - void>::type* = 0) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + void>::type* = 0) : pbegin(a.data()) { ETL_ASSERT(a.size() == Extent, ETL_ERROR(span_size_mismatch)); From 9fcbb0af477776d661915f926595a37958158054 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Wed, 12 Aug 2026 10:36:09 +0200 Subject: [PATCH 23/38] Various cleanup (#1544) * Add explicit static_cast in binary.h This silences implicit conversion warnings on the 16- and 32-bit binary_interleave return values. * Fix copy constructor and copy assignment in alignment.h --------- Co-authored-by: John Wellbelove --- include/etl/alignment.h | 8 ++++---- include/etl/binary.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/etl/alignment.h b/include/etl/alignment.h index 89412503..497151bd 100644 --- a/include/etl/alignment.h +++ b/include/etl/alignment.h @@ -571,8 +571,8 @@ namespace etl private: - typed_storage(etl::typed_storage&) ETL_DELETE; - typed_storage& operator=(etl::typed_storage&) ETL_DELETE; + typed_storage(const etl::typed_storage&) ETL_DELETE; + typed_storage& operator=(const etl::typed_storage&) ETL_DELETE; struct dummy_t { @@ -842,8 +842,8 @@ namespace etl private: - typed_storage_ext(etl::typed_storage_ext&) ETL_DELETE; - typed_storage_ext& operator=(etl::typed_storage_ext&) ETL_DELETE; + typed_storage_ext(const etl::typed_storage_ext&) ETL_DELETE; + typed_storage_ext& operator=(const etl::typed_storage_ext&) ETL_DELETE; pointer pbuffer; bool valid; diff --git a/include/etl/binary.h b/include/etl/binary.h index 230fbf4a..2e02c41e 100644 --- a/include/etl/binary.h +++ b/include/etl/binary.h @@ -2128,7 +2128,7 @@ namespace etl s = (s | (s << 2U)) & 0x33333333UL; s = (s | (s << 1U)) & 0x55555555UL; - return (f | (s << 1U)); + return static_cast(f | (s << 1U)); } #if ETL_USING_64BIT_TYPES @@ -2156,7 +2156,7 @@ namespace etl s = (s | (s << 2U)) & 0x3333333333333333ULL; s = (s | (s << 1U)) & 0x5555555555555555ULL; - return (f | (s << 1U)); + return static_cast(f | (s << 1U)); } #endif From 7ee5a661b6e7fd0aef7e8ba7542038bc77e5661f Mon Sep 17 00:00:00 2001 From: Marco Nilsson Date: Thu, 13 Aug 2026 16:41:12 +0200 Subject: [PATCH 24/38] Add span deduction guide for types with data() and size(). (#1550) * Add span deduction guide for types with data() and size(). This enables span construct with type deduction from containers which support .data() and .size(), primarily vector. --------- Co-authored-by: John Wellbelove --- include/etl/span.h | 5 ++++ test/test_span_dynamic_extent.cpp | 45 +++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/include/etl/span.h b/include/etl/span.h index b0f7aaf8..d948c355 100644 --- a/include/etl/span.h +++ b/include/etl/span.h @@ -1509,6 +1509,11 @@ namespace etl template span(const std::array&) -> span; #endif + + // Any other type that supports data() and size() member functions + template ::value && etl::has_size::value + && etl::is_lvalue_reference::value>> + span(TContainer&&) -> span().data())>, etl::dynamic_extent>; #endif //************************************************************************* diff --git a/test/test_span_dynamic_extent.cpp b/test/test_span_dynamic_extent.cpp index b19a4032..05f56880 100644 --- a/test/test_span_dynamic_extent.cpp +++ b/test/test_span_dynamic_extent.cpp @@ -983,6 +983,51 @@ namespace CHECK((std::is_same_v>)); } + //************************************************************************* + TEST(test_template_deduction_guide_for_std_vector) + { + std::vector data = {1, 2, 3, 4}; + const std::vector data2 = {1, 2, 3, 4}; + + etl::span span = data; + etl::span span2 = data2; + + CHECK_EQUAL(etl::dynamic_extent, span.extent); + CHECK_EQUAL(ETL_OR_STD17::size(data), span.size()); + CHECK_EQUAL(etl::dynamic_extent, span2.extent); + CHECK_EQUAL(ETL_OR_STD17::size(data2), span2.size()); + + CHECK((std::is_same_v>)); + CHECK((std::is_same_v>)); + } + + //************************************************************************* + TEST(test_template_deduction_guide_for_type_with_data_and_size) + { + struct DataAndSize + { + int buffer[4] = {1, 2, 3, 4}; + int* data() + { + return buffer; + } + size_t size() const + { + return 4U; + } + + using value_type = int; + }; + + DataAndSize data; + + etl::span span = data; + + CHECK_EQUAL(etl::dynamic_extent, span.extent); + CHECK_EQUAL(data.size(), span.size()); + CHECK((std::is_same_v>)); + } + //************************************************************************* TEST(test_template_deduction_guide_for_iterators) { From cc91f2ed783b462d192ada02f1e7c10e67d86c26 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Fri, 14 Aug 2026 08:59:17 +0200 Subject: [PATCH 25/38] Fix formatting of int8_t and uint8_t (#1551) Fixes https://github.com/ETLCPP/etl/issues/1549 --- include/etl/format.h | 99 ++++++++++++++++++++++++++++---------------- test/test_format.cpp | 37 ++++++----------- 2 files changed, 77 insertions(+), 59 deletions(-) diff --git a/include/etl/format.h b/include/etl/format.h index 75ac5952..30152dc9 100644 --- a/include/etl/format.h +++ b/include/etl/format.h @@ -91,44 +91,70 @@ namespace etl POINTER // const void* }; - // Map a type to its category. Decays and removes cv-qualifiers. + // Trait mapping a type (with references and cv-qualifiers removed) to its type_category. + // + // NOTE: signed char/unsigned char (typically int8_t/uint8_t) are NOT + // categorised as CHAR here - only plain 'char' gets character semantics. + // signed/unsigned char are formatted as integers, matching basic_format_arg + // storage and std::format's formatter/. This is + // enforced structurally: the INTEGER partial specialisation is constrained + // with is_integral && !is_same && !is_same, so + // signed/unsigned char (is_integral == true) fall into INTEGER while the + // dedicated bool/char full specialisations take priority for those exact types. + template + struct type_category_trait + { + static constexpr type_category value = type_category::NONE; // unknown type: custom formatter, be permissive + }; + + template <> + struct type_category_trait + { + static constexpr type_category value = type_category::BOOLEAN; + }; + + template <> + struct type_category_trait + { + static constexpr type_category value = type_category::CHAR; + }; + + template + struct type_category_trait< + T, typename etl::enable_if::value && !etl::is_same::value && !etl::is_same::value>::type> + { + static constexpr type_category value = type_category::INTEGER; + }; + + template + struct type_category_trait::value>::type> + { + static constexpr type_category value = type_category::FLOAT; + }; + + template + struct type_category_trait::value || etl::is_same::value + || etl::is_same::value || etl::is_base_of::value>::type> + { + static constexpr type_category value = type_category::STRING; + }; + + template + struct type_category_trait< + T, typename etl::enable_if::value && !etl::is_same::value && !etl::is_same::value>::type> + { + static constexpr type_category value = type_category::POINTER; + }; + + // Map a type to its category. Removes references and cv-qualifiers, then defers + // entirely to the type_category_trait specialisations above. template constexpr type_category get_type_category() { using U = typename etl::remove_cv::type>::type; - // Order matters: bool before integral, char before integral - if (etl::is_same::value) - return type_category::BOOLEAN; - if (etl::is_same::value) - return type_category::CHAR; - if (etl::is_same::value) - return type_category::CHAR; - if (etl::is_same::value) - return type_category::CHAR; - if (etl::is_integral::value) - return type_category::INTEGER; - if (etl::is_same::value) - return type_category::FLOAT; - if (etl::is_same::value) - return type_category::FLOAT; - if (etl::is_same::value) - return type_category::FLOAT; - if (etl::is_same::value) - return type_category::STRING; - if (etl::is_same::value) - return type_category::STRING; - if (etl::is_same::value) - return type_category::STRING; - if (etl::is_base_of::value) - return type_category::STRING; - if (etl::is_pointer::value) - return type_category::POINTER; - if (etl::is_same::value) - return type_category::POINTER; - if (etl::is_same::value) - return type_category::POINTER; - return type_category::NONE; // unknown type: custom formatter, be permissive + return type_category_trait::value; } // Check if a format type character is valid for a given type category. @@ -727,13 +753,16 @@ namespace etl { } + // int8_t / uint8_t are typically defined as signed char / unsigned char, but + // (unlike plain char) they are formatted as integers by default, matching + // std::format's formatter/formatter behaviour. basic_format_arg(const signed char v) - : data(static_cast(v)) + : data(static_cast(v)) { } basic_format_arg(const unsigned char v) - : data(static_cast(v)) + : data(static_cast(v)) { } diff --git a/test/test_format.cpp b/test/test_format.cpp index 6fd2cbf4..480345b5 100644 --- a/test/test_format.cpp +++ b/test/test_format.cpp @@ -218,10 +218,9 @@ namespace { etl::string<100> s; - // mapped to unsigned char - // CHECK_EQUAL("34", test_format(s, "{}", static_cast(34))); - // mapped to signed char - // CHECK_EQUAL("-14", test_format(s, "{}", static_cast(-14))); + // int8_t/uint8_t are formatted as integers (issue #1549), not as characters + CHECK_EQUAL("34", test_format(s, "{}", static_cast(34))); + CHECK_EQUAL("-14", test_format(s, "{}", static_cast(-14))); CHECK_EQUAL("6534", test_format(s, "{}", static_cast(6534))); CHECK_EQUAL("-9414", test_format(s, "{}", static_cast(-9414))); CHECK_EQUAL("236534", test_format(s, "{}", static_cast(236534))); @@ -619,45 +618,35 @@ namespace //************************************************************************* TEST(test_format_signed_char) { + // signed char (typically int8_t) is formatted as an integer by default + // (issue #1549), matching std::format. Only {:c} converts it to a character. + // '?' (debug-escape) is NOT a valid presentation type for signed char. etl::string<100> s; - CHECK_EQUAL("a s b", test_format(s, "a {} b", static_cast('s'))); + CHECK_EQUAL("a 115 b", test_format(s, "a {} b", static_cast('s'))); CHECK_EQUAL("a s b", test_format(s, "a {:c} b", static_cast('s'))); - CHECK_EQUAL("a 's' b", test_format(s, "a {:?} b", static_cast('s'))); - CHECK_EQUAL("a \t b", test_format(s, "a {} b", static_cast('\t'))); - CHECK_EQUAL("a '\\t' b", test_format(s, "a {:?} b", static_cast('\t'))); - CHECK_EQUAL("a '\\n' b", test_format(s, "a {:?} b", static_cast('\n'))); - CHECK_EQUAL("a '\\r' b", test_format(s, "a {:?} b", static_cast('\r'))); - CHECK_EQUAL("a '\\\"' b", test_format(s, "a {:?} b", static_cast('"'))); - CHECK_EQUAL("a '\\'' b", test_format(s, "a {:?} b", static_cast('\''))); - CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast('\\'))); - CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast('\\'))); CHECK_EQUAL("a 97 b", test_format(s, "a {:d} b", static_cast('a'))); CHECK_EQUAL("a 61 b", test_format(s, "a {:X} b", static_cast('a'))); CHECK_EQUAL("a 61 b", test_format(s, "a {:x} b", static_cast('a'))); CHECK_EQUAL("a 0x61 b", test_format(s, "a {:#x} b", static_cast('a'))); + CHECK_EQUAL("a -12 b", test_format(s, "a {} b", static_cast(-12))); } //************************************************************************* TEST(test_format_unsigned_char) { + // unsigned char (typically uint8_t) is formatted as an integer by default + // (issue #1549), matching std::format. Only {:c} converts it to a character. + // '?' (debug-escape) is NOT a valid presentation type for unsigned char. etl::string<100> s; - CHECK_EQUAL("a s b", test_format(s, "a {} b", static_cast('s'))); + CHECK_EQUAL("a 115 b", test_format(s, "a {} b", static_cast('s'))); CHECK_EQUAL("a s b", test_format(s, "a {:c} b", static_cast('s'))); - CHECK_EQUAL("a 's' b", test_format(s, "a {:?} b", static_cast('s'))); - CHECK_EQUAL("a \t b", test_format(s, "a {} b", static_cast('\t'))); - CHECK_EQUAL("a '\\t' b", test_format(s, "a {:?} b", static_cast('\t'))); - CHECK_EQUAL("a '\\n' b", test_format(s, "a {:?} b", static_cast('\n'))); - CHECK_EQUAL("a '\\r' b", test_format(s, "a {:?} b", static_cast('\r'))); - CHECK_EQUAL("a '\\\"' b", test_format(s, "a {:?} b", static_cast('"'))); - CHECK_EQUAL("a '\\'' b", test_format(s, "a {:?} b", static_cast('\''))); - CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast('\\'))); - CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast('\\'))); CHECK_EQUAL("a 97 b", test_format(s, "a {:d} b", static_cast('a'))); CHECK_EQUAL("a 61 b", test_format(s, "a {:X} b", static_cast('a'))); CHECK_EQUAL("a 61 b", test_format(s, "a {:x} b", static_cast('a'))); CHECK_EQUAL("a 0x61 b", test_format(s, "a {:#x} b", static_cast('a'))); + CHECK_EQUAL("a 65 b", test_format(s, "a {} b", static_cast(65))); } //************************************************************************* From 5f33e2256da94cfeac6d74ba4ff0674feb663fa5 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Sun, 16 Aug 2026 17:20:44 +0200 Subject: [PATCH 26/38] Fix flat_map/reference_flat_map to use random-access iterators in lower_bound (#1540) ireference_flat_map::lower_bound/upper_bound/equal_range searched over its own bidirectional iterator/const_iterator, forcing etl::distance/ advance into an O(n) fallback even though the underlying lookup_t (etl::ivector) is random access. Add pointer overloads to the Compare functor and search directly over lookup.begin()/end(), wrapping the result back into iterator/const_iterator. Fixes #1518 Do the same for: * reference_flat_multimap.h * reference_flat_multiset.h * reference_flat_set.h Co-authored-by: John Wellbelove --- include/etl/reference_flat_map.h | 58 ++++++++---- include/etl/reference_flat_multimap.h | 58 ++++++++---- include/etl/reference_flat_multiset.h | 121 +++++++++++++++++++------ include/etl/reference_flat_set.h | 125 ++++++++++++++++++++------ 4 files changed, 274 insertions(+), 88 deletions(-) diff --git a/include/etl/reference_flat_map.h b/include/etl/reference_flat_map.h index b6bf0447..c8be6dcb 100644 --- a/include/etl/reference_flat_map.h +++ b/include/etl/reference_flat_map.h @@ -330,6 +330,16 @@ namespace etl return comp(key, element.first); } + bool operator()(const value_type* element, const key_type& key) const + { + return comp(element->first, key); + } + + bool operator()(const key_type& key, const value_type* element) const + { + return comp(key, element->first); + } + #if ETL_USING_CPP11 template ::value, int> = 0> bool operator()(const value_type& element, const K& key) const @@ -342,6 +352,18 @@ namespace etl { return comp(key, element.first); } + + template ::value, int> = 0> + bool operator()(const value_type* element, const K& key) const + { + return comp(element->first, key); + } + + template ::value, int> = 0> + bool operator()(const K& key, const value_type* element) const + { + return comp(key, element->first); + } #endif key_compare comp; @@ -802,7 +824,7 @@ namespace etl //********************************************************************* iterator lower_bound(key_parameter_t key) { - return etl::lower_bound(begin(), end(), key, compare); + return iterator(etl::lower_bound(lookup.begin(), lookup.end(), key, compare)); } #if ETL_USING_CPP11 @@ -810,7 +832,7 @@ namespace etl template ::value, int> = 0> iterator lower_bound(const K& key) { - return etl::lower_bound(begin(), end(), key, compare); + return iterator(etl::lower_bound(lookup.begin(), lookup.end(), key, compare)); } #endif @@ -821,7 +843,7 @@ namespace etl //********************************************************************* const_iterator lower_bound(key_parameter_t key) const { - return etl::lower_bound(cbegin(), cend(), key, compare); + return const_iterator(etl::lower_bound(lookup.cbegin(), lookup.cend(), key, compare)); } #if ETL_USING_CPP11 @@ -829,7 +851,7 @@ namespace etl template ::value, int> = 0> const_iterator lower_bound(const K& key) const { - return etl::lower_bound(cbegin(), cend(), key, compare); + return const_iterator(etl::lower_bound(lookup.cbegin(), lookup.cend(), key, compare)); } #endif @@ -840,7 +862,7 @@ namespace etl //********************************************************************* iterator upper_bound(key_parameter_t key) { - return etl::upper_bound(begin(), end(), key, compare); + return iterator(etl::upper_bound(lookup.begin(), lookup.end(), key, compare)); } #if ETL_USING_CPP11 @@ -848,7 +870,7 @@ namespace etl template ::value, int> = 0> iterator upper_bound(const K& key) { - return etl::upper_bound(begin(), end(), key, compare); + return iterator(etl::upper_bound(lookup.begin(), lookup.end(), key, compare)); } #endif @@ -859,7 +881,7 @@ namespace etl //********************************************************************* const_iterator upper_bound(key_parameter_t key) const { - return etl::upper_bound(begin(), end(), key, compare); + return const_iterator(etl::upper_bound(lookup.cbegin(), lookup.cend(), key, compare)); } #if ETL_USING_CPP11 @@ -867,7 +889,7 @@ namespace etl template ::value, int> = 0> const_iterator upper_bound(const K& key) const { - return etl::upper_bound(begin(), end(), key, compare); + return const_iterator(etl::upper_bound(lookup.cbegin(), lookup.cend(), key, compare)); } #endif @@ -878,9 +900,10 @@ namespace etl //********************************************************************* ETL_OR_STD::pair equal_range(key_parameter_t key) { - iterator i_lower = etl::lower_bound(begin(), end(), key, compare); + typename lookup_t::iterator i_lower = etl::lower_bound(lookup.begin(), lookup.end(), key, compare); + typename lookup_t::iterator i_upper = etl::upper_bound(i_lower, lookup.end(), key, compare); - return ETL_OR_STD::make_pair(i_lower, etl::upper_bound(i_lower, end(), key, compare)); + return ETL_OR_STD::make_pair(iterator(i_lower), iterator(i_upper)); } #if ETL_USING_CPP11 @@ -888,9 +911,10 @@ namespace etl template ::value, int> = 0> ETL_OR_STD::pair equal_range(const K& key) { - iterator i_lower = etl::lower_bound(begin(), end(), key, compare); + typename lookup_t::iterator i_lower = etl::lower_bound(lookup.begin(), lookup.end(), key, compare); + typename lookup_t::iterator i_upper = etl::upper_bound(i_lower, lookup.end(), key, compare); - return ETL_OR_STD::make_pair(i_lower, etl::upper_bound(i_lower, end(), key, compare)); + return ETL_OR_STD::make_pair(iterator(i_lower), iterator(i_upper)); } #endif @@ -901,9 +925,10 @@ namespace etl //********************************************************************* ETL_OR_STD::pair equal_range(key_parameter_t key) const { - const_iterator i_lower = etl::lower_bound(cbegin(), cend(), key, compare); + typename lookup_t::const_iterator i_lower = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, compare); + typename lookup_t::const_iterator i_upper = etl::upper_bound(i_lower, lookup.cend(), key, compare); - return ETL_OR_STD::make_pair(i_lower, etl::upper_bound(i_lower, cend(), key, compare)); + return ETL_OR_STD::make_pair(const_iterator(i_lower), const_iterator(i_upper)); } #if ETL_USING_CPP11 @@ -911,9 +936,10 @@ namespace etl template ::value, int> = 0> ETL_OR_STD::pair equal_range(const K& key) const { - const_iterator i_lower = etl::lower_bound(cbegin(), cend(), key, compare); + typename lookup_t::const_iterator i_lower = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, compare); + typename lookup_t::const_iterator i_upper = etl::upper_bound(i_lower, lookup.cend(), key, compare); - return ETL_OR_STD::make_pair(i_lower, etl::upper_bound(i_lower, cend(), key, compare)); + return ETL_OR_STD::make_pair(const_iterator(i_lower), const_iterator(i_upper)); } #endif diff --git a/include/etl/reference_flat_multimap.h b/include/etl/reference_flat_multimap.h index 57934215..9e077044 100644 --- a/include/etl/reference_flat_multimap.h +++ b/include/etl/reference_flat_multimap.h @@ -303,6 +303,16 @@ namespace etl return comp(key, element.first); } + bool operator()(const value_type* element, key_type key) const + { + return comp(element->first, key); + } + + bool operator()(key_type key, const value_type* element) const + { + return comp(key, element->first); + } + #if ETL_USING_CPP11 template ::value, int> = 0> bool operator()(const value_type& element, const K& key) const @@ -315,6 +325,18 @@ namespace etl { return comp(key, element.first); } + + template ::value, int> = 0> + bool operator()(const value_type* element, const K& key) const + { + return comp(element->first, key); + } + + template ::value, int> = 0> + bool operator()(const K& key, const value_type* element) const + { + return comp(key, element->first); + } #endif key_compare comp; @@ -730,7 +752,7 @@ namespace etl //********************************************************************* iterator lower_bound(key_parameter_t key) { - return etl::lower_bound(begin(), end(), key, compare); + return iterator(etl::lower_bound(lookup.begin(), lookup.end(), key, compare)); } #if ETL_USING_CPP11 @@ -738,7 +760,7 @@ namespace etl template ::value, int> = 0> iterator lower_bound(const K& key) { - return etl::lower_bound(begin(), end(), key, compare); + return iterator(etl::lower_bound(lookup.begin(), lookup.end(), key, compare)); } #endif @@ -749,7 +771,7 @@ namespace etl //********************************************************************* const_iterator lower_bound(key_parameter_t key) const { - return etl::lower_bound(cbegin(), cend(), key, compare); + return const_iterator(etl::lower_bound(lookup.cbegin(), lookup.cend(), key, compare)); } #if ETL_USING_CPP11 @@ -757,7 +779,7 @@ namespace etl template ::value, int> = 0> const_iterator lower_bound(const K& key) const { - return etl::lower_bound(cbegin(), cend(), key, compare); + return const_iterator(etl::lower_bound(lookup.cbegin(), lookup.cend(), key, compare)); } #endif @@ -768,7 +790,7 @@ namespace etl //********************************************************************* iterator upper_bound(key_parameter_t key) { - return etl::upper_bound(begin(), end(), key, compare); + return iterator(etl::upper_bound(lookup.begin(), lookup.end(), key, compare)); } #if ETL_USING_CPP11 @@ -776,7 +798,7 @@ namespace etl template ::value, int> = 0> iterator upper_bound(const K& key) { - return etl::upper_bound(begin(), end(), key, compare); + return iterator(etl::upper_bound(lookup.begin(), lookup.end(), key, compare)); } #endif @@ -787,7 +809,7 @@ namespace etl //********************************************************************* const_iterator upper_bound(key_parameter_t key) const { - return etl::upper_bound(begin(), end(), key, compare); + return const_iterator(etl::upper_bound(lookup.cbegin(), lookup.cend(), key, compare)); } #if ETL_USING_CPP11 @@ -795,7 +817,7 @@ namespace etl template ::value, int> = 0> const_iterator upper_bound(const K& key) const { - return etl::upper_bound(begin(), end(), key, compare); + return const_iterator(etl::upper_bound(lookup.cbegin(), lookup.cend(), key, compare)); } #endif @@ -806,9 +828,10 @@ namespace etl //********************************************************************* ETL_OR_STD::pair equal_range(key_parameter_t key) { - iterator i_lower = etl::lower_bound(begin(), end(), key, compare); + typename lookup_t::iterator i_lower = etl::lower_bound(lookup.begin(), lookup.end(), key, compare); + typename lookup_t::iterator i_upper = etl::upper_bound(i_lower, lookup.end(), key, compare); - return ETL_OR_STD::make_pair(i_lower, etl::upper_bound(i_lower, end(), key, compare)); + return ETL_OR_STD::make_pair(iterator(i_lower), iterator(i_upper)); } #if ETL_USING_CPP11 @@ -816,9 +839,10 @@ namespace etl template ::value, int> = 0> ETL_OR_STD::pair equal_range(const K& key) { - iterator i_lower = etl::lower_bound(begin(), end(), key, compare); + typename lookup_t::iterator i_lower = etl::lower_bound(lookup.begin(), lookup.end(), key, compare); + typename lookup_t::iterator i_upper = etl::upper_bound(i_lower, lookup.end(), key, compare); - return ETL_OR_STD::make_pair(i_lower, etl::upper_bound(i_lower, end(), key, compare)); + return ETL_OR_STD::make_pair(iterator(i_lower), iterator(i_upper)); } #endif @@ -829,9 +853,10 @@ namespace etl //********************************************************************* ETL_OR_STD::pair equal_range(key_parameter_t key) const { - const_iterator i_lower = etl::lower_bound(cbegin(), cend(), key, compare); + typename lookup_t::const_iterator i_lower = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, compare); + typename lookup_t::const_iterator i_upper = etl::upper_bound(i_lower, lookup.cend(), key, compare); - return ETL_OR_STD::make_pair(i_lower, etl::upper_bound(i_lower, cend(), key, compare)); + return ETL_OR_STD::make_pair(const_iterator(i_lower), const_iterator(i_upper)); } #if ETL_USING_CPP11 @@ -839,9 +864,10 @@ namespace etl template ::value, int> = 0> ETL_OR_STD::pair equal_range(const K& key) const { - const_iterator i_lower = etl::lower_bound(cbegin(), cend(), key, compare); + typename lookup_t::const_iterator i_lower = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, compare); + typename lookup_t::const_iterator i_upper = etl::upper_bound(i_lower, lookup.cend(), key, compare); - return ETL_OR_STD::make_pair(i_lower, etl::upper_bound(i_lower, cend(), key, compare)); + return ETL_OR_STD::make_pair(const_iterator(i_lower), const_iterator(i_upper)); } #endif diff --git a/include/etl/reference_flat_multiset.h b/include/etl/reference_flat_multiset.h index 8b2d0b41..3836dbcf 100644 --- a/include/etl/reference_flat_multiset.h +++ b/include/etl/reference_flat_multiset.h @@ -294,6 +294,47 @@ namespace etl typedef typename etl::parameter_type::type parameter_t; + //********************************************************************* + /// Compares a pointer to an element (as stored in 'lookup') against a + /// key, allowing binary searches directly over the random-access + /// 'lookup' container instead of the bidirectional iterator/const_iterator. + //********************************************************************* + class Compare + { + public: + + Compare(const TKeyCompare& comp_) + : comp(comp_) + { + } + + bool operator()(const value_type* element, parameter_t key) const + { + return comp(*element, key); + } + + bool operator()(parameter_t key, const value_type* element) const + { + return comp(key, *element); + } + +#if ETL_USING_CPP11 + template ::value, int> = 0> + bool operator()(const value_type* element, const K& key) const + { + return comp(*element, key); + } + + template ::value, int> = 0> + bool operator()(const K& key, const value_type* element) const + { + return comp(key, *element); + } +#endif + + const TKeyCompare& comp; + }; + public: typedef ETL_OR_STD::reverse_iterator reverse_iterator; @@ -458,7 +499,7 @@ namespace etl ETL_ASSERT(!lookup.full(), ETL_ERROR(flat_multiset_full)); - iterator i_element = etl::upper_bound(begin(), end(), value, compare); + iterator i_element = upper_bound(value); if (i_element == end()) { @@ -606,13 +647,14 @@ namespace etl //********************************************************************* iterator find(parameter_t key) { - iterator itr = etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::iterator itr = etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare); - if (itr != end()) + if (itr != lookup.end()) { - if (!key_compare()(*itr, key) && !key_compare()(key, *itr)) + if (!compare(**itr, key) && !compare(key, **itr)) { - return itr; + return iterator(itr); } else { @@ -628,13 +670,14 @@ namespace etl template ::value, int> = 0> iterator find(const K& key) { - iterator itr = etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::iterator itr = etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare); - if (itr != end()) + if (itr != lookup.end()) { - if (!key_compare()(*itr, key) && !key_compare()(key, *itr)) + if (!compare(**itr, key) && !compare(key, **itr)) { - return itr; + return iterator(itr); } else { @@ -653,13 +696,14 @@ namespace etl //********************************************************************* const_iterator find(parameter_t key) const { - const_iterator itr = etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::const_iterator itr = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare); - if (itr != end()) + if (itr != lookup.cend()) { - if (!key_compare()(*itr, key) && !key_compare()(key, *itr)) + if (!compare(**itr, key) && !compare(key, **itr)) { - return itr; + return const_iterator(itr); } else { @@ -675,13 +719,14 @@ namespace etl template ::value, int> = 0> const_iterator find(const K& key) const { - const_iterator itr = etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::const_iterator itr = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare); - if (itr != end()) + if (itr != lookup.cend()) { - if (!key_compare()(*itr, key) && !key_compare()(key, *itr)) + if (!compare(**itr, key) && !compare(key, **itr)) { - return itr; + return const_iterator(itr); } else { @@ -723,7 +768,8 @@ namespace etl //********************************************************************* iterator lower_bound(parameter_t key) { - return etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + return iterator(etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare)); } #if ETL_USING_CPP11 @@ -731,7 +777,8 @@ namespace etl template ::value, int> = 0> iterator lower_bound(const K& key) { - return etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + return iterator(etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare)); } #endif @@ -742,7 +789,8 @@ namespace etl //********************************************************************* const_iterator lower_bound(parameter_t key) const { - return etl::lower_bound(cbegin(), cend(), key, compare); + Compare ptr_compare(compare); + return const_iterator(etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare)); } #if ETL_USING_CPP11 @@ -750,7 +798,8 @@ namespace etl template ::value, int> = 0> const_iterator lower_bound(const K& key) const { - return etl::lower_bound(cbegin(), cend(), key, compare); + Compare ptr_compare(compare); + return const_iterator(etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare)); } #endif @@ -761,7 +810,8 @@ namespace etl //********************************************************************* iterator upper_bound(parameter_t key) { - return etl::upper_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + return iterator(etl::upper_bound(lookup.begin(), lookup.end(), key, ptr_compare)); } #if ETL_USING_CPP11 @@ -769,7 +819,8 @@ namespace etl template ::value, int> = 0> iterator upper_bound(const K& key) { - return etl::upper_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + return iterator(etl::upper_bound(lookup.begin(), lookup.end(), key, ptr_compare)); } #endif @@ -780,7 +831,8 @@ namespace etl //********************************************************************* const_iterator upper_bound(parameter_t key) const { - return etl::upper_bound(cbegin(), cend(), key, compare); + Compare ptr_compare(compare); + return const_iterator(etl::upper_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare)); } #if ETL_USING_CPP11 @@ -788,7 +840,8 @@ namespace etl template ::value, int> = 0> const_iterator upper_bound(const K& key) const { - return etl::upper_bound(cbegin(), cend(), key, compare); + Compare ptr_compare(compare); + return const_iterator(etl::upper_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare)); } #endif @@ -799,7 +852,11 @@ namespace etl //********************************************************************* ETL_OR_STD::pair equal_range(parameter_t key) { - return etl::equal_range(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::iterator i_lower = etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare); + typename lookup_t::iterator i_upper = etl::upper_bound(i_lower, lookup.end(), key, ptr_compare); + + return ETL_OR_STD::make_pair(iterator(i_lower), iterator(i_upper)); } #if ETL_USING_CPP11 @@ -807,7 +864,11 @@ namespace etl template ::value, int> = 0> ETL_OR_STD::pair equal_range(const K& key) { - return etl::equal_range(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::iterator i_lower = etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare); + typename lookup_t::iterator i_upper = etl::upper_bound(i_lower, lookup.end(), key, ptr_compare); + + return ETL_OR_STD::make_pair(iterator(i_lower), iterator(i_upper)); } #endif @@ -835,7 +896,11 @@ namespace etl //********************************************************************* ETL_OR_STD::pair equal_range(parameter_t key) const { - return etl::equal_range(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::const_iterator i_lower = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare); + typename lookup_t::const_iterator i_upper = etl::upper_bound(i_lower, lookup.cend(), key, ptr_compare); + + return ETL_OR_STD::make_pair(const_iterator(i_lower), const_iterator(i_upper)); } //************************************************************************* diff --git a/include/etl/reference_flat_set.h b/include/etl/reference_flat_set.h index 5da865aa..9cfd2d6e 100644 --- a/include/etl/reference_flat_set.h +++ b/include/etl/reference_flat_set.h @@ -295,6 +295,47 @@ namespace etl typedef typename etl::parameter_type::type parameter_t; + //********************************************************************* + /// Compares a pointer to an element (as stored in 'lookup') against a + /// key, allowing binary searches directly over the random-access + /// 'lookup' container instead of the bidirectional iterator/const_iterator. + //********************************************************************* + class Compare + { + public: + + Compare(const TKeyCompare& comp_) + : comp(comp_) + { + } + + bool operator()(const value_type* element, parameter_t key) const + { + return comp(*element, key); + } + + bool operator()(parameter_t key, const value_type* element) const + { + return comp(key, *element); + } + +#if ETL_USING_CPP11 + template ::value, int> = 0> + bool operator()(const value_type* element, const K& key) const + { + return comp(*element, key); + } + + template ::value, int> = 0> + bool operator()(const K& key, const value_type* element) const + { + return comp(key, *element); + } +#endif + + const TKeyCompare& comp; + }; + public: typedef ETL_OR_STD::reverse_iterator reverse_iterator; @@ -581,13 +622,14 @@ namespace etl //********************************************************************* iterator find(parameter_t key) { - iterator itr = etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::iterator itr = etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare); - if (itr != end()) + if (itr != lookup.end()) { - if (!key_compare()(*itr, key) && !key_compare()(key, *itr)) + if (!compare(**itr, key) && !compare(key, **itr)) { - return itr; + return iterator(itr); } else { @@ -603,13 +645,14 @@ namespace etl template ::value, int> = 0> iterator find(const K& key) { - iterator itr = etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::iterator itr = etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare); - if (itr != end()) + if (itr != lookup.end()) { - if (!key_compare()(*itr, key) && !key_compare()(key, *itr)) + if (!compare(**itr, key) && !compare(key, **itr)) { - return itr; + return iterator(itr); } else { @@ -628,13 +671,14 @@ namespace etl //********************************************************************* const_iterator find(parameter_t key) const { - const_iterator itr = etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::const_iterator itr = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare); - if (itr != end()) + if (itr != lookup.cend()) { - if (!key_compare()(*itr, key) && !key_compare()(key, *itr)) + if (!compare(**itr, key) && !compare(key, **itr)) { - return itr; + return const_iterator(itr); } else { @@ -650,13 +694,14 @@ namespace etl template ::value, int> = 0> const_iterator find(const K& key) const { - const_iterator itr = etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::const_iterator itr = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare); - if (itr != end()) + if (itr != lookup.cend()) { - if (!key_compare()(*itr, key) && !key_compare()(key, *itr)) + if (!compare(**itr, key) && !compare(key, **itr)) { - return itr; + return const_iterator(itr); } else { @@ -694,7 +739,8 @@ namespace etl //********************************************************************* iterator lower_bound(parameter_t key) { - return etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + return iterator(etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare)); } #if ETL_USING_CPP11 @@ -702,7 +748,8 @@ namespace etl template ::value, int> = 0> iterator lower_bound(const K& key) { - return etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + return iterator(etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare)); } #endif @@ -713,7 +760,8 @@ namespace etl //********************************************************************* const_iterator lower_bound(parameter_t key) const { - return etl::lower_bound(cbegin(), cend(), key, compare); + Compare ptr_compare(compare); + return const_iterator(etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare)); } #if ETL_USING_CPP11 @@ -721,7 +769,8 @@ namespace etl template ::value, int> = 0> const_iterator lower_bound(const K& key) const { - return etl::lower_bound(cbegin(), cend(), key, compare); + Compare ptr_compare(compare); + return const_iterator(etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare)); } #endif @@ -732,7 +781,8 @@ namespace etl //********************************************************************* iterator upper_bound(parameter_t key) { - return etl::upper_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + return iterator(etl::upper_bound(lookup.begin(), lookup.end(), key, ptr_compare)); } #if ETL_USING_CPP11 @@ -740,7 +790,8 @@ namespace etl template ::value, int> = 0> iterator upper_bound(const K& key) { - return etl::upper_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + return iterator(etl::upper_bound(lookup.begin(), lookup.end(), key, ptr_compare)); } #endif @@ -751,7 +802,8 @@ namespace etl //********************************************************************* const_iterator upper_bound(parameter_t key) const { - return etl::upper_bound(cbegin(), cend(), key, compare); + Compare ptr_compare(compare); + return const_iterator(etl::upper_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare)); } #if ETL_USING_CPP11 @@ -759,7 +811,8 @@ namespace etl template ::value, int> = 0> const_iterator upper_bound(const K& key) const { - return etl::upper_bound(cbegin(), cend(), key, compare); + Compare ptr_compare(compare); + return const_iterator(etl::upper_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare)); } #endif @@ -770,7 +823,11 @@ namespace etl //********************************************************************* ETL_OR_STD::pair equal_range(parameter_t key) { - return etl::equal_range(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::iterator i_lower = etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare); + typename lookup_t::iterator i_upper = etl::upper_bound(i_lower, lookup.end(), key, ptr_compare); + + return ETL_OR_STD::make_pair(iterator(i_lower), iterator(i_upper)); } #if ETL_USING_CPP11 @@ -778,7 +835,11 @@ namespace etl template ::value, int> = 0> ETL_OR_STD::pair equal_range(const K& key) { - return etl::equal_range(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::iterator i_lower = etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare); + typename lookup_t::iterator i_upper = etl::upper_bound(i_lower, lookup.end(), key, ptr_compare); + + return ETL_OR_STD::make_pair(iterator(i_lower), iterator(i_upper)); } #endif @@ -789,7 +850,11 @@ namespace etl //********************************************************************* ETL_OR_STD::pair equal_range(parameter_t key) const { - return etl::upper_bound(cbegin(), cend(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::const_iterator i_lower = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare); + typename lookup_t::const_iterator i_upper = etl::upper_bound(i_lower, lookup.cend(), key, ptr_compare); + + return ETL_OR_STD::make_pair(const_iterator(i_lower), const_iterator(i_upper)); } #if ETL_USING_CPP11 @@ -797,7 +862,11 @@ namespace etl template ::value, int> = 0> ETL_OR_STD::pair equal_range(const K& key) const { - return etl::upper_bound(cbegin(), cend(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::const_iterator i_lower = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare); + typename lookup_t::const_iterator i_upper = etl::upper_bound(i_lower, lookup.cend(), key, ptr_compare); + + return ETL_OR_STD::make_pair(const_iterator(i_lower), const_iterator(i_upper)); } #endif From 3ff6ca1efc8cc825d08f6b86d6897d85cde973a1 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Sun, 16 Aug 2026 17:31:19 +0200 Subject: [PATCH 27/38] Fix ambiguous byte_stream_reader construction from etl::span (#1537) Add unsigned char/const unsigned char span overloads to byte_stream_reader to match byte_stream_writer, resolving overload ambiguity (#1533). Add tests covering span construction for both classes. Co-authored-by: John Wellbelove --- include/etl/byte_stream.h | 22 ++++++++++++++++ test/test_byte_stream.cpp | 53 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/include/etl/byte_stream.h b/include/etl/byte_stream.h index 51dc9e92..76a4bf04 100644 --- a/include/etl/byte_stream.h +++ b/include/etl/byte_stream.h @@ -508,6 +508,28 @@ namespace etl { } + //*************************************************************************** + /// Construct from span. + //*************************************************************************** + byte_stream_reader(etl::span span_, etl::endian stream_endianness_) + : pdata(reinterpret_cast(span_.begin())) + , pcurrent(reinterpret_cast(span_.begin())) + , stream_length(span_.size_bytes()) + , stream_endianness(stream_endianness_) + { + } + + //*************************************************************************** + /// Construct from span. + //*************************************************************************** + byte_stream_reader(etl::span span_, etl::endian stream_endianness_) + : pdata(reinterpret_cast(span_.begin())) + , pcurrent(reinterpret_cast(span_.begin())) + , stream_length(span_.size_bytes()) + , stream_endianness(stream_endianness_) + { + } + //*************************************************************************** /// Construct from range. //*************************************************************************** diff --git a/test/test_byte_stream.cpp b/test/test_byte_stream.cpp index 06b2a453..514dbd81 100644 --- a/test/test_byte_stream.cpp +++ b/test/test_byte_stream.cpp @@ -188,6 +188,59 @@ namespace CHECK(reader_little.get_endianness() == etl::endian::little); } + //************************************************************************* + TEST(byte_stream_writer_construct_from_span) + { + char storage[8]; + + etl::span char_span(storage, storage + ETL_OR_STD17::size(storage)); + etl::byte_stream_writer writer_char(char_span, etl::endian::little); + CHECK_EQUAL(char_span.size(), writer_char.capacity()); + + etl::span uchar_span(reinterpret_cast(storage), ETL_OR_STD17::size(storage)); + etl::byte_stream_writer writer_uchar(uchar_span, etl::endian::little); + CHECK_EQUAL(uchar_span.size(), writer_uchar.capacity()); + } + + //************************************************************************* + TEST(byte_stream_reader_construct_from_span) + { + char storage[8] = {0}; + + etl::span char_span(storage, storage + ETL_OR_STD17::size(storage)); + etl::byte_stream_reader reader_char(char_span, etl::endian::little); + CHECK_EQUAL(char_span.size(), reader_char.size_bytes()); + + etl::span const_char_span(storage, storage + ETL_OR_STD17::size(storage)); + etl::byte_stream_reader reader_const_char(const_char_span, etl::endian::little); + CHECK_EQUAL(const_char_span.size(), reader_const_char.size_bytes()); + + etl::span uchar_span(reinterpret_cast(storage), ETL_OR_STD17::size(storage)); + etl::byte_stream_reader reader_uchar(uchar_span, etl::endian::little); + CHECK_EQUAL(uchar_span.size(), reader_uchar.size_bytes()); + + etl::span const_uchar_span(reinterpret_cast(storage), ETL_OR_STD17::size(storage)); + etl::byte_stream_reader reader_const_uchar(const_uchar_span, etl::endian::little); + CHECK_EQUAL(const_uchar_span.size(), reader_const_uchar.size_bytes()); + } + + //************************************************************************* + // https://github.com/ETLCPP/etl/issues/1533 + // Passing an etl::span to byte_stream_writer/byte_stream_reader + // must not be ambiguous. + TEST(byte_stream_reader_writer_construct_from_span_uint8_t_no_ambiguity) + { + uint8_t storage[8] = {0}; + + etl::span data(storage, ETL_OR_STD17::size(storage)); + + etl::byte_stream_writer writer(etl::span(data), etl::endian::little); + etl::byte_stream_reader reader(etl::span(data), etl::endian::little); + + CHECK_EQUAL(data.size(), writer.capacity()); + CHECK_EQUAL(data.size(), reader.size_bytes()); + } + //************************************************************************* TEST(write_bool) { From 70d64496e6253cdf4d2637df90c63cb1b4a40443 Mon Sep 17 00:00:00 2001 From: rleigh-electradx Date: Mon, 17 Aug 2026 09:13:34 +0100 Subject: [PATCH 28/38] Fix make_array and make_deque rejecting const-qualified lvalues of the element type (#1526) * Fixed make_array rejecting const-qualified lvalues of the element type make_array forwarded each argument as T (the element type) rather than as its own deduced type. etl::forward accepts only a non-const T& or a T&&, and a T&& cannot bind to a reference-related const lvalue, so any call passing a const-qualified lvalue of type T failed to compile: static const char c = 42; etl::make_array(c, c); // error: would lose const qualifier Arguments of *other* types slipped through via temporary materialisation, which is why the existing conversion and move tests never caught it. Forward each argument as its own deduced type and convert explicitly with static_cast. The explicit conversion is required rather than plain forwarding: function parameters are not constant expressions, so narrowing initialisers such as make_array(0, 1) - which the old code accepted and the tests cover - would otherwise be rejected by the braced initialiser list. Adds regression tests for const/non-const lvalues of the element type and for constexpr evaluation. Verified with clang 22 (C++14/17/20) and GCC 14 (C++11/20); existing conversion and move-only patterns still compile. * Fixed make_deque rejecting const-qualified lvalues of the element type make_deque had the same defect fixed for make_array in the previous commit: it forwarded each argument as T (the element type) rather than as its own deduced type. etl::forward accepts only a non-const T& or a T&&, and a T&& cannot bind to a reference-related const lvalue, so any call passing a const-qualified lvalue of type T failed to compile: static const char c = 42; etl::make_deque(c, c); // error: would lose const qualifier An audit of all other make_xxx container factories found no further instances - they forward each argument as its own deduced pack type, which is the correct idiom. Forward each argument as its own deduced type and convert explicitly with static_cast. The explicit conversion keeps narrowing initialisers such as make_deque(0, 1) - which the old code accepted and test_make_deque covers - working, which plain forwarding into the braced initialiser list would reject. Adds a regression test for const/non-const lvalues of the element type. No constexpr test, unlike make_array: etl::deque is not a literal type, so make_deque cannot be evaluated at compile time. Verified with clang 22 (C++14/17/20); the new test fails to compile before the fix and passes after, and all 101 deque tests pass. * Added narrowing-conversion regression tests for make_array and make_deque Review feedback on the make_deque fix: the const-lvalue regression test only passes arguments that already have the element type, so nothing exercises the static_cast conversion that keeps narrowing initialisers working. If the implementation regressed to plain forwarding into the braced initialiser list, narrowing calls such as make_deque(0, 1) would fail to compile. make_array already covered narrowing from int literals via test_make_array, but neither container covered conversion from const-qualified lvalues of a different type (const int narrowed to char), which combines both dimensions of the fix. Adds a matching test to each container covering int literals plus const/non-const int lvalues, all narrowed to char. Verified with clang 22 (C++17): 102 deque and 53 array tests pass. * Added const-lvalue regression tests for the remaining make_xxx factories Review feedback on the make_array/make_deque fixes asked whether the same defect - rejecting const-qualified lvalue arguments by forwarding each argument as the element type rather than as its own deduced type - exists in the other make_xxx container factories. The audit found that it does not: the other factories forward each argument as its own deduced pack type. These tests pin that down explicitly for every remaining factory, mirroring the make_array and make_deque regression tests: - make_vector, make_forward_list, make_indirect_vector (element type deduced via common_type): const/non-const char lvalues, element type checked. - make_set, make_multiset, make_flat_set, make_flat_multiset, make_unordered_set, make_unordered_multiset: const/non-const lvalues of the key type. - make_map, make_multimap, make_flat_map, make_flat_multimap, make_unordered_map, make_unordered_multimap: const/non-const lvalues of the pair type. make_reference_flat_* are excluded: they cannot currently be instantiated at all (no initializer_list constructor exists in the reference containers), which is reported separately. Verified with clang 22 (C++17): all 15 test suites pass (996 tests). * Added make_forward_list parity tests matching the make_list fixes Checked whether the make_list defect - taking its argument pack by value, costing an extra move per lvalue argument - also applies to make_forward_list. It does not: make_forward_list already takes its pack by forwarding reference, as make_vector does. No header change is needed. Adds the two tests that accompanied the make_list fix so that forward_list has the same coverage and the same regression detector: - test_make_forward_list_common_type_from_mixed_types, confirming the element type deduction through common_type. - test_make_forward_list_argument_copy_and_move_counts, an instrumented type counting copy/move constructions. This is the test that fails if the pack ever regresses to by-value (one extra move per lvalue argument). The exact move counts are C++17-only, as before C++17 the non-guaranteed return elision may add a move per element. Verified with clang 22 (C++17): all 67 forward_list tests pass. --------- Co-authored-by: Roland Reichwein --- include/etl/array.h | 8 ++- include/etl/deque.h | 8 ++- test/test_array.cpp | 63 ++++++++++++++++++ test/test_deque.cpp | 47 +++++++++++++ test/test_flat_map.cpp | 23 +++++++ test/test_flat_multimap.cpp | 23 +++++++ test/test_flat_multiset.cpp | 28 ++++++++ test/test_flat_set.cpp | 28 ++++++++ test/test_forward_list.cpp | 111 +++++++++++++++++++++++++++++++ test/test_indirect_vector.cpp | 23 +++++++ test/test_map.cpp | 23 +++++++ test/test_multimap.cpp | 23 +++++++ test/test_multiset.cpp | 28 ++++++++ test/test_set.cpp | 28 ++++++++ test/test_unordered_map.cpp | 23 +++++++ test/test_unordered_multimap.cpp | 23 +++++++ test/test_unordered_multiset.cpp | 21 ++++++ test/test_unordered_set.cpp | 21 ++++++ test/test_vector.cpp | 23 +++++++ 19 files changed, 573 insertions(+), 2 deletions(-) diff --git a/include/etl/array.h b/include/etl/array.h index 1662419b..1bb92cea 100644 --- a/include/etl/array.h +++ b/include/etl/array.h @@ -1056,7 +1056,13 @@ namespace etl template constexpr auto make_array(TValues&&... values) ETL_NOEXCEPT -> etl::array { - return {etl::forward(values)...}; + // Forward each argument as its own deduced type, then convert explicitly to T. + // Forwarding as T (the old behaviour) made the call ill-formed for any const-qualified + // lvalue of type T: forward requires a non-const T&, and a T&& cannot bind to a + // reference-related const lvalue. The explicit conversion also keeps narrowing + // initialisers (e.g. make_array(0, 1)) working, which plain forwarding into the + // braced initialiser list would reject. + return {static_cast(etl::forward(values))...}; } #endif diff --git a/include/etl/deque.h b/include/etl/deque.h index 4ac0ac92..a35eda34 100644 --- a/include/etl/deque.h +++ b/include/etl/deque.h @@ -2535,7 +2535,13 @@ namespace etl template constexpr auto make_deque(TValues&&... values) -> etl::deque { - return {etl::forward(values)...}; + // Forward each argument as its own deduced type, then convert explicitly to T. + // Forwarding as T (the old behaviour) made the call ill-formed for any const-qualified + // lvalue of type T: forward requires a non-const T&, and a T&& cannot bind to a + // reference-related const lvalue. The explicit conversion also keeps narrowing + // initialisers (e.g. make_deque(0, 1)) working, which plain forwarding into the + // braced initialiser list would reject. + return {static_cast(etl::forward(values))...}; } #endif diff --git a/test/test_array.cpp b/test/test_array.cpp index f76e617f..8c8fa168 100644 --- a/test/test_array.cpp +++ b/test/test_array.cpp @@ -950,6 +950,69 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the element type used to be rejected: + // the old implementation forwarded each argument as T, and forward cannot accept a + // const T lvalue (it requires a non-const T&, and T&& cannot bind to a reference-related + // const lvalue). + TEST(test_make_array_from_const_lvalues_of_element_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_array(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + using Type = etl::remove_reference_t; + CHECK((std::is_same::value)); + + CHECK_EQUAL(42, data[0]); + CHECK_EQUAL(43, data[1]); + CHECK_EQUAL(44, data[2]); + } +#endif + + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // The arguments are converted to the element type with static_cast, so narrowing + // initialisers (int literals and int lvalues narrowed to char) must keep working. + // Plain forwarding into the braced initialiser list would reject them. + TEST(test_make_array_from_narrowing_values) + { + static const int static_const_int = 45; + const int local_const_int = 46; + int mutable_int = 47; + + auto data = etl::make_array(0, 1, static_const_int, local_const_int, mutable_int); + + using Type = etl::remove_reference_t; + CHECK((std::is_same::value)); + + CHECK_EQUAL(0, data[0]); + CHECK_EQUAL(1, data[1]); + CHECK_EQUAL(45, data[2]); + CHECK_EQUAL(46, data[3]); + CHECK_EQUAL(47, data[4]); + } +#endif + + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST && ETL_USING_CPP14 + TEST(test_make_array_is_constexpr) + { + static constexpr unsigned char constexpr_value = 0x18U; + + constexpr auto data = etl::make_array(constexpr_value, constexpr_value); + + static_assert(data.size() == 2U, "make_array size"); + static_assert(data[0] == 0x18U, "make_array element"); + + CHECK_EQUAL(0x18U, data[0]); + CHECK_EQUAL(0x18U, data[1]); + } +#endif + #if ETL_USING_CPP14 //************************************************************************* using Array = etl::array; diff --git a/test/test_deque.cpp b/test/test_deque.cpp index a09f67d5..85365284 100644 --- a/test/test_deque.cpp +++ b/test/test_deque.cpp @@ -2299,6 +2299,53 @@ namespace } #endif + //************************************************************************* +#if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the element type used to be rejected: + // the old implementation forwarded each argument as T, and forward cannot accept a + // const T lvalue (it requires a non-const T&, and T&& cannot bind to a reference-related + // const lvalue). + TEST(test_make_deque_from_const_lvalues_of_element_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_deque(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + using Type = etl::remove_reference_t; + CHECK((std::is_same::value)); + + CHECK_EQUAL(42, data[0]); + CHECK_EQUAL(43, data[1]); + CHECK_EQUAL(44, data[2]); + } +#endif + + //************************************************************************* +#if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST + // The arguments are converted to the element type with static_cast, so narrowing + // initialisers (int literals and int lvalues narrowed to char) must keep working. + // Plain forwarding into the braced initialiser list would reject them. + TEST(test_make_deque_from_narrowing_values) + { + static const int static_const_int = 45; + const int local_const_int = 46; + int mutable_int = 47; + + auto data = etl::make_deque(0, 1, static_const_int, local_const_int, mutable_int); + + using Type = etl::remove_reference_t; + CHECK((std::is_same::value)); + + CHECK_EQUAL(0, data[0]); + CHECK_EQUAL(1, data[1]); + CHECK_EQUAL(45, data[2]); + CHECK_EQUAL(46, data[3]); + CHECK_EQUAL(47, data[4]); + } +#endif + //************************************************************************* TEST(test_fill) { diff --git a/test/test_flat_map.cpp b/test/test_flat_map.cpp index c88bedd9..ae9ddbdd 100644 --- a/test/test_flat_map.cpp +++ b/test/test_flat_map.cpp @@ -1544,6 +1544,29 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the pair type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST_FIXTURE(SetupFixture, test_make_flat_map_from_const_lvalues_of_pair_type) + { + using Pair = ETL_OR_STD::pair; + + static const Pair static_const_lvalue('a', 0); + const Pair local_const_lvalue('b', 1); + Pair mutable_lvalue('c', 2); + + auto data = etl::make_flat_map(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + CHECK_EQUAL(3U, data.size()); + CHECK_EQUAL(0, data.at('a')); + CHECK_EQUAL(1, data.at('b')); + CHECK_EQUAL(2, data.at('c')); + } +#endif + //************************************************************************* TEST_FIXTURE(SetupFixture, test_contains) { diff --git a/test/test_flat_multimap.cpp b/test/test_flat_multimap.cpp index 41962cc2..d1f3ccd9 100644 --- a/test/test_flat_multimap.cpp +++ b/test/test_flat_multimap.cpp @@ -1300,6 +1300,29 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the pair type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST_FIXTURE(SetupFixture, test_make_flat_multimap_from_const_lvalues_of_pair_type) + { + using Pair = ETL_OR_STD::pair; + + static const Pair static_const_lvalue('a', 0); + const Pair local_const_lvalue('b', 1); + Pair mutable_lvalue('c', 2); + + auto data = etl::make_flat_multimap(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + CHECK_EQUAL(3U, data.size()); + CHECK_EQUAL(0, data.find('a')->second); + CHECK_EQUAL(1, data.find('b')->second); + CHECK_EQUAL(2, data.find('c')->second); + } +#endif + //************************************************************************* TEST_FIXTURE(SetupFixture, test_contains) { diff --git a/test/test_flat_multiset.cpp b/test/test_flat_multiset.cpp index 694bb802..2d91748e 100644 --- a/test/test_flat_multiset.cpp +++ b/test/test_flat_multiset.cpp @@ -1293,6 +1293,34 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the key type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST_FIXTURE(SetupFixture, test_make_flat_multiset_from_const_lvalues_of_key_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_flat_multiset(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + auto v = *data.begin(); + using Type = decltype(v); + CHECK((std::is_same::value)); + + decltype(data)::const_iterator itr = data.begin(); + + CHECK_EQUAL(42, *itr); + ++itr; + CHECK_EQUAL(43, *itr); + ++itr; + CHECK_EQUAL(44, *itr); + } +#endif + //************************************************************************* TEST_FIXTURE(SetupFixture, test_contains) { diff --git a/test/test_flat_set.cpp b/test/test_flat_set.cpp index 1355f8c0..7930c72d 100644 --- a/test/test_flat_set.cpp +++ b/test/test_flat_set.cpp @@ -1292,6 +1292,34 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the key type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST_FIXTURE(SetupFixture, test_make_flat_set_from_const_lvalues_of_key_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_flat_set(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + auto v = *data.begin(); + using Type = decltype(v); + CHECK((std::is_same::value)); + + decltype(data)::const_iterator itr = data.begin(); + + CHECK_EQUAL(42, *itr); + ++itr; + CHECK_EQUAL(43, *itr); + ++itr; + CHECK_EQUAL(44, *itr); + } +#endif + //************************************************************************* TEST_FIXTURE(SetupFixture, test_contains) { diff --git a/test/test_forward_list.cpp b/test/test_forward_list.cpp index 3f805b7f..8687bf82 100644 --- a/test/test_forward_list.cpp +++ b/test/test_forward_list.cpp @@ -1445,6 +1445,117 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues must be accepted and must not + // affect the deduced element type. Guards against the defect fixed for + // make_array and make_deque, which forwarded each argument as the element + // type rather than as its own deduced type and so rejected const lvalues. + TEST(test_make_forward_list_from_const_lvalues_of_element_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_forward_list(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + auto v = *data.begin(); + using Type = decltype(v); + CHECK((std::is_same::value)); + + decltype(data)::const_iterator itr = data.begin(); + + CHECK_EQUAL(42, *itr++); + CHECK_EQUAL(43, *itr++); + CHECK_EQUAL(44, *itr++); + } +#endif + + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + TEST(test_make_forward_list_common_type_from_mixed_types) + { + auto data = etl::make_forward_list(1, 2L, 3); + + CHECK((std::is_same, decltype(data)>::value)); + + auto itr = data.begin(); + CHECK_EQUAL(1L, *itr++); + CHECK_EQUAL(2L, *itr++); + CHECK_EQUAL(3L, *itr++); + } +#endif + + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + struct CopyMoveCounted + { + explicit CopyMoveCounted(int value_) + : value(value_) + { + } + + CopyMoveCounted(const CopyMoveCounted& other) + : value(other.value) + { + ++copy_count; + } + + CopyMoveCounted(CopyMoveCounted&& other) + : value(other.value) + { + ++move_count; + } + + int value; + + static size_t copy_count; + static size_t move_count; + }; + + size_t CopyMoveCounted::copy_count = 0; + size_t CopyMoveCounted::move_count = 0; + + // The arguments must be forwarded into the initializer list, not taken by + // value: a by-value parameter pack would cost an extra move per argument + // (the defect fixed for make_list). Each element is then copied from the + // initializer_list backing array into its node, which is why lvalue + // arguments count two copies. + TEST(test_make_forward_list_argument_copy_and_move_counts) + { + const CopyMoveCounted const_lvalue(1); + CopyMoveCounted mutable_lvalue(2); + + CopyMoveCounted::copy_count = 0; + CopyMoveCounted::move_count = 0; + + auto data = etl::make_forward_list(const_lvalue, mutable_lvalue); + + CHECK_EQUAL(4U, CopyMoveCounted::copy_count); + #if ETL_USING_CPP17 + // The exact move counts rely on guaranteed copy elision. Before C++17 the + // returned forward_list may be move-constructed once more, which moves each + // element, so only the copy counts are checked there. + CHECK_EQUAL(0U, CopyMoveCounted::move_count); + #endif + + CopyMoveCounted::copy_count = 0; + CopyMoveCounted::move_count = 0; + + auto data2 = etl::make_forward_list(CopyMoveCounted(3)); + + CHECK_EQUAL(1U, CopyMoveCounted::copy_count); + #if ETL_USING_CPP17 + CHECK_EQUAL(1U, CopyMoveCounted::move_count); + #endif + + auto itr = data.begin(); + CHECK_EQUAL(1, (*itr++).value); + CHECK_EQUAL(2, (*itr).value); + CHECK_EQUAL(3, data2.begin()->value); + } +#endif + //************************************************************************* TEST(test_forward_list_exception_types) { diff --git a/test/test_indirect_vector.cpp b/test/test_indirect_vector.cpp index f0a5d54e..2d8033cd 100644 --- a/test/test_indirect_vector.cpp +++ b/test/test_indirect_vector.cpp @@ -1621,5 +1621,28 @@ namespace CHECK(std::equal(blank_data.begin(), blank_data.end(), data.begin())); } + + //************************************************************************* +#if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues must be accepted and must not + // affect the deduced element type. Guards against the defect fixed for + // make_array and make_deque, which forwarded each argument as the element + // type rather than as its own deduced type and so rejected const lvalues. + TEST(test_make_indirect_vector_from_const_lvalues_of_element_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_indirect_vector(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + using Type = typename std::remove_reference::type; + CHECK((std::is_same::value)); + + CHECK_EQUAL(42, data[0]); + CHECK_EQUAL(43, data[1]); + CHECK_EQUAL(44, data[2]); + } +#endif } } // namespace diff --git a/test/test_map.cpp b/test/test_map.cpp index c57b5b5e..9ce01897 100644 --- a/test/test_map.cpp +++ b/test/test_map.cpp @@ -1765,6 +1765,29 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the pair type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST_FIXTURE(SetupFixture, test_make_map_from_const_lvalues_of_pair_type) + { + using Pair = ETL_OR_STD::pair; + + static const Pair static_const_lvalue('a', 0); + const Pair local_const_lvalue('b', 1); + Pair mutable_lvalue('c', 2); + + auto data = etl::make_map(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + CHECK_EQUAL(3U, data.size()); + CHECK_EQUAL(0, data.at('a')); + CHECK_EQUAL(1, data.at('b')); + CHECK_EQUAL(2, data.at('c')); + } +#endif + //************************************************************************* TEST_FIXTURE(SetupFixture, test_contains) { diff --git a/test/test_multimap.cpp b/test/test_multimap.cpp index 86f9af0f..4273932b 100644 --- a/test/test_multimap.cpp +++ b/test/test_multimap.cpp @@ -1685,6 +1685,29 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the pair type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST_FIXTURE(SetupFixture, test_make_multimap_from_const_lvalues_of_pair_type) + { + using Pair = ETL_OR_STD::pair; + + static const Pair static_const_lvalue('a', 0); + const Pair local_const_lvalue('b', 1); + Pair mutable_lvalue('c', 2); + + auto data = etl::make_multimap(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + CHECK_EQUAL(3U, data.size()); + CHECK_EQUAL(0, data.find('a')->second); + CHECK_EQUAL(1, data.find('b')->second); + CHECK_EQUAL(2, data.find('c')->second); + } +#endif + //************************************************************************* TEST_FIXTURE(SetupFixture, test_contains_with_transparent_comparator) { diff --git a/test/test_multiset.cpp b/test/test_multiset.cpp index fcbae16a..7a31a65f 100644 --- a/test/test_multiset.cpp +++ b/test/test_multiset.cpp @@ -1608,6 +1608,34 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the key type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST_FIXTURE(SetupFixture, test_make_multiset_from_const_lvalues_of_key_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_multiset(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + auto v = *data.begin(); + using Type = decltype(v); + CHECK((std::is_same::value)); + + decltype(data)::const_iterator itr = data.begin(); + + CHECK_EQUAL(42, *itr); + ++itr; + CHECK_EQUAL(43, *itr); + ++itr; + CHECK_EQUAL(44, *itr); + } +#endif + //************************************************************************* TEST_FIXTURE(SetupFixture, test_contains) { diff --git a/test/test_set.cpp b/test/test_set.cpp index 863325c0..e23ca03a 100644 --- a/test/test_set.cpp +++ b/test/test_set.cpp @@ -1465,6 +1465,34 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the key type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST_FIXTURE(SetupFixture, test_make_set_from_const_lvalues_of_key_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_set(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + auto v = *data.begin(); + using Type = decltype(v); + CHECK((std::is_same::value)); + + decltype(data)::const_iterator itr = data.begin(); + + CHECK_EQUAL(42, *itr); + ++itr; + CHECK_EQUAL(43, *itr); + ++itr; + CHECK_EQUAL(44, *itr); + } +#endif + //************************************************************************* TEST_FIXTURE(SetupFixture, test_contains) { diff --git a/test/test_unordered_map.cpp b/test/test_unordered_map.cpp index 793343eb..57ccd928 100644 --- a/test/test_unordered_map.cpp +++ b/test/test_unordered_map.cpp @@ -1484,5 +1484,28 @@ namespace CHECK_TRUE(data.contains("FF")); CHECK_FALSE(data.contains(not_inserted)); } + + //************************************************************************* +#if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the pair type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST(test_make_unordered_map_from_const_lvalues_of_pair_type) + { + using Pair = ETL_OR_STD::pair; + + static const Pair static_const_lvalue('a', 0); + const Pair local_const_lvalue('b', 1); + Pair mutable_lvalue('c', 2); + + auto data = etl::make_unordered_map(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + CHECK_EQUAL(3U, data.size()); + CHECK_EQUAL(0, data.at('a')); + CHECK_EQUAL(1, data.at('b')); + CHECK_EQUAL(2, data.at('c')); + } +#endif } } // namespace diff --git a/test/test_unordered_multimap.cpp b/test/test_unordered_multimap.cpp index bf2c2aa6..2207f642 100644 --- a/test/test_unordered_multimap.cpp +++ b/test/test_unordered_multimap.cpp @@ -1243,5 +1243,28 @@ namespace CHECK_TRUE(data.contains("FF")); CHECK_FALSE(data.contains(not_inserted)); } + + //************************************************************************* +#if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the pair type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST(test_make_unordered_multimap_from_const_lvalues_of_pair_type) + { + using Pair = ETL_OR_STD::pair; + + static const Pair static_const_lvalue('a', 0); + const Pair local_const_lvalue('b', 1); + Pair mutable_lvalue('c', 2); + + auto data = etl::make_unordered_multimap(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + CHECK_EQUAL(3U, data.size()); + CHECK_EQUAL(0, data.find('a')->second); + CHECK_EQUAL(1, data.find('b')->second); + CHECK_EQUAL(2, data.find('c')->second); + } +#endif } } // namespace diff --git a/test/test_unordered_multiset.cpp b/test/test_unordered_multiset.cpp index 9a05aca8..862baa6c 100644 --- a/test/test_unordered_multiset.cpp +++ b/test/test_unordered_multiset.cpp @@ -1113,5 +1113,26 @@ namespace CHECK_TRUE(data.contains("FF")); CHECK_FALSE(data.contains(not_inserted)); } + + //************************************************************************* +#if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the key type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST(test_make_unordered_multiset_from_const_lvalues_of_key_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_unordered_multiset(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + CHECK_EQUAL(3U, data.size()); + CHECK_EQUAL(1U, data.count(42)); + CHECK_EQUAL(1U, data.count(43)); + CHECK_EQUAL(1U, data.count(44)); + } +#endif } } // namespace diff --git a/test/test_unordered_set.cpp b/test/test_unordered_set.cpp index da0831c8..cd959a2a 100644 --- a/test/test_unordered_set.cpp +++ b/test/test_unordered_set.cpp @@ -1122,6 +1122,27 @@ namespace CHECK_TRUE(data.contains("FF")); CHECK_FALSE(data.contains(not_inserted)); } + + //************************************************************************* +#if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the key type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST(test_make_unordered_set_from_const_lvalues_of_key_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_unordered_set(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + CHECK_EQUAL(3U, data.size()); + CHECK_EQUAL(1U, data.count(42)); + CHECK_EQUAL(1U, data.count(43)); + CHECK_EQUAL(1U, data.count(44)); + } +#endif } #include "etl/private/diagnostic_pop.h" } // namespace diff --git a/test/test_vector.cpp b/test/test_vector.cpp index 026e2176..0340c4cb 100644 --- a/test/test_vector.cpp +++ b/test/test_vector.cpp @@ -1567,6 +1567,29 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues must be accepted and must not + // affect the deduced element type. Guards against the defect fixed for + // make_array and make_deque, which forwarded each argument as the element + // type rather than as its own deduced type and so rejected const lvalues. + TEST(test_make_vector_from_const_lvalues_of_element_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_vector(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + using Type = typename std::remove_reference::type; + CHECK((std::is_same::value)); + + CHECK_EQUAL(42, data[0]); + CHECK_EQUAL(43, data[1]); + CHECK_EQUAL(44, data[2]); + } +#endif + //************************************************************************* TEST(test_fill) { From 88b629c1214761b1a996ce1cfa6a3ffd7417c4e6 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Mon, 17 Aug 2026 12:52:42 +0200 Subject: [PATCH 29/38] Fix expected::operator*() const& and && failing to compile (#1538) ETL_ASSERT_OR_RETURN_VALUE tried to return ETL_NULLPTR from functions returning references, which cannot be initialized from nullptr. Use plain ETL_ASSERT instead, matching the other operator*() overloads. Add regression test for dereferencing const and temporary expected. Co-authored-by: John Wellbelove --- include/etl/expected.h | 4 ++-- test/test_expected.cpp | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/include/etl/expected.h b/include/etl/expected.h index bbfc514b..1feb1487 100644 --- a/include/etl/expected.h +++ b/include/etl/expected.h @@ -746,7 +746,7 @@ namespace etl //******************************************* const value_type& operator*() const ETL_LVALUE_REF_QUALIFIER { - ETL_ASSERT_OR_RETURN_VALUE(has_value(), ETL_ERROR(expected_invalid), ETL_NULLPTR); + ETL_ASSERT(has_value(), ETL_ERROR(expected_invalid)); return etl::get(storage); } @@ -757,7 +757,7 @@ namespace etl //******************************************* value_type&& operator*() && { - ETL_ASSERT_OR_RETURN_VALUE(has_value(), ETL_ERROR(expected_invalid), ETL_NULLPTR); + ETL_ASSERT(has_value(), ETL_ERROR(expected_invalid)); return etl::move(etl::get(storage)); } diff --git a/test/test_expected.cpp b/test/test_expected.cpp index 621727b2..b7bee13d 100644 --- a/test/test_expected.cpp +++ b/test/test_expected.cpp @@ -618,6 +618,20 @@ namespace etl::expected_invalid); } + //************************************************************************* + // See https://github.com/ETLCPP/etl/issues/1532 + TEST(test_dereference_const_and_rvalue_expected) + { + typedef etl::expected Expected1532; + + Expected1532 e = 42; + const Expected1532& ce = e; + + CHECK_EQUAL(42, *e); + CHECK_EQUAL(42, *ce); + CHECK_EQUAL(42, *Expected1532(42)); + } + //************************************************************************* struct value_or_helper { From 67d033cab08d86f87cae28632f5a8c5bb9c0848a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BCthing?= Date: Thu, 20 Aug 2026 09:11:15 +0200 Subject: [PATCH 30/38] Fix PowerPC Dockerfile to use Snapshot consistently (#1554) * Update _index.md * Update _index.md * Fix PowerPC Dockerfile to use Snapshot consistently --------- Co-authored-by: John Wellbelove --- .devcontainer/powerpc/Dockerfile | 36 +++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/.devcontainer/powerpc/Dockerfile b/.devcontainer/powerpc/Dockerfile index a2442782..59809392 100644 --- a/.devcontainer/powerpc/Dockerfile +++ b/.devcontainer/powerpc/Dockerfile @@ -1,10 +1,32 @@ # powerpc Test Environment for ETL # Uses QEMU user-mode emulation to run powerpc binaries on x64 host -FROM debian:sid-20260406 +# +# NOTE: floating tags like "sid" or "sid-slim" drift over time and cannot be +# pinned retroactively (Docker Hub keeps only the latest manifest per tag). +# debian:sid-20260202 is a dated, immutable tag (built and pushed once, not +# retagged later), pinned here by digest for full reproducibility. All apt +# sources are also switched to the matching snapshot.debian.org archive +# before the first "apt-get update" runs. +FROM debian@sha256:9ab6e6f3543eeb30510c516377bbb05f95ebb7507a956c0b772184deb7ecd340 # Avoid prompts from apt ENV DEBIAN_FRONTEND=noninteractive +# Pin apt to the snapshot.debian.org archive for the given date BEFORE any +# package installation, so every apt-get update/install in this file (not +# just the ones after this point) resolves to the exact same package set. +RUN rm -f /etc/apt/sources.list /etc/apt/sources.list.d/*.sources + +RUN cat < /etc/apt/sources.list.d/debian.sources +Types: deb +URIs: http://snapshot.debian.org/archive/debian/20260202T000000Z +Suites: sid +Components: main +Signed-By: /usr/share/keyrings/debian-archive-keyring.pgp +EOF + +RUN echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/99no-check-valid + # Install QEMU user-mode emulation and powerpc cross-compilation tools RUN dpkg --add-architecture powerpc && \ apt-get update && apt-get install -y --no-install-recommends \ @@ -20,25 +42,15 @@ RUN dpkg --add-architecture powerpc && \ debian-ports-archive-keyring \ && rm -rf /var/lib/apt/lists/* -RUN cat < /etc/apt/sources.list.d/debian.sources -Types: deb -URIs: http://snapshot.debian.org/archive/debian/20260406T000000Z -Suites: sid -Components: main -Signed-By: /usr/share/keyrings/debian-archive-keyring.pgp -EOF - RUN cat < /etc/apt/sources.list.d/powerpc.sources Types: deb -URIs: http://snapshot.debian.org/archive/debian-ports/20260406T000000Z +URIs: http://snapshot.debian.org/archive/debian-ports/20260202T000000Z Suites: sid Components: main Architectures: powerpc Signed-By: /usr/share/keyrings/debian-ports-archive-keyring.gpg EOF -RUN echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/99no-check-valid - RUN apt-get update && apt-get install -y --no-install-recommends \ qemu-user-static \ qemu-user \ From 9125f119640849241ffcc7b12cb477b4982b1b98 Mon Sep 17 00:00:00 2001 From: Eden Frosst Date: Thu, 20 Aug 2026 04:00:10 -0400 Subject: [PATCH 31/38] delegate: restore argument deduction behaviour (#1548) include new test that verifies arg deduction works Co-authored-by: John Wellbelove --- include/etl/private/delegate_cpp11.h | 53 +++++++++------------------- test/test_delegate.cpp | 17 ++++++++- 2 files changed, 32 insertions(+), 38 deletions(-) diff --git a/include/etl/private/delegate_cpp11.h b/include/etl/private/delegate_cpp11.h index 0770e0f0..d373325f 100644 --- a/include/etl/private/delegate_cpp11.h +++ b/include/etl/private/delegate_cpp11.h @@ -413,32 +413,23 @@ namespace etl //************************************************************************* /// Execute the delegate. //************************************************************************* - template - ETL_CONSTEXPR14 return_type operator()(TCallArgs&&... args) const + ETL_CONSTEXPR14 return_type operator()(TArgs... args) const { - ETL_STATIC_ASSERT((sizeof...(TCallArgs) == sizeof...(TArgs)), "Incorrect number of parameters passed to delegate"); - ETL_STATIC_ASSERT((etl::type_lists_are_convertible, argument_types>::value), - "Incompatible parameter types passed to delegate"); - ETL_ASSERT(is_valid(), ETL_ERROR(delegate_uninitialised)); - return (*invocation.stub)(invocation, etl::forward(args)...); + return (*invocation.stub)(invocation, etl::forward(args)...); } //************************************************************************* /// Execute the delegate if valid. /// 'void' return delegate. //************************************************************************* - template - ETL_CONSTEXPR14 typename etl::enable_if_t::value, bool> call_if(TCallArgs&&... args) const + template + ETL_CONSTEXPR14 typename etl::enable_if_t::value, bool> call_if(TArgs... args) const { - ETL_STATIC_ASSERT((sizeof...(TCallArgs) == sizeof...(TArgs)), "Incorrect number of parameters passed to delegate"); - ETL_STATIC_ASSERT((etl::type_lists_are_convertible, argument_types>::value), - "Incompatible parameter types passed to delegate"); - if (is_valid()) { - (*invocation.stub)(invocation, etl::forward(args)...); + (*invocation.stub)(invocation, etl::forward(args)...); return true; } else @@ -451,18 +442,14 @@ namespace etl /// Execute the delegate if valid. /// Non 'void' return delegate. //************************************************************************* - template - ETL_CONSTEXPR14 typename etl::enable_if_t::value, etl::optional> call_if(TCallArgs&&... args) const + template + ETL_CONSTEXPR14 typename etl::enable_if_t::value, etl::optional> call_if(TArgs... args) const { - ETL_STATIC_ASSERT((sizeof...(TCallArgs) == sizeof...(TArgs)), "Incorrect number of parameters passed to delegate"); - ETL_STATIC_ASSERT((etl::type_lists_are_convertible, argument_types>::value), - "Incompatible parameter types passed to delegate"); - etl::optional result; if (is_valid()) { - result = (*invocation.stub)(invocation, etl::forward(args)...); + result = (*invocation.stub)(invocation, etl::forward(args)...); } return result; @@ -472,20 +459,16 @@ namespace etl /// Execute the delegate if valid or call alternative. /// Run time alternative. //************************************************************************* - template - ETL_CONSTEXPR14 TReturn call_or(TAlternative&& alternative, TCallArgs&&... args) const + template + ETL_CONSTEXPR14 TReturn call_or(TAlternative&& alternative, TArgs... args) const { - ETL_STATIC_ASSERT((sizeof...(TCallArgs) == sizeof...(TArgs)), "Incorrect number of parameters passed to delegate"); - ETL_STATIC_ASSERT((etl::type_lists_are_convertible, argument_types>::value), - "Incompatible parameter types passed to delegate"); - if (is_valid()) { - return (*invocation.stub)(invocation, etl::forward(args)...); + return (*invocation.stub)(invocation, etl::forward(args)...); } else { - return etl::forward(alternative)(etl::forward(args)...); + return etl::forward(alternative)(etl::forward(args)...); } } @@ -493,20 +476,16 @@ namespace etl /// Execute the delegate if valid or call alternative. /// Compile time alternative. //************************************************************************* - template - ETL_CONSTEXPR14 TReturn call_or(TCallArgs&&... args) const + template + ETL_CONSTEXPR14 TReturn call_or(TArgs... args) const { - ETL_STATIC_ASSERT((sizeof...(TCallArgs) == sizeof...(TArgs)), "Incorrect number of parameters passed to delegate"); - ETL_STATIC_ASSERT((etl::type_lists_are_convertible, argument_types>::value), - "Incompatible parameter types passed to delegate"); - if (is_valid()) { - return (*invocation.stub)(invocation, etl::forward(args)...); + return (*invocation.stub)(invocation, etl::forward(args)...); } else { - return (Method)(etl::forward(args)...); + return (Method)(etl::forward(args)...); } } diff --git a/test/test_delegate.cpp b/test/test_delegate.cpp index f990e115..6d07d2a1 100644 --- a/test/test_delegate.cpp +++ b/test/test_delegate.cpp @@ -96,7 +96,11 @@ namespace MoveableOnlyData& operator=(const MoveableOnlyData&) = delete; MoveableOnlyData(MoveableOnlyData&&) = default; MoveableOnlyData& operator=(MoveableOnlyData&&) = default; - int d; + MoveableOnlyData(int _d) + : d{_d} + { + } + int d; }; //***************************************************************************** @@ -583,6 +587,17 @@ namespace CHECK(parameter_correct); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_free_moveableonly_deduced_type) + { + auto d = etl::delegate::create(); + + d({VALUE1}); // deduce argument type from braced initializer list + + CHECK(function_called == FunctionCalled::Free_Moveableonly_Called); + CHECK(parameter_correct); + } + //************************************************************************* #if ETL_USING_CPP17 TEST_FIXTURE(SetupFixture, test_make_delegate_free_moveableonly) From e34ba686ba60c96e7c17869d3c654cfa19ca286e Mon Sep 17 00:00:00 2001 From: Drew Rife Date: Thu, 20 Aug 2026 06:15:16 -0400 Subject: [PATCH 32/38] CI: Filter compiler workflows by path (#1558) * Update _index.md * Update _index.md * ci: filter compiler workflows by path --------- Co-authored-by: John Wellbelove --- .github/workflows/bazel-gcc-c++23-no-stl.yml | 24 ++++++++++++++++++ .github/workflows/clang-c++11.yml | 26 +++++++++++++++++++- .github/workflows/clang-c++14.yml | 26 +++++++++++++++++++- .github/workflows/clang-c++17.yml | 26 +++++++++++++++++++- .github/workflows/clang-c++20.yml | 25 ++++++++++++++++++- .github/workflows/clang-c++23.yml | 25 ++++++++++++++++++- .github/workflows/clang-c++26.yml | 24 ++++++++++++++++++ .github/workflows/clang-syntax-checks.yml | 24 ++++++++++++++++++ .github/workflows/clang-tidy.yaml | 24 ++++++++++++++++++ .github/workflows/coverage.yml | 24 ++++++++++++++++++ .github/workflows/gcc-c++11.yml | 24 ++++++++++++++++++ .github/workflows/gcc-c++14.yml | 26 +++++++++++++++++++- .github/workflows/gcc-c++17.yml | 24 ++++++++++++++++++ .github/workflows/gcc-c++20.yml | 26 +++++++++++++++++++- .github/workflows/gcc-c++23-armhf.yml | 24 ++++++++++++++++++ .github/workflows/gcc-c++23-i386.yml | 24 ++++++++++++++++++ .github/workflows/gcc-c++23-powerpc.yml | 24 ++++++++++++++++++ .github/workflows/gcc-c++23-riscv64.yml | 24 ++++++++++++++++++ .github/workflows/gcc-c++23-s390x.yml | 24 ++++++++++++++++++ .github/workflows/gcc-c++23.yml | 24 ++++++++++++++++++ .github/workflows/gcc-c++26.yml | 24 ++++++++++++++++++ .github/workflows/gcc-syntax-checks.yml | 24 ++++++++++++++++++ .github/workflows/meson-gcc-c++23-no-stl.yml | 24 ++++++++++++++++++ .github/workflows/msvc.yml | 25 ++++++++++++++++++- 24 files changed, 581 insertions(+), 8 deletions(-) diff --git a/.github/workflows/bazel-gcc-c++23-no-stl.yml b/.github/workflows/bazel-gcc-c++23-no-stl.yml index 06a94a9f..7ea97e00 100644 --- a/.github/workflows/bazel-gcc-c++23-no-stl.yml +++ b/.github/workflows/bazel-gcc-c++23-no-stl.yml @@ -2,9 +2,33 @@ name: bazel-gcc-c++23-no-stl on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: diff --git a/.github/workflows/clang-c++11.yml b/.github/workflows/clang-c++11.yml index 12fe33b5..4f507561 100644 --- a/.github/workflows/clang-c++11.yml +++ b/.github/workflows/clang-c++11.yml @@ -2,9 +2,33 @@ name: clang-c++11 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: @@ -79,4 +103,4 @@ jobs: make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests - run: ./test/etl_tests -v \ No newline at end of file + run: ./test/etl_tests -v diff --git a/.github/workflows/clang-c++14.yml b/.github/workflows/clang-c++14.yml index 7735118a..28896881 100644 --- a/.github/workflows/clang-c++14.yml +++ b/.github/workflows/clang-c++14.yml @@ -2,9 +2,33 @@ name: clang-c++14 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: @@ -79,4 +103,4 @@ jobs: make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests - run: ./test/etl_tests -v \ No newline at end of file + run: ./test/etl_tests -v diff --git a/.github/workflows/clang-c++17.yml b/.github/workflows/clang-c++17.yml index 73df80e8..53de40a0 100644 --- a/.github/workflows/clang-c++17.yml +++ b/.github/workflows/clang-c++17.yml @@ -2,9 +2,33 @@ name: clang-c++17 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: @@ -79,4 +103,4 @@ jobs: make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests - run: ./test/etl_tests -v \ No newline at end of file + run: ./test/etl_tests -v diff --git a/.github/workflows/clang-c++20.yml b/.github/workflows/clang-c++20.yml index 07bfede7..72ca48ae 100644 --- a/.github/workflows/clang-c++20.yml +++ b/.github/workflows/clang-c++20.yml @@ -2,9 +2,33 @@ name: clang-c++20 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: @@ -216,4 +240,3 @@ jobs: - name: Run tests run: ./test/etl_tests -v - diff --git a/.github/workflows/clang-c++23.yml b/.github/workflows/clang-c++23.yml index f2125e8e..f1c93bee 100644 --- a/.github/workflows/clang-c++23.yml +++ b/.github/workflows/clang-c++23.yml @@ -2,9 +2,33 @@ name: clang-c++23 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: @@ -190,4 +214,3 @@ jobs: - name: Run tests run: ./test/etl_tests -v - diff --git a/.github/workflows/clang-c++26.yml b/.github/workflows/clang-c++26.yml index 3a6fc8bc..db30c82c 100644 --- a/.github/workflows/clang-c++26.yml +++ b/.github/workflows/clang-c++26.yml @@ -2,9 +2,33 @@ name: clang-c++26 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: diff --git a/.github/workflows/clang-syntax-checks.yml b/.github/workflows/clang-syntax-checks.yml index 742be83c..69fdb83e 100644 --- a/.github/workflows/clang-syntax-checks.yml +++ b/.github/workflows/clang-syntax-checks.yml @@ -2,9 +2,33 @@ name: clang syntax checks on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: build-clang-cpp03-linux-STL: diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index d580f6ef..bcb7e665 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -3,9 +3,33 @@ name: clang-tidy on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: clang-tidy: diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index a8f19c75..addb4827 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -3,9 +3,33 @@ name: coverage on: push: branches: [ master, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' # Allow only one concurrent deployment to GitHub Pages concurrency: diff --git a/.github/workflows/gcc-c++11.yml b/.github/workflows/gcc-c++11.yml index 67ca6a1b..a3d944c6 100644 --- a/.github/workflows/gcc-c++11.yml +++ b/.github/workflows/gcc-c++11.yml @@ -2,9 +2,33 @@ name: gcc-c++11 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: diff --git a/.github/workflows/gcc-c++14.yml b/.github/workflows/gcc-c++14.yml index 1d875a9e..4e7d70ba 100644 --- a/.github/workflows/gcc-c++14.yml +++ b/.github/workflows/gcc-c++14.yml @@ -2,9 +2,33 @@ name: gcc-c++14 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: @@ -50,4 +74,4 @@ jobs: make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests - run: ./test/etl_tests -v \ No newline at end of file + run: ./test/etl_tests -v diff --git a/.github/workflows/gcc-c++17.yml b/.github/workflows/gcc-c++17.yml index 977e3700..851721a0 100644 --- a/.github/workflows/gcc-c++17.yml +++ b/.github/workflows/gcc-c++17.yml @@ -2,9 +2,33 @@ name: gcc-c++17 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: diff --git a/.github/workflows/gcc-c++20.yml b/.github/workflows/gcc-c++20.yml index 0439fc7a..6ba6679d 100644 --- a/.github/workflows/gcc-c++20.yml +++ b/.github/workflows/gcc-c++20.yml @@ -2,9 +2,33 @@ name: gcc-c++20 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: @@ -94,4 +118,4 @@ jobs: make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests - run: ./test/etl_tests -v \ No newline at end of file + run: ./test/etl_tests -v diff --git a/.github/workflows/gcc-c++23-armhf.yml b/.github/workflows/gcc-c++23-armhf.yml index 277fcb05..5c11011b 100644 --- a/.github/workflows/gcc-c++23-armhf.yml +++ b/.github/workflows/gcc-c++23-armhf.yml @@ -2,9 +2,33 @@ name: gcc-c++23-armhf on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: diff --git a/.github/workflows/gcc-c++23-i386.yml b/.github/workflows/gcc-c++23-i386.yml index 7fb1f9db..d5a47110 100644 --- a/.github/workflows/gcc-c++23-i386.yml +++ b/.github/workflows/gcc-c++23-i386.yml @@ -2,9 +2,33 @@ name: gcc-c++23-i386 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: diff --git a/.github/workflows/gcc-c++23-powerpc.yml b/.github/workflows/gcc-c++23-powerpc.yml index 4467414f..94c0a8cc 100644 --- a/.github/workflows/gcc-c++23-powerpc.yml +++ b/.github/workflows/gcc-c++23-powerpc.yml @@ -2,9 +2,33 @@ name: gcc-c++23-powerpc on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: diff --git a/.github/workflows/gcc-c++23-riscv64.yml b/.github/workflows/gcc-c++23-riscv64.yml index 0c3a2d6c..b6fa36e6 100644 --- a/.github/workflows/gcc-c++23-riscv64.yml +++ b/.github/workflows/gcc-c++23-riscv64.yml @@ -2,9 +2,33 @@ name: gcc-c++23-riscv64 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: diff --git a/.github/workflows/gcc-c++23-s390x.yml b/.github/workflows/gcc-c++23-s390x.yml index 8e022473..963f48d2 100644 --- a/.github/workflows/gcc-c++23-s390x.yml +++ b/.github/workflows/gcc-c++23-s390x.yml @@ -2,9 +2,33 @@ name: gcc-c++23-s390x on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: diff --git a/.github/workflows/gcc-c++23.yml b/.github/workflows/gcc-c++23.yml index fd933127..0ede6219 100644 --- a/.github/workflows/gcc-c++23.yml +++ b/.github/workflows/gcc-c++23.yml @@ -2,9 +2,33 @@ name: gcc-c++23 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: diff --git a/.github/workflows/gcc-c++26.yml b/.github/workflows/gcc-c++26.yml index 76a6075f..376db564 100644 --- a/.github/workflows/gcc-c++26.yml +++ b/.github/workflows/gcc-c++26.yml @@ -2,9 +2,33 @@ name: gcc-c++26 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: diff --git a/.github/workflows/gcc-syntax-checks.yml b/.github/workflows/gcc-syntax-checks.yml index 01f69e55..7ea8911d 100644 --- a/.github/workflows/gcc-syntax-checks.yml +++ b/.github/workflows/gcc-syntax-checks.yml @@ -2,9 +2,33 @@ name: gcc syntax checks on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: build-gcc-cpp03-linux-STL: diff --git a/.github/workflows/meson-gcc-c++23-no-stl.yml b/.github/workflows/meson-gcc-c++23-no-stl.yml index bf3c2e76..cb37d7dd 100644 --- a/.github/workflows/meson-gcc-c++23-no-stl.yml +++ b/.github/workflows/meson-gcc-c++23-no-stl.yml @@ -2,9 +2,33 @@ name: meson-gcc-c++23-no-stl on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: diff --git a/.github/workflows/msvc.yml b/.github/workflows/msvc.yml index d72f2477..1b7542b2 100644 --- a/.github/workflows/msvc.yml +++ b/.github/workflows/msvc.yml @@ -2,9 +2,33 @@ name: msvc on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' jobs: build-windows-msvc-stl: @@ -93,4 +117,3 @@ jobs: - name: Run tests run: test/Debug/etl_tests.exe -v - From e2a9f2b10f8f1e6ceeae4d30070ba080880a7a11 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Thu, 20 Aug 2026 16:47:17 +0200 Subject: [PATCH 33/38] Remove redundant ETL_USING_STL in tests (#1559) --- test/test_base64_RFC2152_decoder.cpp | 8 ++------ test/test_base64_RFC2152_encoder.cpp | 8 ++------ test/test_base64_RFC3501_decoder.cpp | 8 ++------ test/test_base64_RFC3501_encoder.cpp | 8 ++------ test/test_base64_RFC4648_URL_decoder_with_no_padding.cpp | 8 ++------ test/test_base64_RFC4648_URL_decoder_with_padding.cpp | 8 ++------ test/test_base64_RFC4648_URL_encoder_with_no_padding.cpp | 8 ++------ test/test_base64_RFC4648_URL_encoder_with_padding.cpp | 8 ++------ test/test_base64_RFC4648_decoder_with_no_padding.cpp | 8 ++------ test/test_base64_RFC4648_decoder_with_padding.cpp | 8 ++------ test/test_base64_RFC4648_encoder_with_no_padding.cpp | 8 ++------ test/test_base64_RFC4648_encoder_with_padding.cpp | 8 ++------ test/test_circular_buffer.cpp | 2 +- test/test_circular_buffer_external_buffer.cpp | 2 +- test/test_message_router_registry.cpp | 2 +- test/test_numeric.cpp | 2 -- test/test_ranges.cpp | 2 +- test/test_type_traits.cpp | 1 + 18 files changed, 29 insertions(+), 78 deletions(-) diff --git a/test/test_base64_RFC2152_decoder.cpp b/test/test_base64_RFC2152_decoder.cpp index f1ff542e..e7f1cc3e 100644 --- a/test/test_base64_RFC2152_decoder.cpp +++ b/test/test_base64_RFC2152_decoder.cpp @@ -32,20 +32,16 @@ SOFTWARE. #include "etl/base64_decoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-2152 //***************************************************************************** diff --git a/test/test_base64_RFC2152_encoder.cpp b/test/test_base64_RFC2152_encoder.cpp index 07e5b9a3..cfeb62f9 100644 --- a/test/test_base64_RFC2152_encoder.cpp +++ b/test/test_base64_RFC2152_encoder.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_encoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-2152 with no padding // Using minimum buffer size, with callback. diff --git a/test/test_base64_RFC3501_decoder.cpp b/test/test_base64_RFC3501_decoder.cpp index 7dc5baec..75439d63 100644 --- a/test/test_base64_RFC3501_decoder.cpp +++ b/test/test_base64_RFC3501_decoder.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_decoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-3501 //***************************************************************************** diff --git a/test/test_base64_RFC3501_encoder.cpp b/test/test_base64_RFC3501_encoder.cpp index 57a4f3a1..032057b5 100644 --- a/test/test_base64_RFC3501_encoder.cpp +++ b/test/test_base64_RFC3501_encoder.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_encoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-3501 with no padding // Using minimum buffer size, with callback. diff --git a/test/test_base64_RFC4648_URL_decoder_with_no_padding.cpp b/test/test_base64_RFC4648_URL_decoder_with_no_padding.cpp index 16bc0935..c0abb3bd 100644 --- a/test/test_base64_RFC4648_URL_decoder_with_no_padding.cpp +++ b/test/test_base64_RFC4648_URL_decoder_with_no_padding.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_decoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-4648-URL with no padding //***************************************************************************** diff --git a/test/test_base64_RFC4648_URL_decoder_with_padding.cpp b/test/test_base64_RFC4648_URL_decoder_with_padding.cpp index d7cc35d3..d99758ed 100644 --- a/test/test_base64_RFC4648_URL_decoder_with_padding.cpp +++ b/test/test_base64_RFC4648_URL_decoder_with_padding.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_decoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-4648-URL with no padding //***************************************************************************** diff --git a/test/test_base64_RFC4648_URL_encoder_with_no_padding.cpp b/test/test_base64_RFC4648_URL_encoder_with_no_padding.cpp index c1cb2b9c..3c32c94f 100644 --- a/test/test_base64_RFC4648_URL_encoder_with_no_padding.cpp +++ b/test/test_base64_RFC4648_URL_encoder_with_no_padding.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_encoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-4648-URL with no padding // Using minimum buffer size, with callback. diff --git a/test/test_base64_RFC4648_URL_encoder_with_padding.cpp b/test/test_base64_RFC4648_URL_encoder_with_padding.cpp index 4bb21b13..f1327786 100644 --- a/test/test_base64_RFC4648_URL_encoder_with_padding.cpp +++ b/test/test_base64_RFC4648_URL_encoder_with_padding.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_encoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-4648-URL with padding // Using minimum buffer size, with callback. diff --git a/test/test_base64_RFC4648_decoder_with_no_padding.cpp b/test/test_base64_RFC4648_decoder_with_no_padding.cpp index 77533716..0e31ee4e 100644 --- a/test/test_base64_RFC4648_decoder_with_no_padding.cpp +++ b/test/test_base64_RFC4648_decoder_with_no_padding.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_decoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-4648 with no padding //***************************************************************************** diff --git a/test/test_base64_RFC4648_decoder_with_padding.cpp b/test/test_base64_RFC4648_decoder_with_padding.cpp index f4cd0c4a..54d0f2fc 100644 --- a/test/test_base64_RFC4648_decoder_with_padding.cpp +++ b/test/test_base64_RFC4648_decoder_with_padding.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_decoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-4648 with no padding //***************************************************************************** diff --git a/test/test_base64_RFC4648_encoder_with_no_padding.cpp b/test/test_base64_RFC4648_encoder_with_no_padding.cpp index d925b4d8..d71b44b1 100644 --- a/test/test_base64_RFC4648_encoder_with_no_padding.cpp +++ b/test/test_base64_RFC4648_encoder_with_no_padding.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_encoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-4648 with no padding // Using minimum buffer size, with callback. diff --git a/test/test_base64_RFC4648_encoder_with_padding.cpp b/test/test_base64_RFC4648_encoder_with_padding.cpp index f40fc733..92c63c0f 100644 --- a/test/test_base64_RFC4648_encoder_with_padding.cpp +++ b/test/test_base64_RFC4648_encoder_with_padding.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_encoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-4648 with padding // Using minimum buffer size, with callback. diff --git a/test/test_circular_buffer.cpp b/test/test_circular_buffer.cpp index 34452537..2bbf9feb 100644 --- a/test/test_circular_buffer.cpp +++ b/test/test_circular_buffer.cpp @@ -67,7 +67,7 @@ namespace CHECK(data.crbegin() == data.crend()); } -#if ETL_USING_STL +#if ETL_HAS_INITIALIZER_LIST //************************************************************************* TEST(test_constructor_from_literal) { diff --git a/test/test_circular_buffer_external_buffer.cpp b/test/test_circular_buffer_external_buffer.cpp index a1374986..cd0c40cd 100644 --- a/test/test_circular_buffer_external_buffer.cpp +++ b/test/test_circular_buffer_external_buffer.cpp @@ -99,7 +99,7 @@ namespace CHECK(data.crbegin() == data.crend()); } -#if ETL_USING_STL +#if ETL_HAS_INITIALIZER_LIST //************************************************************************* TEST(test_constructor_from_literal) { diff --git a/test/test_message_router_registry.cpp b/test/test_message_router_registry.cpp index 22da80cb..17e27797 100644 --- a/test/test_message_router_registry.cpp +++ b/test/test_message_router_registry.cpp @@ -187,7 +187,7 @@ namespace CHECK_EQUAL(Registry_Size, registry.max_size()); } -#if ETL_USING_STL +#if ETL_HAS_INITIALIZER_LIST //************************************************************************* TEST(test_initializer_list_construction) { diff --git a/test/test_numeric.cpp b/test/test_numeric.cpp index f7407fef..8251a27c 100644 --- a/test/test_numeric.cpp +++ b/test/test_numeric.cpp @@ -135,7 +135,6 @@ namespace CHECK_EQUAL(*c, v); } -#if ETL_USING_STL //************************************************************************* TEST(test_midpoint_std_random_access_iterator) { @@ -163,7 +162,6 @@ namespace CHECK_EQUAL(*c, v); } -#endif //************************************************************************* TEST(test_lerp_floating_point) diff --git a/test/test_ranges.cpp b/test/test_ranges.cpp index cab77148..f7a668fe 100644 --- a/test/test_ranges.cpp +++ b/test/test_ranges.cpp @@ -41,7 +41,7 @@ SOFTWARE. // Issue #1391: include when available to verify etl::operator| // does not conflict with std::ranges::operator|. -#if ETL_USING_STL && ETL_USING_CPP20 && defined(__has_include) +#if ETL_USING_CPP20 && defined(__has_include) #if __has_include() #include #endif diff --git a/test/test_type_traits.cpp b/test/test_type_traits.cpp index 603c992b..531557de 100644 --- a/test/test_type_traits.cpp +++ b/test/test_type_traits.cpp @@ -759,6 +759,7 @@ namespace CHECK(etl::is_fundamental::value == std::is_fundamental::value); CHECK(etl::is_fundamental::value == std::is_fundamental::value); #if ETL_USING_STL + // etl::is_fundamental does not report std::nullptr_t as fundamental when not using the STL. CHECK(etl::is_fundamental::value == std::is_fundamental::value); #endif } From d12874c24acd0ac8aed4f883345a66260ca09534 Mon Sep 17 00:00:00 2001 From: Drew Rife Date: Thu, 20 Aug 2026 15:20:24 -0400 Subject: [PATCH 34/38] CI: cancel superseded workflow runs (#1557) * Update _index.md * Update _index.md * ci: cancel superseded workflow runs * ci: serialize documentation deployments --------- Co-authored-by: John Wellbelove --- .github/workflows/bazel-gcc-c++23-no-stl.yml | 4 ++++ .github/workflows/clang-c++11.yml | 4 ++++ .github/workflows/clang-c++14.yml | 4 ++++ .github/workflows/clang-c++17.yml | 4 ++++ .github/workflows/clang-c++20.yml | 4 ++++ .github/workflows/clang-c++23.yml | 4 ++++ .github/workflows/clang-c++26.yml | 4 ++++ .github/workflows/clang-format.yaml | 4 ++++ .github/workflows/clang-format_update.yaml | 4 ++++ .github/workflows/clang-syntax-checks.yml | 4 ++++ .github/workflows/clang-tidy.yaml | 4 ++++ .github/workflows/coverage.yml | 3 +-- .github/workflows/deploy-etl-documentation.yml | 4 ++++ .github/workflows/gcc-c++11.yml | 4 ++++ .github/workflows/gcc-c++14.yml | 4 ++++ .github/workflows/gcc-c++17.yml | 4 ++++ .github/workflows/gcc-c++20.yml | 4 ++++ .github/workflows/gcc-c++23-armhf.yml | 4 ++++ .github/workflows/gcc-c++23-i386.yml | 4 ++++ .github/workflows/gcc-c++23-powerpc.yml | 4 ++++ .github/workflows/gcc-c++23-riscv64.yml | 4 ++++ .github/workflows/gcc-c++23-s390x.yml | 4 ++++ .github/workflows/gcc-c++23.yml | 4 ++++ .github/workflows/gcc-c++26.yml | 4 ++++ .github/workflows/gcc-syntax-checks.yml | 4 ++++ .github/workflows/generator.yml | 4 ++++ .github/workflows/meson-gcc-c++23-no-stl.yml | 4 ++++ .github/workflows/msvc.yml | 4 ++++ .github/workflows/platformio-update.yml | 4 ++++ 29 files changed, 113 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bazel-gcc-c++23-no-stl.yml b/.github/workflows/bazel-gcc-c++23-no-stl.yml index 7ea97e00..648e0bcb 100644 --- a/.github/workflows/bazel-gcc-c++23-no-stl.yml +++ b/.github/workflows/bazel-gcc-c++23-no-stl.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-bazel-gcc-cpp23-no-stl: diff --git a/.github/workflows/clang-c++11.yml b/.github/workflows/clang-c++11.yml index 4f507561..6ca727ef 100644 --- a/.github/workflows/clang-c++11.yml +++ b/.github/workflows/clang-c++11.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-clang-cpp11-linux-stl: diff --git a/.github/workflows/clang-c++14.yml b/.github/workflows/clang-c++14.yml index 28896881..3ddb0cfa 100644 --- a/.github/workflows/clang-c++14.yml +++ b/.github/workflows/clang-c++14.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-clang-cpp14-linux-stl: diff --git a/.github/workflows/clang-c++17.yml b/.github/workflows/clang-c++17.yml index 53de40a0..ccec532b 100644 --- a/.github/workflows/clang-c++17.yml +++ b/.github/workflows/clang-c++17.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-clang-cpp17-linux-stl: diff --git a/.github/workflows/clang-c++20.yml b/.github/workflows/clang-c++20.yml index 72ca48ae..0d3fa3f7 100644 --- a/.github/workflows/clang-c++20.yml +++ b/.github/workflows/clang-c++20.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-clang-cpp20-linux-stl-libcxx: diff --git a/.github/workflows/clang-c++23.yml b/.github/workflows/clang-c++23.yml index f1c93bee..d25a346c 100644 --- a/.github/workflows/clang-c++23.yml +++ b/.github/workflows/clang-c++23.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-clang-cpp23-linux-stl-libcxx: diff --git a/.github/workflows/clang-c++26.yml b/.github/workflows/clang-c++26.yml index db30c82c..e118c66a 100644 --- a/.github/workflows/clang-c++26.yml +++ b/.github/workflows/clang-c++26.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-clang-cpp26-linux-stl-libcxx: diff --git a/.github/workflows/clang-format.yaml b/.github/workflows/clang-format.yaml index b4dd04a0..8f98f80d 100644 --- a/.github/workflows/clang-format.yaml +++ b/.github/workflows/clang-format.yaml @@ -7,6 +7,10 @@ on: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: clang-format: name: clang-format diff --git a/.github/workflows/clang-format_update.yaml b/.github/workflows/clang-format_update.yaml index a3d61e92..a15cc3f4 100644 --- a/.github/workflows/clang-format_update.yaml +++ b/.github/workflows/clang-format_update.yaml @@ -3,6 +3,10 @@ name: clang-format-update on: workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: clang-format: runs-on: ubuntu-24.04 diff --git a/.github/workflows/clang-syntax-checks.yml b/.github/workflows/clang-syntax-checks.yml index 69fdb83e..25795ebe 100644 --- a/.github/workflows/clang-syntax-checks.yml +++ b/.github/workflows/clang-syntax-checks.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-clang-cpp03-linux-STL: name: Syntax Check - Clang C++03 Linux STL diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index bcb7e665..ff368d50 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -31,6 +31,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: clang-tidy: name: clang-tidy diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index addb4827..bf138cf9 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -31,9 +31,8 @@ on: - 'subprojects/**' - '.devcontainer/**' -# Allow only one concurrent deployment to GitHub Pages concurrency: - group: coverage-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true # Grant GITHUB_TOKEN the minimum permissions needed at the workflow level diff --git a/.github/workflows/deploy-etl-documentation.yml b/.github/workflows/deploy-etl-documentation.yml index 597bfa9d..b33941b0 100644 --- a/.github/workflows/deploy-etl-documentation.yml +++ b/.github/workflows/deploy-etl-documentation.yml @@ -4,6 +4,10 @@ on: push: branches: [ master ] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + jobs: deploy: runs-on: ubuntu-latest diff --git a/.github/workflows/gcc-c++11.yml b/.github/workflows/gcc-c++11.yml index a3d944c6..4d33a909 100644 --- a/.github/workflows/gcc-c++11.yml +++ b/.github/workflows/gcc-c++11.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-gcc-cpp11-linux-stl: diff --git a/.github/workflows/gcc-c++14.yml b/.github/workflows/gcc-c++14.yml index 4e7d70ba..bc7cca67 100644 --- a/.github/workflows/gcc-c++14.yml +++ b/.github/workflows/gcc-c++14.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-gcc-cpp14-linux-stl: diff --git a/.github/workflows/gcc-c++17.yml b/.github/workflows/gcc-c++17.yml index 851721a0..1378fc69 100644 --- a/.github/workflows/gcc-c++17.yml +++ b/.github/workflows/gcc-c++17.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-gcc-cpp17-linux-stl: diff --git a/.github/workflows/gcc-c++20.yml b/.github/workflows/gcc-c++20.yml index 6ba6679d..6e004456 100644 --- a/.github/workflows/gcc-c++20.yml +++ b/.github/workflows/gcc-c++20.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-gcc-cpp20-linux-stl: diff --git a/.github/workflows/gcc-c++23-armhf.yml b/.github/workflows/gcc-c++23-armhf.yml index 5c11011b..00cb78d6 100644 --- a/.github/workflows/gcc-c++23-armhf.yml +++ b/.github/workflows/gcc-c++23-armhf.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-gcc-cpp23-linux-no-stl-armhf: diff --git a/.github/workflows/gcc-c++23-i386.yml b/.github/workflows/gcc-c++23-i386.yml index d5a47110..dec5833f 100644 --- a/.github/workflows/gcc-c++23-i386.yml +++ b/.github/workflows/gcc-c++23-i386.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-gcc-cpp23-linux-no-stl-i386: diff --git a/.github/workflows/gcc-c++23-powerpc.yml b/.github/workflows/gcc-c++23-powerpc.yml index 94c0a8cc..71a90f20 100644 --- a/.github/workflows/gcc-c++23-powerpc.yml +++ b/.github/workflows/gcc-c++23-powerpc.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-gcc-cpp23-linux-no-stl-powerpc: diff --git a/.github/workflows/gcc-c++23-riscv64.yml b/.github/workflows/gcc-c++23-riscv64.yml index b6fa36e6..b7a2611d 100644 --- a/.github/workflows/gcc-c++23-riscv64.yml +++ b/.github/workflows/gcc-c++23-riscv64.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-gcc-cpp23-linux-no-stl-riscv64: diff --git a/.github/workflows/gcc-c++23-s390x.yml b/.github/workflows/gcc-c++23-s390x.yml index 963f48d2..365f44dc 100644 --- a/.github/workflows/gcc-c++23-s390x.yml +++ b/.github/workflows/gcc-c++23-s390x.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-gcc-cpp23-linux-no-stl-s390x: diff --git a/.github/workflows/gcc-c++23.yml b/.github/workflows/gcc-c++23.yml index 0ede6219..a7e29e56 100644 --- a/.github/workflows/gcc-c++23.yml +++ b/.github/workflows/gcc-c++23.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-gcc-cpp23-linux-stl: diff --git a/.github/workflows/gcc-c++26.yml b/.github/workflows/gcc-c++26.yml index 376db564..63677261 100644 --- a/.github/workflows/gcc-c++26.yml +++ b/.github/workflows/gcc-c++26.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-gcc-cpp26-linux-stl: diff --git a/.github/workflows/gcc-syntax-checks.yml b/.github/workflows/gcc-syntax-checks.yml index 7ea8911d..0faa94c0 100644 --- a/.github/workflows/gcc-syntax-checks.yml +++ b/.github/workflows/gcc-syntax-checks.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-gcc-cpp03-linux-STL: name: Syntax Check - GCC C++03 Linux STL diff --git a/.github/workflows/generator.yml b/.github/workflows/generator.yml index b40dda0a..6cad5c54 100644 --- a/.github/workflows/generator.yml +++ b/.github/workflows/generator.yml @@ -6,6 +6,10 @@ on: branches: [ master, pull-request/* ] types: [opened, synchronize, reopened] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: generator-run: name: Header Generator diff --git a/.github/workflows/meson-gcc-c++23-no-stl.yml b/.github/workflows/meson-gcc-c++23-no-stl.yml index cb37d7dd..0902f2f9 100644 --- a/.github/workflows/meson-gcc-c++23-no-stl.yml +++ b/.github/workflows/meson-gcc-c++23-no-stl.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-meson-gcc-cpp23-no-stl: diff --git a/.github/workflows/msvc.yml b/.github/workflows/msvc.yml index 1b7542b2..8f7d4c00 100644 --- a/.github/workflows/msvc.yml +++ b/.github/workflows/msvc.yml @@ -30,6 +30,10 @@ on: - 'subprojects/**' - '.devcontainer/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build-windows-msvc-stl: name: Windows - STL diff --git a/.github/workflows/platformio-update.yml b/.github/workflows/platformio-update.yml index 04e64889..ebc02081 100644 --- a/.github/workflows/platformio-update.yml +++ b/.github/workflows/platformio-update.yml @@ -4,6 +4,10 @@ on: release: types: [published] # Trigger only when a GitHub release is published +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: publish: runs-on: ubuntu-latest From 7966ba86dfe6dab2db0cc119ce56d645b9fb9726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Sch=C3=A4pers?= Date: Fri, 21 Aug 2026 08:27:09 +0200 Subject: [PATCH 35/38] Fix delegate clear through assignment (#1546) Because the ptr was not nulled, it did not compare equal to a default constructed delegate anymore. --- include/etl/private/delegate_cpp11.h | 8 ++++++++ test/test_delegate.cpp | 14 ++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/include/etl/private/delegate_cpp11.h b/include/etl/private/delegate_cpp11.h index d373325f..eafbde77 100644 --- a/include/etl/private/delegate_cpp11.h +++ b/include/etl/private/delegate_cpp11.h @@ -669,6 +669,14 @@ namespace etl //*********************************************************************** ETL_CONSTEXPR14 void clear() ETL_NOEXCEPT { + if (stub == function_ptr_stub) + { + ptr.fp = ETL_NULLPTR; + } + else + { + ptr.object = ETL_NULLPTR; + } stub = ETL_NULLPTR; } diff --git a/test/test_delegate.cpp b/test/test_delegate.cpp index 6d07d2a1..c4e870bd 100644 --- a/test/test_delegate.cpp +++ b/test/test_delegate.cpp @@ -2182,6 +2182,20 @@ namespace #endif } #endif + + TEST(test_delegate_clear_equals_default_constructed) + { + using delegate_type = etl::delegate; + + delegate_type delegate = {}; + const delegate_type default_constructed; + + CHECK(delegate == default_constructed); + delegate = delegate_type::create(free_int); + CHECK(delegate != default_constructed); + delegate = {}; + CHECK(delegate == default_constructed); + } } } // namespace From 266b0f99c67269527ffc07782d1eed45ed17d49c Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Fri, 21 Aug 2026 12:27:32 +0200 Subject: [PATCH 36/38] Constexpr for unaligned_type (#1555) --- include/etl/bit.h | 13 +- .../etl/profiles/determine_builtin_support.h | 27 + include/etl/unaligned_type.h | 244 +++++-- test/test_unaligned_type.cpp | 612 ++++++++++++++++++ 4 files changed, 847 insertions(+), 49 deletions(-) diff --git a/include/etl/bit.h b/include/etl/bit.h index 4947a17b..861c776d 100644 --- a/include/etl/bit.h +++ b/include/etl/bit.h @@ -50,15 +50,22 @@ namespace etl //*************************************************************************** template ETL_NODISCARD - typename etl::enable_if< !(etl::is_integral::value && etl::is_integral::value) && (sizeof(TDestination) == sizeof(TSource)) - && etl::is_trivially_copyable::value && etl::is_trivially_copyable::value, - TDestination>::type bit_cast(const TSource& source) ETL_NOEXCEPT +#if ETL_USING_BUILTIN_BIT_CAST + ETL_CONSTEXPR14 +#endif + typename etl::enable_if< !(etl::is_integral::value && etl::is_integral::value) && (sizeof(TDestination) == sizeof(TSource)) + && etl::is_trivially_copyable::value && etl::is_trivially_copyable::value, + TDestination>::type bit_cast(const TSource& source) ETL_NOEXCEPT { +#if ETL_USING_BUILTIN_BIT_CAST + return __builtin_bit_cast(TDestination, source); +#else TDestination destination; memcpy(&destination, &source, sizeof(TDestination)); return destination; +#endif } //*************************************************************************** diff --git a/include/etl/profiles/determine_builtin_support.h b/include/etl/profiles/determine_builtin_support.h index b73c9ecd..deadec8c 100644 --- a/include/etl/profiles/determine_builtin_support.h +++ b/include/etl/profiles/determine_builtin_support.h @@ -68,6 +68,10 @@ SOFTWARE. #if !defined(ETL_USING_BUILTIN_IS_CONSTANT_EVALUATED) #define ETL_USING_BUILTIN_IS_CONSTANT_EVALUATED 1 #endif + + #if !defined(ETL_USING_BUILTIN_BIT_CAST) + #define ETL_USING_BUILTIN_BIT_CAST 1 + #endif #endif #if defined(ETL_USE_BUILTIN_MEM_FUNCTIONS) // Set all of them to be true if not @@ -132,6 +136,10 @@ SOFTWARE. #define ETL_USING_BUILTIN_IS_CONSTANT_EVALUATED __has_builtin(__builtin_is_constant_evaluated) #endif + #if !defined(ETL_USING_BUILTIN_BIT_CAST) + #define ETL_USING_BUILTIN_BIT_CAST __has_builtin(__builtin_bit_cast) + #endif + #if !defined(ETL_USING_BUILTIN_MEMCPY) #define ETL_USING_BUILTIN_MEMCPY __has_builtin(__builtin_memcpy) #endif @@ -174,6 +182,13 @@ SOFTWARE. #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE) #define ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE 1 #endif + + // MSVC has supported __builtin_bit_cast since VS2019 16.6. __has_builtin is not + // used for Microsoft (see above) to avoid a VS2022 intellisense issue, so the + // intrinsic is enabled here directly for the no-STL configuration. + #if !defined(ETL_USING_BUILTIN_BIT_CAST) && (_MSC_VER >= 1926) + #define ETL_USING_BUILTIN_BIT_CAST 1 + #endif #endif // The default. Set to 0, if not already set. @@ -245,6 +260,17 @@ SOFTWARE. #define ETL_USING_BUILTIN_BUILTIN_IS_CPP_TRIVIALLY_RELOCATABLE 0 #endif +#if !defined(ETL_USING_BUILTIN_BIT_CAST) + #define ETL_USING_BUILTIN_BIT_CAST 0 +#endif + +// __builtin_bit_cast is only of use to the ETL from C++11 onwards. +// Disable it for older standards, so that the generated code for those users is unchanged. +#if ETL_USING_BUILTIN_BIT_CAST && !ETL_USING_CPP11 + #undef ETL_USING_BUILTIN_BIT_CAST + #define ETL_USING_BUILTIN_BIT_CAST 0 +#endif + namespace etl { namespace traits @@ -268,6 +294,7 @@ namespace etl static ETL_CONSTANT bool using_builtin_is_virtual_base_of = (ETL_USING_BUILTIN_IS_VIRTUAL_BASE_OF == 1); static ETL_CONSTANT bool using_builtin_is_trivially_relocatable = (ETL_USING_BUILTIN_IS_TRIVIALLY_RELOCATABLE == 1); static ETL_CONSTANT bool using_builtin_builtin_is_cpp_trivially_relocatable = (ETL_USING_BUILTIN_BUILTIN_IS_CPP_TRIVIALLY_RELOCATABLE == 1); + static ETL_CONSTANT bool using_builtin_bit_cast = (ETL_USING_BUILTIN_BIT_CAST == 1); } // namespace traits } // namespace etl diff --git a/include/etl/unaligned_type.h b/include/etl/unaligned_type.h index 6f4189dc..cba1874f 100644 --- a/include/etl/unaligned_type.h +++ b/include/etl/unaligned_type.h @@ -46,10 +46,7 @@ SOFTWARE. #include "iterator.h" #include "type_traits.h" -#if ETL_USING_CPP20 && ETL_USING_STL - #include -#endif - +#include #include namespace etl @@ -102,12 +99,14 @@ namespace etl //************************************************************************* /// Default constructor //************************************************************************* - unaligned_type_common() {} + ETL_CONSTEXPR14 unaligned_type_common() + { + } //************************************************************************* /// Size of the storage. //************************************************************************* - size_t size() const + ETL_CONSTEXPR14 size_t size() const { return Size_; } @@ -115,7 +114,7 @@ namespace etl //************************************************************************* /// Pointer to the beginning of the storage. //************************************************************************* - pointer data() + ETL_CONSTEXPR14 pointer data() { return get_storage(); } @@ -123,7 +122,7 @@ namespace etl //************************************************************************* /// Const pointer to the beginning of the storage. //************************************************************************* - const_pointer data() const + ETL_CONSTEXPR14 const_pointer data() const { return get_storage(); } @@ -245,7 +244,7 @@ namespace etl //************************************************************************* /// Get a pointer to the storage. //************************************************************************* - pointer get_storage() + ETL_CONSTEXPR14 pointer get_storage() { return static_cast(this)->storage; } @@ -253,7 +252,7 @@ namespace etl //************************************************************************* /// Get a const pointer to the storage. //************************************************************************* - const_pointer get_storage() const + ETL_CONSTEXPR14 const_pointer get_storage() const { return static_cast(this)->storage; } @@ -277,7 +276,7 @@ namespace etl protected: //******************************* - unaligned_type_storage() + ETL_CONSTEXPR14 unaligned_type_storage() : storage() { } @@ -326,6 +325,52 @@ namespace etl }; ETL_END_PACKED + //************************************************************************* + /// The shift, in bits, that positions byte 'index' of a value of 'Size' + /// bytes, for the target endianness. + //************************************************************************* + template + ETL_CONSTEXPR size_t byte_shift(size_t index) + { + return ((Endian == ETL_ENDIAN_LITTLE) ? index : (Size - 1U - index)) * 8U; + } + +#if ETL_USING_BUILTIN_BIT_CAST + //************************************************************************* + /// Unsigned integer type of a given byte size, used as a constexpr-capable + /// 'proxy' for extracting/inserting the bytes of a floating point value. + //************************************************************************* + template + struct uint_of_size; + + template <> + struct uint_of_size<2U> + { + typedef uint16_t type; + }; + + template <> + struct uint_of_size<4U> + { + typedef uint32_t type; + }; + + template <> + struct uint_of_size<8U> + { + typedef uint64_t type; + }; + + //************************************************************************* + /// Whether the bit_cast fast path may be used for a type of 'Size' bytes. + /// It requires a same-size unsigned integer proxy type to exist. + //************************************************************************* + template + struct use_bit_cast : etl::conditional<(Size == 2U) || (Size == 4U) || (Size == 8U), etl::true_type, etl::false_type> + { + }; +#endif + //************************************************************************* /// Unaligned copy //************************************************************************* @@ -335,6 +380,10 @@ namespace etl //************************************************************************* /// Unaligned copy /// For integrals. + /// Bytes are extracted/inserted directly in the target endianness using + /// shifts, rather than a native-endianness memcpy + conditional reverse. + /// This makes the conversion independent of the host's endianness and + /// usable in a constexpr context (C++14 and later). //************************************************************************* template ETL_PACKED_CLASS(unaligned_copy) @@ -347,44 +396,42 @@ namespace etl //******************************* template - static void copy_value_to_store(const T& value, pointer store) + static ETL_CONSTEXPR14 void copy_value_to_store(const T& value, pointer store) { - memcpy(store, &value, Size_); + // Note: 'etl::unsigned_type' is used rather than 'etl::make_unsigned', as the + // latter is not defined for 'bool'. + typedef typename etl::unsigned_type::type unsigned_t; -#if ETL_HAS_CONSTEXPR_ENDIANNESS - if ETL_IF_CONSTEXPR (Endian_ != etl::endianness::value()) -#else - if (Endian_ != etl::endianness::value()) -#endif + unsigned_t uvalue = static_cast(value); + + for (size_t i = 0UL; i < Size_; ++i) { - etl::reverse(store, store + Size_); + store[i] = static_cast(uvalue >> private_unaligned_type::byte_shift(i)); } } //******************************* template - static void copy_store_to_value(const_pointer store, T & value) + static ETL_CONSTEXPR14 void copy_store_to_value(const_pointer store, T & value) { - memcpy(&value, store, Size_); + typedef typename etl::unsigned_type::type unsigned_t; -#if ETL_HAS_CONSTEXPR_ENDIANNESS - if ETL_IF_CONSTEXPR (Endian_ != etl::endianness::value()) -#else - if (Endian_ != etl::endianness::value()) -#endif + unsigned_t uvalue = unsigned_t(0); + + for (size_t i = 0UL; i < Size_; ++i) { - value = etl::reverse_bytes(value); + uvalue = static_cast(uvalue | (static_cast(store[i]) << private_unaligned_type::byte_shift(i))); } + + value = static_cast(uvalue); } //******************************* - static void copy_store_to_store(const_pointer src, int endian_src, pointer dst) + static ETL_CONSTEXPR14 void copy_store_to_store(const_pointer src, int endian_src, pointer dst) { - memcpy(dst, src, Size_); - - if (Endian_ != endian_src) + for (size_t i = 0UL; i < Size_; ++i) { - etl::reverse(dst, dst + Size_); + dst[i] = (Endian_ == endian_src) ? src[i] : src[Size_ - 1U - i]; } } }; @@ -404,8 +451,28 @@ namespace etl typedef typename private_unaligned_type::unaligned_type_storage< Size_>::const_pointer const_pointer; //******************************* + // The bit_cast fast path is only valid when a same-size unsigned integer + // proxy type exists (currently 2, 4 or 8 bytes, e.g. a 16-bit float, float, + // or double). Other sizes (e.g. 80/96/128-bit 'long double') fall back to + // memcpy. + //******************************* +#if ETL_USING_BUILTIN_BIT_CAST template - static void copy_value_to_store(const T& value, pointer store) + static ETL_CONSTEXPR14 void do_copy_value_to_store(const T& value, pointer store, etl::true_type) + { + typedef typename private_unaligned_type::uint_of_size::type uint_t; + + uint_t uvalue = etl::bit_cast(value); + + for (size_t i = 0UL; i < Size_; ++i) + { + store[i] = static_cast(uvalue >> private_unaligned_type::byte_shift(i)); + } + } +#endif + + template + static void do_copy_value_to_store(const T& value, pointer store, etl::false_type) { memcpy(store, &value, Size_); @@ -421,7 +488,41 @@ namespace etl //******************************* template - static void copy_store_to_value(const_pointer store, T & value) + static +#if ETL_USING_BUILTIN_BIT_CAST + ETL_CONSTEXPR14 +#endif + void + copy_value_to_store(const T& value, pointer store) + { +#if ETL_USING_BUILTIN_BIT_CAST + typedef typename private_unaligned_type::use_bit_cast::type use_bit_cast_t; +#else + typedef etl::false_type use_bit_cast_t; +#endif + do_copy_value_to_store(value, store, use_bit_cast_t()); + } + + //******************************* +#if ETL_USING_BUILTIN_BIT_CAST + template + static ETL_CONSTEXPR14 void do_copy_store_to_value(const_pointer store, T & value, etl::true_type) + { + typedef typename private_unaligned_type::uint_of_size::type uint_t; + + uint_t uvalue = uint_t(0); + + for (size_t i = 0UL; i < Size_; ++i) + { + uvalue = static_cast(uvalue | (static_cast(store[i]) << private_unaligned_type::byte_shift(i))); + } + + value = etl::bit_cast(uvalue); + } +#endif + + template + static void do_copy_store_to_value(const_pointer store, T & value, etl::false_type) { memcpy(&value, store, Size_); @@ -436,13 +537,32 @@ namespace etl } //******************************* - static void copy_store_to_store(const_pointer src, int endian_src, pointer dst) + template + static +#if ETL_USING_BUILTIN_BIT_CAST + ETL_CONSTEXPR14 +#endif + void + copy_store_to_value(const_pointer store, T & value) { - memcpy(dst, src, Size_); +#if ETL_USING_BUILTIN_BIT_CAST + typedef typename private_unaligned_type::use_bit_cast::type use_bit_cast_t; +#else + typedef etl::false_type use_bit_cast_t; +#endif + do_copy_store_to_value(store, value, use_bit_cast_t()); + } - if (Endian_ != endian_src) + //******************************* + // This is pure byte manipulation (copy + optional reversal), with no + // floating point arithmetic involved, so it is always constexpr-capable + // (C++14 and later), regardless of bit_cast/builtin availability. + //******************************* + static ETL_CONSTEXPR14 void copy_store_to_store(const_pointer src, int endian_src, pointer dst) + { + for (size_t i = 0UL; i < Size_; ++i) { - etl::reverse(dst, dst + Size_); + dst[i] = (Endian_ == endian_src) ? src[i] : src[Size_ - 1U - i]; } } }; @@ -486,7 +606,7 @@ namespace etl //************************************************************************* /// Construct from a value. //************************************************************************* - unaligned_type(T value) + ETL_CONSTEXPR14 unaligned_type(T value) { unaligned_copy::copy_value_to_store(value, this->storage); } @@ -501,6 +621,10 @@ namespace etl //************************************************************************* /// Construct from an address and buffer size. + /// \note 'buffer_size' must be greater than or equal to 'sizeof(T)'. + /// This is a precondition, checked by ETL_ASSERT. If the checks are + /// disabled (e.g. ETL_NO_CHECKS) then passing a smaller buffer size + /// results in a read beyond the end of the buffer. //************************************************************************* unaligned_type(const void* address, size_t buffer_size) { @@ -509,10 +633,38 @@ namespace etl etl::copy_n(reinterpret_cast(address), sizeof(T), this->storage); } + //************************************************************************* + /// Construct from a byte buffer. + /// Unlike the 'const void*' overload above, this does not require a + /// reinterpret_cast, so it is usable in a constexpr context (C++14 and + /// later), allowing compile time decoding of a byte buffer with an + /// explicit endianness. + //************************************************************************* + ETL_CONSTEXPR14 unaligned_type(const unsigned char* address) + { + etl::copy_n(address, sizeof(T), this->storage); + } + + //************************************************************************* + /// Construct from a byte buffer and buffer size. + /// See the note on the 'const unsigned char*' overload above regarding + /// constexpr usability. + /// \note 'buffer_size' must be greater than or equal to 'sizeof(T)'. + /// This is a precondition, checked by ETL_ASSERT. If the checks are + /// disabled (e.g. ETL_NO_CHECKS) then passing a smaller buffer size + /// results in a read beyond the end of the buffer. + //************************************************************************* + ETL_CONSTEXPR14 unaligned_type(const unsigned char* address, size_t buffer_size) + { + ETL_ASSERT(sizeof(T) <= buffer_size, ETL_ERROR(etl::unaligned_type_buffer_size)); + + etl::copy_n(address, sizeof(T), this->storage); + } + //************************************************************************* /// Copy constructor //************************************************************************* - unaligned_type(const unaligned_type& other) + ETL_CONSTEXPR14 unaligned_type(const unaligned_type& other) { unaligned_copy::copy_store_to_store(other.data(), Endian, this->storage); } @@ -521,7 +673,7 @@ namespace etl /// Copy constructor //************************************************************************* template - unaligned_type(const unaligned_type& other) + ETL_CONSTEXPR14 unaligned_type(const unaligned_type& other) { unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); } @@ -529,7 +681,7 @@ namespace etl //************************************************************************* /// Assignment operator //************************************************************************* - unaligned_type& operator=(T value) + ETL_CONSTEXPR14 unaligned_type& operator=(T value) { unaligned_copy::copy_value_to_store(value, this->storage); @@ -539,7 +691,7 @@ namespace etl //************************************************************************* /// Assignment operator. //************************************************************************* - unaligned_type& operator=(const unaligned_type& other) + ETL_CONSTEXPR14 unaligned_type& operator=(const unaligned_type& other) { unaligned_copy::copy_store_to_store(other.data(), Endian_, this->storage); @@ -550,7 +702,7 @@ namespace etl /// Assignment operator from other endianness. //************************************************************************* template - unaligned_type& operator=(const unaligned_type& other) + ETL_CONSTEXPR14 unaligned_type& operator=(const unaligned_type& other) { unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); @@ -560,7 +712,7 @@ namespace etl //************************************************************************* /// Conversion operator //************************************************************************* - operator T() const + ETL_CONSTEXPR14 operator T() const { T value = T(); @@ -572,7 +724,7 @@ namespace etl //************************************************************************* /// Get the value. //************************************************************************* - T value() const + ETL_CONSTEXPR14 T value() const { T value = T(); diff --git a/test/test_unaligned_type.cpp b/test/test_unaligned_type.cpp index 62cf500e..ad1e50c9 100644 --- a/test/test_unaligned_type.cpp +++ b/test/test_unaligned_type.cpp @@ -34,9 +34,69 @@ SOFTWARE. #include "etl/private/diagnostic_useless_cast_push.h" #include +#include namespace { + //*************************************************************************** + /// Representation level helpers. + /// 'memcpy' is used rather than 'etl::bit_cast' so that these work in every + /// configuration, including those without a bit_cast builtin. + //*************************************************************************** + uint32_t bits_of(float value) + { + uint32_t bits = 0U; + std::memcpy(&bits, &value, sizeof(bits)); + return bits; + } + + //*************************************************************************** + float float_from_bits(uint32_t bits) + { + float value = 0.0f; + std::memcpy(&value, &bits, sizeof(value)); + return value; + } + + //*************************************************************************** + uint64_t bits_of(double value) + { + uint64_t bits = 0U; + std::memcpy(&bits, &value, sizeof(bits)); + return bits; + } + + //*************************************************************************** + double double_from_bits(uint64_t bits) + { + double value = 0.0; + std::memcpy(&value, &bits, sizeof(value)); + return value; + } + +#if ETL_USING_CPP14 && ETL_USING_BUILTIN_BIT_CAST + //*************************************************************************** + /// Encodes 'bits' as a floating point value in the storage of 'TUnaligned', + /// then decodes it back, returning the resulting bit pattern. + /// Used to check that the constexpr bit_cast path is representation + /// preserving. + //*************************************************************************** + template + constexpr TBits round_trip_bits(TBits bits) + { + return etl::bit_cast(TUnaligned(etl::bit_cast(bits)).value()); + } + + //*************************************************************************** + /// The storage byte at 'index' when 'bits' is encoded in 'TUnaligned'. + //*************************************************************************** + template + constexpr unsigned char storage_byte_of(TBits bits, size_t index) + { + return TUnaligned(etl::bit_cast(bits)).data()[index]; + } +#endif + SUITE(test_unaligned_type) { //************************************************************************* @@ -175,6 +235,490 @@ namespace CHECK_THROW(etl::be_uint32_t be_v3(buffer.data(), buffer.size() - 1), etl::unaligned_type_buffer_size); } + //************************************************************************* + /// The 'const unsigned char*' overloads are distinct from the + /// 'const void*' ones above, so check their buffer size contract too. + /// Note that the size check is an ETL_ASSERT, so this only holds when + /// checks are enabled. See the note on the constructor. + //************************************************************************* + TEST(test_construction_from_byte_buffer_size_boundary) + { + const std::array buffer = {0x12, 0x34, 0x56, 0x78, 0x9A}; + + // Exactly the required number of bytes. + etl::le_uint32_t le_exact(buffer.data(), sizeof(uint32_t)); + etl::be_uint32_t be_exact(buffer.data(), sizeof(uint32_t)); + + CHECK_EQUAL(uint32_t(0x78563412), le_exact.value()); + CHECK_EQUAL(uint32_t(0x12345678), be_exact.value()); + + // More bytes than required. The surplus is ignored. + etl::le_uint32_t le_larger(buffer.data(), buffer.size()); + etl::be_uint32_t be_larger(buffer.data(), buffer.size()); + + CHECK_EQUAL(uint32_t(0x78563412), le_larger.value()); + CHECK_EQUAL(uint32_t(0x12345678), be_larger.value()); + + // One byte short. + CHECK_THROW(etl::le_uint32_t le_short(buffer.data(), sizeof(uint32_t) - 1U), etl::unaligned_type_buffer_size); + CHECK_THROW(etl::be_uint32_t be_short(buffer.data(), sizeof(uint32_t) - 1U), etl::unaligned_type_buffer_size); + + // Empty buffer. + CHECK_THROW(etl::le_uint32_t le_empty(buffer.data(), 0U), etl::unaligned_type_buffer_size); + CHECK_THROW(etl::be_uint32_t be_empty(buffer.data(), 0U), etl::unaligned_type_buffer_size); + + // The widest type is checked too, as it has the largest storage. + CHECK_THROW(etl::le_uint64_t le_wide(buffer.data(), buffer.size()), etl::unaligned_type_buffer_size); + CHECK_THROW(etl::be_uint64_t be_wide(buffer.data(), buffer.size()), etl::unaligned_type_buffer_size); + + // Single byte types accept a single byte buffer. + etl::le_uint8_t le_byte(buffer.data(), 1U); + etl::be_uint8_t be_byte(buffer.data(), 1U); + + CHECK_EQUAL(uint8_t(0x12), le_byte.value()); + CHECK_EQUAL(uint8_t(0x12), be_byte.value()); + + CHECK_THROW(etl::le_uint8_t le_byte_empty(buffer.data(), 0U), etl::unaligned_type_buffer_size); + CHECK_THROW(etl::be_uint8_t be_byte_empty(buffer.data(), 0U), etl::unaligned_type_buffer_size); + } + + //************************************************************************* + // The following tests demonstrate the 'decode' direction: given a raw byte + // buffer (e.g. as received from a file, network socket or memory-mapped + // device), interpret it as an explicitly little/big endian unaligned_type + // and read back the correctly decoded native value. + //************************************************************************* + TEST(test_decode_buffer_uint16) + { + // The same two bytes, interpreted with an explicit endianness. + const std::array buffer = {0x12, 0x34}; + + etl::le_uint16_t le_v(buffer.data(), buffer.size()); + etl::be_uint16_t be_v(buffer.data(), buffer.size()); + + CHECK_EQUAL(uint16_t(0x3412), le_v.value()); + CHECK_EQUAL(uint16_t(0x1234), be_v.value()); + } + + //************************************************************************* + // 'bool' has no 'make_unsigned' equivalent, so check that it is supported. + TEST(test_bool) + { + etl::unaligned_type le_v(true); + etl::unaligned_type be_v(false); + + CHECK_EQUAL(true, le_v.value()); + CHECK_EQUAL(false, be_v.value()); + + le_v = false; + be_v = true; + + CHECK_EQUAL(false, le_v.value()); + CHECK_EQUAL(true, be_v.value()); + } + + //************************************************************************* + TEST(test_decode_buffer_int32) + { + const std::array buffer = {0x12, 0x34, 0x56, 0x78}; + + etl::le_int32_t le_v(buffer.data(), buffer.size()); + etl::be_int32_t be_v(buffer.data(), buffer.size()); + + CHECK_EQUAL(int32_t(0x78563412), le_v.value()); + CHECK_EQUAL(int32_t(0x12345678), be_v.value()); + } + + //************************************************************************* + TEST(test_decode_buffer_uint64) + { + const std::array buffer = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}; + + etl::le_uint64_t le_v(buffer.data(), buffer.size()); + etl::be_uint64_t be_v(buffer.data(), buffer.size()); + + CHECK_EQUAL(uint64_t(0xEFCDAB8967452301), le_v.value()); + CHECK_EQUAL(uint64_t(0x0123456789ABCDEF), be_v.value()); + } + + //************************************************************************* + /// The decode tests above cover a subset of the widths only, and no + /// negative or extreme values. These cover every signed and unsigned + /// width at its boundary values, in both directions. + //************************************************************************* + template + void check_boundary_value(TValue value) + { + ETL_STATIC_ASSERT(sizeof(TValue) == TLittle::Size, "Mismatched size"); + ETL_STATIC_ASSERT(sizeof(TValue) == TBig::Size, "Mismatched size"); + + const TLittle le_v(value); + const TBig be_v(value); + + // The two encodings are byte reversals of each other. + for (size_t i = 0U; i < sizeof(TValue); ++i) + { + CHECK_EQUAL(int(le_v.data()[i]), int(be_v.data()[sizeof(TValue) - 1U - i])); + } + + // Decoding the encoded bytes returns the original value. + CHECK_EQUAL(value, TLittle(le_v.data(), sizeof(TValue)).value()); + CHECK_EQUAL(value, TBig(be_v.data(), sizeof(TValue)).value()); + } + + //************************************************************************* + TEST(test_integral_boundary_values) + { + check_boundary_value(0); + check_boundary_value(-1); + check_boundary_value(etl::integral_limits::min); + check_boundary_value(etl::integral_limits::max); + + check_boundary_value(0U); + check_boundary_value(etl::integral_limits::max); + + check_boundary_value(0); + check_boundary_value(-1); + check_boundary_value(etl::integral_limits::min); + check_boundary_value(etl::integral_limits::max); + + check_boundary_value(0U); + check_boundary_value(etl::integral_limits::max); + + check_boundary_value(0); + check_boundary_value(-1); + check_boundary_value(etl::integral_limits::min); + check_boundary_value(etl::integral_limits::max); + + check_boundary_value(0U); + check_boundary_value(etl::integral_limits::max); + + check_boundary_value(0); + check_boundary_value(-1); + check_boundary_value(etl::integral_limits::min); + check_boundary_value(etl::integral_limits::max); + + check_boundary_value(0U); + check_boundary_value(etl::integral_limits::max); + } + + //************************************************************************* + /// Explicit byte level checks for the widths that the decode tests above + /// do not cover, using negative values so that the sign bit is exercised. + //************************************************************************* + TEST(test_decode_buffer_negative_values) + { + // -2 == 0xFE, 0xFFFE, 0xFFFFFFFE, 0xFFFFFFFFFFFFFFFE. + const std::array buffer = {0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + CHECK_EQUAL(int8_t(-2), etl::le_int8_t(buffer.data(), buffer.size()).value()); + CHECK_EQUAL(int8_t(-2), etl::be_int8_t(buffer.data(), buffer.size()).value()); + + CHECK_EQUAL(int16_t(-2), etl::le_int16_t(buffer.data(), buffer.size()).value()); + CHECK_EQUAL(int32_t(-2), etl::le_int32_t(buffer.data(), buffer.size()).value()); + CHECK_EQUAL(int64_t(-2), etl::le_int64_t(buffer.data(), buffer.size()).value()); + + // Big endian reads the same bytes in the opposite order. + CHECK_EQUAL(int16_t(-257), etl::be_int16_t(buffer.data(), buffer.size()).value()); + CHECK_EQUAL(int32_t(-16777217), etl::be_int32_t(buffer.data(), buffer.size()).value()); + CHECK_EQUAL(int64_t(-72057594037927937LL), etl::be_int64_t(buffer.data(), buffer.size()).value()); + } + + //************************************************************************* + TEST(test_decode_buffer_float) + { + // 1.5f in IEEE-754 single precision is 0x3FC00000. + const std::array le_buffer = {0x00, 0x00, 0xC0, 0x3F}; + const std::array be_buffer = {0x3F, 0xC0, 0x00, 0x00}; + + etl::le_float_t le_v(le_buffer.data(), le_buffer.size()); + etl::be_float_t be_v(be_buffer.data(), be_buffer.size()); + + CHECK_EQUAL(1.5f, le_v.value()); + CHECK_EQUAL(1.5f, be_v.value()); + } + + //************************************************************************* + TEST(test_decode_buffer_double) + { + // 1.5 in IEEE-754 double precision is 0x3FF8000000000000. + const std::array le_buffer = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x3F}; + const std::array be_buffer = {0x3F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + etl::le_double_t le_v(le_buffer.data(), le_buffer.size()); + etl::be_double_t be_v(be_buffer.data(), be_buffer.size()); + + CHECK_EQUAL(1.5, le_v.value()); + CHECK_EQUAL(1.5, be_v.value()); + } + + //************************************************************************* + /// Representation level checks for 'float'. + /// Ordinary values such as 1.5f have a zero mantissa tail, a clear sign + /// bit and a mid range exponent, so they would still pass under several + /// plausible masking or byte ordering faults. These patterns exercise the + /// sign of zero, the infinities, a NaN payload, the subnormal range and + /// the normal range limits. + //************************************************************************* + TEST(test_float_representations) + { + static const uint32_t patterns[] = { + 0x00000000U, // +0.0 + 0x80000000U, // -0.0 + 0x7F800000U, // +infinity + 0xFF800000U, // -infinity + 0x7FC0DEADU, // Quiet NaN with a payload. + 0xFFC0DEADU, // Negative quiet NaN with a payload. + 0x00000001U, // Smallest positive subnormal. + 0x007FFFFFU, // Largest subnormal. + 0x00800000U, // Smallest positive normal. + 0x7F7FFFFFU, // Largest normal. + 0xBFC00000U // -1.5 + }; + + for (size_t i = 0U; i < (sizeof(patterns) / sizeof(patterns[0])); ++i) + { + const uint32_t bits = patterns[i]; + + etl::le_float_t le_v(float_from_bits(bits)); + etl::be_float_t be_v(float_from_bits(bits)); + + // The storage must hold the exact IEEE-754 bytes, in the requested order. + for (size_t b = 0U; b < sizeof(float); ++b) + { + CHECK_EQUAL(int((bits >> (8U * b)) & 0xFFU), int(le_v.data()[b])); + CHECK_EQUAL(int((bits >> (8U * (sizeof(float) - 1U - b))) & 0xFFU), int(be_v.data()[b])); + } + + // The value must survive the round trip bit for bit, including the + // sign of zero and the NaN payload. + CHECK_EQUAL(bits, bits_of(le_v.value())); + CHECK_EQUAL(bits, bits_of(be_v.value())); + + // Decoding the raw bytes must give the same result as encoding them. + etl::le_float_t le_decoded(le_v.data(), le_v.size()); + etl::be_float_t be_decoded(be_v.data(), be_v.size()); + + CHECK_EQUAL(bits, bits_of(le_decoded.value())); + CHECK_EQUAL(bits, bits_of(be_decoded.value())); + } + } + + //************************************************************************* + /// Representation level checks for 'double'. See the note on the 'float' + /// test above. + //************************************************************************* + TEST(test_double_representations) + { + static const uint64_t patterns[] = { + 0x0000000000000000ULL, // +0.0 + 0x8000000000000000ULL, // -0.0 + 0x7FF0000000000000ULL, // +infinity + 0xFFF0000000000000ULL, // -infinity + 0x7FF80000DEADBEEFULL, // Quiet NaN with a payload. + 0xFFF80000DEADBEEFULL, // Negative quiet NaN with a payload. + 0x0000000000000001ULL, // Smallest positive subnormal. + 0x000FFFFFFFFFFFFFULL, // Largest subnormal. + 0x0010000000000000ULL, // Smallest positive normal. + 0x7FEFFFFFFFFFFFFFULL, // Largest normal. + 0xBFF8000000000000ULL // -1.5 + }; + + for (size_t i = 0U; i < (sizeof(patterns) / sizeof(patterns[0])); ++i) + { + const uint64_t bits = patterns[i]; + + etl::le_double_t le_v(double_from_bits(bits)); + etl::be_double_t be_v(double_from_bits(bits)); + + for (size_t b = 0U; b < sizeof(double); ++b) + { + CHECK_EQUAL(int((bits >> (8U * b)) & 0xFFU), int(le_v.data()[b])); + CHECK_EQUAL(int((bits >> (8U * (sizeof(double) - 1U - b))) & 0xFFU), int(be_v.data()[b])); + } + + CHECK_EQUAL(bits, bits_of(le_v.value())); + CHECK_EQUAL(bits, bits_of(be_v.value())); + + etl::le_double_t le_decoded(le_v.data(), le_v.size()); + etl::be_double_t be_decoded(be_v.data(), be_v.size()); + + CHECK_EQUAL(bits, bits_of(le_decoded.value())); + CHECK_EQUAL(bits, bits_of(be_decoded.value())); + } + } + + //************************************************************************* + /// 'long double' is typically 80, 96 or 128 bits wide, so it has no same + /// sized unsigned integer proxy and falls back to the non bit_cast, + /// non constexpr path. That fallback still has to work. + //************************************************************************* + TEST(test_long_double_round_trip) + { + const long double value = 1.5L; + + etl::le_long_double_t le_v(value); + etl::be_long_double_t be_v(value); + + CHECK_EQUAL(sizeof(long double), le_v.size()); + CHECK_EQUAL(sizeof(long double), be_v.size()); + + // The little and big endian storage must be byte reversals of each other. + for (size_t i = 0U; i < sizeof(long double); ++i) + { + CHECK_EQUAL(int(le_v.data()[i]), int(be_v.data()[sizeof(long double) - 1U - i])); + } + + CHECK_EQUAL(value, le_v.value()); + CHECK_EQUAL(value, be_v.value()); + + // Decoding the raw bytes must give the same result as encoding them. + etl::le_long_double_t le_decoded(le_v.data(), le_v.size()); + etl::be_long_double_t be_decoded(be_v.data(), be_v.size()); + + CHECK_EQUAL(value, le_decoded.value()); + CHECK_EQUAL(value, be_decoded.value()); + } + +#if ETL_HAS_CONSTEXPR_ENDIANNESS + //************************************************************************* + TEST(test_decode_buffer_host_order) + { + // Decoding a buffer known to be in host order must yield the same + // value regardless of what that host order actually is. + const uint32_t value = 0x12345678U; + + const etl::host_uint32_t encoded(value); + etl::host_uint32_t decoded(encoded.data(), encoded.size()); + + CHECK_EQUAL(value, decoded.value()); + } +#endif + +#if ETL_USING_CPP14 + //************************************************************************* + // Demonstrates that decoding a raw byte buffer with an explicit endianness + // is now usable at compile time (constexpr), via the 'const unsigned char*' + // constructor overloads - not just runtime CHECK_EQUAL. + //************************************************************************* + TEST(test_constexpr_decode_buffer_integral) + { + static ETL_CONSTANT unsigned char le_buffer[4] = {0x78, 0x56, 0x34, 0x12}; + static ETL_CONSTANT unsigned char be_buffer[4] = {0x12, 0x34, 0x56, 0x78}; + + constexpr etl::le_uint32_t le_v(le_buffer, sizeof(le_buffer)); + constexpr etl::be_uint32_t be_v(be_buffer, sizeof(be_buffer)); + + static_assert(le_v.value() == 0x12345678U, "le_uint32_t constexpr decode from buffer + size"); + static_assert(be_v.value() == 0x12345678U, "be_uint32_t constexpr decode from buffer + size"); + + // Buffer-only overload (no explicit size). + constexpr etl::le_uint32_t le_v2(le_buffer); + constexpr etl::be_uint32_t be_v2(be_buffer); + + static_assert(le_v2.value() == 0x12345678U, "le_uint32_t constexpr decode from buffer"); + static_assert(be_v2.value() == 0x12345678U, "be_uint32_t constexpr decode from buffer"); + + // Prove the checks above are genuine compile-time facts, not just + // syntactically valid constexpr, by also exercising them at runtime. + CHECK_EQUAL(0x12345678U, le_v.value()); + CHECK_EQUAL(0x12345678U, be_v.value()); + } + + #if ETL_USING_BUILTIN_BIT_CAST + //************************************************************************* + TEST(test_constexpr_decode_buffer_float) + { + // 1.5f in IEEE-754 single precision is 0x3FC00000. + static ETL_CONSTANT unsigned char le_buffer[4] = {0x00, 0x00, 0xC0, 0x3F}; + static ETL_CONSTANT unsigned char be_buffer[4] = {0x3F, 0xC0, 0x00, 0x00}; + + constexpr etl::le_float_t le_v(le_buffer, sizeof(le_buffer)); + constexpr etl::be_float_t be_v(be_buffer, sizeof(be_buffer)); + + // Bit patterns are compared, rather than the float values, to avoid + // '-Wfloat-equal'. + static_assert(etl::bit_cast(le_v.value()) == 0x3FC00000U, "le_float_t constexpr decode from buffer"); + static_assert(etl::bit_cast(be_v.value()) == 0x3FC00000U, "be_float_t constexpr decode from buffer"); + + CHECK_EQUAL(1.5f, le_v.value()); + CHECK_EQUAL(1.5f, be_v.value()); + } + + //************************************************************************* + /// The constexpr path bit_casts through a same sized unsigned integer. + /// Check, at compile time, that this is exact for the awkward + /// representations too, not just for ordinary values such as 1.5f. + //************************************************************************* + TEST(test_constexpr_float_representations) + { + // Encode, then decode, and require the bit pattern to be unchanged. + static_assert(round_trip_bits(0x00000000U) == 0x00000000U, "le +0.0"); + static_assert(round_trip_bits(0x80000000U) == 0x80000000U, "le -0.0"); + static_assert(round_trip_bits(0x7F800000U) == 0x7F800000U, "le +infinity"); + static_assert(round_trip_bits(0xFF800000U) == 0xFF800000U, "le -infinity"); + static_assert(round_trip_bits(0x7FC0DEADU) == 0x7FC0DEADU, "le NaN payload"); + static_assert(round_trip_bits(0x00000001U) == 0x00000001U, "le smallest subnormal"); + static_assert(round_trip_bits(0x007FFFFFU) == 0x007FFFFFU, "le largest subnormal"); + static_assert(round_trip_bits(0x00800000U) == 0x00800000U, "le smallest normal"); + static_assert(round_trip_bits(0x7F7FFFFFU) == 0x7F7FFFFFU, "le largest normal"); + + static_assert(round_trip_bits(0x80000000U) == 0x80000000U, "be -0.0"); + static_assert(round_trip_bits(0xFF800000U) == 0xFF800000U, "be -infinity"); + static_assert(round_trip_bits(0x7FC0DEADU) == 0x7FC0DEADU, "be NaN payload"); + static_assert(round_trip_bits(0x00000001U) == 0x00000001U, "be smallest subnormal"); + static_assert(round_trip_bits(0x7F7FFFFFU) == 0x7F7FFFFFU, "be largest normal"); + + // The stored bytes must be the IEEE-754 bytes, in the requested order. + static_assert(storage_byte_of(0x7FC0DEADU, 0U) == 0xAD, "le NaN byte 0"); + static_assert(storage_byte_of(0x7FC0DEADU, 1U) == 0xDE, "le NaN byte 1"); + static_assert(storage_byte_of(0x7FC0DEADU, 2U) == 0xC0, "le NaN byte 2"); + static_assert(storage_byte_of(0x7FC0DEADU, 3U) == 0x7F, "le NaN byte 3"); + + static_assert(storage_byte_of(0x7FC0DEADU, 0U) == 0x7F, "be NaN byte 0"); + static_assert(storage_byte_of(0x7FC0DEADU, 1U) == 0xC0, "be NaN byte 1"); + static_assert(storage_byte_of(0x7FC0DEADU, 2U) == 0xDE, "be NaN byte 2"); + static_assert(storage_byte_of(0x7FC0DEADU, 3U) == 0xAD, "be NaN byte 3"); + + // Prove the checks above are genuine compile time facts, not just + // syntactically valid constexpr, by also exercising them at runtime. + CHECK_EQUAL(0x80000000U, (round_trip_bits(0x80000000U))); + CHECK_EQUAL(0x7FC0DEADU, (round_trip_bits(0x7FC0DEADU))); + } + + //************************************************************************* + /// As 'test_constexpr_float_representations', for the 8 byte proxy. + //************************************************************************* + TEST(test_constexpr_double_representations) + { + static_assert(round_trip_bits(0x0000000000000000ULL) == 0x0000000000000000ULL, "le +0.0"); + static_assert(round_trip_bits(0x8000000000000000ULL) == 0x8000000000000000ULL, "le -0.0"); + static_assert(round_trip_bits(0x7FF0000000000000ULL) == 0x7FF0000000000000ULL, "le +infinity"); + static_assert(round_trip_bits(0xFFF0000000000000ULL) == 0xFFF0000000000000ULL, "le -infinity"); + static_assert(round_trip_bits(0x7FF80000DEADBEEFULL) == 0x7FF80000DEADBEEFULL, "le NaN payload"); + static_assert(round_trip_bits(0x0000000000000001ULL) == 0x0000000000000001ULL, "le smallest subnormal"); + static_assert(round_trip_bits(0x000FFFFFFFFFFFFFULL) == 0x000FFFFFFFFFFFFFULL, "le largest subnormal"); + static_assert(round_trip_bits(0x0010000000000000ULL) == 0x0010000000000000ULL, "le smallest normal"); + static_assert(round_trip_bits(0x7FEFFFFFFFFFFFFFULL) == 0x7FEFFFFFFFFFFFFFULL, "le largest normal"); + + static_assert(round_trip_bits(0x8000000000000000ULL) == 0x8000000000000000ULL, "be -0.0"); + static_assert(round_trip_bits(0xFFF0000000000000ULL) == 0xFFF0000000000000ULL, "be -infinity"); + static_assert(round_trip_bits(0x7FF80000DEADBEEFULL) == 0x7FF80000DEADBEEFULL, "be NaN payload"); + static_assert(round_trip_bits(0x0000000000000001ULL) == 0x0000000000000001ULL, "be smallest subnormal"); + static_assert(round_trip_bits(0x7FEFFFFFFFFFFFFFULL) == 0x7FEFFFFFFFFFFFFFULL, "be largest normal"); + + static_assert(storage_byte_of(0x7FF80000DEADBEEFULL, 0U) == 0xEF, "le NaN byte 0"); + static_assert(storage_byte_of(0x7FF80000DEADBEEFULL, 7U) == 0x7F, "le NaN byte 7"); + + static_assert(storage_byte_of(0x7FF80000DEADBEEFULL, 0U) == 0x7F, "be NaN byte 0"); + static_assert(storage_byte_of(0x7FF80000DEADBEEFULL, 7U) == 0xEF, "be NaN byte 7"); + + CHECK_EQUAL(0x8000000000000000ULL, (round_trip_bits(0x8000000000000000ULL))); + CHECK_EQUAL(0x7FF80000DEADBEEFULL, (round_trip_bits(0x7FF80000DEADBEEFULL))); + } + #endif +#endif + //************************************************************************* TEST(test_endianness) { @@ -1122,6 +1666,74 @@ namespace CHECK_EQUAL(value, uint16_t(be_from_host)); } #endif + +#if ETL_USING_CPP14 + //************************************************************************* + // Demonstrates that integral endian conversion is now usable at compile + // time (constexpr), via genuine static_assert checks - not just runtime + // CHECK_EQUAL. The conversion is evaluated entirely by the compiler. + //************************************************************************* + TEST(test_constexpr_integral_endian_conversion) + { + // Constructing from a value and reading it back via value()/operator T() + // is constexpr for integral types, regardless of endianness. + constexpr etl::le_uint32_t le_v(0x12345678U); + constexpr etl::be_uint32_t be_v(0x12345678U); + + static_assert(le_v.value() == 0x12345678U, "le_uint32_t constexpr value() round trip"); + static_assert(be_v.value() == 0x12345678U, "be_uint32_t constexpr value() round trip"); + + static_assert(uint32_t(le_v) == 0x12345678U, "le_uint32_t constexpr operator T() round trip"); + static_assert(uint32_t(be_v) == 0x12345678U, "be_uint32_t constexpr operator T() round trip"); + + // Verify the underlying byte order is also usable at compile time. + static_assert(le_v.data()[0] == 0x78, "le_uint32_t constexpr byte 0"); + static_assert(le_v.data()[1] == 0x56, "le_uint32_t constexpr byte 1"); + static_assert(le_v.data()[2] == 0x34, "le_uint32_t constexpr byte 2"); + static_assert(le_v.data()[3] == 0x12, "le_uint32_t constexpr byte 3"); + + static_assert(be_v.data()[0] == 0x12, "be_uint32_t constexpr byte 0"); + static_assert(be_v.data()[1] == 0x34, "be_uint32_t constexpr byte 1"); + static_assert(be_v.data()[2] == 0x56, "be_uint32_t constexpr byte 2"); + static_assert(be_v.data()[3] == 0x78, "be_uint32_t constexpr byte 3"); + + // Prove the checks above are genuine compile-time facts, not just + // syntactically valid constexpr, by also exercising them at runtime. + CHECK_EQUAL(0x12345678U, uint32_t(le_v)); + CHECK_EQUAL(0x12345678U, uint32_t(be_v)); + } +#endif + +#if ETL_USING_CPP14 && ETL_USING_BUILTIN_BIT_CAST + //************************************************************************* + // Demonstrates that floating point endian conversion is now usable at + // compile time (constexpr) too, via etl::bit_cast (no std::bit_cast or + // STL dependency required). + //************************************************************************* + TEST(test_constexpr_float_endian_conversion) + { + constexpr etl::le_float_t le_v(1.5f); + constexpr etl::be_float_t be_v(1.5f); + + // Bit patterns are compared, rather than the float values, to avoid + // '-Wfloat-equal'. + static_assert(etl::bit_cast(le_v.value()) == 0x3FC00000U, "le_float_t constexpr value() round trip"); + static_assert(etl::bit_cast(be_v.value()) == 0x3FC00000U, "be_float_t constexpr value() round trip"); + + static_assert(etl::bit_cast(float(le_v)) == 0x3FC00000U, "le_float_t constexpr operator T() round trip"); + static_assert(etl::bit_cast(float(be_v)) == 0x3FC00000U, "be_float_t constexpr operator T() round trip"); + + // LE and BE storage bytes must be the reverse of each other, checked at + // compile time. + static_assert(le_v.data()[0] == be_v.data()[3], "byte 0/3 mismatch"); + static_assert(le_v.data()[1] == be_v.data()[2], "byte 1/2 mismatch"); + static_assert(le_v.data()[2] == be_v.data()[1], "byte 2/1 mismatch"); + static_assert(le_v.data()[3] == be_v.data()[0], "byte 3/0 mismatch"); + + CHECK_EQUAL(1.5f, float(le_v)); + CHECK_EQUAL(1.5f, float(be_v)); + } +#endif } } // namespace From ed6c46bf7dc1455e2a2ff3d425a16b602255f648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Sch=C3=A4pers?= Date: Sat, 22 Aug 2026 09:02:39 +0200 Subject: [PATCH 37/38] Allow easier construction of const_(multi_)map (#1561) When forcing to feed into value_types it has to be an ETL_OR_STD::pair, but when you want to use CTAD on the pairs the const is missing and the assert triggers. --- include/etl/const_map.h | 6 ++++-- include/etl/const_multimap.h | 6 ++++-- test/test_const_map.cpp | 21 +++++++++++++++++++++ test/test_const_multimap.cpp | 22 ++++++++++++++++++++++ 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/include/etl/const_map.h b/include/etl/const_map.h index f96f616b..051e6303 100644 --- a/include/etl/const_map.h +++ b/include/etl/const_map.h @@ -508,7 +508,8 @@ namespace etl //************************************************************************* ///\brief Construct a const_map from a variadic list of elements. - /// Static asserts if the elements are not of type value_type. + /// Static asserts if the elements are not constructible into + /// value_type. /// Static asserts if the number of elements is greater than the capacity of /// the const_map. //************************************************************************* @@ -517,7 +518,8 @@ namespace etl : iconst_map(element_list, sizeof...(elements), Size) , element_list{etl::forward(elements)...} { - static_assert((etl::are_all_same...>::value), "All elements must be value_type"); + static_assert((etl::conjunction>...>::value), + "All elements must be constructible into value_type"); static_assert(sizeof...(elements) <= Size, "Number of elements exceeds capacity"); } diff --git a/include/etl/const_multimap.h b/include/etl/const_multimap.h index dea60ee1..589fe331 100644 --- a/include/etl/const_multimap.h +++ b/include/etl/const_multimap.h @@ -458,7 +458,8 @@ namespace etl //************************************************************************* ///\brief Construct a const_map from a variadic list of elements. /// Static asserts if the element type is not constructible. - /// Static asserts if the elements are not of type value_type. + /// Static asserts if the elements are not constructible into + /// value_type. /// Static asserts if the number of elements is greater than the capacity of /// the const_map. //************************************************************************* @@ -467,7 +468,8 @@ namespace etl : iconst_multimap(element_list, sizeof...(elements), Size) , element_list{etl::forward(elements)...} { - static_assert((etl::are_all_same...>::value), "All elements must be value_type"); + static_assert((etl::conjunction>...>::value), + "All elements must be constructible into value_type"); static_assert(sizeof...(elements) <= Size, "Number of elements exceeds capacity"); } diff --git a/test/test_const_map.cpp b/test/test_const_map.cpp index fe998a7d..a3dc8f14 100644 --- a/test/test_const_map.cpp +++ b/test/test_const_map.cpp @@ -257,6 +257,27 @@ namespace CHECK_TRUE(data.max_size() == Max_Size); CHECK_FALSE(data.begin() == data.end()); } + + //************************************************************************* + TEST(test_cpp17_deduced_constructor_of_plain_pair) + { + static const etl::const_map data{ETL_OR_STD::pair{Key('A'), 0}, ETL_OR_STD::pair{Key('B'), 1}, ETL_OR_STD::pair{Key('C'), 2}, + ETL_OR_STD::pair{Key('D'), 3}, ETL_OR_STD::pair{Key('E'), 4}, ETL_OR_STD::pair{Key('F'), 5}, + ETL_OR_STD::pair{Key('G'), 6}, ETL_OR_STD::pair{Key('H'), 7}, ETL_OR_STD::pair{Key('I'), 8}, + ETL_OR_STD::pair{Key('J'), 9}}; + etl::const_map check{ETL_OR_STD::pair{Key('A'), 0}, ETL_OR_STD::pair{Key('B'), 1}, ETL_OR_STD::pair{Key('C'), 2}, + ETL_OR_STD::pair{Key('D'), 3}, ETL_OR_STD::pair{Key('E'), 4}, ETL_OR_STD::pair{Key('F'), 5}, + ETL_OR_STD::pair{Key('G'), 6}, ETL_OR_STD::pair{Key('H'), 7}, ETL_OR_STD::pair{Key('I'), 8}, + ETL_OR_STD::pair{Key('J'), 9}}; + + CHECK_TRUE(data.is_valid()); + CHECK_TRUE(data.size() == Max_Size); + CHECK_FALSE(data.empty()); + CHECK_TRUE(data.full()); + CHECK_TRUE(data.capacity() == Max_Size); + CHECK_TRUE(data.max_size() == Max_Size); + CHECK_FALSE(data.begin() == data.end()); + } #endif //************************************************************************* diff --git a/test/test_const_multimap.cpp b/test/test_const_multimap.cpp index 8c134899..3e181311 100644 --- a/test/test_const_multimap.cpp +++ b/test/test_const_multimap.cpp @@ -226,6 +226,28 @@ namespace CHECK_TRUE(data.max_size() == Max_Size); CHECK_FALSE(data.begin() == data.end()); } + + //************************************************************************* + TEST(test_cpp17_deduced_constructor_of_plain_pair) + { + static const etl::const_multimap data{ETL_OR_STD::pair{Key('A'), 0}, ETL_OR_STD::pair{Key('B'), 1}, ETL_OR_STD::pair{Key('C'), 2}, + ETL_OR_STD::pair{Key('D'), 3}, ETL_OR_STD::pair{Key('E'), 4}, ETL_OR_STD::pair{Key('F'), 5}, + ETL_OR_STD::pair{Key('G'), 6}, ETL_OR_STD::pair{Key('G'), 7}, ETL_OR_STD::pair{Key('G'), 8}, + ETL_OR_STD::pair{Key('J'), 9}}; + + etl::const_multimap check{ETL_OR_STD::pair{Key('A'), 0}, ETL_OR_STD::pair{Key('B'), 1}, ETL_OR_STD::pair{Key('C'), 2}, + ETL_OR_STD::pair{Key('D'), 3}, ETL_OR_STD::pair{Key('E'), 4}, ETL_OR_STD::pair{Key('F'), 5}, + ETL_OR_STD::pair{Key('G'), 6}, ETL_OR_STD::pair{Key('H'), 7}, ETL_OR_STD::pair{Key('I'), 8}, + ETL_OR_STD::pair{Key('J'), 9}}; + + CHECK_TRUE(data.is_valid()); + CHECK_TRUE(data.size() == Max_Size); + CHECK_FALSE(data.empty()); + CHECK_TRUE(data.full()); + CHECK_TRUE(data.capacity() == Max_Size); + CHECK_TRUE(data.max_size() == Max_Size); + CHECK_FALSE(data.begin() == data.end()); + } #endif //************************************************************************* From 9b1890e00ce7fe0f73595002dc7256f710480ad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BCthing?= Date: Sat, 22 Aug 2026 10:31:29 +0200 Subject: [PATCH 38/38] Add Macro for Inline Control (#1552) In specific situations there might be the need to enforce that a method is not inlined or always inlined (when using e.g. raw assembly). Therefore, we provide two macros ETL_NO_INLINE and ETL_ALWAYS_INLINE. Co-authored-by: John Wellbelove --- include/etl/platform.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/etl/platform.h b/include/etl/platform.h index 82e31588..494b4981 100644 --- a/include/etl/platform.h +++ b/include/etl/platform.h @@ -524,6 +524,20 @@ SOFTWARE. #define ETL_INDETERMINATE #endif +//************************************* +// inline +#if ETL_USING_GCC_COMPILER || ETL_USING_CLANG_COMPILER + #define ETL_NO_INLINE __attribute__((noinline)) + #define ETL_ALWAYS_INLINE __attribute__((always_inline)) +#elif ETL_USING_MICROSOFT_COMPILER + #define ETL_NO_INLINE __declspec(noinline) + #define ETL_ALWAYS_INLINE __forceinline +#else + // will trigger a compilation error if used + #define ETL_NO_INLINE unsupported compiler + #define ETL_ALWAYS_INLINE unsupported compiler +#endif + //************************************* // Determine if the ETL can use char8_t type. #if ETL_NO_SMALL_CHAR_SUPPORT