From 86c3dad1e93627b63af33798b00ca134459fa2ad Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Wed, 15 Jul 2026 08:24:36 +0100 Subject: [PATCH 01/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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/77] 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 From 22fc884d4516de51dc87cede0d3e657c4b0266b4 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Sat, 22 Aug 2026 17:47:16 +0200 Subject: [PATCH 39/77] Various cleanup (#1562) * Fix wrong templating * Fix stray double parens * Documentation cleanup: Some key/value actually are value only --- docs/containers/maps/flat-map.md | 2 +- docs/containers/maps/flat-multimap.md | 2 +- docs/containers/sets/const-multiset.md | 2 +- docs/containers/sets/const-set.md | 2 +- docs/containers/sets/flat-multiset.md | 37 ++++++++------- docs/containers/sets/flat-set.md | 40 +++++++++------- docs/strings/to-arithmetic.md | 2 +- include/etl/const_map.h | 1 - include/etl/const_multimap.h | 1 - include/etl/const_multiset.h | 1 - include/etl/const_set.h | 1 - include/etl/flat_multiset.h | 1 - include/etl/format.h | 2 +- include/etl/signal.h | 4 +- include/etl/to_arithmetic.h | 4 +- test/test_to_arithmetic.cpp | 65 ++++++++++++++++++++++++++ 16 files changed, 120 insertions(+), 47 deletions(-) diff --git a/docs/containers/maps/flat-map.md b/docs/containers/maps/flat-map.md index cc3ea886..0231aac5 100644 --- a/docs/containers/maps/flat-map.md +++ b/docs/containers/maps/flat-map.md @@ -239,7 +239,7 @@ The return type is either `std::pair` (default) or `etl::pair` (`ETL_NO_STL`) --- ```cpp -pair emplace((const value_type& value)) +pair emplace(const value_type& value) pair emplace(const key_type& key, const mapped_type& value) ``` **Description** diff --git a/docs/containers/maps/flat-multimap.md b/docs/containers/maps/flat-multimap.md index 58bda46b..4fede72c 100644 --- a/docs/containers/maps/flat-multimap.md +++ b/docs/containers/maps/flat-multimap.md @@ -239,7 +239,7 @@ The return type is either `std::pair` (default) or `etl::pair` (`ETL_NO_STL`) --- ```cpp -pair emplace((const value_type& value)) +pair emplace(const value_type& value) pair emplace(const key_type& key, const mapped_type& value) ``` **Description** diff --git a/docs/containers/sets/const-multiset.md b/docs/containers/sets/const-multiset.md index f4200dad..13add134 100644 --- a/docs/containers/sets/const-multiset.md +++ b/docs/containers/sets/const-multiset.md @@ -333,4 +333,4 @@ operator >= ## Technical stuff -The const set is implemented as a sorted array key/value pairs. +The const set is implemented as a sorted array of values. diff --git a/docs/containers/sets/const-set.md b/docs/containers/sets/const-set.md index 100549f7..dbb3b3ad 100644 --- a/docs/containers/sets/const-set.md +++ b/docs/containers/sets/const-set.md @@ -333,4 +333,4 @@ operator >= ## Technical stuff -The const set is implemented as a sorted array key/value pairs. +The const set is implemented as a sorted array of values. diff --git a/docs/containers/sets/flat-multiset.md b/docs/containers/sets/flat-multiset.md index d88e4a17..29bd454d 100644 --- a/docs/containers/sets/flat-multiset.md +++ b/docs/containers/sets/flat-multiset.md @@ -235,55 +235,60 @@ The return type is either `std::pair` (default) or `etl::pair` (`ETL_NO_STL`) --- ```cpp -pair emplace((const_reference value)) -pair emplace(const key_type& key, const mapped_type& value) +pair emplace(const_reference value) ``` **Description** -Inserts key/value pairs into the set by constructing directly into storage. +Inserts a value into the set by constructing directly into storage. The return type is either `std::pair` (default) or `etl::pair` (`ETL_NO_STL`) --- **C++03** -The emplace functions differ from that of std::set in that, due to C++03 not supporting 'perfect forwarding'. +The emplace functions differ from that of std::multiset in that, due to C++03 not supporting 'perfect forwarding'. + +```cpp +pair emplace() +``` +**Description** +Emplaces a default constructed value into the set. ```cpp template -pair emplace(const key_type& key, const T1& value1) +pair emplace(const T1& value1) ``` **Description** -Emplaces a value constructed from `key` and 1 argument into the set. +Emplaces a value constructed from 1 argument into the set. ```cpp template -pair emplace(const key_type& key, const T1& value1, const T2& value2) +pair emplace(const T1& value1, const T2& value2) ``` **Description** -Emplaces a value constructed from `key` and 2 arguments into the set. +Emplaces a value constructed from 2 arguments into the set. ```cpp template -pair emplace(const key_type& key, const T1& value1, const T2& value2, const T3& value3) +pair emplace(const T1& value1, const T2& value2, const T3& value3) ``` **Description** -Emplaces a value constructed from `key` and 3 arguments into the set. +Emplaces a value constructed from 3 arguments into the set. ```cpp template -pair emplace(const key_type& key, const T1& value1, const T2& value2, const T3& value3, const T4& value4) +pair emplace(const T1& value1, const T2& value2, const T3& value3, const T4& value4) ``` **Description** -Emplaces a value constructed from `key` and 4 arguments into the set. +Emplaces a value constructed from 4 arguments into the set. --- **C++11** ```cpp template -pair emplace(const key_type& key, Args&& ... args) +pair emplace(Args&& ... args) ``` **Description** -Emplaces a value constructed from the `key` and arguments into the set. +Emplaces a value constructed from the arguments into the set. --- @@ -493,9 +498,9 @@ operator != ## Technical stuff -Flat maps are usually implemented internally as a sorted vector of key/value pairs. Whilst this makes searching fast, it can have a detrimental effect when items are inserted into a container that stores complex, non-trivial keys or values. +Flat sets are usually implemented internally as a sorted vector of values. Whilst this makes searching fast, it can have a detrimental effect when items are inserted into a container that stores complex, non-trivial values. As inserting requires that all of the items to the right of the insert position must be shifted this can become an expensive operation for larger containers. -To improve insertion performance ETL flat maps are implemented as vectors of pointers to key/value pairs, sorted by key value. An insertion will involve a copy of a range of pointers; an operation that can be very fast. +To improve insertion performance ETL flat sets are implemented as vectors of pointers to values, sorted by value. An insertion will involve a copy of a range of pointers; an operation that can be very fast. The downside is that access to an item via an iterator will involve one indirection and the overhead of the container will be one pointer per item. A normal flat set implementation does not have this overhead. diff --git a/docs/containers/sets/flat-set.md b/docs/containers/sets/flat-set.md index e5c08610..4216b30b 100644 --- a/docs/containers/sets/flat-set.md +++ b/docs/containers/sets/flat-set.md @@ -235,11 +235,10 @@ The return type is either `std::pair` (default) or `etl::pair` (`ETL_NO_STL`) --- ```cpp -pair emplace((const_reference value)) -pair emplace(const key_type& key, const mapped_type& value) +pair emplace(const_reference value) ``` **Description** -Inserts key/value pairs into the set by constructing directly into storage. +Inserts a value into the set by constructing directly into storage. The return type is either `std::pair` (default) or `etl::pair` (`ETL_NO_STL`) --- @@ -248,39 +247,48 @@ The return type is either `std::pair` (default) or `etl::pair` (`ETL_NO_STL`) The emplace functions differ from that of `std::set` due to C++03 not supporting 'perfect forwarding'. ```cpp -template -pair emplace(const key_type& key, const T1& value1) +pair emplace() ``` **Description** -Emplaces a value constructed from `key` and 1 argument into the set. +Emplaces a default constructed value into the set. + +--- + +```cpp +template +pair emplace(const T1& value1) +``` +**Description** + +Emplaces a value constructed from 1 argument into the set. --- ```cpp template -pair emplace(const key_type& key, const T1& value1, const T2& value2) +pair emplace(const T1& value1, const T2& value2) ``` **Description** -Emplaces a value constructed from `key` and 2 arguments into the set. +Emplaces a value constructed from 2 arguments into the set. --- ```cpp template -pair emplace(const key_type& key, const T1& value1, const T2& value2, const T3& value3) +pair emplace(const T1& value1, const T2& value2, const T3& value3) ``` **Description** -Emplaces a value constructed from `key` and 3 arguments into the set. +Emplaces a value constructed from 3 arguments into the set. --- ```cpp template -pair emplace(const key_type& key, const T1& value1, const T2& value2, const T3& value3, const T4& value4) +pair emplace(const T1& value1, const T2& value2, const T3& value3, const T4& value4) ``` **Description** -Emplaces a value constructed from `key` and 4 arguments into the set. +Emplaces a value constructed from 4 arguments into the set. --- @@ -288,10 +296,10 @@ Emplaces a value constructed from `key` and 4 arguments into the set. ```cpp template -pair emplace(const key_type& key, Args&& ... args) +pair emplace(Args&& ... args) ``` **Description** -Emplaces a value constructed from the `key` and arguments into the set. +Emplaces a value constructed from the arguments into the set. --- @@ -509,9 +517,9 @@ operator != ## Technical stuff -Flat maps are usually implemented internally as a sorted vector of key/value pairs. Whilst this makes searching fast, it can have a detrimental effect when items are inserted into a container that stores complex, non-trivial keys or values. +Flat sets are usually implemented internally as a sorted vector of values. Whilst this makes searching fast, it can have a detrimental effect when items are inserted into a container that stores complex, non-trivial values. As inserting requires that all of the items to the right of the insert position must be shifted this can become an expensive operation for larger containers. -To improve insertion performance ETL flat maps are implemented as vectors of pointers to key/value pairs, sorted by key value. An insertion will involve a copy of a range of pointers; an operation that can be very fast. +To improve insertion performance ETL flat sets are implemented as vectors of pointers to values, sorted by value. An insertion will involve a copy of a range of pointers; an operation that can be very fast. The downside is that access to an item via an iterator will involve one indirection and the overhead of the container will be one pointer per item. A normal flat set implementation does not have this overhead. diff --git a/docs/strings/to-arithmetic.md b/docs/strings/to-arithmetic.md index be571377..297ae45d 100644 --- a/docs/strings/to-arithmetic.md +++ b/docs/strings/to-arithmetic.md @@ -327,7 +327,7 @@ Inequality test for etl::to_arithmetic_result ```cpp template ETL_CONSTEXPR14 bool operator !=(const T& lhs, - const etl::to_arithmetic_result& rhs) + const etl::to_arithmetic_result& rhs) ``` Inequality test for etl::to_arithmetic_result diff --git a/include/etl/const_map.h b/include/etl/const_map.h index 051e6303..747cc5ed 100644 --- a/include/etl/const_map.h +++ b/include/etl/const_map.h @@ -449,7 +449,6 @@ namespace etl //************************************************************************* /// Constructor //************************************************************************* - template ETL_CONSTEXPR14 explicit iconst_map(const value_type* element_list_, size_type size_, size_type max_elements_) ETL_NOEXCEPT : element_list(element_list_) , element_list_end{element_list_ + size_} diff --git a/include/etl/const_multimap.h b/include/etl/const_multimap.h index 589fe331..824abf77 100644 --- a/include/etl/const_multimap.h +++ b/include/etl/const_multimap.h @@ -398,7 +398,6 @@ namespace etl //************************************************************************* /// Constructor //************************************************************************* - template ETL_CONSTEXPR14 explicit iconst_multimap(const value_type* element_list_, size_type size_, size_type max_elements_) ETL_NOEXCEPT : element_list(element_list_) , element_list_end{element_list_ + size_} diff --git a/include/etl/const_multiset.h b/include/etl/const_multiset.h index 6184f9e7..28e9567d 100644 --- a/include/etl/const_multiset.h +++ b/include/etl/const_multiset.h @@ -354,7 +354,6 @@ namespace etl //************************************************************************* /// Constructor //************************************************************************* - template ETL_CONSTEXPR14 explicit iconst_multiset(const value_type* element_list_, size_type size_, size_type max_elements_) ETL_NOEXCEPT : element_list(element_list_) , element_list_end{element_list_ + size_} diff --git a/include/etl/const_set.h b/include/etl/const_set.h index df7f0989..e734de43 100644 --- a/include/etl/const_set.h +++ b/include/etl/const_set.h @@ -345,7 +345,6 @@ namespace etl //************************************************************************* /// Constructor //************************************************************************* - template ETL_CONSTEXPR14 explicit iconst_set(const value_type* element_list_, size_type size_, size_type max_elements_) ETL_NOEXCEPT : element_list(element_list_) , element_list_end{element_list_ + size_} diff --git a/include/etl/flat_multiset.h b/include/etl/flat_multiset.h index a3d5b048..e0d13220 100644 --- a/include/etl/flat_multiset.h +++ b/include/etl/flat_multiset.h @@ -321,7 +321,6 @@ namespace etl //************************************************************************* /// Emplaces a value to the set. //************************************************************************* - template ETL_OR_STD::pair emplace(const_reference value) { return insert(value); diff --git a/include/etl/format.h b/include/etl/format.h index 30152dc9..8820ac6f 100644 --- a/include/etl/format.h +++ b/include/etl/format.h @@ -1516,7 +1516,7 @@ namespace etl } // for integers - template + template void format_num(OutputIt& it, T value, const format_spec_t& spec) { size_t width = 0; diff --git a/include/etl/signal.h b/include/etl/signal.h index 695bd0c1..eda5dff5 100644 --- a/include/etl/signal.h +++ b/include/etl/signal.h @@ -384,7 +384,7 @@ namespace etl //************************************************************************* /// For a delegate slot type. //************************************************************************* - template + template static typename etl::enable_if_t::value, bool> slot_is_valid(const TSlotType& s) ETL_NOEXCEPT { return s.is_valid(); @@ -393,7 +393,7 @@ namespace etl //************************************************************************* /// For a non-delegate slot type. //************************************************************************* - template + template static typename etl::enable_if_t::value, bool> slot_is_valid(const TSlotType&) ETL_NOEXCEPT { return true; diff --git a/include/etl/to_arithmetic.h b/include/etl/to_arithmetic.h index 090ec110..941fc09b 100644 --- a/include/etl/to_arithmetic.h +++ b/include/etl/to_arithmetic.h @@ -908,7 +908,7 @@ ETL_CONSTEXPR14 bool operator==(const etl::to_arithmetic_result& lhs, const e } else { - return (lhs.status() == rhs.status()); + return (lhs.error() == rhs.error()); } } @@ -952,7 +952,7 @@ ETL_CONSTEXPR14 bool operator!=(const etl::to_arithmetic_result& lhs, const U /// Inequality test for etl::to_arithmetic_result //*************************************************************************** template -ETL_CONSTEXPR14 bool operator!=(const T& lhs, const etl::to_arithmetic_result& rhs) +ETL_CONSTEXPR14 bool operator!=(const T& lhs, const etl::to_arithmetic_result& rhs) { return !(lhs == rhs); } diff --git a/test/test_to_arithmetic.cpp b/test/test_to_arithmetic.cpp index 1dadabae..f78895b4 100644 --- a/test/test_to_arithmetic.cpp +++ b/test/test_to_arithmetic.cpp @@ -1039,6 +1039,71 @@ namespace CHECK_EQUAL(83, int(etl::to_arithmetic(ETLText(text.c_str(), text.size()), etl::radix::decimal).value())); } + //************************************************************************* + TEST(test_comparison_operators) + { + const Text text(STR("123")); + const Text bad_text(STR("1X3")); + const Text other_bad_text(STR("2X3")); + const Text overflow_text(STR("99999999999999999999")); + + const etl::to_arithmetic_result result = etl::to_arithmetic(text.c_str(), text.size()); + const etl::to_arithmetic_result same = etl::to_arithmetic(text.c_str(), text.size()); + const etl::to_arithmetic_result bad_result = etl::to_arithmetic(bad_text.c_str(), bad_text.size()); + const etl::to_arithmetic_result same_bad_result = etl::to_arithmetic(other_bad_text.c_str(), other_bad_text.size()); + const etl::to_arithmetic_result overflow_result = etl::to_arithmetic(overflow_text.c_str(), overflow_text.size()); + + // result <-> result + CHECK_TRUE(result == same); + CHECK_FALSE(result != same); + CHECK_TRUE(result != bad_result); + CHECK_FALSE(result == bad_result); + + // Two errored results with the same status compare equal. + CHECK_EQUAL(bad_result.error(), same_bad_result.error()); + CHECK_TRUE(bad_result == same_bad_result); + CHECK_FALSE(bad_result != same_bad_result); + + // Two errored results with different statuses do not compare equal. + CHECK(bad_result.error() != overflow_result.error()); + CHECK_TRUE(bad_result != overflow_result); + CHECK_FALSE(bad_result == overflow_result); + + // result <-> value + CHECK_TRUE(result == 123); + CHECK_FALSE(result != 123); + CHECK_TRUE(result != 124); + CHECK_FALSE(result == 124); + + // value <-> result + CHECK_TRUE(123 == result); + CHECK_FALSE(123 != result); + CHECK_TRUE(124 != result); + CHECK_FALSE(124 == result); + + // An errored result never compares equal to a value. + CHECK_FALSE(bad_result == 123); + CHECK_TRUE(bad_result != 123); + CHECK_FALSE(123 == bad_result); + CHECK_TRUE(123 != bad_result); + + // Heterogeneous result <-> value, using a different result value type. + const etl::to_arithmetic_result long_result = etl::to_arithmetic(text.c_str(), text.size()); + const etl::to_arithmetic_result small_result = etl::to_arithmetic(text.c_str(), text.size()); + + CHECK_TRUE(long_result == 123); + CHECK_FALSE(long_result != 123); + CHECK_TRUE(123 == long_result); + CHECK_TRUE(long_result != 124); + CHECK_FALSE(123 != long_result); + CHECK_TRUE(124 != long_result); + + CHECK_TRUE(small_result == 123); + CHECK_FALSE(small_result != 123); + CHECK_TRUE(123 == small_result); + CHECK_TRUE(small_result != 124); + } + //************************************************************************* #if ETL_USING_CPP14 TEST(test_constexpr_integral) From 1a72c88d47ec5d6ae4565fe22ab58ec20147fcdd Mon Sep 17 00:00:00 2001 From: Mike Bloom <91038685+mike919192@users.noreply.github.com> Date: Sat, 22 Aug 2026 15:09:07 -0400 Subject: [PATCH 40/77] string_view: clamp n in remove_prefix and remove_suffix (#1513) * remove_prefix, premove_suffix, clamp n to size() * Add function documentation * Add ETL_ASSERT * Remove ETL_NOEXCEPT_IF --------- Co-authored-by: John Wellbelove --- include/etl/string_view.h | 18 ++++++++++++---- test/test_string_view.cpp | 44 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/include/etl/string_view.h b/include/etl/string_view.h index 1178e4c4..81446bcf 100644 --- a/include/etl/string_view.h +++ b/include/etl/string_view.h @@ -425,18 +425,28 @@ namespace etl //************************************************************************* /// Shrinks the view by moving its start forward. + /// When n >= size(), the view is set to empty. //************************************************************************* - ETL_CONSTEXPR14 void remove_prefix(size_type n) ETL_NOEXCEPT + ETL_CONSTEXPR14 void remove_prefix(size_type n) { - mbegin += n; + ETL_ASSERT(n <= size(), ETL_ERROR(string_view_bounds)); + if (n < size()) + mbegin += n; + else + mbegin = mend; } //************************************************************************* /// Shrinks the view by moving its end backward. + /// When n >= size(), the view is set to empty. //************************************************************************* - ETL_CONSTEXPR14 void remove_suffix(size_type n) ETL_NOEXCEPT + ETL_CONSTEXPR14 void remove_suffix(size_type n) { - mend -= n; + ETL_ASSERT(n <= size(), ETL_ERROR(string_view_bounds)); + if (n < size()) + mend -= n; + else + mend = mbegin; } //************************************************************************* diff --git a/test/test_string_view.cpp b/test/test_string_view.cpp index a9314dd2..1d016160 100644 --- a/test/test_string_view.cpp +++ b/test/test_string_view.cpp @@ -542,6 +542,50 @@ namespace CHECK(isEqual); } + //************************************************************************* + TEST(test_remove_prefix_empty) + { + std::string original = "Hello World"; + View view(original.c_str()); + + // remove n == size() should result in an empty view + view.remove_prefix(original.size()); + CHECK_EQUAL(view.begin(), view.end()); + CHECK(view.empty()); + } + + //************************************************************************* + TEST(test_remove_prefix_assert) + { + std::string original = "Hello World"; + View view(original.c_str()); + + // remove n > size() should trigger ETL_ASSERT + CHECK_THROW(view.remove_prefix(original.size() + 1), etl::string_view_bounds); + } + + //************************************************************************* + TEST(test_remove_suffix_empty) + { + std::string original = "Hello World"; + View view(original.c_str()); + + // remove n == size() should result in an empty view + view.remove_suffix(original.size()); + CHECK_EQUAL(view.begin(), view.end()); + CHECK(view.empty()); + } + + //************************************************************************* + TEST(test_remove_suffix_assert) + { + std::string original = "Hello World"; + View view(original.c_str()); + + // remove n > size() should trigger ETL_ASSERT + CHECK_THROW(view.remove_suffix(original.size() + 1), etl::string_view_bounds); + } + //************************************************************************* TEST(test_copy) { From ffd64f374bac21b02e685ec8381fd6f5c5f69136 Mon Sep 17 00:00:00 2001 From: Drew Rife Date: Sat, 22 Aug 2026 15:10:38 -0400 Subject: [PATCH 41/77] circular_buffer: add initialization and assignment overloads (#1556) * feat(circular_buffer): add initialization and assignment overloads * fix(circular_buffer): handle self-referential assignment * docs: document constructors and assign methods --------- Co-authored-by: John Wellbelove --- .../queues & stacks/circular-buffer.md | 36 +++++ include/etl/circular_buffer.h | 120 +++++++++++++++++ test/test_circular_buffer.cpp | 117 ++++++++++++++++ test/test_circular_buffer_external_buffer.cpp | 125 ++++++++++++++++++ 4 files changed, 398 insertions(+) diff --git a/docs/containers/queues & stacks/circular-buffer.md b/docs/containers/queues & stacks/circular-buffer.md index 687ac4b9..3c3faf2a 100644 --- a/docs/containers/queues & stacks/circular-buffer.md +++ b/docs/containers/queues & stacks/circular-buffer.md @@ -69,11 +69,19 @@ const_reverse_iterator reverse_iterator ```cpp circular_buffer(); +circular_buffer(size_t initial_size); + +circular_buffer(size_t initial_size, const_reference value); + circular_buffer(const etl::circular_buffer& other); circular_buffer(etl::circular_buffer&& other); ``` +The `initial_size` constructor fills the buffer with default-constructed values. +The `initial_size` and `value` constructor fills the buffer with copies of `value`. +If `initial_size` exceeds `SIZE`, the buffer is filled to its capacity. + --- ```cpp @@ -81,6 +89,10 @@ circular_buffer_ext(void* buffer, size_t max_size) circular_buffer_ext(size_t max_size) 20.32.1 +circular_buffer_ext(size_t initial_size, void* buffer, size_t max_size) + +circular_buffer_ext(size_t initial_size, const_reference value, void* buffer, size_t max_size) + template circular_buffer_ext(TIterator first, const TIterator& last, void* buffer, size_t max_size) @@ -89,6 +101,10 @@ circular_buffer_ext(std::initializer_list init, void* buffer, size_t max_size circular_buffer_ext(const circular_buffer_ext& other, void* buffer, size_t max_size) ``` +The `initial_size` constructor fills the buffer with default-constructed values. +The `initial_size` and `value` constructor fills the buffer with copies of `value`. +If `initial_size` exceeds `max_size`, the buffer is filled to its capacity. The external storage must contain at least `max_size + 1` elements. + ## Status ```cpp @@ -225,6 +241,26 @@ Returns the remaining available capacity in the `circular_buffer`. ## Modifiers +```cpp +void assign(iterator first, const iterator& last) +void assign(const_iterator first, const const_iterator& last) + +template +void assign(TIterator first, const TIterator& last) +``` +**Description** +Replaces the contents of the circular buffer with the elements in the range `[first, last)`. If the range exceeds the capacity, only the last `capacity()` elements are retained. The range may refer to the circular buffer being assigned. + +--- + +```cpp +void assign(size_type n, const_reference value) +``` +**Description** +Replaces the contents of the circular buffer with `n` copies of `value`. If `n` exceeds the capacity, the buffer is filled to its capacity. `value` may refer to an element in the circular buffer being assigned. + +--- + ```cpp void set_buffer(void* buffer) ``` diff --git a/include/etl/circular_buffer.h b/include/etl/circular_buffer.h index 4c8290c4..b0586cfb 100644 --- a/include/etl/circular_buffer.h +++ b/include/etl/circular_buffer.h @@ -900,6 +900,53 @@ namespace etl return pbuffer[(out + index) % buffer_size]; } + //************************************************************************* + /// Assigns an iterator range to the circular buffer. + //************************************************************************* + void assign(iterator first, const iterator& last) + { + assign_range(first, last); + } + + //************************************************************************* + /// Assigns a const iterator range to the circular buffer. + //************************************************************************* + void assign(const_iterator first, const const_iterator& last) + { + assign_range(first, last); + } + + //************************************************************************* + /// Assigns a range to the circular buffer. + //************************************************************************* + template + typename etl::enable_if::value, void>::type assign(TIterator first, const TIterator& last) + { + clear(); + push(first, last); + } + + //************************************************************************* + /// Assigns 'n' copies of a value to the circular buffer. + //************************************************************************* + void assign(size_type n, const_reference value) + { + if (n == 0U) + { + clear(); + return; + } + + const T value_copy(value); + + clear(); + + while (n-- != 0U) + { + push(value_copy); + } + } + //************************************************************************* /// push. /// Adds an item to the buffer. @@ -1036,6 +1083,43 @@ namespace etl protected: + //************************************************************************* + /// Assigns a circular buffer iterator range. + //************************************************************************* + template + void assign_range(TIterator first, const TIterator& last) + { + const bool is_self_range = (&first.container() == this) && (&last.container() == this); + + if (!is_self_range) + { + clear(); + push(first, last); + } + else + { + const size_type first_index = static_cast(first.get_index()); + const size_type last_index = static_cast(last.get_index()); + + while (out != first_index) + { + pop(); + } + + while (in != last_index) + { + if (in == 0U) + { + in = buffer_size; + } + + --in; + pbuffer[in].~T(); + ETL_DECREMENT_DEBUG_COUNT; + } + } + } + //************************************************************************* /// Protected constructor. //************************************************************************* @@ -1127,6 +1211,24 @@ namespace etl { } + //************************************************************************* + /// Constructor, with size. + //************************************************************************* + explicit circular_buffer(size_t initial_size) + : icircular_buffer(reinterpret_cast(buffer.raw), MAX_SIZE) + { + this->assign(initial_size, T()); + } + + //************************************************************************* + /// Constructor, from initial size and value. + //************************************************************************* + circular_buffer(size_t initial_size, typename etl::icircular_buffer::const_reference value) + : icircular_buffer(reinterpret_cast(buffer.raw), MAX_SIZE) + { + this->assign(initial_size, value); + } + //************************************************************************* /// Constructor. /// Constructs a buffer from an iterator range. @@ -1274,6 +1376,24 @@ namespace etl { } + //************************************************************************* + /// Constructor, with size. + //************************************************************************* + explicit circular_buffer_ext(size_t initial_size, void* buffer, size_t max_size) + : icircular_buffer(reinterpret_cast(buffer), max_size) + { + this->assign(initial_size, T()); + } + + //************************************************************************* + /// Constructor, from initial size and value. + //************************************************************************* + circular_buffer_ext(size_t initial_size, typename etl::icircular_buffer::const_reference value, void* buffer, size_t max_size) + : icircular_buffer(reinterpret_cast(buffer), max_size) + { + this->assign(initial_size, value); + } + //************************************************************************* /// Constructor. /// Constructs a buffer from an iterator range. diff --git a/test/test_circular_buffer.cpp b/test/test_circular_buffer.cpp index 2bbf9feb..cdbd162d 100644 --- a/test/test_circular_buffer.cpp +++ b/test/test_circular_buffer.cpp @@ -67,6 +67,123 @@ namespace CHECK(data.crbegin() == data.crend()); } + //************************************************************************* + TEST(test_constructor_with_size) + { + etl::circular_buffer data(5U); + + CHECK_EQUAL(5U, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [](int value) { return value == 0; })); + } + + //************************************************************************* + TEST(test_constructor_with_excess_size) + { + etl::circular_buffer data(SIZE + 3U); + + CHECK(data.full()); + CHECK_EQUAL(SIZE, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [](int value) { return value == 0; })); + } + + //************************************************************************* + TEST(test_constructor_with_size_and_value) + { + const Ndc value("value"); + Data data(5U, value); + + CHECK_EQUAL(5U, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [&value](const Ndc& item) { return item == value; })); + } + + //************************************************************************* + TEST(test_constructor_with_excess_size_and_value) + { + const Ndc value("value"); + Data data(SIZE + 3U, value); + + CHECK(data.full()); + CHECK_EQUAL(SIZE, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [&value](const Ndc& item) { return item == value; })); + } + + //************************************************************************* + TEST(test_assign_size_and_value) + { + Data data; + data.push(Ndc("old")); + + const Ndc value("value"); + data.assign(5U, value); + + CHECK_EQUAL(5U, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [&value](const Ndc& item) { return item == value; })); + + data.assign(0U, value); + CHECK(data.empty()); + + data.assign(SIZE + 3U, value); + CHECK(data.full()); + CHECK_EQUAL(SIZE, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [&value](const Ndc& item) { return item == value; })); + } + + //************************************************************************* + TEST(test_assign_range) + { + Compare input{Ndc("0"), Ndc("1"), Ndc("2"), Ndc("3"), Ndc("4"), Ndc("5"), Ndc("6"), + Ndc("7"), Ndc("8"), Ndc("9"), Ndc("10"), Ndc("11"), Ndc("12")}; + Compare expected(input.end() - SIZE, input.end()); + Data data; + data.push(Ndc("old")); + + data.assign(input.begin(), input.end()); + + CHECK(data.full()); + CHECK_EQUAL(expected.size(), data.size()); + CHECK(std::equal(expected.begin(), expected.end(), data.begin())); + + data.assign(input.begin(), input.begin()); + CHECK(data.empty()); + } + + //************************************************************************* + TEST(test_assign_self_range) + { + Compare input{Ndc("0"), Ndc("1"), Ndc("2"), Ndc("3"), Ndc("4"), Ndc("5"), Ndc("6"), + Ndc("7"), Ndc("8"), Ndc("9"), Ndc("10"), Ndc("11"), Ndc("12")}; + Compare expected(input.end() - SIZE, input.end()); + Data data(input.begin(), input.end()); + + data.assign(data.begin(), data.end()); + + CHECK(data.full()); + CHECK_EQUAL(expected.size(), data.size()); + CHECK(std::equal(expected.begin(), expected.end(), data.begin())); + + Data::const_iterator first = data.cbegin() + 2; + Data::const_iterator last = data.cend() - 2; + Compare partial_expected(expected.begin() + 2, expected.end() - 2); + + data.assign(first, last); + + CHECK_EQUAL(partial_expected.size(), data.size()); + CHECK(std::equal(partial_expected.begin(), partial_expected.end(), data.begin())); + } + + //************************************************************************* + TEST(test_assign_aliased_value) + { + Compare input{Ndc("0"), Ndc("1"), Ndc("2"), Ndc("3"), Ndc("4")}; + Data data(input.begin(), input.end()); + + const Ndc expected(data.front()); + data.assign(7U, data.front()); + + CHECK_EQUAL(7U, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [&expected](const Ndc& item) { return item == expected; })); + } + #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 cd0c40cd..425d92b0 100644 --- a/test/test_circular_buffer_external_buffer.cpp +++ b/test/test_circular_buffer_external_buffer.cpp @@ -78,6 +78,131 @@ namespace CHECK(data.crbegin() == data.crend()); } + //************************************************************************* + TEST(test_constructor_with_size) + { + etl::uninitialized_buffer_of buffer1; + etl::circular_buffer_ext data(5U, buffer1.raw, SIZE); + + CHECK_EQUAL(5U, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [](int value) { return value == 0; })); + } + + //************************************************************************* + TEST(test_constructor_with_excess_size) + { + etl::uninitialized_buffer_of buffer1; + etl::circular_buffer_ext data(SIZE + 3U, buffer1.raw, SIZE); + + CHECK(data.full()); + CHECK_EQUAL(SIZE, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [](int value) { return value == 0; })); + } + + //************************************************************************* + TEST(test_constructor_with_size_and_value) + { + Buffer_t buffer1; + const Ndc value("value"); + Data data(5U, value, buffer1.raw, SIZE); + + CHECK_EQUAL(5U, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [&value](const Ndc& item) { return item == value; })); + } + + //************************************************************************* + TEST(test_constructor_with_excess_size_and_value) + { + Buffer_t buffer1; + const Ndc value("value"); + Data data(SIZE + 3U, value, buffer1.raw, SIZE); + + CHECK(data.full()); + CHECK_EQUAL(SIZE, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [&value](const Ndc& item) { return item == value; })); + } + + //************************************************************************* + TEST(test_assign_size_and_value) + { + Buffer_t buffer1; + Data data(buffer1.raw, SIZE); + data.push(Ndc("old")); + + const Ndc value("value"); + data.assign(5U, value); + + CHECK_EQUAL(5U, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [&value](const Ndc& item) { return item == value; })); + + data.assign(0U, value); + CHECK(data.empty()); + + data.assign(SIZE + 3U, value); + CHECK(data.full()); + CHECK_EQUAL(SIZE, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [&value](const Ndc& item) { return item == value; })); + } + + //************************************************************************* + TEST(test_assign_range) + { + Buffer_t buffer1; + Compare input{Ndc("0"), Ndc("1"), Ndc("2"), Ndc("3"), Ndc("4"), Ndc("5"), Ndc("6"), + Ndc("7"), Ndc("8"), Ndc("9"), Ndc("10"), Ndc("11"), Ndc("12")}; + Compare expected(input.end() - SIZE, input.end()); + Data data(buffer1.raw, SIZE); + data.push(Ndc("old")); + + data.assign(input.begin(), input.end()); + + CHECK(data.full()); + CHECK_EQUAL(expected.size(), data.size()); + CHECK(std::equal(expected.begin(), expected.end(), data.begin())); + + data.assign(input.begin(), input.begin()); + CHECK(data.empty()); + } + + //************************************************************************* + TEST(test_assign_self_range) + { + Buffer_t buffer1; + Compare input{Ndc("0"), Ndc("1"), Ndc("2"), Ndc("3"), Ndc("4"), Ndc("5"), Ndc("6"), + Ndc("7"), Ndc("8"), Ndc("9"), Ndc("10"), Ndc("11"), Ndc("12")}; + Compare expected(input.end() - SIZE, input.end()); + Data data(input.begin(), input.end(), buffer1.raw, SIZE); + + data.assign(data.begin(), data.end()); + + CHECK(data.full()); + CHECK_EQUAL(expected.size(), data.size()); + CHECK(std::equal(expected.begin(), expected.end(), data.begin())); + + Data::const_iterator first = data.cbegin() + 2; + Data::const_iterator last = data.cend() - 2; + Compare partial_expected(expected.begin() + 2, expected.end() - 2); + + data.assign(first, last); + + CHECK_EQUAL(partial_expected.size(), data.size()); + CHECK(std::equal(partial_expected.begin(), partial_expected.end(), data.begin())); + } + + //************************************************************************* + TEST(test_assign_aliased_value) + { + Buffer_t buffer1; + Compare input{Ndc("0"), Ndc("1"), Ndc("2"), Ndc("3"), Ndc("4")}; + Data data(input.begin(), input.end(), buffer1.raw, SIZE); + + const Ndc expected(data.front()); + data.assign(7U, data.front()); + + CHECK_EQUAL(7U, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [&expected](const Ndc& item) { return item == expected; })); + } + //************************************************************************* TEST(test_set_buffer_after_default_constructor) { From 82e41547bb0e99e7cd4ce8ec85023e687487a7b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BCthing?= Date: Sun, 23 Aug 2026 19:47:28 +0200 Subject: [PATCH 42/77] Make etl_profile Incompatible by Default (#1535) * Make etl_profile Incompatible by Default * Configure etl_profile for CI Execution of Tests * Update _index.md * Update _index.md * Cherry pick the Docker fixes for PowerPC (#1563) Co-authored-by: John Wellbelove --------- Co-authored-by: John Wellbelove Co-authored-by: Roland Reichwein Co-authored-by: John Wellbelove --- .github/workflows/bazel-gcc-c++23-no-stl.yml | 4 ++-- BUILD.bazel | 13 +++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/bazel-gcc-c++23-no-stl.yml b/.github/workflows/bazel-gcc-c++23-no-stl.yml index 648e0bcb..1521793d 100644 --- a/.github/workflows/bazel-gcc-c++23-no-stl.yml +++ b/.github/workflows/bazel-gcc-c++23-no-stl.yml @@ -44,7 +44,7 @@ jobs: - uses: actions/checkout@v6.0.3 - name: Build - run: bazel build //test:etl_tests --cxxopt=-std=c++23 --copt=-DETL_NO_STL + run: bazel build //test:etl_tests --cxxopt=-std=c++23 --copt=-DETL_NO_STL --//:etl_profile=//:no_profile - name: Run tests - run: bazel test //test:etl_tests --cxxopt=-std=c++23 --copt=-DETL_NO_STL --test_output=all + run: bazel test //test:etl_tests --cxxopt=-std=c++23 --copt=-DETL_NO_STL --//:etl_profile=//:no_profile --test_output=all diff --git a/BUILD.bazel b/BUILD.bazel index 59ea5184..83d337ac 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -20,14 +20,23 @@ package(default_visibility = ["//visibility:public"]) # strip_include_prefix = ".", # ) # -# The default is empty, so ETL's built-in defaults are used unless overridden. +# The default of this label_flag is incompatible. This makes sure that the consumer +# explicitly sets a profile, even if it is the `no_profile` defined below. + label_flag( name = "etl_profile", - build_setting_default = ":no_profile", + build_setting_default = ":incompatible_profile", ) cc_library(name = "no_profile") +cc_library( + name = "incompatible_profile", + target_compatible_with = [ + "@platforms//:incompatible", + ], +) + cc_library( name = "etl", hdrs = glob(["include/**/*.h"]), From 294b788942b5f53857f1769342e7899ee5c6201d Mon Sep 17 00:00:00 2001 From: myrkle11 <54760496+myrkle11@users.noreply.github.com> Date: Sun, 23 Aug 2026 15:30:39 -0700 Subject: [PATCH 43/77] Update etl::expected documentation (#1564) * Update _index.md * Update _index.md * Cherry pick the Docker fixes for PowerPC (#1563) Co-authored-by: John Wellbelove * Update expected documentation to reflect the current state of the code * Fix code blocks for expected class section * Change wording slightly * make changes ala coderabbitai * Fixed typos --------- Co-authored-by: John Wellbelove Co-authored-by: John Wellbelove --- docs/utilities/expected.md | 446 ++++++++++++++++++++++++++++++++++++- 1 file changed, 438 insertions(+), 8 deletions(-) diff --git a/docs/utilities/expected.md b/docs/utilities/expected.md index f6cbcd63..8443a782 100644 --- a/docs/utilities/expected.md +++ b/docs/utilities/expected.md @@ -192,13 +192,13 @@ Swap with another etl::unexpected ```cpp template class expected -```cpp +``` Specialisation for void value type. ```cpp template class expected -```cpp +``` ### Types @@ -421,6 +421,7 @@ value_type& value() const Get the value. Undefined if has_value() returns false. Not valid for void specialisation. +Before: C++11 --- @@ -536,13 +537,46 @@ Before: C++11 --- +```cpp +template +ETL_NODISCARD +ETL_CONSTEXPR14 +error_type error_or(G&& default_error) const& +``` +**Description** +Returns default_error if has_value() returns true; otherwise returns the stored error. +Since: C++11 + +--- + +```cpp +template +ETL_NODISCARD +ETL_CONSTEXPR14 +error_type error_or(G&& default_error)&& +``` +**Description** +Returns default_error if has_value() returns true; otherwise returns the stored error. +Since: C++11 + +--- + +```cpp +template +error_type error_or(const G& default_error) const +``` +**Description** +Returns default_error if has_value() returns true; otherwise returns the stored error. +Before: C++11 + +--- + ```cpp template ETL_CONSTEXPR14 value_type& emplace(TArgs&&... args) ETL_NOEXCEPT ``` **Description** -Returns the error if has_value() returns false. -Undefined if has_value() returns true. +Create from arguments. Since: C++11 --- @@ -574,15 +608,411 @@ Const class member access operator. --- ```cpp -value_type& operator *() +value_type& operator *() & ``` -**Description** +**Description** Dereference operator. --- ```cpp -const value_type& operator *() const +const value_type& operator *() const& ``` -**Description** +**Description** Dereference operator. + +--- + +```cpp +value_type&& operator *() && +``` +**Description** +Dereference operator. +Since: C++11 + +--- + +```cpp +const value_type&& operator *() const&& +``` +**Description** +Dereference operator. +Since: C++11 + +--- + +```cpp +ETL_NODISCARD +ETL_CONSTEXPR14 +value_type* begin() ETL_NOEXCEPT +``` +**Description** +Returns a pointer to the value if has_value() returns true, otherwise returns nullptr. +Allows expected to be used as a range of 0 or 1 elements. Not valid for void specialisation. + +--- + +```cpp +ETL_NODISCARD +ETL_CONSTEXPR14 +const value_type* begin() const ETL_NOEXCEPT +``` +**Description** +Returns a pointer to the value if has_value() returns true, otherwise returns nullptr. Not valid for void specialisation. + +--- + +```cpp +ETL_NODISCARD +ETL_CONSTEXPR14 +value_type* end() ETL_NOEXCEPT +``` +**Description** +Returns a pointer past the value if has_value() returns true, otherwise returns nullptr. Not valid for void specialisation. + +--- + +```cpp +ETL_NODISCARD +ETL_CONSTEXPR14 +const value_type* end() const ETL_NOEXCEPT +``` +**Description** +Returns a pointer past the value if has_value() returns true, otherwise returns nullptr. Not valid for void specialisation. + +--- + +```cpp +template ::type>::type> +auto transform(F&& f) & -> expected +``` +**Description** +If has_value() returns true, it invokes f on the value and returns the result wrapped in an expected object; otherwise it returns an expected object that contains error(). For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform(F&& f) & -> expected +``` +**Description** +For void specialisation. +If has_value() returns true, it invokes f and returns the result wrapped in an expected object; otherwise it returns an expected object that contains error(). +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform(F&& f) const& -> expected +``` +**Description** +If has_value() returns true, it invokes f on the value and returns the result wrapped in an expected object; otherwise it returns an expected object that contains error(). For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform(F&& f) const& -> expected +``` +**Description** +For void specialisation. +If has_value() returns true, it invokes f and returns the result wrapped in an expected object; otherwise it returns an expected object that contains error(). +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform(F&& f) && -> expected +``` +**Description** +If has_value() returns true, it invokes f on the value and returns the result wrapped in an expected object; otherwise it returns an expected object that contains error(). For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform(F&& f) && -> expected +``` +**Description** +For void specialisation. +If has_value() returns true, it invokes f and returns the result wrapped in an expected object; otherwise it returns an expected object that contains error(). +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform(F&& f) const&& -> expected +``` +**Description** +If has_value() returns true, it invokes f and returns the result wrapped in an expected object; otherwise it returns an expected object that contains error(). For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform(F&& f) const&& -> expected +``` +**Description** +For void specialisation. +If has_value() returns true, it invokes f and returns the result wrapped in an expected object; otherwise it returns an expected object that contains error(). +Since: C++11 + +--- + +```cpp +template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> +auto and_then(F&& f) & -> U +``` +**Description** +If has_value() returns true, it invokes f on the value and returns the result; otherwise it returns an expected object that contains error(). For void specialisation see below. +Since: C++11 + +--- + +```cpp +template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type> +auto and_then(F&& f) & -> U +``` +**Description** +For void specialisation. +If has_value() returns true, it invokes f and returns the result; otherwise it returns an expected object that contains error(). +Since: C++11 + +--- + +```cpp +template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> +auto and_then(F&& f) const& -> U +``` +**Description** +If has_value() returns true, it invokes f on the value and returns the result; otherwise it returns an expected object that contains error(). For void specialisation see below. +Since: C++11 + +--- + +```cpp +template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> +auto and_then(F&& f) const& -> U +``` +**Description** +For void specialisation. +If has_value() returns true, it invokes f and returns the result; otherwise it returns an expected object that contains error(). +Since: C++11 + +--- + +```cpp +template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> +auto and_then(F&& f) && -> U +``` +**Description** +If has_value() returns true, it invokes f on the value and returns the result; otherwise it returns an expected object that contains error(). For void specialisation see below. +Since: C++11 + +--- + +```cpp +template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> +auto and_then(F&& f) && -> U +``` +**Description** +For void specialisation. +If has_value() returns true, it invokes f and returns the result; otherwise it returns an expected object that contains error(). +Since: C++11 + +--- + +```cpp +template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> +auto and_then(F&& f) const&& -> U +``` +**Description** +If has_value() returns true, it invokes f on the value and returns the result; otherwise it returns an expected object that contains error(). For void specialisation see below. +Since: C++11 + +--- + +```cpp +template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> +auto and_then(F&& f) const&& -> U +``` +**Description** +For void specialisation. +If has_value() returns true, it invokes f and returns the result; otherwise it returns an expected object that contains error(). +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto or_else(F&& f) & -> U +``` +**Description** +If has_value() returns true, it returns an expected object that contains value(); otherwise it invokes f on error() and returns the result. For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto or_else(F&& f) & -> U +``` +**Description** +For void specialisation. +If has_value() returns true, it returns a default constructed expected object of the appropriate type; otherwise it invokes f on error() and returns the result. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto or_else(F&& f) const& -> U +``` +**Description** +If has_value() returns true, it returns an expected object that contains value(); otherwise it invokes f on error() and returns the result. For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto or_else(F&& f) const& -> U +``` +**Description** +For void specialisation. +If has_value() returns true, it returns a default constructed expected object of the appropriate type; otherwise it invokes f on error() and returns the result. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto or_else(F&& f) && -> U +``` +**Description** +If has_value() returns true, it returns an expected object that contains value(); otherwise it invokes f on error() and returns the result. For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto or_else(F&& f) && -> U +``` +**Description** +For void specialisation. +If has_value() returns true, it returns a default constructed expected object of the appropriate type; otherwise it invokes f on error() and returns the result. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto or_else(F&& f) const&& -> U +``` +**Description** +If has_value() returns true, it returns an expected object that contains value(); otherwise it invokes f on error() and returns the result. For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto or_else(F&& f) const&& -> U +``` +**Description** +For void specialisation. +If has_value() returns true, it returns a default constructed expected object of the appropriate type; otherwise it invokes f on error() and returns the result. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform_error(F&& f) & -> expected +``` +**Description** +If has_value() returns true, it returns an expected that contains value(); otherwise it invokes f on error() and returns the result wrapped in an expected object. For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform_error(F&& f) & -> expected +``` +**Description** +For void specialisation. +If has_value() returns true, it returns a default constructed expected object of the appropriate type; otherwise it invokes f on error() and returns the result wrapped in an expected object. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform_error(F&& f) const& -> expected +``` +**Description** +If has_value() returns true, it returns an expected that contains value(); otherwise it invokes f on error() and returns the result wrapped in an expected object. For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform_error(F&& f) const& -> expected +``` +**Description** +For void specialisation. +If has_value() returns true, it returns a default constructed expected object of the appropriate type; otherwise it invokes f on error() and returns the result wrapped in an expected object. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform_error(F&& f) && -> expected +``` +**Description** +If has_value() returns true, it returns an expected that contains value(); otherwise it invokes f on error() and returns the result wrapped in an expected object. For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform_error(F&& f) && -> expected +``` +**Description** +For void specialisation. +If has_value() returns true, it returns a default constructed expected object of the appropriate type; otherwise it invokes f on error() and returns the result wrapped in an expected object. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform_error(F&& f) const&& -> expected +``` +**Description** +If has_value() returns true, it returns an expected that contains value(); otherwise it invokes f on error() and returns the result wrapped in an expected object. For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform_error(F&& f) const&& -> expected +``` +**Description** +For void specialisation. +If has_value() returns true, it returns a default constructed expected object of the appropriate type; otherwise it invokes f on error() and returns the result wrapped in an expected object. +Since: C++11 + From 25e2d6e8a3fd3ed6337c33be20ec41a06fb3743f Mon Sep 17 00:00:00 2001 From: rleigh-electradx Date: Mon, 24 Aug 2026 08:53:25 +0100 Subject: [PATCH 44/77] Adopt the Library Fundamentals TS `make_array` design for the sequence factories (#1553) * Adopt the Library Fundamentals TS make_array design for the sequence factories make_array, make_deque, make_vector, make_list, make_forward_list and make_indirect_vector now follow the Library Fundamentals TS std::experimental::make_array design: a defaulted leading template parameter selects the element type explicitly, falling back to the decayed common type of the arguments when it is not supplied. A shared trait (etl::private_make::element_type) performs the selection lazily, so common_type is not instantiated when the element type is explicit. This fixes the explicit-element-type calling style for the four common-type factories, where make_vector(lvalue) never compiled: the explicit argument pinned the first pack parameter to char, making it a char&& that no lvalue can bind to. It also gives make_array and make_deque the deduced form (e.g. make_array(1, 2L) is array) for free. All arguments are converted with static_cast, as in the make_array const-lvalue fix, so const-qualified lvalues and narrowing initialisers work in both forms. Intentional behaviour change: an explicit element type now converts every argument instead of pinning only the first. make_vector(0, 1, ...) previously produced vector - the explicit char affected only the first argument and the element type was still the common type - and now produces vector. test_make_vector documented the old behaviour and has been updated. The keyed set and map factories are unchanged: their key/mapped types are separate mandatory template parameters ahead of a fully-deduced pack, so the explicit-type style already works, and retrofitting deduction there would interact with the defaulted comparator/hash parameters. Adds explicit-element-type tests for the four common-type factories (const/non-const lvalues plus narrowing initialisers) and deduced-element-type tests for make_array and make_deque, and updates the four documented signatures. * Address review comments on the make_xxx factory design - Forward same-type arguments in the make_xxx factories instead of casting them. static_cast produced a prvalue that, before C++17, had to be materialised through the move constructor, rejecting copyable-but-non-movable element types. The new private_make::convert helper forwards arguments that already have the element type and static_casts the rest. - Drop ETL_NOEXCEPT from make_array: conversions can throw, and the other factories do not declare it. - Remove the redundant etl::decay in element_type; etl::common_type already decays its arguments. - Stop leaking the etl::private_make implementation namespace into the documented factory signatures. * Add a test for make_array with an explicit element type and no arguments etl::make_array() produces an empty etl::array and now has a regression test. A call with neither an element type nor arguments remains ill-formed, matching the Library Fundamentals TS: there is nothing to deduce the element type from. The array documentation now states this explicitly. --------- Co-authored-by: John Wellbelove Co-authored-by: Roland Reichwein --- docs/containers/arrays/array.md | 16 +++++- docs/containers/lists/forward-list.md | 11 +++- docs/containers/lists/list.md | 11 +++- docs/containers/queues & stacks/deque.md | 13 ++++- docs/containers/vectors/indirect-vector.md | 20 +++++++ docs/containers/vectors/vector.md | 19 ++++++ include/etl/array.h | 19 +++--- include/etl/deque.h | 19 +++--- include/etl/forward_list.h | 10 +++- include/etl/indirect_vector.h | 11 +++- include/etl/list.h | 10 +++- include/etl/type_traits.h | 40 +++++++++++++ include/etl/vector.h | 10 +++- test/test_array.cpp | 67 ++++++++++++++++++++++ test/test_deque.cpp | 19 ++++++ test/test_forward_list.cpp | 24 ++++++++ test/test_indirect_vector.cpp | 23 ++++++++ test/test_list.cpp | 24 ++++++++ test/test_vector.cpp | 29 +++++++++- 19 files changed, 352 insertions(+), 43 deletions(-) diff --git a/docs/containers/arrays/array.md b/docs/containers/arrays/array.md index fcf8af32..96e58195 100644 --- a/docs/containers/arrays/array.md +++ b/docs/containers/arrays/array.md @@ -38,13 +38,23 @@ Defines data as an array of 'int', of length '10', containing the supplied data. **C++11 and above** ```cpp -template -constexpr auto make_array(TValues&&... values) -> etl::array +template +constexpr auto make_array(TValues&&... values) -> etl::array ``` +The element type is `T` when supplied, otherwise the decayed common type of +the arguments (the Library Fundamentals TS `make_array` design). An empty +array requires the element type to be supplied explicitly, e.g. +`etl::make_array()`; a call with neither an element type nor arguments +is ill-formed, as there is nothing to deduce the element type from. + ### Example ```cpp -auto data = etl::make_array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); +// Deduced element type: etl::array. +auto data = etl::make_array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + +// Explicit element type: etl::array. Each argument is converted. +auto chars = etl::make_array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); ``` ## Member types diff --git a/docs/containers/lists/forward-list.md b/docs/containers/lists/forward-list.md index 442d6b51..f51acc9e 100644 --- a/docs/containers/lists/forward-list.md +++ b/docs/containers/lists/forward-list.md @@ -65,15 +65,20 @@ 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(T&&... t) -> etl::forward_list, sizeof...(T)> +template +constexpr auto make_forward_list(TValues&&... values) -> etl::forward_list ``` -The element type is deduced as the common type of the arguments. +The element type is `T` when supplied, otherwise the decayed common type of +the arguments (the Library Fundamentals TS `make_array` design). ### Example ```cpp +// Deduced element type: etl::forward_list. auto data = etl::make_forward_list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + +// Explicit element type: etl::forward_list. Each argument is converted. +auto chars = etl::make_forward_list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); ``` ## Member types diff --git a/docs/containers/lists/list.md b/docs/containers/lists/list.md index 0ae64e39..598b900b 100644 --- a/docs/containers/lists/list.md +++ b/docs/containers/lists/list.md @@ -69,15 +69,20 @@ 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(T&&... t) -> etl::list, sizeof...(T)> +template +constexpr auto make_list(TValues&&... values) -> etl::list ``` -The element type is deduced as the common type of the arguments. +The element type is `T` when supplied, otherwise the decayed common type of +the arguments (the Library Fundamentals TS `make_array` design). ### Example ```cpp +// Deduced element type: etl::list. auto data = etl::make_list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + +// Explicit element type: etl::list. Each argument is converted. +auto chars = etl::make_list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); ``` ## Member types diff --git a/docs/containers/queues & stacks/deque.md b/docs/containers/queues & stacks/deque.md index f326e308..b0bb1ad1 100644 --- a/docs/containers/queues & stacks/deque.md +++ b/docs/containers/queues & stacks/deque.md @@ -34,13 +34,20 @@ Defines data as an `deque` of `int`, of length `10`, containing the supplied dat ## Make template **C++11 and above** ```cpp -template -constexpr auto make_deque(TValues&&... values) -> etl::deque +template +constexpr auto make_deque(TValues&&... values) -> etl::deque ``` +The element type is `T` when supplied, otherwise the decayed common type of +the arguments (the Library Fundamentals TS `make_array` design). + ### Example ```cpp -auto data = etl::make_deque(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); +// Deduced element type: etl::deque. +auto data = etl::make_deque(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + +// Explicit element type: etl::deque. Each argument is converted. +auto chars = etl::make_deque(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); ``` ## Member types diff --git a/docs/containers/vectors/indirect-vector.md b/docs/containers/vectors/indirect-vector.md index 2588f453..b1e0766a 100644 --- a/docs/containers/vectors/indirect-vector.md +++ b/docs/containers/vectors/indirect-vector.md @@ -42,6 +42,26 @@ etl::indirect_vector data{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; ``` Defines data as an `indirect_vector` of `int`, of length `10`, containing the supplied data. +## Make template +**C++11 and above** +```cpp +template +constexpr auto make_indirect_vector(TValues&&... values) + -> etl::indirect_vector +``` + +The element type is `T` when supplied, otherwise the decayed common type of +the arguments (the Library Fundamentals TS `make_array` design). + +### Example +```cpp +// Deduced element type: etl::indirect_vector. +auto data = etl::make_indirect_vector(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + +// Explicit element type: etl::indirect_vector. Each argument is converted. +auto chars = etl::make_indirect_vector(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); +``` + ## Member types ```cpp diff --git a/docs/containers/vectors/vector.md b/docs/containers/vectors/vector.md index 216169a7..5b47dff1 100644 --- a/docs/containers/vectors/vector.md +++ b/docs/containers/vectors/vector.md @@ -54,6 +54,25 @@ etl::vector data{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; Defines data as an vector of int, of length 10, containing the supplied data. +## Make template +**C++11 and above** +```cpp +template +constexpr auto make_vector(TValues&&... values) -> etl::vector +``` + +The element type is `T` when supplied, otherwise the decayed common type of +the arguments (the Library Fundamentals TS `make_array` design). + +### Example +```cpp +// Deduced element type: etl::vector. +auto data = etl::make_vector(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + +// Explicit element type: etl::vector. Each argument is converted. +auto chars = etl::make_vector(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); +``` + ## Member types ```cpp diff --git a/include/etl/array.h b/include/etl/array.h index 1bb92cea..349e7711 100644 --- a/include/etl/array.h +++ b/include/etl/array.h @@ -1053,16 +1053,17 @@ namespace etl /// Make //************************************************************************* #if ETL_HAS_INITIALIZER_LIST - template - constexpr auto make_array(TValues&&... values) ETL_NOEXCEPT -> etl::array + template + constexpr auto make_array(TValues&&... values) -> etl::array, sizeof...(TValues)> { - // 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))...}; + // Library Fundamentals TS make_array design: the element type is T when + // supplied explicitly, otherwise the decayed common type of the arguments. + // convert forwards arguments that already have the element type and + // converts the rest with static_cast, so const-qualified lvalues, + // narrowing initialisers such as make_array(0, 1) and non-movable + // element types all work. + using TElement = etl::private_make::element_type_t; + return {etl::private_make::convert(etl::forward(values))...}; } #endif diff --git a/include/etl/deque.h b/include/etl/deque.h index a35eda34..ffa689a9 100644 --- a/include/etl/deque.h +++ b/include/etl/deque.h @@ -2532,16 +2532,17 @@ namespace etl /// Make //************************************************************************* #if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST - template - constexpr auto make_deque(TValues&&... values) -> etl::deque + template + constexpr auto make_deque(TValues&&... values) -> etl::deque, sizeof...(TValues)> { - // 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))...}; + // Library Fundamentals TS make_array design: the element type is T when + // supplied explicitly, otherwise the decayed common type of the arguments. + // convert forwards arguments that already have the element type and + // converts the rest with static_cast, so const-qualified lvalues, + // narrowing initialisers such as make_deque(0, 1) and non-movable + // element types all work. + using TElement = etl::private_make::element_type_t; + return {etl::private_make::convert(etl::forward(values))...}; } #endif diff --git a/include/etl/forward_list.h b/include/etl/forward_list.h index cfba7bf9..f46ec9f5 100644 --- a/include/etl/forward_list.h +++ b/include/etl/forward_list.h @@ -1793,10 +1793,14 @@ namespace etl /// Make //************************************************************************* #if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST - template - constexpr auto make_forward_list(T&&... t) -> etl::forward_list, sizeof...(T)> + template + constexpr auto make_forward_list(TValues&&... values) -> etl::forward_list, sizeof...(TValues)> { - return {etl::forward(t)...}; + // Library Fundamentals TS make_array design: the element type is T when + // supplied explicitly, otherwise the decayed common type of the arguments. + // convert forwards same-type arguments and static_casts the rest. + using TElement = etl::private_make::element_type_t; + return {etl::private_make::convert(etl::forward(values))...}; } #endif diff --git a/include/etl/indirect_vector.h b/include/etl/indirect_vector.h index 0f0849c9..2fcac9ba 100644 --- a/include/etl/indirect_vector.h +++ b/include/etl/indirect_vector.h @@ -1551,10 +1551,15 @@ namespace etl /// Make //************************************************************************* #if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST - template - constexpr auto make_indirect_vector(T&&... t) -> etl::indirect_vector, sizeof...(T)> + template + constexpr auto + make_indirect_vector(TValues&&... values) -> etl::indirect_vector, sizeof...(TValues)> { - return {etl::forward(t)...}; + // Library Fundamentals TS make_array design: the element type is T when + // supplied explicitly, otherwise the decayed common type of the arguments. + // convert forwards same-type arguments and static_casts the rest. + using TElement = etl::private_make::element_type_t; + return {etl::private_make::convert(etl::forward(values))...}; } #endif diff --git a/include/etl/list.h b/include/etl/list.h index 7be34fcc..433aebef 100644 --- a/include/etl/list.h +++ b/include/etl/list.h @@ -2218,10 +2218,14 @@ namespace etl /// Make //************************************************************************* #if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST - template - constexpr auto make_list(T&&... t) -> etl::list, sizeof...(T)> + template + constexpr auto make_list(TValues&&... values) -> etl::list, sizeof...(TValues)> { - return {etl::forward(t)...}; + // Library Fundamentals TS make_array design: the element type is T when + // supplied explicitly, otherwise the decayed common type of the arguments. + // convert forwards same-type arguments and static_casts the rest. + using TElement = etl::private_make::element_type_t; + return {etl::private_make::convert(etl::forward(values))...}; } #endif diff --git a/include/etl/type_traits.h b/include/etl/type_traits.h index 6ecf9451..ab8d13d7 100644 --- a/include/etl/type_traits.h +++ b/include/etl/type_traits.h @@ -3751,6 +3751,46 @@ namespace etl template using common_type_t = typename common_type::type; + + //*********************************** + // Implementation details of the make_xxx container factories. + namespace private_make + { + // Selects the element type, following the Library Fundamentals TS + // make_array design: TDesired if it was supplied explicitly, otherwise the + // common type of the arguments (common_type decays). The specialisation + // keeps common_type uninstantiated when TDesired is supplied, so arguments + // with no common type are still accepted in that case. + template + struct element_type + { + using type = TDesired; + }; + + template + struct element_type : etl::common_type + { + }; + + template + using element_type_t = typename element_type::type; + + // Converts a factory argument to the element type. An argument that + // already has the element type is forwarded unchanged; any other argument + // is converted with static_cast, which keeps narrowing initialisers such + // as make_array(0, 1) working. Forwarding rather than casting in the + // same-type case avoids materialising a temporary, which before C++17 + // requires an accessible copy or move constructor even though the copy is + // elided, and would reject copyable-but-non-movable element types. + template + using convert_type_t = typename etl::conditional::type, TElement>::value, TValue&&, TElement>::type; + + template + constexpr convert_type_t convert(TValue&& value) + { + return static_cast>(value); + } + } // namespace private_make #endif //*************************************************************************** diff --git a/include/etl/vector.h b/include/etl/vector.h index 2c5b3713..beac6cdc 100644 --- a/include/etl/vector.h +++ b/include/etl/vector.h @@ -1999,10 +1999,14 @@ namespace etl /// Make //************************************************************************* #if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST - template - constexpr auto make_vector(T&&... t) -> etl::vector, sizeof...(T)> + template + constexpr auto make_vector(TValues&&... values) -> etl::vector, sizeof...(TValues)> { - return {etl::forward(t)...}; + // Library Fundamentals TS make_array design: the element type is T when + // supplied explicitly, otherwise the decayed common type of the arguments. + // convert forwards same-type arguments and static_casts the rest. + using TElement = etl::private_make::element_type_t; + return {etl::private_make::convert(etl::forward(values))...}; } #endif diff --git a/test/test_array.cpp b/test/test_array.cpp index 8c8fa168..b7d9162e 100644 --- a/test/test_array.cpp +++ b/test/test_array.cpp @@ -997,6 +997,73 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // With no explicit element type, the element type is deduced as the + // decayed common type of the arguments (Library Fundamentals TS + // make_array design). + TEST(test_make_array_deduced_element_type) + { + static const int static_const_lvalue = 1; + + auto data = etl::make_array(static_const_lvalue, 2L, 3); + + CHECK((std::is_same, decltype(data)>::value)); + + CHECK_EQUAL(1L, data[0]); + CHECK_EQUAL(2L, data[1]); + CHECK_EQUAL(3L, data[2]); + } +#endif + + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // An empty array requires the element type to be supplied explicitly. + // etl::make_array() with no element type and no arguments is ill-formed, + // as in the Library Fundamentals TS: there is nothing to deduce the + // element type from. + TEST(test_make_array_empty) + { + auto data = etl::make_array(); + + CHECK((std::is_same, decltype(data)>::value)); + CHECK_TRUE(data.empty()); + CHECK_EQUAL(0U, data.size()); + } +#endif + + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that already have the element type are forwarded rather than + // cast. Casting created a prvalue that, before C++17, had to be + // materialised through the move constructor, so a copyable type with a + // deleted move constructor was rejected. + TEST(test_make_array_for_copyable_non_movable) + { + struct NonMovable + { + explicit NonMovable(int v_) + : v(v_) + { + } + NonMovable(const NonMovable&) = default; + NonMovable(NonMovable&&) = delete; + int v; + }; + + NonMovable mutable_lvalue(1); + const NonMovable const_lvalue(2); + + auto data = etl::make_array(mutable_lvalue, const_lvalue); + + using Type = etl::remove_reference_t; + CHECK((std::is_same::value)); + + CHECK_EQUAL(1, data[0].v); + CHECK_EQUAL(2, data[1].v); + } +#endif + //************************************************************************* #if ETL_HAS_INITIALIZER_LIST && ETL_USING_CPP14 TEST(test_make_array_is_constexpr) diff --git a/test/test_deque.cpp b/test/test_deque.cpp index 85365284..796b21ac 100644 --- a/test/test_deque.cpp +++ b/test/test_deque.cpp @@ -2346,6 +2346,25 @@ namespace } #endif + //************************************************************************* +#if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST + // With no explicit element type, the element type is deduced as the + // decayed common type of the arguments (Library Fundamentals TS + // make_array design). + TEST(test_make_deque_deduced_element_type) + { + static const int static_const_lvalue = 1; + + auto data = etl::make_deque(static_const_lvalue, 2L, 3); + + CHECK((std::is_same, decltype(data)>::value)); + + CHECK_EQUAL(1L, data[0]); + CHECK_EQUAL(2L, data[1]); + CHECK_EQUAL(3L, data[2]); + } +#endif + //************************************************************************* TEST(test_fill) { diff --git a/test/test_forward_list.cpp b/test/test_forward_list.cpp index 8687bf82..65d9eb81 100644 --- a/test/test_forward_list.cpp +++ b/test/test_forward_list.cpp @@ -1556,6 +1556,30 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // An explicit element type selects the element type and converts every + // argument (Library Fundamentals TS make_array design), accepting + // const-qualified lvalues and narrowing initialisers. + TEST(test_make_forward_list_explicit_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, 45, 46); + + CHECK((std::is_same, decltype(data)>::value)); + + auto itr = data.begin(); + CHECK_EQUAL(42, *itr++); + CHECK_EQUAL(43, *itr++); + CHECK_EQUAL(44, *itr++); + CHECK_EQUAL(45, *itr++); + CHECK_EQUAL(46, *itr++); + } +#endif + //************************************************************************* TEST(test_forward_list_exception_types) { diff --git a/test/test_indirect_vector.cpp b/test/test_indirect_vector.cpp index 2d8033cd..f9973441 100644 --- a/test/test_indirect_vector.cpp +++ b/test/test_indirect_vector.cpp @@ -1644,5 +1644,28 @@ namespace CHECK_EQUAL(44, data[2]); } #endif + + //************************************************************************* +#if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST + // An explicit element type selects the element type and converts every + // argument (Library Fundamentals TS make_array design), accepting + // const-qualified lvalues and narrowing initialisers. + TEST(test_make_indirect_vector_explicit_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, 45, 46); + + CHECK((std::is_same, decltype(data)>::value)); + + CHECK_EQUAL(42, data[0]); + CHECK_EQUAL(43, data[1]); + CHECK_EQUAL(44, data[2]); + CHECK_EQUAL(45, data[3]); + CHECK_EQUAL(46, data[4]); + } +#endif } } // namespace diff --git a/test/test_list.cpp b/test/test_list.cpp index 767166e7..45e0d276 100644 --- a/test/test_list.cpp +++ b/test/test_list.cpp @@ -2377,5 +2377,29 @@ namespace CHECK_EQUAL(3, data2.begin()->value); } #endif + + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // An explicit element type selects the element type and converts every + // argument (Library Fundamentals TS make_array design), accepting + // const-qualified lvalues and narrowing initialisers. + TEST(test_make_list_explicit_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, 45, 46); + + CHECK((std::is_same, decltype(data)>::value)); + + auto itr = data.begin(); + CHECK_EQUAL(42, *itr++); + CHECK_EQUAL(43, *itr++); + CHECK_EQUAL(44, *itr++); + CHECK_EQUAL(45, *itr++); + CHECK_EQUAL(46, *itr++); + } +#endif } } // namespace diff --git a/test/test_vector.cpp b/test/test_vector.cpp index 0340c4cb..32799337 100644 --- a/test/test_vector.cpp +++ b/test/test_vector.cpp @@ -1531,8 +1531,12 @@ namespace { auto data = etl::make_vector(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + // The explicit element type now selects the element type for all + // arguments (Library Fundamentals TS make_array design). Previously it + // pinned only the first argument's deduced type, and the element type + // was still the common type of the arguments - int for this call. using Type = typename std::remove_reference::type; - CHECK((std::is_same::value)); + CHECK((std::is_same::value)); CHECK_EQUAL(0, data[0]); CHECK_EQUAL(1, data[1]); @@ -1590,6 +1594,29 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // An explicit element type selects the element type and converts every + // argument (Library Fundamentals TS make_array design), accepting + // const-qualified lvalues and narrowing initialisers. + TEST(test_make_vector_explicit_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, 45, 46); + + CHECK((std::is_same, decltype(data)>::value)); + + CHECK_EQUAL(42, data[0]); + CHECK_EQUAL(43, data[1]); + CHECK_EQUAL(44, data[2]); + CHECK_EQUAL(45, data[3]); + CHECK_EQUAL(46, data[4]); + } +#endif + //************************************************************************* TEST(test_fill) { From fa3ac28f555b1b9b7405045ca030c2b6a98c4b8b Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Mon, 24 Aug 2026 14:11:11 +0200 Subject: [PATCH 45/77] Add etl::is_standard_layout and other type traits according to std (#1545) * Update _index.md * Update _index.md * Cherry pick the Docker fixes for PowerPC (#1563) Co-authored-by: John Wellbelove * Add etl::is_standard_layout and further type traits Added type traits: * etl::is_standard_layout * etl::is_null_pointer * etl::is_union * etl::is_scalar * etl::is_empty * etl::is_polymorphic * etl::is_abstract * etl::is_final * etl::is_aggregate * etl::is_destructible * etl::is_nothrow_destructible * etl::has_virtual_destructor * etl::is_swappable * etl::is_swappable_with * etl::is_nothrow_swappable * etl::is_nothrow_swappable_with * etl::has_unique_object_representation * etl::is_bounded_array * etl::is_unbounded_array * etl::is_trivial Document etl::is_pod Note that etl::swap needed adjustment to enable nothrow detection via SFINAE constraints. This change closes in to the standard. * Fix preprocessor directive formatting in header --------- Co-authored-by: John Wellbelove Co-authored-by: John Wellbelove --- docs/types/type-traits.md | 399 +++++++ .../etl/profiles/determine_builtin_support.h | 184 +++ include/etl/type_traits.h | 823 +++++++++++++ include/etl/utility.h | 27 +- test/test_type_traits.cpp | 1025 +++++++++++++++++ 5 files changed, 2457 insertions(+), 1 deletion(-) diff --git a/docs/types/type-traits.md b/docs/types/type-traits.md index 721fdfc3..98326be6 100644 --- a/docs/types/type-traits.md +++ b/docs/types/type-traits.md @@ -32,6 +32,26 @@ Not all traits have been defined as some rely on compiler intrinsics that are no `is_compound` `is_array` `is_pointer` +`is_null_pointer` +`is_scalar` +`is_union` +`is_empty` +`is_polymorphic` +`is_abstract` +`is_final` +`is_aggregate` +`is_trivial` +`is_standard_layout` +`is_destructible` +`is_nothrow_destructible` +`has_virtual_destructor` +`has_unique_object_representations` +`is_bounded_array` +`is_unbounded_array` +`is_swappable` +`is_swappable_with` +`is_nothrow_swappable` +`is_nothrow_swappable_with` `is_reference` `is_base_of` `make_signed` @@ -476,3 +496,382 @@ template inline constexpr bool is_object_pointer_v = etl::is_object_pointer::value; ``` C++17 + +## is_pod +From: `20.49.0` + +```cpp +template +struct is_pod +``` +Checks if `T` is a POD (Plain Old Data) type. +The result is found in the member `value`. + When using the STL, this is defined as `is_standard_layout && is_trivially_default_constructible && is_trivially_copyable`. + When not using the STL, this is conservatively defined as `is_fundamental || is_pointer`. + +```cpp +template +inline constexpr bool is_pod_v = etl::is_pod::value; +``` +C++17 + +## is_standard_layout +From: `20.49.0` + +```cpp +template +struct is_standard_layout +``` +Checks if `T` is a standard-layout type. +The result is found in the member `value`. + +```cpp +template +inline constexpr bool is_standard_layout_v = etl::is_standard_layout::value; +``` +C++17 + +## is_trivial +From: `20.49.0` + +```cpp +template +struct is_trivial +``` +Checks if `T` is a trivial type; i.e. it is trivially copyable and has one or more +default constructors, all of which are trivial. +The result is found in the member `value`. + +```cpp +template +inline constexpr bool is_trivial_v = etl::is_trivial::value; +``` +C++17 + +**Note:** When not using the STL, this trait requires compiler support (the `__is_trivial` intrinsic). +If that is not available then the trait is not defined, as there is no portable way of +determining whether a type is trivial. + +## is_bounded_array +From: `20.49.0` + +```cpp +template +struct is_bounded_array +``` +Checks if `T` is an array type of known bound. +`T[N]` returns `true`, `T[]` and all other types return `false`. +The result is found in the member `value`. + +```cpp +template +inline constexpr bool is_bounded_array_v = etl::is_bounded_array::value; +``` +C++17 + +## is_unbounded_array +From: `20.49.0` + +```cpp +template +struct is_unbounded_array +``` +Checks if `T` is an array type of unknown bound. +`T[]` returns `true`, `T[N]` and all other types return `false`. +The result is found in the member `value`. + +```cpp +template +inline constexpr bool is_unbounded_array_v = etl::is_unbounded_array::value; +``` +C++17 + +## is_destructible +From: `20.49.0` + +```cpp +template +struct is_destructible +``` +Checks if `T` is a destructible type; that is, an object type whose destructor is not deleted and is accessible, a reference type, or an array of a destructible type. +Incomplete types, `void`, function types and arrays of unknown bound are not destructible. +The result is found in the member `value`. + When not using the STL, the trait is detected by checking whether `declval().~T()` is a valid expression. + Requires either compiler support or C++11. When neither is available, this trait is not defined. + +```cpp +template +inline constexpr bool is_destructible_v = etl::is_destructible::value; +``` +C++17 + +## is_nothrow_destructible +From: `20.49.0` + +```cpp +template +struct is_nothrow_destructible +``` +Checks if `T` is a destructible type whose destructor is known not to throw. +Types that are not destructible are not nothrow destructible. +The result is found in the member `value`. + When not using the STL, the trait is detected by checking `noexcept(declval().~T())` for destructible types. + Requires either compiler support or C++11. When neither is available, this trait is not defined. + +```cpp +template +inline constexpr bool is_nothrow_destructible_v = etl::is_nothrow_destructible::value; +``` +C++17 + +## is_swappable +From: `20.49.0` + +```cpp +template +struct is_swappable +``` +Checks if `T` is a referenceable type and lvalues of type `T` can be swapped by an unqualified call to `swap`. +The result is found in the member `value`. + When using the STL and C++17, this is defined as `std::is_swappable`. + Otherwise the trait checks whether `swap(declval(), declval())` is a valid expression, where the `swap` considered is `etl::swap`, `std::swap` if it is available, and any `swap` found by argument dependent lookup. + Requires C++11. Without it there is no way of detecting the validity of an expression, so this trait is not defined. + Note that when using the STL with C++11 or C++14, the accuracy of this trait depends on `std::swap` being constrained by the standard library implementation. Standard libraries that only constrain `std::swap` from C++17 will cause this trait to report `true` for types that cannot actually be swapped. + +```cpp +template +inline constexpr bool is_swappable_v = etl::is_swappable::value; +``` +C++17 + +## is_swappable_with +From: `20.49.0` + +```cpp +template +struct is_swappable_with +``` +Checks if expressions of type `T` and `U` can be swapped by unqualified calls to `swap`, in both directions. +The result is found in the member `value`. + When using the STL and C++17, this is defined as `std::is_swappable_with`. + Otherwise the trait checks whether both `swap(declval(), declval())` and `swap(declval(), declval())` are valid expressions, where the `swap` considered is `etl::swap`, `std::swap` if it is available, and any `swap` found by argument dependent lookup. + Note that, unlike `is_swappable`, no references are added to `T` and `U`, so the value categories of the arguments are those of `T` and `U`. `is_swappable_with::value` is `false`, whereas `is_swappable_with::value` is `true`. + Requires C++11. Without it there is no way of detecting the validity of an expression, so this trait is not defined. + Note that when using the STL with C++11 or C++14, the accuracy of this trait depends on `std::swap` being constrained by the standard library implementation. Standard libraries that only constrain `std::swap` from C++17 will cause this trait to report `true` for types that cannot actually be swapped. + +```cpp +template +inline constexpr bool is_swappable_with_v = etl::is_swappable_with::value; +``` +C++17 + +## is_nothrow_swappable +From: `20.49.0` + +```cpp +template +struct is_nothrow_swappable +``` +Checks if `T` is a referenceable type and lvalues of type `T` can be swapped by an unqualified call to `swap`, and that the swap cannot throw. +The result is found in the member `value`. + When using the STL and C++17, this is defined as `std::is_nothrow_swappable`. + Otherwise the trait checks that `etl::is_swappable::value` is `true` and that `swap(declval(), declval())` is `noexcept`. + Requires C++11. Without it there is no way of detecting the validity of an expression, so this trait is not defined. + Note that when neither the STL nor the type traits builtins are available, `etl::is_nothrow_move_constructible` and `etl::is_nothrow_move_assignable` are conservatively `false` for class types, so `etl::swap` is not `noexcept` for them and this trait is conservatively `false` too. + +```cpp +template +inline constexpr bool is_nothrow_swappable_v = etl::is_nothrow_swappable::value; +``` +C++17 + +## is_nothrow_swappable_with +From: `20.49.0` + +```cpp +template +struct is_nothrow_swappable_with +``` +Checks if expressions of type `T` and `U` can be swapped by unqualified calls to `swap`, in both directions, and that neither swap can throw. +The result is found in the member `value`. + When using the STL and C++17, this is defined as `std::is_nothrow_swappable_with`. + Otherwise the trait checks that `etl::is_swappable_with::value` is `true` and that both `swap(declval(), declval())` and `swap(declval(), declval())` are `noexcept`. + Note that, as for `is_swappable_with`, no references are added to `T` and `U`, so the value categories of the arguments are those of `T` and `U`. `is_nothrow_swappable_with::value` is `false`, whereas `is_nothrow_swappable_with::value` is `true`. + Requires C++11. Without it there is no way of detecting the validity of an expression, so this trait is not defined. + Note that when neither the STL nor the type traits builtins are available, `etl::is_nothrow_move_constructible` and `etl::is_nothrow_move_assignable` are conservatively `false` for class types, so `etl::swap` is not `noexcept` for them and this trait is conservatively `false` too. + +```cpp +template +inline constexpr bool is_nothrow_swappable_with_v = etl::is_nothrow_swappable_with::value; +``` +C++17 + +## is_union +From: `20.49.0` + +```cpp +template +struct is_union +``` +Checks if `T` is a union type. +The result is found in the member `value`. + When the compiler does not provide the required intrinsic, this trait conservatively reports `false` for every type. + +```cpp +template +inline constexpr bool is_union_v = etl::is_union::value; +``` +C++17 + +## is_empty +From: `20.49.0` + +```cpp +template +struct is_empty +``` +Checks if `T` is an empty class type; that is, a non-union class type with no non-static data members other than zero-sized bit-fields, no virtual functions, no virtual base classes and no non-empty base classes. +The result is found in the member `value`. + Requires compiler support. When the compiler does not provide the required intrinsic, this trait is not defined. + +```cpp +template +inline constexpr bool is_empty_v = etl::is_empty::value; +``` +C++17 + +## is_polymorphic +From: `20.49.0` + +```cpp +template +struct is_polymorphic +``` +Checks if `T` is a polymorphic class type; that is, a class that declares or inherits at least one virtual function. +The result is found in the member `value`. + Requires compiler support. When the compiler does not provide the required intrinsic, this trait is not defined. + +```cpp +template +inline constexpr bool is_polymorphic_v = etl::is_polymorphic::value; +``` +C++17 + +## is_abstract +From: `20.49.0` + +```cpp +template +struct is_abstract +``` +Checks if `T` is an abstract class type; that is, a class that declares or inherits at least one pure virtual function. +The result is found in the member `value`. + Requires compiler support. When the compiler does not provide the required intrinsic, this trait is not defined. + +```cpp +template +inline constexpr bool is_abstract_v = etl::is_abstract::value; +``` +C++17 + +## is_final +From: `20.49.0` + +```cpp +template +struct is_final +``` +Checks if `T` is a class or union type marked `final`, optionally cv-qualified. +The result is found in the member `value`. + When using the STL, this requires C++14 or above. Otherwise it requires compiler support. When neither is available, this trait is not defined. + +```cpp +template +inline constexpr bool is_final_v = etl::is_final::value; +``` +C++17 + +## is_aggregate +From: `20.49.0` + +```cpp +template +struct is_aggregate +``` +Checks if `T` is an aggregate type, optionally cv-qualified. +An aggregate is an array type or a class type that has no user-declared or inherited constructors, no private or protected non-static data members, no virtual functions and no virtual, private or protected base classes. +The result is found in the member `value`. + When using the STL, this requires C++17 or above. Otherwise it requires compiler support. When neither is available, this trait is not defined. + +```cpp +template +inline constexpr bool is_aggregate_v = etl::is_aggregate::value; +``` +C++17 + +## has_virtual_destructor +From: `20.49.0` + +```cpp +template +struct has_virtual_destructor +``` +Checks if `T` is a class type with a virtual destructor, optionally cv-qualified. +The result is found in the member `value`. + When not using the STL, this requires compiler support. When it is not available, this trait is not defined. + +```cpp +template +inline constexpr bool has_virtual_destructor_v = etl::has_virtual_destructor::value; +``` +C++17 + +## has_unique_object_representations +From: `20.49.0` + +```cpp +template +struct has_unique_object_representations +``` +Checks if `T` is trivially copyable and any two objects of type `T` with the same value have the same object representation; that is, the type has no padding bits and no two distinct object representations compare equal. +Arrays and cv-qualifiers are ignored; the trait is applied to `etl::remove_all_extents::type>::type`. +The result is found in the member `value`. + When using the STL, this requires C++17 or above. Otherwise it requires compiler support. When neither is available, this trait is not defined. + +```cpp +template +inline constexpr bool has_unique_object_representations_v = etl::has_unique_object_representations::value; +``` +C++17 + +## is_null_pointer +From: `20.49.0` + +```cpp +template +struct is_null_pointer +``` +Checks if `T` is `etl::nullptr_t`, optionally cv-qualified. +The result is found in the member `value`. + +```cpp +template +inline constexpr bool is_null_pointer_v = etl::is_null_pointer::value; +``` +C++17 + +## is_scalar +From: `20.49.0` + +```cpp +template +struct is_scalar +``` +Checks if `T` is a scalar type; that is, an arithmetic, enumeration, pointer, pointer to member or `etl::nullptr_t` type, optionally cv-qualified. +The result is found in the member `value`. + +```cpp +template +inline constexpr bool is_scalar_v = etl::is_scalar::value; +``` +C++17 diff --git a/include/etl/profiles/determine_builtin_support.h b/include/etl/profiles/determine_builtin_support.h index deadec8c..3db1a474 100644 --- a/include/etl/profiles/determine_builtin_support.h +++ b/include/etl/profiles/determine_builtin_support.h @@ -57,10 +57,58 @@ SOFTWARE. #define ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE 1 #endif + #if !defined(ETL_USING_BUILTIN_IS_DESTRUCTIBLE) + #define ETL_USING_BUILTIN_IS_DESTRUCTIBLE 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_NOTHROW_DESTRUCTIBLE) + #define ETL_USING_BUILTIN_IS_NOTHROW_DESTRUCTIBLE 1 + #endif + #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE) #define ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE 1 #endif + #if !defined(ETL_USING_BUILTIN_IS_STANDARD_LAYOUT) + #define ETL_USING_BUILTIN_IS_STANDARD_LAYOUT 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_TRIVIAL) + #define ETL_USING_BUILTIN_IS_TRIVIAL 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_UNION) + #define ETL_USING_BUILTIN_IS_UNION 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_EMPTY) + #define ETL_USING_BUILTIN_IS_EMPTY 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_POLYMORPHIC) + #define ETL_USING_BUILTIN_IS_POLYMORPHIC 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_ABSTRACT) + #define ETL_USING_BUILTIN_IS_ABSTRACT 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_FINAL) + #define ETL_USING_BUILTIN_IS_FINAL 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_AGGREGATE) + #define ETL_USING_BUILTIN_IS_AGGREGATE 1 + #endif + + #if !defined(ETL_USING_BUILTIN_HAS_VIRTUAL_DESTRUCTOR) + #define ETL_USING_BUILTIN_HAS_VIRTUAL_DESTRUCTOR 1 + #endif + + #if !defined(ETL_USING_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS) + #define ETL_USING_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS 1 + #endif + #if !defined(ETL_USING_BUILTIN_UNDERLYING_TYPE) #define ETL_USING_BUILTIN_UNDERLYING_TYPE 1 #endif @@ -124,10 +172,58 @@ SOFTWARE. #define ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE (__has_builtin(__has_trivial_destructor) || __has_builtin(__is_trivially_destructible)) #endif + #if !defined(ETL_USING_BUILTIN_IS_DESTRUCTIBLE) + #define ETL_USING_BUILTIN_IS_DESTRUCTIBLE __has_builtin(__is_destructible) + #endif + + #if !defined(ETL_USING_BUILTIN_IS_NOTHROW_DESTRUCTIBLE) + #define ETL_USING_BUILTIN_IS_NOTHROW_DESTRUCTIBLE __has_builtin(__is_nothrow_destructible) + #endif + #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE) #define ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE (__has_builtin(__has_trivial_copy) || __has_builtin(__is_trivially_copyable)) #endif + #if !defined(ETL_USING_BUILTIN_IS_STANDARD_LAYOUT) + #define ETL_USING_BUILTIN_IS_STANDARD_LAYOUT __has_builtin(__is_standard_layout) + #endif + + #if !defined(ETL_USING_BUILTIN_IS_TRIVIAL) + #define ETL_USING_BUILTIN_IS_TRIVIAL __has_builtin(__is_trivial) + #endif + + #if !defined(ETL_USING_BUILTIN_IS_UNION) + #define ETL_USING_BUILTIN_IS_UNION __has_builtin(__is_union) + #endif + + #if !defined(ETL_USING_BUILTIN_IS_EMPTY) + #define ETL_USING_BUILTIN_IS_EMPTY __has_builtin(__is_empty) + #endif + + #if !defined(ETL_USING_BUILTIN_IS_POLYMORPHIC) + #define ETL_USING_BUILTIN_IS_POLYMORPHIC __has_builtin(__is_polymorphic) + #endif + + #if !defined(ETL_USING_BUILTIN_IS_ABSTRACT) + #define ETL_USING_BUILTIN_IS_ABSTRACT __has_builtin(__is_abstract) + #endif + + #if !defined(ETL_USING_BUILTIN_IS_FINAL) + #define ETL_USING_BUILTIN_IS_FINAL __has_builtin(__is_final) + #endif + + #if !defined(ETL_USING_BUILTIN_IS_AGGREGATE) + #define ETL_USING_BUILTIN_IS_AGGREGATE __has_builtin(__is_aggregate) + #endif + + #if !defined(ETL_USING_BUILTIN_HAS_VIRTUAL_DESTRUCTOR) + #define ETL_USING_BUILTIN_HAS_VIRTUAL_DESTRUCTOR __has_builtin(__has_virtual_destructor) + #endif + + #if !defined(ETL_USING_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS) + #define ETL_USING_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS __has_builtin(__has_unique_object_representations) + #endif + #if !defined(ETL_USING_BUILTIN_UNDERLYING_TYPE) #define ETL_USING_BUILTIN_UNDERLYING_TYPE __has_builtin(__underlying_type) #endif @@ -183,6 +279,46 @@ SOFTWARE. #define ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE 1 #endif + #if !defined(ETL_USING_BUILTIN_IS_STANDARD_LAYOUT) + #define ETL_USING_BUILTIN_IS_STANDARD_LAYOUT 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_TRIVIAL) + #define ETL_USING_BUILTIN_IS_TRIVIAL 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_UNION) + #define ETL_USING_BUILTIN_IS_UNION 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_EMPTY) + #define ETL_USING_BUILTIN_IS_EMPTY 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_POLYMORPHIC) + #define ETL_USING_BUILTIN_IS_POLYMORPHIC 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_ABSTRACT) + #define ETL_USING_BUILTIN_IS_ABSTRACT 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_FINAL) + #define ETL_USING_BUILTIN_IS_FINAL 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_AGGREGATE) + #define ETL_USING_BUILTIN_IS_AGGREGATE 1 + #endif + + #if !defined(ETL_USING_BUILTIN_HAS_VIRTUAL_DESTRUCTOR) + #define ETL_USING_BUILTIN_HAS_VIRTUAL_DESTRUCTOR 1 + #endif + + #if !defined(ETL_USING_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS) + #define ETL_USING_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS 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. @@ -216,10 +352,58 @@ SOFTWARE. #define ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE 0 #endif +#if !defined(ETL_USING_BUILTIN_IS_DESTRUCTIBLE) + #define ETL_USING_BUILTIN_IS_DESTRUCTIBLE 0 +#endif + +#if !defined(ETL_USING_BUILTIN_IS_NOTHROW_DESTRUCTIBLE) + #define ETL_USING_BUILTIN_IS_NOTHROW_DESTRUCTIBLE 0 +#endif + #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE) #define ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE 0 #endif +#if !defined(ETL_USING_BUILTIN_IS_STANDARD_LAYOUT) + #define ETL_USING_BUILTIN_IS_STANDARD_LAYOUT 0 +#endif + +#if !defined(ETL_USING_BUILTIN_IS_TRIVIAL) + #define ETL_USING_BUILTIN_IS_TRIVIAL 0 +#endif + +#if !defined(ETL_USING_BUILTIN_IS_UNION) + #define ETL_USING_BUILTIN_IS_UNION 0 +#endif + +#if !defined(ETL_USING_BUILTIN_IS_EMPTY) + #define ETL_USING_BUILTIN_IS_EMPTY 0 +#endif + +#if !defined(ETL_USING_BUILTIN_IS_POLYMORPHIC) + #define ETL_USING_BUILTIN_IS_POLYMORPHIC 0 +#endif + +#if !defined(ETL_USING_BUILTIN_IS_ABSTRACT) + #define ETL_USING_BUILTIN_IS_ABSTRACT 0 +#endif + +#if !defined(ETL_USING_BUILTIN_IS_FINAL) + #define ETL_USING_BUILTIN_IS_FINAL 0 +#endif + +#if !defined(ETL_USING_BUILTIN_IS_AGGREGATE) + #define ETL_USING_BUILTIN_IS_AGGREGATE 0 +#endif + +#if !defined(ETL_USING_BUILTIN_HAS_VIRTUAL_DESTRUCTOR) + #define ETL_USING_BUILTIN_HAS_VIRTUAL_DESTRUCTOR 0 +#endif + +#if !defined(ETL_USING_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS) + #define ETL_USING_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS 0 +#endif + #if !defined(ETL_USING_BUILTIN_UNDERLYING_TYPE) #define ETL_USING_BUILTIN_UNDERLYING_TYPE 0 #endif diff --git a/include/etl/type_traits.h b/include/etl/type_traits.h index ab8d13d7..0c9da447 100644 --- a/include/etl/type_traits.h +++ b/include/etl/type_traits.h @@ -47,6 +47,12 @@ SOFTWARE. #include #endif +// Required for std::swap, which etl::is_swappable must consider in any +// configuration that does not define etl::swap. +#if ETL_USING_CPP11 && (ETL_USING_STL || defined(ETL_IN_UNIT_TEST)) + #include +#endif + namespace etl { #if ETL_USING_CPP11 @@ -741,6 +747,38 @@ namespace etl inline constexpr bool is_array_v = is_array::value; #endif + //*************************************************************************** + /// is_bounded_array + template + struct is_bounded_array : false_type + { + }; + template + struct is_bounded_array : true_type + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_bounded_array_v = is_bounded_array::value; + #endif + + //*************************************************************************** + /// is_unbounded_array + template + struct is_unbounded_array : false_type + { + }; + template + struct is_unbounded_array : true_type + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_unbounded_array_v = is_unbounded_array::value; + #endif + //*************************************************************************** /// is_pointer template @@ -846,6 +884,38 @@ namespace etl inline constexpr bool is_pod_v = etl::is_pod::value; #endif + //*************************************************************************** + /// is_standard_layout + /// Requires compiler support. Without it there is no portable way to + /// determine whether a type is standard layout, so the trait is not defined. + #if ETL_USING_BUILTIN_IS_STANDARD_LAYOUT + template + struct is_standard_layout : etl::bool_constant<__is_standard_layout(T)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_standard_layout_v = etl::is_standard_layout::value; + #endif + #endif + + //*************************************************************************** + /// is_trivial + /// Requires compiler support. Without it there is no portable way to + /// determine whether a type is trivial, so the trait is not defined. + #if ETL_USING_BUILTIN_IS_TRIVIAL + template + struct is_trivial : etl::bool_constant<__is_trivial(T)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_trivial_v = etl::is_trivial::value; + #endif + #endif + //*************************************************************************** /// conditional template @@ -1144,6 +1214,137 @@ namespace etl inline constexpr bool is_class_v = is_class::value; #endif + //*************************************************************************** + /// is_union + /// Requires compiler support. Without it there is no portable way to + /// distinguish a union from a class, so the trait is not defined. + #if ETL_USING_BUILTIN_IS_UNION + template + struct is_union : etl::bool_constant<__is_union(T)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_union_v = is_union::value; + #endif + #endif + + //*************************************************************************** + /// is_empty + /// Requires compiler support. Without it there is no portable way to + /// determine whether a class type is empty, so the trait is not defined. + #if ETL_USING_BUILTIN_IS_EMPTY + template + struct is_empty : etl::bool_constant<__is_empty(T)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_empty_v = is_empty::value; + #endif + #endif + + //*************************************************************************** + /// is_polymorphic + /// Requires compiler support. Without it there is no portable way to + /// determine whether a class type is polymorphic, so the trait is not defined. + #if ETL_USING_BUILTIN_IS_POLYMORPHIC + template + struct is_polymorphic : etl::bool_constant<__is_polymorphic(T)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_polymorphic_v = is_polymorphic::value; + #endif + #endif + + //*************************************************************************** + /// is_abstract + /// Requires compiler support. Without it there is no portable way to + /// determine whether a class type is abstract, so the trait is not defined. + #if ETL_USING_BUILTIN_IS_ABSTRACT + template + struct is_abstract : etl::bool_constant<__is_abstract(T)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_abstract_v = is_abstract::value; + #endif + #endif + + //*************************************************************************** + /// is_final + /// Requires compiler support. Without it there is no portable way to + /// determine whether a class type is final, so the trait is not defined. + #if ETL_USING_BUILTIN_IS_FINAL + template + struct is_final : etl::bool_constant<__is_final(T)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_final_v = is_final::value; + #endif + #endif + + //*************************************************************************** + /// is_aggregate + /// Requires compiler support. Without it there is no portable way to + /// determine whether a type is an aggregate, so the trait is not defined. + #if ETL_USING_BUILTIN_IS_AGGREGATE + template + struct is_aggregate : etl::bool_constant<__is_aggregate(typename etl::remove_cv::type)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_aggregate_v = is_aggregate::value; + #endif + #endif + + //*************************************************************************** + /// has_virtual_destructor + /// Requires compiler support. Without it there is no portable way to + /// determine whether a class type has a virtual destructor, so the trait is + /// not defined. + #if ETL_USING_BUILTIN_HAS_VIRTUAL_DESTRUCTOR + template + struct has_virtual_destructor : etl::bool_constant<__has_virtual_destructor(T)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool has_virtual_destructor_v = has_virtual_destructor::value; + #endif + #endif + + //*************************************************************************** + /// has_unique_object_representations + /// Requires compiler support. Without it there is no portable way to + /// determine whether a type has a unique object representation, so the trait + /// is not defined. + #if ETL_USING_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS + template + struct has_unique_object_representations + : etl::bool_constant<__has_unique_object_representations(typename etl::remove_cv::type>::type)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool has_unique_object_representations_v = has_unique_object_representations::value; + #endif + #endif + //*************************************************************************** /// is_base_of #if ETL_USING_CPP11 @@ -1464,6 +1665,132 @@ namespace etl template inline constexpr bool is_nothrow_convertible_v = etl::is_nothrow_convertible::value; + #endif + + //*************************************************************************** + /// is_destructible + /// Requires either compiler support or C++11. Without them there is no + /// portable way to determine whether a type is destructible, so the trait + /// is not defined. + #if ETL_USING_BUILTIN_IS_DESTRUCTIBLE + template + struct is_destructible : etl::bool_constant<__is_destructible(T)> + { + }; + #elif ETL_USING_CPP11 + namespace private_type_traits + { + // Selected only if 'declval().~T()' is a valid expression. + // Void types, function types and types with an inaccessible or deleted + // destructor all fail substitution and select the fallback. + template + struct is_destructible_impl + { + private: + + template ().~U())> + static etl::true_type test(int); + + template + static etl::false_type test(...); + + public: + + static ETL_CONSTANT bool value = decltype(test(0))::value; + }; + + template + struct is_destructible_helper : etl::bool_constant::type>::value> + { + }; + + // Reference types are always destructible. + template + struct is_destructible_helper : etl::true_type + { + }; + + template + struct is_destructible_helper : etl::true_type + { + }; + + // Arrays of unknown bound are incomplete types. + template + struct is_destructible_helper : etl::false_type + { + }; + } // namespace private_type_traits + + template + struct is_destructible : etl::bool_constant::value> + { + }; + #endif + + // C++17 implies C++11, so the trait is always defined when the alias is. + #if ETL_USING_CPP17 + template + inline constexpr bool is_destructible_v = etl::is_destructible::value; + #endif + + //*************************************************************************** + /// is_nothrow_destructible + /// Requires either compiler support or C++11. Without them there is no + /// portable way to determine whether a type is nothrow destructible, so the + /// trait is not defined. + #if ETL_USING_BUILTIN_IS_NOTHROW_DESTRUCTIBLE + template + struct is_nothrow_destructible : etl::bool_constant<__is_nothrow_destructible(T)> + { + }; + #elif ETL_USING_CPP11 + namespace private_type_traits + { + // Only instantiated for destructible types, so 'declval().~T()' is a valid expression. + template ::value> + struct is_nothrow_destructible_impl : etl::false_type + { + }; + + template + struct is_nothrow_destructible_impl : etl::bool_constant().~T())> + { + }; + + template + struct is_nothrow_destructible_helper : etl::bool_constant::type>::value> + { + }; + + // Reference types are always nothrow destructible. + template + struct is_nothrow_destructible_helper : etl::true_type + { + }; + + template + struct is_nothrow_destructible_helper : etl::true_type + { + }; + + // Arrays of unknown bound are incomplete types. + template + struct is_nothrow_destructible_helper : etl::false_type + { + }; + } // namespace private_type_traits + + template + struct is_nothrow_destructible : etl::bool_constant::value> + { + }; + #endif + + // C++17 implies C++11, so the trait is always defined when the alias is. + #if ETL_USING_CPP17 + template + inline constexpr bool is_nothrow_destructible_v = etl::is_nothrow_destructible::value; #endif //*************************************************************************** @@ -1849,6 +2176,56 @@ namespace etl #if ETL_USING_CPP17 template inline constexpr bool is_array_v = std::is_array_v; + #endif + + //*************************************************************************** + /// is_bounded_array + ///\ingroup type_traits + #if ETL_USING_CPP20 && defined(__cpp_lib_bounded_array_traits) + template + struct is_bounded_array : std::is_bounded_array + { + }; + #else + template + struct is_bounded_array : std::false_type + { + }; + + template + struct is_bounded_array : std::true_type + { + }; + #endif + + #if ETL_USING_CPP17 + template + inline constexpr bool is_bounded_array_v = etl::is_bounded_array::value; + #endif + + //*************************************************************************** + /// is_unbounded_array + ///\ingroup type_traits + #if ETL_USING_CPP20 && defined(__cpp_lib_bounded_array_traits) + template + struct is_unbounded_array : std::is_unbounded_array + { + }; + #else + template + struct is_unbounded_array : std::false_type + { + }; + + template + struct is_unbounded_array : std::true_type + { + }; + #endif + + #if ETL_USING_CPP17 + template + inline constexpr bool is_unbounded_array_v = etl::is_unbounded_array::value; #endif //*************************************************************************** @@ -1920,6 +2297,72 @@ namespace etl inline constexpr bool is_pod_v = std::is_standard_layout_v && std::is_trivially_default_constructible_v && std::is_trivially_copyable_v; #endif + //*************************************************************************** + /// is_standard_layout + ///\ingroup type_traits + template + struct is_standard_layout : std::is_standard_layout + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_standard_layout_v = std::is_standard_layout_v; + #endif + + //*************************************************************************** + /// is_trivial + ///\ingroup type_traits + #if ETL_USING_CPP26 + // std::is_trivial is deprecated in C++26 and later; avoid using it and + // compose the trait from is_trivially_copyable && is_trivially_default_constructible instead. + template + struct is_trivial : etl::bool_constant::value && std::is_trivially_default_constructible::value> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_trivial_v = std::is_trivially_copyable_v && std::is_trivially_default_constructible_v; + #endif + #else + template + struct is_trivial : std::is_trivial + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_trivial_v = std::is_trivial_v; + #endif + #endif + + //*************************************************************************** + /// is_destructible + ///\ingroup type_traits + template + struct is_destructible : std::is_destructible + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_destructible_v = std::is_destructible_v; + #endif + + //*************************************************************************** + /// is_nothrow_destructible + ///\ingroup type_traits + template + struct is_nothrow_destructible : std::is_nothrow_destructible + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_nothrow_destructible_v = std::is_nothrow_destructible_v; + #endif + #if defined(ETL_COMPILER_GCC) #if ETL_COMPILER_VERSION >= 5 #define ETL_GCC_V5_TYPE_TRAITS_SUPPORTED @@ -2100,6 +2543,127 @@ namespace etl inline constexpr bool is_class_v = is_class::value; #endif + //*************************************************************************** + /// is_union + template + struct is_union : std::is_union + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_union_v = std::is_union_v; + #endif + + //*************************************************************************** + /// is_empty + template + struct is_empty : std::is_empty + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_empty_v = std::is_empty_v; + #endif + + //*************************************************************************** + /// is_polymorphic + template + struct is_polymorphic : std::is_polymorphic + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_polymorphic_v = std::is_polymorphic_v; + #endif + + //*************************************************************************** + /// is_abstract + template + struct is_abstract : std::is_abstract + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_abstract_v = std::is_abstract_v; + #endif + + //*************************************************************************** + /// is_final + /// std::is_final is only available from C++14, so for C++11 the compiler + /// builtin is used, if available. Otherwise the trait is not defined. + #if ETL_USING_CPP14 + template + struct is_final : std::is_final + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_final_v = std::is_final_v; + #endif + #elif ETL_USING_BUILTIN_IS_FINAL + template + struct is_final : etl::bool_constant<__is_final(T)> + { + }; + #endif + + //*************************************************************************** + /// is_aggregate + /// std::is_aggregate is only available from C++17, so for C++11 and C++14 the + /// compiler builtin is used, if available. Otherwise the trait is not defined. + #if ETL_USING_CPP17 + template + struct is_aggregate : std::is_aggregate + { + }; + + template + inline constexpr bool is_aggregate_v = std::is_aggregate_v; + #elif ETL_USING_BUILTIN_IS_AGGREGATE + template + struct is_aggregate : etl::bool_constant<__is_aggregate(typename etl::remove_cv::type)> + { + }; + #endif + + //*************************************************************************** + /// has_virtual_destructor + template + struct has_virtual_destructor : std::has_virtual_destructor + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool has_virtual_destructor_v = std::has_virtual_destructor_v; + #endif + + //*************************************************************************** + /// has_unique_object_representations + /// std::has_unique_object_representations is only available from C++17, so for + /// C++11 and C++14 the compiler builtin is used, if available. Otherwise the + /// trait is not defined. + #if ETL_USING_CPP17 + template + struct has_unique_object_representations : std::has_unique_object_representations + { + }; + + template + inline constexpr bool has_unique_object_representations_v = std::has_unique_object_representations_v; + #elif ETL_USING_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS + template + struct has_unique_object_representations + : etl::bool_constant<__has_unique_object_representations(typename etl::remove_cv::type>::type)> + { + }; + #endif + //*************************************************************************** /// add_lvalue_reference template @@ -2253,6 +2817,19 @@ namespace etl #endif // Condition = ETL_USING_STL && ETL_USING_CPP11 + //*************************************************************************** + /// is_null_pointer + ///\ingroup type_traits + template + struct is_null_pointer : etl::is_same::type, etl::nullptr_t> + { + }; + +#if ETL_USING_CPP17 + template + inline constexpr bool is_null_pointer_v = is_null_pointer::value; +#endif + //*************************************************************************** // ETL extended type traits. //*************************************************************************** @@ -3996,6 +4573,23 @@ namespace etl inline constexpr bool is_member_pointer_v = etl::is_member_pointer::value; #endif + //*************************************************************************** + /// is_scalar + /// An arithmetic, enumeration, pointer, pointer to member or + /// etl::nullptr_t type, optionally cv-qualified. + //*************************************************************************** + template + struct is_scalar + : etl::bool_constant::value || etl::is_enum::value || etl::is_pointer::value || etl::is_member_pointer::value + || etl::is_null_pointer::value> + { + }; + +#if ETL_USING_CPP17 + template + inline constexpr bool is_scalar_v = etl::is_scalar::value; +#endif + #if ETL_USING_CPP11 //********************************* /// Check if T is a function type @@ -4130,6 +4724,235 @@ namespace etl inline constexpr bool is_object_pointer_v = etl::is_object_pointer::value; #endif + //*************************************************************************** + /// swap + /// Declared here, and defined in utility.h, so that etl::is_swappable below + /// finds it by ordinary unqualified lookup. + /// 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. + //*************************************************************************** +#if ETL_NOT_USING_STL && !defined(ETL_IN_UNIT_TEST) + #if ETL_USING_CPP11 + namespace private_type_traits + { + template + struct is_swappable_helper; + + template + struct is_nothrow_swappable_helper; + } // namespace private_type_traits + + // Constrained, as the standard requires, so that an attempt to swap a type + // that cannot be moved is a substitution failure rather than a hard error. + // This is what allows etl::is_swappable to be implemented. + // The exception specification is conditional, as the standard requires, so + // that etl::is_nothrow_swappable can be implemented. + template + ETL_CONSTEXPR14 typename etl::enable_if::value && etl::is_move_assignable::value>::type swap(T& a, T& b) + ETL_NOEXCEPT_IF(etl::is_nothrow_move_constructible::value&& etl::is_nothrow_move_assignable::value); + + template + ETL_CONSTEXPR14 typename etl::enable_if::value>::type swap(T (&a)[Size], T (&b)[Size]) + ETL_NOEXCEPT_IF(private_type_traits::is_nothrow_swappable_helper::value); + #else + template + ETL_CONSTEXPR14 void swap(T& a, T& b) ETL_NOEXCEPT; + + template + ETL_CONSTEXPR14 void swap(T (&a)[Size], T (&b)[Size]) ETL_NOEXCEPT; + #endif +#endif + + //*************************************************************************** + /// is_swappable + /// Determines whether lvalues of type T can be swapped by an unqualified + /// call to swap. + /// Requires C++11. Without it there is no way of detecting the validity of + /// an expression, so the trait is not defined. + //*************************************************************************** +#if ETL_USING_STL && ETL_USING_CPP17 + template + struct is_swappable : std::is_swappable + { + }; + + //*************************************************************************** + /// is_swappable_with + //*************************************************************************** + template + struct is_swappable_with : std::is_swappable_with + { + }; +#elif ETL_USING_CPP11 + namespace private_type_traits + { + namespace swap_detect + { + // Bring std::swap into scope, if it is available, so that the unqualified + // call below finds it in those configurations that do not define etl::swap. + // Otherwise etl::swap is found by ordinary lookup in the enclosing + // namespace. Either way, a swap found by argument dependent lookup takes + // part in overload resolution. + #if ETL_USING_STL || defined(ETL_IN_UNIT_TEST) + using std::swap; + #endif + + // Selected only if 'swap(declval(), declval())' is a valid expression. + template + struct is_swappable_test + { + private: + + template (), etl::declval()))> + static etl::true_type test(int); + + template + static etl::false_type test(...); + + public: + + static ETL_CONSTANT bool value = decltype(test(0))::value; + }; + + // Selected only if both 'swap(declval(), declval())' and + // 'swap(declval(), declval())' are valid expressions. + template + struct is_swappable_with_test + { + private: + + template (), etl::declval())), + typename = decltype(swap(etl::declval(), etl::declval()))> + static etl::true_type test(int); + + template + static etl::false_type test(...); + + public: + + static ETL_CONSTANT bool value = decltype(test(0))::value; + }; + } // namespace swap_detect + + template + struct is_swappable_helper : etl::bool_constant::value> + { + }; + + template + struct is_swappable_with_helper : etl::bool_constant::value> + { + }; + } // namespace private_type_traits + + template + struct is_swappable : etl::bool_constant::value> + { + }; + + //*************************************************************************** + /// is_swappable_with + //*************************************************************************** + template + struct is_swappable_with : etl::bool_constant::value> + { + }; +#endif + + //*************************************************************************** + /// is_nothrow_swappable + /// Determines whether lvalues of type T can be swapped by an unqualified + /// call to swap, and that the swap cannot throw. + /// Requires C++11. Without it there is no way of detecting the validity of + /// an expression, so the trait is not defined. + //*************************************************************************** +#if ETL_USING_STL && ETL_USING_CPP17 + template + struct is_nothrow_swappable : std::is_nothrow_swappable + { + }; + + //*************************************************************************** + /// is_nothrow_swappable_with + //*************************************************************************** + template + struct is_nothrow_swappable_with : std::is_nothrow_swappable_with + { + }; +#elif ETL_USING_CPP11 + namespace private_type_traits + { + namespace swap_detect + { + // Only 'true' if the type is swappable and the swap expression is noexcept. + template ::value> + struct is_nothrow_swappable_test + { + static ETL_CONSTANT bool value = false; + }; + + template + struct is_nothrow_swappable_test + { + static ETL_CONSTANT bool value = noexcept(swap(etl::declval(), etl::declval())); + }; + + // Only 'true' if the types are swappable with each other and both of the + // swap expressions are noexcept. + template ::value> + struct is_nothrow_swappable_with_test + { + static ETL_CONSTANT bool value = false; + }; + + template + struct is_nothrow_swappable_with_test + { + static ETL_CONSTANT bool value = noexcept(swap(etl::declval(), etl::declval())) && noexcept(swap(etl::declval(), etl::declval())); + }; + } // namespace swap_detect + + template + struct is_nothrow_swappable_helper : etl::bool_constant::value> + { + }; + + template + struct is_nothrow_swappable_with_helper : etl::bool_constant::value> + { + }; + } // namespace private_type_traits + + template + struct is_nothrow_swappable : etl::bool_constant::value> + { + }; + + //*************************************************************************** + /// is_nothrow_swappable_with + //*************************************************************************** + template + struct is_nothrow_swappable_with : etl::bool_constant::value> + { + }; +#endif + + // C++17 implies C++11, so the trait is always defined when the alias is. +#if ETL_USING_CPP17 + template + inline constexpr bool is_swappable_v = etl::is_swappable::value; + + template + inline constexpr bool is_swappable_with_v = etl::is_swappable_with::value; + + template + inline constexpr bool is_nothrow_swappable_v = etl::is_nothrow_swappable::value; + + template + inline constexpr bool is_nothrow_swappable_with_v = etl::is_nothrow_swappable_with::value; +#endif + #if ETL_USING_CPP11 //********************************* /// Check for the presence of operator() diff --git a/include/etl/utility.h b/include/etl/utility.h index 3c7da3a5..f11ccca8 100644 --- a/include/etl/utility.h +++ b/include/etl/utility.h @@ -340,7 +340,31 @@ namespace etl // 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. + // These are declared in type_traits.h, so that etl::is_swappable can find + // them, and defined here. #if ETL_NOT_USING_STL && !defined(ETL_IN_UNIT_TEST) + #if ETL_USING_CPP11 + //*************************************************************************** + // swap + template + ETL_CONSTEXPR14 typename etl::enable_if::value && etl::is_move_assignable::value>::type swap(T& a, T& b) + ETL_NOEXCEPT_IF(etl::is_nothrow_move_constructible::value&& etl::is_nothrow_move_assignable::value) + { + T temp(ETL_MOVE(a)); + a = ETL_MOVE(b); + b = ETL_MOVE(temp); + } + + template + ETL_CONSTEXPR14 typename etl::enable_if::value>::type swap(T (&a)[Size], T (&b)[Size]) + ETL_NOEXCEPT_IF(private_type_traits::is_nothrow_swappable_helper::value) + { + for (size_t i = 0UL; i < Size; ++i) + { + swap(a[i], b[i]); + } + } + #else //*************************************************************************** // swap template @@ -351,7 +375,7 @@ namespace etl b = ETL_MOVE(temp); } - template < class T, size_t Size > + template ETL_CONSTEXPR14 void swap(T (&a)[Size], T (&b)[Size]) ETL_NOEXCEPT { for (size_t i = 0UL; i < Size; ++i) @@ -359,6 +383,7 @@ namespace etl swap(a[i], b[i]); } } + #endif #endif //*************************************************************************** diff --git a/test/test_type_traits.cpp b/test/test_type_traits.cpp index 531557de..ab2e7c4c 100644 --- a/test/test_type_traits.cpp +++ b/test/test_type_traits.cpp @@ -421,8 +421,148 @@ struct etl::is_move_assignable : public etl::true_type }; #endif +#if ETL_USING_CPP11 namespace { + //***************************************************************************** + // Types for the is_swappable tests. + struct Swappable + { + int a; + }; + + struct NonSwappable + { + NonSwappable(const NonSwappable&) = delete; + NonSwappable& operator=(const NonSwappable&) = delete; + }; + + // Detects whether the compiler allows an rvalue to bind to a non-const lvalue + // reference. This is a Microsoft extension, disabled by /permissive-, and it makes + // types such as Swappable swappable as rvalues. + template + void takes_lvalue_reference(T&); + + template + struct rvalue_binds_to_lvalue_reference : etl::false_type + { + }; + + template + struct rvalue_binds_to_lvalue_reference(etl::declval()))> > : etl::true_type + { + }; + + // Types for the is_nothrow_swappable tests. + // Swappable, as it is movable, but the move operations may throw. + struct ThrowingSwappable + { + ThrowingSwappable() {} + ThrowingSwappable(ThrowingSwappable&&) noexcept(false) {} + ThrowingSwappable& operator=(ThrowingSwappable&&) noexcept(false) + { + return *this; + } + }; +} // namespace + +namespace adl +{ + // Not movable, so only the swap found by argument dependent lookup makes + // this type swappable. + struct AdlSwappable + { + AdlSwappable(const AdlSwappable&) = delete; + AdlSwappable& operator=(const AdlSwappable&) = delete; + + int a; + }; + + inline void swap(AdlSwappable&, AdlSwappable&) {} + + // As above, but the swap found by argument dependent lookup cannot throw. + struct NothrowAdlSwappable + { + NothrowAdlSwappable(const NothrowAdlSwappable&) = delete; + NothrowAdlSwappable& operator=(const NothrowAdlSwappable&) = delete; + + int a; + }; + + inline void swap(NothrowAdlSwappable&, NothrowAdlSwappable&) noexcept {} + + // Types for the is_swappable_with tests. + struct SwapLhs + { + int a; + }; + + struct SwapRhs + { + int a; + }; + + inline void swap(SwapLhs&, SwapRhs&) {} + inline void swap(SwapRhs&, SwapLhs&) {} + + // Only swappable in one direction, so is_swappable_with is false. + struct SwapOneWay + { + int a; + }; + + inline void swap(SwapLhs&, SwapOneWay&) {} + + // Swappable only as rvalues. + struct RvalueSwappable + { + int a; + }; + + inline void swap(RvalueSwappable&&, RvalueSwappable&&) {} + + // Types for the is_nothrow_swappable_with tests. + // Swappable with each other, in both directions, without throwing. + struct NothrowSwapLhs + { + int a; + }; + + struct NothrowSwapRhs + { + int a; + }; + + inline void swap(NothrowSwapLhs&, NothrowSwapRhs&) noexcept {} + inline void swap(NothrowSwapRhs&, NothrowSwapLhs&) noexcept {} + + // Swappable with NothrowSwapLhs in both directions, but one of the swaps may throw. + struct HalfNothrowSwap + { + int a; + }; + + inline void swap(NothrowSwapLhs&, HalfNothrowSwap&) noexcept {} + inline void swap(HalfNothrowSwap&, NothrowSwapLhs&) {} +} // namespace adl +#endif + +namespace +{ + //************************************************************************* + // std::is_trivial is deprecated in C++26. + // Use the suggested replacement when compiling to that standard. + //************************************************************************* + template + struct std_is_trivial +#if ETL_USING_CPP26 + : std::integral_constant::value && std::is_trivially_default_constructible::value> +#else + : std::integral_constant::value> +#endif + { + }; + SUITE(test_type_traits) { //************************************************************************* @@ -556,6 +696,891 @@ namespace CHECK(etl::is_pointer::value == std::is_pointer::value); } + //************************************************************************* + TEST(test_is_bounded_array) + { + CHECK(etl::is_bounded_array::value); + CHECK(etl::is_bounded_array::value); + CHECK(etl::is_bounded_array::value); + CHECK(!etl::is_bounded_array::value); + CHECK(!etl::is_bounded_array::value); + CHECK(!etl::is_bounded_array::value); + CHECK(!etl::is_bounded_array::value); + +#if ETL_USING_CPP20 && defined(__cpp_lib_bounded_array_traits) + CHECK(etl::is_bounded_array::value == std::is_bounded_array::value); + CHECK(etl::is_bounded_array::value == std::is_bounded_array::value); + CHECK(etl::is_bounded_array::value == std::is_bounded_array::value); +#endif + +#if ETL_USING_CPP17 + CHECK(etl::is_bounded_array_v); + CHECK(!etl::is_bounded_array_v); + CHECK(!etl::is_bounded_array_v); +#endif + } + + //************************************************************************* + TEST(test_is_unbounded_array) + { + CHECK(etl::is_unbounded_array::value); + CHECK(etl::is_unbounded_array::value); + CHECK(etl::is_unbounded_array::value); + CHECK(!etl::is_unbounded_array::value); + CHECK(!etl::is_unbounded_array::value); + CHECK(!etl::is_unbounded_array::value); + CHECK(!etl::is_unbounded_array::value); + CHECK(!etl::is_unbounded_array::value); + +#if ETL_USING_CPP20 && defined(__cpp_lib_bounded_array_traits) + CHECK(etl::is_unbounded_array::value == std::is_unbounded_array::value); + CHECK(etl::is_unbounded_array::value == std::is_unbounded_array::value); + CHECK(etl::is_unbounded_array::value == std::is_unbounded_array::value); +#endif + +#if ETL_USING_CPP17 + CHECK(etl::is_unbounded_array_v); + CHECK(!etl::is_unbounded_array_v); + CHECK(!etl::is_unbounded_array_v); +#endif + } + + //************************************************************************* + TEST(test_is_null_pointer) + { + CHECK(etl::is_null_pointer::value); + CHECK(!etl::is_null_pointer::value); + CHECK(!etl::is_null_pointer::value); + CHECK(!etl::is_null_pointer::value); + + CHECK(etl::is_null_pointer::value); + CHECK(etl::is_null_pointer::value); + CHECK(etl::is_null_pointer::value); + +#if ETL_USING_CPP14 + CHECK(etl::is_null_pointer::value == std::is_null_pointer::value); + CHECK(etl::is_null_pointer::value == std::is_null_pointer::value); +#endif + +#if ETL_USING_CPP17 + CHECK(etl::is_null_pointer_v); + CHECK(!etl::is_null_pointer_v); +#endif + } + + //************************************************************************* + TEST(test_is_standard_layout) + { + struct StandardLayout + { + int a; + int b; + }; + + struct NonStandardLayout + { + virtual void foo() {} + int a; + }; + + CHECK(etl::is_standard_layout::value == std::is_standard_layout::value); + CHECK(etl::is_standard_layout::value == std::is_standard_layout::value); + CHECK(etl::is_standard_layout::value == std::is_standard_layout::value); + CHECK(etl::is_standard_layout::value == std::is_standard_layout::value); + CHECK(etl::is_standard_layout::value); + CHECK(!etl::is_standard_layout::value); + +#if ETL_USING_CPP17 + CHECK(etl::is_standard_layout_v); + CHECK(!etl::is_standard_layout_v); +#endif + } + + //************************************************************************* + TEST(test_is_trivial) + { + struct Trivial + { + int a; + int b; + }; + + struct NonTrivialConstructor + { + NonTrivialConstructor() + : a(1) + { + } + int a; + }; + + struct NonTrivialCopy + { + NonTrivialCopy(const NonTrivialCopy&) {} + int a; + }; + + struct NonTrivialDestructor + { + ~NonTrivialDestructor() {} + int a; + }; + + CHECK(etl::is_trivial::value == std_is_trivial::value); + CHECK(etl::is_trivial::value == std_is_trivial::value); + CHECK(etl::is_trivial::value == std_is_trivial::value); + CHECK(etl::is_trivial::value == std_is_trivial::value); + CHECK(etl::is_trivial::value == std_is_trivial::value); + CHECK(etl::is_trivial::value == std_is_trivial::value); + CHECK(etl::is_trivial::value == std_is_trivial::value); + CHECK(etl::is_trivial::value == std_is_trivial::value); + + CHECK(etl::is_trivial::value); + CHECK(etl::is_trivial::value); + CHECK(!etl::is_trivial::value); + CHECK(!etl::is_trivial::value); + CHECK(!etl::is_trivial::value); + +#if ETL_USING_CPP17 + CHECK(etl::is_trivial_v); + CHECK(!etl::is_trivial_v); +#endif + } + + //************************************************************************* + TEST(test_is_destructible) + { +#if ETL_USING_CPP11 || ETL_USING_BUILTIN_IS_DESTRUCTIBLE + CHECK(etl::is_destructible::value); + CHECK(etl::is_destructible::value); + CHECK(!etl::is_destructible::value); +#endif + +#if ETL_USING_CPP11 + struct Destructible + { + int a; + }; + + struct NonDestructible + { + ~NonDestructible() = delete; + }; + + struct ProtectedDestructor + { + protected: + + ~ProtectedDestructor() {} + }; + + CHECK(etl::is_destructible::value == std::is_destructible::value); + CHECK(etl::is_destructible::value == std::is_destructible::value); + CHECK(etl::is_destructible::value == std::is_destructible::value); + CHECK(etl::is_destructible::value == std::is_destructible::value); + CHECK(etl::is_destructible::value == std::is_destructible::value); + CHECK(etl::is_destructible::value == std::is_destructible::value); + CHECK(etl::is_destructible::value == std::is_destructible::value); + CHECK(etl::is_destructible::value == std::is_destructible::value); + CHECK(etl::is_destructible::value == std::is_destructible::value); + + CHECK(etl::is_destructible::value); + CHECK(etl::is_destructible::value); + CHECK(etl::is_destructible::value); + CHECK(!etl::is_destructible::value); + CHECK(!etl::is_destructible::value); + CHECK(!etl::is_destructible::value); + CHECK(!etl::is_destructible::value); +#endif + +#if ETL_USING_CPP17 + CHECK(etl::is_destructible_v); + CHECK(!etl::is_destructible_v); +#endif + } + + //************************************************************************* + TEST(test_is_nothrow_destructible) + { +#if ETL_USING_CPP11 || ETL_USING_BUILTIN_IS_NOTHROW_DESTRUCTIBLE + CHECK(etl::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value); + CHECK(!etl::is_nothrow_destructible::value); +#endif + +#if ETL_USING_CPP11 + struct NothrowDestructible + { + int a; + }; + + struct ThrowingDestructible + { + ~ThrowingDestructible() noexcept(false) {} + }; + + struct NotDestructible + { + ~NotDestructible() = delete; + }; + + CHECK(etl::is_nothrow_destructible::value == std::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value == std::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value == std::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value == std::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value == std::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value == std::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value == std::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value == std::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value == std::is_nothrow_destructible::value); + + CHECK(etl::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value); + CHECK(!etl::is_nothrow_destructible::value); + CHECK(!etl::is_nothrow_destructible::value); + CHECK(!etl::is_nothrow_destructible::value); + CHECK(!etl::is_nothrow_destructible::value); + CHECK(!etl::is_nothrow_destructible::value); +#endif + +#if ETL_USING_CPP17 + CHECK(etl::is_nothrow_destructible_v); + CHECK(!etl::is_nothrow_destructible_v); +#endif + } + +#if ETL_USING_CPP11 + //************************************************************************* + TEST(test_is_swappable) + { + CHECK(etl::is_swappable::value); + CHECK(etl::is_swappable::value); + CHECK(etl::is_swappable::value); + CHECK(etl::is_swappable::value); + CHECK(etl::is_swappable::value); + + // Not movable, so swappable only via the swap found by ADL. + CHECK(etl::is_swappable::value); + CHECK(etl::is_swappable::value); + + CHECK(!etl::is_swappable::value); + CHECK(!etl::is_swappable::value); + CHECK(!etl::is_swappable::value); + CHECK(!etl::is_swappable::value); + CHECK(!etl::is_swappable::value); + CHECK(!etl::is_swappable::value); + + #if ETL_USING_CPP17 + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + + CHECK(etl::is_swappable_v); + CHECK(!etl::is_swappable_v); + #endif + } + + //************************************************************************* + TEST(test_is_swappable_with) + { + // Note that is_swappable_with does not add references, so the value + // categories of the arguments are those of T and U. + CHECK((etl::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value)); + + // Heterogeneous swaps, found by argument dependent lookup. + CHECK((etl::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value)); + + // Swappable in one direction only. + CHECK((!etl::is_swappable_with::value)); + CHECK((!etl::is_swappable_with::value)); + + // Swappable as rvalues, via the swap found by ADL. + CHECK((etl::is_swappable_with::value)); + + // Rvalues do not bind to the lvalue references taken by swap, unless the + // compiler allows it as an extension. + CHECK((!etl::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value == rvalue_binds_to_lvalue_reference::value)); + + CHECK((!etl::is_swappable_with::value)); + CHECK((!etl::is_swappable_with::value)); + CHECK((!etl::is_swappable_with::value)); + CHECK((!etl::is_swappable_with::value)); + CHECK((!etl::is_swappable_with::value)); + + #if ETL_USING_CPP17 + CHECK((etl::is_swappable_with::value == std::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value == std::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value == std::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value == std::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value == std::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value == std::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value == std::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value == std::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value + == std::is_swappable_with::value)); + + CHECK((etl::is_swappable_with_v)); + CHECK((!etl::is_swappable_with_v)); + #endif + } + + //************************************************************************* + TEST(test_is_nothrow_swappable) + { + CHECK(etl::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value); + + // Not movable, so swappable only via the swap found by ADL, which is noexcept. + CHECK(etl::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value); + + // Swappable, but the swap may throw. + CHECK(!etl::is_nothrow_swappable::value); + CHECK(!etl::is_nothrow_swappable::value); + + // Swappable via the swap found by ADL, which is not noexcept. + CHECK(!etl::is_nothrow_swappable::value); + CHECK(!etl::is_nothrow_swappable::value); + + // Not swappable at all. + CHECK(!etl::is_nothrow_swappable::value); + CHECK(!etl::is_nothrow_swappable::value); + CHECK(!etl::is_nothrow_swappable::value); + CHECK(!etl::is_nothrow_swappable::value); + CHECK(!etl::is_nothrow_swappable::value); + CHECK(!etl::is_nothrow_swappable::value); + + #if ETL_USING_CPP17 + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + + CHECK(etl::is_nothrow_swappable_v); + CHECK(!etl::is_nothrow_swappable_v); + #endif + } + + //************************************************************************* + TEST(test_is_nothrow_swappable_with) + { + CHECK((etl::is_nothrow_swappable_with::value)); + CHECK((etl::is_nothrow_swappable_with::value)); + + // Swappable with each other, in both directions, without throwing. + CHECK((etl::is_nothrow_swappable_with::value)); + CHECK((etl::is_nothrow_swappable_with::value)); + + // Swappable in both directions, but one of the swaps may throw. + CHECK((!etl::is_nothrow_swappable_with::value)); + CHECK((!etl::is_nothrow_swappable_with::value)); + + // Swappable with each other, but the swaps may throw. + CHECK((!etl::is_nothrow_swappable_with::value)); + + // Swappable, but the move operations may throw. + CHECK((!etl::is_nothrow_swappable_with::value)); + + // Rvalues do not bind to the lvalue references taken by swap. + CHECK((!etl::is_nothrow_swappable_with::value)); + + // Not swappable at all. + CHECK((!etl::is_nothrow_swappable_with::value)); + CHECK((!etl::is_nothrow_swappable_with::value)); + CHECK((!etl::is_nothrow_swappable_with::value)); + CHECK((!etl::is_nothrow_swappable_with::value)); + CHECK((!etl::is_nothrow_swappable_with::value)); + + #if ETL_USING_CPP17 + CHECK((etl::is_nothrow_swappable_with::value == std::is_nothrow_swappable_with::value)); + CHECK((etl::is_nothrow_swappable_with::value == std::is_nothrow_swappable_with::value)); + CHECK((etl::is_nothrow_swappable_with::value == std::is_nothrow_swappable_with::value)); + CHECK((etl::is_nothrow_swappable_with::value == std::is_nothrow_swappable_with::value)); + CHECK( + (etl::is_nothrow_swappable_with::value == std::is_nothrow_swappable_with::value)); + CHECK((etl::is_nothrow_swappable_with::value + == std::is_nothrow_swappable_with::value)); + CHECK( + (etl::is_nothrow_swappable_with::value == std::is_nothrow_swappable_with::value)); + CHECK((etl::is_nothrow_swappable_with::value + == std::is_nothrow_swappable_with::value)); + CHECK((etl::is_nothrow_swappable_with::value + == std::is_nothrow_swappable_with::value)); + + CHECK((etl::is_nothrow_swappable_with_v)); + CHECK((!etl::is_nothrow_swappable_with_v)); + #endif + } +#endif + + //************************************************************************* + TEST(test_is_union) + { + union Union + { + int a; + float b; + }; + + struct NonUnion + { + int a; + int b; + }; + + CHECK(etl::is_union::value == std::is_union::value); + CHECK(etl::is_union::value == std::is_union::value); + CHECK(etl::is_union::value == std::is_union::value); + CHECK(etl::is_union::value == std::is_union::value); + CHECK(etl::is_union::value); + CHECK(!etl::is_union::value); + CHECK(!etl::is_union::value); + +#if ETL_USING_CPP17 + CHECK(etl::is_union_v); + CHECK(!etl::is_union_v); +#endif + } + + //************************************************************************* + TEST(test_is_empty) + { + struct Empty + { + }; + + struct EmptyDerived : public Empty + { + }; + + struct NotEmpty + { + int a; + }; + + struct Virtual + { + virtual void foo() {} + }; + + CHECK(etl::is_empty::value == std::is_empty::value); + CHECK(etl::is_empty::value == std::is_empty::value); + CHECK(etl::is_empty::value == std::is_empty::value); + CHECK(etl::is_empty::value == std::is_empty::value); + CHECK(etl::is_empty::value == std::is_empty::value); + CHECK(etl::is_empty::value == std::is_empty::value); + + CHECK(etl::is_empty::value); + CHECK(etl::is_empty::value); + CHECK(!etl::is_empty::value); + CHECK(!etl::is_empty::value); + CHECK(!etl::is_empty::value); + +#if ETL_USING_CPP17 + CHECK(etl::is_empty_v); + CHECK(!etl::is_empty_v); +#endif + } + + //************************************************************************* + TEST(test_is_polymorphic) + { + struct NonPolymorphic + { + int a; + }; + + struct Polymorphic + { + virtual ~Polymorphic() {} + }; + + struct PolymorphicDerived : public Polymorphic + { + }; + + struct AbstractPolymorphic + { + virtual void foo() = 0; + }; + + CHECK(etl::is_polymorphic::value == std::is_polymorphic::value); + CHECK(etl::is_polymorphic::value == std::is_polymorphic::value); + CHECK(etl::is_polymorphic::value == std::is_polymorphic::value); + CHECK(etl::is_polymorphic::value == std::is_polymorphic::value); + CHECK(etl::is_polymorphic::value == std::is_polymorphic::value); + CHECK(etl::is_polymorphic::value == std::is_polymorphic::value); + + CHECK(etl::is_polymorphic::value); + CHECK(etl::is_polymorphic::value); + CHECK(etl::is_polymorphic::value); + CHECK(!etl::is_polymorphic::value); + CHECK(!etl::is_polymorphic::value); + +#if ETL_USING_CPP17 + CHECK(etl::is_polymorphic_v); + CHECK(!etl::is_polymorphic_v); +#endif + } + + //************************************************************************* + TEST(test_is_abstract) + { + struct NonAbstract + { + int a; + }; + + struct NonAbstractPolymorphic + { + virtual ~NonAbstractPolymorphic() {} + }; + + struct Abstract + { + virtual ~Abstract() {} + virtual void foo() = 0; + }; + + struct AbstractDerived : public Abstract + { + }; + + struct ConcreteDerived : public Abstract + { + void foo() ETL_OVERRIDE {} + }; + + CHECK(etl::is_abstract::value == std::is_abstract::value); + CHECK(etl::is_abstract::value == std::is_abstract::value); + CHECK(etl::is_abstract::value == std::is_abstract::value); + CHECK(etl::is_abstract::value == std::is_abstract::value); + CHECK(etl::is_abstract::value == std::is_abstract::value); + CHECK(etl::is_abstract::value == std::is_abstract::value); + CHECK(etl::is_abstract::value == std::is_abstract::value); + + CHECK(etl::is_abstract::value); + CHECK(etl::is_abstract::value); + CHECK(!etl::is_abstract::value); + CHECK(!etl::is_abstract::value); + CHECK(!etl::is_abstract::value); + CHECK(!etl::is_abstract::value); + +#if ETL_USING_CPP17 + CHECK(etl::is_abstract_v); + CHECK(!etl::is_abstract_v); +#endif + } + + //************************************************************************* +#if ETL_USING_CPP14 + TEST(test_is_final) + { + struct NonFinal + { + int a; + }; + + struct Final final + { + int a; + }; + + struct Base + { + virtual ~Base() {} + }; + + struct FinalDerived final : public Base + { + }; + + struct NonFinalDerived : public Base + { + }; + + union FinalUnion final + { + int a; + }; + + CHECK(etl::is_final::value == std::is_final::value); + CHECK(etl::is_final::value == std::is_final::value); + CHECK(etl::is_final::value == std::is_final::value); + CHECK(etl::is_final::value == std::is_final::value); + CHECK(etl::is_final::value == std::is_final::value); + CHECK(etl::is_final::value == std::is_final::value); + CHECK(etl::is_final::value == std::is_final::value); + CHECK(etl::is_final::value == std::is_final::value); + + CHECK(etl::is_final::value); + CHECK(etl::is_final::value); + CHECK(etl::is_final::value); + CHECK(!etl::is_final::value); + CHECK(!etl::is_final::value); + CHECK(!etl::is_final::value); + CHECK(!etl::is_final::value); + CHECK(!etl::is_final::value); + + #if ETL_USING_CPP17 + CHECK(etl::is_final_v); + CHECK(!etl::is_final_v); + #endif + } +#endif + + //************************************************************************* +#if ETL_USING_CPP17 + TEST(test_is_aggregate) + { + struct Aggregate + { + int a; + double b; + }; + + struct AggregateDerived : public Aggregate + { + int c; + }; + + struct NonAggregate + { + NonAggregate(int a_) + : a(a_) + { + } + int a; + }; + + struct Virtual + { + virtual ~Virtual() {} + int a; + }; + + union AggregateUnion + { + int a; + double b; + }; + + CHECK(etl::is_aggregate::value == std::is_aggregate::value); + CHECK(etl::is_aggregate::value == std::is_aggregate::value); + CHECK(etl::is_aggregate::value == std::is_aggregate::value); + CHECK(etl::is_aggregate::value == std::is_aggregate::value); + CHECK(etl::is_aggregate::value == std::is_aggregate::value); + CHECK(etl::is_aggregate::value == std::is_aggregate::value); + CHECK(etl::is_aggregate::value == std::is_aggregate::value); + CHECK(etl::is_aggregate::value == std::is_aggregate::value); + CHECK(etl::is_aggregate::value == std::is_aggregate::value); + + CHECK(etl::is_aggregate::value); + CHECK(etl::is_aggregate::value); + CHECK(etl::is_aggregate::value); + CHECK(etl::is_aggregate::value); + CHECK(etl::is_aggregate::value); + CHECK(!etl::is_aggregate::value); + CHECK(!etl::is_aggregate::value); + CHECK(!etl::is_aggregate::value); + CHECK(!etl::is_aggregate::value); + + CHECK(etl::is_aggregate_v); + CHECK(!etl::is_aggregate_v); + } +#endif + + //************************************************************************* + TEST(test_has_virtual_destructor) + { + struct NonVirtualDestructor + { + ~NonVirtualDestructor() {} + int a; + }; + + struct VirtualDestructor + { + virtual ~VirtualDestructor() {} + int a; + }; + + struct DerivedFromVirtualDestructor : public VirtualDestructor + { + int b; + }; + + struct VirtualFunctionOnly + { + virtual void foo() {} + int a; + }; + + CHECK(etl::has_virtual_destructor::value == std::has_virtual_destructor::value); + CHECK(etl::has_virtual_destructor::value == std::has_virtual_destructor::value); + CHECK(etl::has_virtual_destructor::value == std::has_virtual_destructor::value); + CHECK(etl::has_virtual_destructor::value == std::has_virtual_destructor::value); + CHECK(etl::has_virtual_destructor::value == std::has_virtual_destructor::value); + CHECK(etl::has_virtual_destructor::value == std::has_virtual_destructor::value); + CHECK(etl::has_virtual_destructor::value == std::has_virtual_destructor::value); + + CHECK(etl::has_virtual_destructor::value); + CHECK(etl::has_virtual_destructor::value); + CHECK(etl::has_virtual_destructor::value); + CHECK(!etl::has_virtual_destructor::value); + CHECK(!etl::has_virtual_destructor::value); + CHECK(!etl::has_virtual_destructor::value); + CHECK(!etl::has_virtual_destructor::value); + +#if ETL_USING_CPP17 + CHECK(etl::has_virtual_destructor_v); + CHECK(!etl::has_virtual_destructor_v); +#endif + } + + //************************************************************************* +#if ETL_USING_CPP17 + TEST(test_has_unique_object_representations) + { + struct NoPadding + { + int a; + int b; + }; + + struct Padded + { + char a; + int b; + }; + + CHECK(etl::has_unique_object_representations::value == std::has_unique_object_representations::value); + CHECK(etl::has_unique_object_representations::value == std::has_unique_object_representations::value); + CHECK(etl::has_unique_object_representations::value == std::has_unique_object_representations::value); + CHECK(etl::has_unique_object_representations::value == std::has_unique_object_representations::value); + CHECK(etl::has_unique_object_representations::value == std::has_unique_object_representations::value); + CHECK(etl::has_unique_object_representations::value == std::has_unique_object_representations::value); + CHECK(etl::has_unique_object_representations::value == std::has_unique_object_representations::value); + CHECK(etl::has_unique_object_representations::value == std::has_unique_object_representations::value); + + CHECK(etl::has_unique_object_representations::value); + CHECK(etl::has_unique_object_representations::value); + CHECK(etl::has_unique_object_representations::value); + CHECK(etl::has_unique_object_representations::value); + CHECK(!etl::has_unique_object_representations::value); + + CHECK(etl::has_unique_object_representations_v); + CHECK(!etl::has_unique_object_representations_v); + } +#endif + + //************************************************************************* + TEST(test_is_scalar) + { + struct Class + { + int a; + void foo() {} + }; + + union Union + { + int a; + float b; + }; + + enum Enum + { + e1 + }; + + // Arithmetic types. + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + + // Enumerations. + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + + // Pointers. + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + + // Pointers to members. + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + + // Null pointer. + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + + // Non-scalars. + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + + CHECK(etl::is_scalar::value); + CHECK(etl::is_scalar::value); + CHECK(etl::is_scalar::value); + CHECK(etl::is_scalar::value); + CHECK(etl::is_scalar::value); + CHECK(!etl::is_scalar::value); + CHECK(!etl::is_scalar::value); + CHECK(!etl::is_scalar::value); + +#if ETL_USING_CPP17 + CHECK(etl::is_scalar_v); + CHECK(etl::is_scalar_v); + CHECK(etl::is_scalar_v); + CHECK(etl::is_scalar_v); + CHECK(!etl::is_scalar_v); + CHECK(!etl::is_scalar_v); +#endif + } + //************************************************************************* TEST(test_is_reference) { From 01a07a17284da86cd1a5303106e573981f89d1f9 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Mon, 24 Aug 2026 15:16:17 +0200 Subject: [PATCH 46/77] Fix guards in array.h and test/test_array.h (#1566) Follow-up for https://github.com/ETLCPP/etl/pull/1553 where this change was intended but missed --- include/etl/array.h | 2 +- test/test_array.cpp | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/etl/array.h b/include/etl/array.h index 349e7711..c1d7d6e4 100644 --- a/include/etl/array.h +++ b/include/etl/array.h @@ -1052,7 +1052,7 @@ namespace etl //************************************************************************* /// Make //************************************************************************* -#if ETL_HAS_INITIALIZER_LIST +#if ETL_USING_CPP11 template constexpr auto make_array(TValues&&... values) -> etl::array, sizeof...(TValues)> { diff --git a/test/test_array.cpp b/test/test_array.cpp index b7d9162e..1f8b4f9c 100644 --- a/test/test_array.cpp +++ b/test/test_array.cpp @@ -77,7 +77,7 @@ namespace CHECK_EQUAL(data.max_size(), 0); } -#if ETL_USING_CPP17 && ETL_HAS_INITIALIZER_LIST && !defined(ETL_TEMPLATE_DEDUCTION_GUIDE_TESTS_DISABLED) +#if ETL_USING_CPP17 && !defined(ETL_TEMPLATE_DEDUCTION_GUIDE_TESTS_DISABLED) //************************************************************************* TEST(test_cpp17_deduced_constructor) { @@ -861,7 +861,7 @@ namespace } //************************************************************************* -#if ETL_USING_CPP17 && ETL_HAS_INITIALIZER_LIST && !defined(ETL_TEMPLATE_DEDUCTION_GUIDE_TESTS_DISABLED) +#if ETL_USING_CPP17 && !defined(ETL_TEMPLATE_DEDUCTION_GUIDE_TESTS_DISABLED) TEST(test_array_template_deduction) { etl::array data{char(0), short(1), 2, long(3), 4, 5, 6, 7, 8, 9}; @@ -883,7 +883,7 @@ namespace #endif //************************************************************************* -#if ETL_USING_CPP17 && ETL_HAS_INITIALIZER_LIST +#if ETL_USING_CPP17 TEST(test_array_template_deduction_for_movable) { etl::array data{Moveable(0), Moveable(1), Moveable(2), Moveable(3), Moveable(4), @@ -906,7 +906,7 @@ namespace #endif //************************************************************************* -#if ETL_HAS_INITIALIZER_LIST +#if ETL_USING_CPP11 TEST(test_make_array) { auto data = etl::make_array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); @@ -928,7 +928,7 @@ namespace #endif //************************************************************************* -#if ETL_HAS_INITIALIZER_LIST +#if ETL_USING_CPP11 TEST(test_make_array_for_movable) { auto data = etl::make_array(Moveable(0), Moveable(1), Moveable(2), Moveable(3), Moveable(4), Moveable(5), Moveable(6), Moveable(7), @@ -951,7 +951,7 @@ namespace #endif //************************************************************************* -#if ETL_HAS_INITIALIZER_LIST +#if ETL_USING_CPP11 // 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 @@ -974,7 +974,7 @@ namespace #endif //************************************************************************* -#if ETL_HAS_INITIALIZER_LIST +#if ETL_USING_CPP11 // 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. @@ -998,7 +998,7 @@ namespace #endif //************************************************************************* -#if ETL_HAS_INITIALIZER_LIST +#if ETL_USING_CPP11 // With no explicit element type, the element type is deduced as the // decayed common type of the arguments (Library Fundamentals TS // make_array design). @@ -1017,7 +1017,7 @@ namespace #endif //************************************************************************* -#if ETL_HAS_INITIALIZER_LIST +#if ETL_USING_CPP11 // An empty array requires the element type to be supplied explicitly. // etl::make_array() with no element type and no arguments is ill-formed, // as in the Library Fundamentals TS: there is nothing to deduce the @@ -1033,7 +1033,7 @@ namespace #endif //************************************************************************* -#if ETL_HAS_INITIALIZER_LIST +#if ETL_USING_CPP11 // Arguments that already have the element type are forwarded rather than // cast. Casting created a prvalue that, before C++17, had to be // materialised through the move constructor, so a copyable type with a @@ -1065,7 +1065,7 @@ namespace #endif //************************************************************************* -#if ETL_HAS_INITIALIZER_LIST && ETL_USING_CPP14 +#if ETL_USING_CPP14 TEST(test_make_array_is_constexpr) { static constexpr unsigned char constexpr_value = 0x18U; From f7520ad0bdebf73bfdd3f557c3db3d00e01b9646 Mon Sep 17 00:00:00 2001 From: davmf Date: Tue, 25 Aug 2026 18:29:01 +1000 Subject: [PATCH 47/77] Topic/optional monadic operations (#1501) * Topic/optional monadic operations * topic/optional-monadic-operations: - added and_then, or_else, and transform to etl::optional with unit tests - added is_optional to optional.h, used in and_then and or_else to ensure that the returned type is an optional - implemented as ref-qualified overloads with C++11 compatible trailing return types, following the etl::expected implementation * Convert to C++11 style type traits * Coderabbit changes Constrain transform before forming etl::optional. Remove references from monadic result types. * clang-format updates --------- Co-authored-by: John Wellbelove Co-authored-by: John Wellbelove --- include/etl/optional.h | 203 +++++++++++++++++++++++++++++++-------- test/test_optional.cpp | 213 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 374 insertions(+), 42 deletions(-) diff --git a/include/etl/optional.h b/include/etl/optional.h index eeb4b5c7..e4372453 100644 --- a/include/etl/optional.h +++ b/include/etl/optional.h @@ -36,6 +36,7 @@ SOFTWARE. #include "error_handler.h" #include "exception.h" #include "initializer_list.h" +#include "invoke.h" #include "memory.h" #include "placement_new.h" #include "type_traits.h" @@ -49,6 +50,16 @@ namespace etl template class optional; + template + struct is_optional : etl::false_type + { + }; + + template + struct is_optional > : etl::true_type + { + }; + //***************************************************************************** /// A null option type. ///\ingroup utilities @@ -174,10 +185,9 @@ namespace etl /// Constructs T in-place from U&&, without requiring T to be /// copy/move constructible. //*************************************************************************** - template ::value && !etl::is_same::type, etl::in_place_t>::value - && !etl::is_same::type, optional_impl>::value, - int>::type = 0> + template ::value && !etl::is_same, etl::in_place_t>::value + && !etl::is_same, optional_impl>::value, + int> = 0> ETL_CONSTEXPR20_STL optional_impl(U&& value_) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) { storage.construct(etl::forward(value_)); @@ -276,9 +286,7 @@ namespace etl /// Assignment operator from value type. //*************************************************************************** #if ETL_USING_CPP11 - template ::value && !etl::is_same::type, optional_impl>::value, - int>::type = 0> + template ::value && !etl::is_same, optional_impl>::value, int> = 0> ETL_CONSTEXPR20_STL optional_impl& operator=(U&& value_) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) { storage.construct(etl::forward(value_)); @@ -511,8 +519,7 @@ namespace etl //******************************************* /// Emplaces a value from initializer_list and arbitrary constructor arguments. //******************************************* - template &, Args...>::value, int>::type = 0 > + template &, Args...>::value, int> = 0 > ETL_CONSTEXPR20_STL T& emplace(std::initializer_list ilist, Args&&... args) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible&, Args...>::value)) { @@ -1217,8 +1224,8 @@ namespace etl }; } // namespace private_optional -#define ETL_OPTIONAL_ENABLE_CPP14 typename etl::enable_if< etl::is_pod::type>::value, int>::type = 0 -#define ETL_OPTIONAL_ENABLE_CPP20_STL typename etl::enable_if< !etl::is_pod::type>::value, int>::type = 0 +#define ETL_OPTIONAL_ENABLE_CPP14 etl::enable_if_t< etl::is_pod::type>::value, int> = 0 +#define ETL_OPTIONAL_ENABLE_CPP20_STL etl::enable_if_t< !etl::is_pod::type>::value, int> = 0 #define ETL_OPTIONAL_ENABLE_CONSTEXPR_BOOL_RETURN_CPP14 \ ETL_CONSTEXPR14 typename etl::enable_if< etl::is_pod::type>::value, bool>::type @@ -1319,12 +1326,10 @@ namespace etl /// Constructs T in-place from U&&, without requiring T to be /// copy/move constructible. //*************************************************************************** - template < typename U, - typename etl::enable_if< etl::is_constructible::value && !etl::is_same::type, etl::optional>::value - && !etl::is_same::type, etl::in_place_t>::value - && !etl::is_same::type, etl::nullopt_t>::value - && etl::is_pod::type>::value, - int>::type = 0> + template < typename U, etl::enable_if_t< etl::is_constructible::value && !etl::is_same, etl::optional>::value + && !etl::is_same, etl::in_place_t>::value + && !etl::is_same, etl::nullopt_t>::value && etl::is_pod>::value, + int> = 0> ETL_CONSTEXPR14 optional(U&& value_) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) : impl_t(etl::forward(value_)) { @@ -1334,11 +1339,10 @@ namespace etl /// Converting constructor from value type. //*************************************************************************** template < typename U, - typename etl::enable_if< etl::is_constructible::value && !etl::is_same::type, etl::optional>::value - && !etl::is_same::type, etl::in_place_t>::value - && !etl::is_same::type, etl::nullopt_t>::value - && !etl::is_pod::type>::value, - int>::type = 0> + etl::enable_if_t< etl::is_constructible::value && !etl::is_same, etl::optional>::value + && !etl::is_same, etl::in_place_t>::value && !etl::is_same, etl::nullopt_t>::value + && !etl::is_pod>::value, + int> = 0> ETL_CONSTEXPR20_STL optional(U&& value_) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) : impl_t(etl::forward(value_)) { @@ -1446,11 +1450,9 @@ namespace etl //*************************************************************************** /// Converting assignment operator from value type. //*************************************************************************** - template < typename U, - typename etl::enable_if< etl::is_constructible::value && !etl::is_same::type, etl::optional>::value - && !etl::is_same::type, etl::nullopt_t>::value - && etl::is_pod::type>::value, - int>::type = 0> + template < typename U, etl::enable_if_t< etl::is_constructible::value && !etl::is_same, etl::optional>::value + && !etl::is_same, etl::nullopt_t>::value && etl::is_pod>::value, + int> = 0> ETL_CONSTEXPR14 optional& operator=(U&& value_) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) { impl_t::operator=(etl::forward(value_)); @@ -1462,10 +1464,9 @@ namespace etl /// Converting assignment operator from value type. //*************************************************************************** template < typename U, - typename etl::enable_if< etl::is_constructible::value && !etl::is_same::type, etl::optional>::value - && !etl::is_same::type, etl::nullopt_t>::value - && !etl::is_pod::type>::value, - int>::type = 0> + etl::enable_if_t< etl::is_constructible::value && !etl::is_same, etl::optional>::value + && !etl::is_same, etl::nullopt_t>::value && !etl::is_pod>::value, + int> = 0> ETL_CONSTEXPR20_STL optional& operator=(U&& value_) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) { impl_t::operator=(etl::forward(value_)); @@ -1519,6 +1520,124 @@ namespace etl { return this->has_value() ? this->operator->() + 1 : ETL_NULLPTR; } + +#if ETL_USING_CPP11 + template >, etl::enable_if_t::value, int> = 0> + auto transform(F&& f) & -> etl::optional + { + return transform_impl(etl::forward(f), *this); + } + + template >, etl::enable_if_t::value, int> = 0> + auto transform(F&& f) const& -> etl::optional + { + return transform_impl(etl::forward(f), *this); + } + + template >, etl::enable_if_t::value, int> = 0> + auto transform(F&& f) && -> etl::optional + { + return transform_impl(etl::forward(f), etl::move(*this)); + } + + template >, etl::enable_if_t::value, int> = 0> + auto transform(F&& f) const&& -> etl::optional + { + return transform_impl(etl::forward(f), etl::move(*this)); + } + + template >> + auto and_then(F&& f) & -> U + { + return and_then_impl(etl::forward(f), *this); + } + + 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 >> + auto and_then(F&& f) && -> U + { + return and_then_impl(etl::forward(f), etl::move(*this)); + } + + 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 >> + auto or_else(F&& f) & -> U + { + return or_else_impl(etl::forward(f), *this); + } + + 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 >> + auto or_else(F&& f) && -> U + { + return or_else_impl(etl::forward(f), etl::move(*this)); + } + + 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)); + } + + private: + + template < typename F, typename TOpt, typename TRet, typename TValueRef, typename = etl::enable_if_t::value>> + auto transform_impl(F&& f, TOpt&& opt) const -> etl::optional + { + if (opt.has_value()) + { + return etl::optional(etl::invoke(etl::forward(f), etl::forward(*opt))); + } + else + { + return etl::optional(); + } + } + + template < typename F, typename TOpt, typename TRet, typename TValueRef, + typename = etl::enable_if_t::value && etl::is_optional::value>> + auto and_then_impl(F&& f, TOpt&& opt) const -> TRet + { + if (opt.has_value()) + { + return etl::invoke(etl::forward(f), etl::forward(*opt)); + } + else + { + return TRet(); + } + } + + template < + typename F, typename TOpt, typename TRet, + typename = etl::enable_if_t< !etl::is_void::value && etl::is_optional::value && etl::is_same::value>> + auto or_else_impl(F&& f, TOpt&& opt) const -> TRet + { + if (opt.has_value()) + { + return TRet(etl::forward(opt)); + } + else + { + return etl::invoke(etl::forward(f)); + } + } +#endif }; #include "private/diagnostic_uninitialized_push.h" @@ -2132,17 +2251,17 @@ namespace etl /// This overload participates in overload resolution only if /// `etl::is_constructible_v` is true. //*************************************************************************** - template ::value, int>::type = 0, // + template ::value, int> = 0, // typename U = T, ETL_OPTIONAL_ENABLE_CPP14> - ETL_CONSTEXPR14 etl::optional make_optional(Args&&... args) // + ETL_CONSTEXPR14 etl::optional make_optional(Args&&... args) // ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) { return etl::optional(etl::in_place_t{}, etl::forward(args)...); } - template ::value, int>::type = 0, // - typename U = T, ETL_OPTIONAL_ENABLE_CPP20_STL> + template ::value, int> = 0, // + typename U = T, ETL_OPTIONAL_ENABLE_CPP20_STL> ETL_CONSTEXPR20_STL etl::optional make_optional(Args&&... args) // ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) { @@ -2156,17 +2275,17 @@ namespace etl /// This overload participates in overload resolution only if /// `etl::is_constructible_v&, Args...>` is true. //*************************************************************************** - template &, Args...>::value, int>::type = 0, // + template &, Args...>::value, int> = 0, // typename U = T, ETL_OPTIONAL_ENABLE_CPP14> ETL_CONSTEXPR14 etl::optional make_optional(std::initializer_list ilist, Args&&... args) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible&, Args...>::value)) { return etl::optional(etl::in_place_t{}, ilist, etl::forward(args)...); } - template &, Args...>::value, int>::type = 0, // - typename U = T, ETL_OPTIONAL_ENABLE_CPP20_STL> + template &, Args...>::value, int> = 0, // + typename U = T, ETL_OPTIONAL_ENABLE_CPP20_STL> ETL_CONSTEXPR20_STL etl::optional make_optional(std::initializer_list ilist, Args&&... args) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible&, Args...>::value)) { diff --git a/test/test_optional.cpp b/test/test_optional.cpp index e1e05f9c..9d95f8a1 100644 --- a/test/test_optional.cpp +++ b/test/test_optional.cpp @@ -1603,5 +1603,218 @@ namespace #endif } #endif + +#if ETL_USING_CPP11 + //************************************************************************* + TEST(test_transform) + { + etl::optional engaged(42); + etl::optional empty; + + etl::optional doubled = engaged.transform([](int i) { return i * 2; }); + CHECK_TRUE(doubled.has_value()); + CHECK_EQUAL(84, doubled.value()); + + etl::optional from_empty = empty.transform([](int i) { return i * 2; }); + CHECK_FALSE(from_empty.has_value()); + } + + //************************************************************************* + TEST(test_transform_const) + { + const etl::optional engaged(42); + const etl::optional empty; + + etl::optional doubled = engaged.transform([](int i) { return i * 2; }); + CHECK_TRUE(doubled.has_value()); + CHECK_EQUAL(84, doubled.value()); + + etl::optional from_empty = empty.transform([](int i) { return i * 2; }); + CHECK_FALSE(from_empty.has_value()); + } + + //************************************************************************* + TEST(test_transform_change_type) + { + etl::optional engaged(42); + etl::optional empty; + + etl::optional text = engaged.transform([](int) { return std::string("forty two"); }); + CHECK_TRUE(text.has_value()); + CHECK_EQUAL("forty two", text.value()); + + etl::optional text_from_empty = empty.transform([](int) { return std::string("forty two"); }); + CHECK_FALSE(text_from_empty.has_value()); + } + + //************************************************************************* + TEST(test_transform_non_pod) + { + etl::optional engaged(Data("transform")); + etl::optional empty; + + etl::optional length = engaged.transform([](const Data& d) { return d.value.size(); }); + CHECK_TRUE(length.has_value()); + CHECK_EQUAL(9U, length.value()); + + etl::optional length_from_empty = empty.transform([](const Data& d) { return d.value.size(); }); + CHECK_FALSE(length_from_empty.has_value()); + } + + //************************************************************************* + TEST(test_transform_move) + { + etl::optional engaged(DataM(42U)); + + etl::optional moved = etl::move(engaged).transform([](DataM&& d) { return DataM(etl::move(d)); }); + CHECK_TRUE(moved.has_value()); + CHECK_TRUE(moved.value().valid); + CHECK_EQUAL(42U, moved.value().value); + CHECK_FALSE(engaged.value().valid); + + etl::optional empty; + etl::optional from_empty = etl::move(empty).transform([](DataM&& d) { return DataM(etl::move(d)); }); + CHECK_FALSE(from_empty.has_value()); + } + + //************************************************************************* + TEST(test_and_then) + { + etl::optional engaged(42); + etl::optional empty; + + etl::optional incremented = engaged.and_then([](int i) { return etl::optional(i + 1); }); + CHECK_TRUE(incremented.has_value()); + CHECK_EQUAL(43, incremented.value()); + + etl::optional from_empty = empty.and_then([](int i) { return etl::optional(i + 1); }); + CHECK_FALSE(from_empty.has_value()); + + etl::optional rejected = engaged.and_then([](int) { return etl::optional(); }); + CHECK_FALSE(rejected.has_value()); + } + + //************************************************************************* + TEST(test_and_then_const) + { + const etl::optional engaged(42); + const etl::optional empty; + + etl::optional incremented = engaged.and_then([](int i) { return etl::optional(i + 1); }); + CHECK_TRUE(incremented.has_value()); + CHECK_EQUAL(43, incremented.value()); + + etl::optional from_empty = empty.and_then([](int i) { return etl::optional(i + 1); }); + CHECK_FALSE(from_empty.has_value()); + } + + //************************************************************************* + TEST(test_and_then_change_type) + { + etl::optional engaged(42); + etl::optional empty; + + etl::optional text = engaged.and_then([](int) { return etl::optional("forty two"); }); + CHECK_TRUE(text.has_value()); + CHECK_EQUAL("forty two", text.value()); + + etl::optional text_from_empty = empty.and_then([](int) { return etl::optional("forty two"); }); + CHECK_FALSE(text_from_empty.has_value()); + } + + //************************************************************************* + TEST(test_and_then_move) + { + etl::optional engaged(DataM(42U)); + + etl::optional value = etl::move(engaged).and_then([](DataM&& d) { return etl::optional(d.value); }); + CHECK_TRUE(value.has_value()); + CHECK_EQUAL(42U, value.value()); + + etl::optional empty; + etl::optional from_empty = etl::move(empty).and_then([](DataM&& d) { return etl::optional(d.value); }); + CHECK_FALSE(from_empty.has_value()); + } + + //************************************************************************* + TEST(test_or_else) + { + etl::optional engaged(42); + etl::optional empty; + bool called = false; + + etl::optional from_engaged = engaged.or_else( + [&called]() + { + called = true; + return etl::optional(99); + }); + CHECK_TRUE(from_engaged.has_value()); + CHECK_EQUAL(42, from_engaged.value()); + CHECK_FALSE(called); + + etl::optional from_empty = empty.or_else( + [&called]() + { + called = true; + return etl::optional(99); + }); + CHECK_TRUE(from_empty.has_value()); + CHECK_EQUAL(99, from_empty.value()); + CHECK_TRUE(called); + + etl::optional still_empty = empty.or_else([]() { return etl::optional(); }); + CHECK_FALSE(still_empty.has_value()); + } + + //************************************************************************* + TEST(test_or_else_const) + { + const etl::optional engaged(42); + const etl::optional empty; + + etl::optional from_engaged = engaged.or_else([]() { return etl::optional(99); }); + CHECK_TRUE(from_engaged.has_value()); + CHECK_EQUAL(42, from_engaged.value()); + + etl::optional from_empty = empty.or_else([]() { return etl::optional(99); }); + CHECK_TRUE(from_empty.has_value()); + CHECK_EQUAL(99, from_empty.value()); + } + + //************************************************************************* + TEST(test_or_else_move) + { + etl::optional engaged(DataM(42U)); + + etl::optional from_engaged = etl::move(engaged).or_else([]() { return etl::optional(DataM(99U)); }); + CHECK_TRUE(from_engaged.has_value()); + CHECK_EQUAL(42U, from_engaged.value().value); + + etl::optional empty; + etl::optional from_empty = etl::move(empty).or_else([]() { return etl::optional(DataM(99U)); }); + CHECK_TRUE(from_empty.has_value()); + CHECK_EQUAL(99U, from_empty.value().value); + } + + //************************************************************************* + TEST(test_monadic_chaining) + { + etl::optional engaged(8); + etl::optional empty; + + etl::optional result = engaged.transform([](int i) { return i * 2; }) + .and_then([](int i) { return etl::optional(i + 1); }) + .or_else([]() { return etl::optional(0); }); + CHECK_TRUE(result.has_value()); + CHECK_EQUAL(17, result.value()); + + etl::optional result_from_empty = empty.transform([](int i) { return i * 2; }) + .and_then([](int i) { return etl::optional(i + 1); }) + .or_else([]() { return etl::optional(123); }); + CHECK_TRUE(result_from_empty.has_value()); + CHECK_EQUAL(123, result_from_empty.value()); + } +#endif } } // namespace From e4fc36d6f28c23128013f7fc3b088053d770fc08 Mon Sep 17 00:00:00 2001 From: Hiten Gupta <140595019+hiten011@users.noreply.github.com> Date: Wed, 26 Aug 2026 20:34:46 +0930 Subject: [PATCH 48/77] Fix FreeRTOS.h/semphr.h include paths for ESP-IDF (fixes #1439) (#1570) mutex_freertos.h unconditionally included the vanilla FreeRTOS header layout (FreeRTOS.h and semphr.h at the include root). On ESP-IDF, and therefore Arduino-ESP32 as well since it's built on top of ESP-IDF, these headers are nested one level deeper under freertos/, so the unconditional include fails to compile on any ESP32 target with ETL_TARGET_OS_FREERTOS defined. Use __has_include to try the standard path first, fall back to the ESP-IDF nested path, and fail with a clear #error if neither is found, rather than a mysterious "file not found" from the compiler. Also resolves a pre-existing include-style inconsistency in this file: the old code mixed "FreeRTOS.h" (quotes) with (angle brackets). All includes now uniformly use angle brackets. --- include/etl/mutex/mutex_freertos.h | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/include/etl/mutex/mutex_freertos.h b/include/etl/mutex/mutex_freertos.h index 080b571a..6fde9d2c 100644 --- a/include/etl/mutex/mutex_freertos.h +++ b/include/etl/mutex/mutex_freertos.h @@ -31,8 +31,26 @@ SOFTWARE. #include "../platform.h" -#include "FreeRTOS.h" -#include +#if defined(__has_include) // Compiler supports __has_include + + #if __has_include() && __has_include() // Standard FreeRTOS include layout + #include + #include + + #elif __has_include() && __has_include() // ESP-IDF style include layout + #include + #include + + #else // Neither include layout found + #error "FreeRTOS.h and semphr.h not found. Please include path to FreeRTOS in your project settings" + #endif + +#else + + #include + #include + +#endif namespace etl { From c6c1ee1b2830a722804ec9f9398091502abc0222 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Wed, 26 Aug 2026 21:16:54 +0200 Subject: [PATCH 49/77] Add missing etl::ranges::distance overload (#1569) * Update _index.md * Update _index.md * Cherry pick the Docker fixes for PowerPC (#1563) Co-authored-by: John Wellbelove * Add missing etl::ranges::distance overload --------- Co-authored-by: John Wellbelove Co-authored-by: John Wellbelove --- docs/iterators/Iterator.md | 2 ++ docs/ranges/ranges.md | 57 ++++++++++++++++++++++++++++++++++++++ include/etl/iterator.h | 16 +++++++++++ test/test_iterator.cpp | 50 +++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+) diff --git a/docs/iterators/Iterator.md b/docs/iterators/Iterator.md index c95017b7..c6028f8c 100644 --- a/docs/iterators/Iterator.md +++ b/docs/iterators/Iterator.md @@ -133,6 +133,8 @@ ETL_CONSTEXPR17 typename std::iterator_traits::difference_type distance(TIterator first, TIterator last) ``` +See [Ranges](../../ranges/ranges) for the `etl::ranges` range access primitives, including `etl::ranges::distance`. + ## iterator A base class provided to simplify definitions of the required types for iterators. https://en.cppreference.com/w/cpp/iterator/iterator diff --git a/docs/ranges/ranges.md b/docs/ranges/ranges.md index 48b9b495..32cffcd8 100644 --- a/docs/ranges/ranges.md +++ b/docs/ranges/ranges.md @@ -119,6 +119,63 @@ Output: This first filters the even numbers and then squares them. Each `|` passes the result of the previous stage as input to the next view adaptor. +## Range Access and Primitives + +These are declared in the `etl::ranges` namespace in `iterator.h`, which is included by `ranges.h`. + +### Range access + +Function objects, equivalent to their `std::ranges` counterparts. + +| Name | Description | +|---|---| +| `begin`, `end` | The iterator to the first element, and the sentinel. | +| `cbegin`, `cend` | The const iterator to the first element, and the const sentinel. | +| `rbegin`, `rend` | The reverse iterator to the last element in forward order, and the past-the-end reverse iterator. | +| `crbegin`, `crend` | The const reverse iterator to the last element in forward order, and the const past-the-end reverse iterator. | +| `size` | The number of elements, as a `size_t`. | +| `ssize` | The number of elements, as a `ptrdiff_t`. | +| `empty` | Whether the range has no elements. | +| `data`, `cdata` | A pointer to the first element. | +| `distance` | The number of elements between two iterators, or in a range. | +| `advance` | Advances an iterator by a given distance and/or to a bound. | +| `next`, `prev` | An iterator advanced forwards or backwards by a given distance and/or to a bound. | + +`size` and `ssize` use the range's `size()` member if it has one; otherwise they count the elements by traversing the range, which requires at least a forward range. + +#### distance + +```cpp +// Iterator and sentinel of the same type. +template +constexpr etl::iter_difference_t distance(I first, I last) + +// Iterator and sentinel of different types. +template +constexpr etl::iter_difference_t distance(I first, S last) + +// Range. +template +constexpr etl::iter_difference_t> distance(R&& r) +``` + +The range overload uses the range's `size()` member if it has one; otherwise it counts the elements by traversing from `begin()` to `end()`. + +### Range primitives + +Alias templates that give the associated types of a range. + +| Name | Description | +|---|---| +| `iterator_t` | The iterator type. | +| `const_iterator_t` | The const iterator type. | +| `sentinel_t` | The sentinel type. | +| `const_sentinel_t` | The const sentinel type. | +| `range_size_t` | The size type. | +| `range_difference_t` | The difference type. | +| `range_value_t` | The element value type. | +| `range_reference_t` | The element reference type. | + ## Supported Views All views are in the `etl::ranges` namespace. Corresponding range adaptor objects are available in `etl::ranges::views`. diff --git a/include/etl/iterator.h b/include/etl/iterator.h index a990fb6c..01de257a 100644 --- a/include/etl/iterator.h +++ b/include/etl/iterator.h @@ -1656,6 +1656,22 @@ namespace etl } return n; } + + // Overload for ranges + template ()))> + constexpr etl::iter_difference_t operator()(R&& r) const + { + using U = etl::remove_cvref_t; + + if constexpr (has_size_member::value) + { + return static_cast>(r.size()); + } + else + { + return (*this)(ETL_OR_STD::begin(r), ETL_OR_STD::end(r)); + } + } }; struct size diff --git a/test/test_iterator.cpp b/test/test_iterator.cpp index db018c40..ed808582 100644 --- a/test/test_iterator.cpp +++ b/test/test_iterator.cpp @@ -29,6 +29,7 @@ SOFTWARE. #include "unit_test_framework.h" #include +#include #include #include #include @@ -1420,6 +1421,55 @@ namespace CHECK_EQUAL(1, *result); } + //************************************************************************* + // etl::ranges::distance(first, last) + //************************************************************************* + TEST(ranges_distance_iterator_sentinel) + { + int data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + CHECK_EQUAL(10, etl::ranges::distance(&data[0], &data[10])); + CHECK_EQUAL(4, etl::ranges::distance(&data[3], &data[7])); + CHECK_EQUAL(0, etl::ranges::distance(&data[3], &data[3])); + + non_random_iterator first(&data[0]); + non_random_sentinel last(&data[6]); + CHECK_EQUAL(6, etl::ranges::distance(first, last)); + } + + //************************************************************************* + // etl::ranges::distance(range) + //************************************************************************* + TEST(ranges_distance_range_with_size_member) + { + std::vector data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + CHECK_EQUAL(10, etl::ranges::distance(data)); + + const std::vector& cdata = data; + CHECK_EQUAL(10, etl::ranges::distance(cdata)); + + std::vector empty_data; + CHECK_EQUAL(0, etl::ranges::distance(empty_data)); + } + + //************************************************************************* + TEST(ranges_distance_c_array) + { + int data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + CHECK_EQUAL(10, etl::ranges::distance(data)); + } + + //************************************************************************* + TEST(ranges_distance_range_without_size_member) + { + std::forward_list data = {0, 1, 2, 3, 4}; + + CHECK_EQUAL(5, etl::ranges::distance(data)); + CHECK_EQUAL(etl::ranges::distance(data.begin(), data.end()), etl::ranges::distance(data)); + } + TEST(test_is_range) { std::vector vec; From bd0d7e67ad2ce081922d0e8b8cc6d27e9844de7f Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Thu, 27 Aug 2026 11:05:35 +0200 Subject: [PATCH 50/77] Drop Debian 11 (bullseye) support (#1571) Replaces PR https://github.com/ETLCPP/etl/pull/1519 --- .devcontainer/clang18/devcontainer.json | 2 +- .devcontainer/clang19/devcontainer.json | 2 +- .devcontainer/clang20/devcontainer.json | 2 +- .devcontainer/clang21/devcontainer.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.devcontainer/clang18/devcontainer.json b/.devcontainer/clang18/devcontainer.json index 5ff70469..3573703e 100644 --- a/.devcontainer/clang18/devcontainer.json +++ b/.devcontainer/clang18/devcontainer.json @@ -5,7 +5,7 @@ "build": { "dockerfile": "../Dockerfile", "args": { - "BASE_IMAGE_NAME": "silkeh/clang:18-bullseye", + "BASE_IMAGE_NAME": "silkeh/clang:18", "DEBIAN_SNAPSHOT": "none" }, "context": "../context" diff --git a/.devcontainer/clang19/devcontainer.json b/.devcontainer/clang19/devcontainer.json index bc617a36..4fdfdd8a 100644 --- a/.devcontainer/clang19/devcontainer.json +++ b/.devcontainer/clang19/devcontainer.json @@ -5,7 +5,7 @@ "build": { "dockerfile": "../Dockerfile", "args": { - "BASE_IMAGE_NAME": "silkeh/clang:19-bullseye", + "BASE_IMAGE_NAME": "silkeh/clang:19", "DEBIAN_SNAPSHOT": "none" }, "context": "../context" diff --git a/.devcontainer/clang20/devcontainer.json b/.devcontainer/clang20/devcontainer.json index 5522774f..13c4e962 100644 --- a/.devcontainer/clang20/devcontainer.json +++ b/.devcontainer/clang20/devcontainer.json @@ -5,7 +5,7 @@ "build": { "dockerfile": "../Dockerfile", "args": { - "BASE_IMAGE_NAME": "silkeh/clang:20-bullseye", + "BASE_IMAGE_NAME": "silkeh/clang:20", "DEBIAN_SNAPSHOT": "none" }, "context": "../context" diff --git a/.devcontainer/clang21/devcontainer.json b/.devcontainer/clang21/devcontainer.json index e09e5c3a..2018216b 100644 --- a/.devcontainer/clang21/devcontainer.json +++ b/.devcontainer/clang21/devcontainer.json @@ -5,7 +5,7 @@ "build": { "dockerfile": "../Dockerfile", "args": { - "BASE_IMAGE_NAME": "silkeh/clang:21-bullseye", + "BASE_IMAGE_NAME": "silkeh/clang:21", "DEBIAN_SNAPSHOT": "none" }, "context": "../context" From bfa29188c1dba61509ce766dd506d3171d60c9a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BE=D0=B9=D1=82=D0=BA=D0=BE=20=D0=92=D0=B0=D0=BB?= =?UTF-8?q?=D0=B5=D1=80=D0=B8=D0=B9=20=D0=9F=D0=B0=D0=B2=D0=BB=D0=BE=D0=B2?= =?UTF-8?q?=D0=B8=D1=87?= <87278254+VoytkoValera@users.noreply.github.com> Date: Thu, 27 Aug 2026 12:26:51 +0300 Subject: [PATCH 51/77] fix: Refactor FreeRTOS includes to use conditional (#1445) * Update _index.md * Update _index.md * Cherry pick the Docker fixes for PowerPC (#1563) Co-authored-by: John Wellbelove --------- Co-authored-by: John Wellbelove Co-authored-by: John Wellbelove Co-authored-by: Roland Reichwein From 74a95cf360a115eae0992e1314ea76538d2906cc Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Thu, 27 Aug 2026 13:17:43 +0200 Subject: [PATCH 52/77] For optional monadic operations, add docs, tests, and complete type (#1568) --- docs/utilities/optional.md | 70 ++++++++++++++++++++ include/etl/optional.h | 25 +++++++ test/test_optional.cpp | 131 +++++++++++++++++++++++++++++++++++++ 3 files changed, 226 insertions(+) diff --git a/docs/utilities/optional.md b/docs/utilities/optional.md index a12aac75..0c41a829 100644 --- a/docs/utilities/optional.md +++ b/docs/utilities/optional.md @@ -222,6 +222,64 @@ bool has_value() const Checks whether *this is in a valid state, i.e. whether the contained value is initialised. Returns `true` if valid, otherwise `false`. +## Monadic operations +C++11 and above. + +Each operation is supplied as four reference qualified overloads (`&`, `const&`, `&&`, `const&&`), +so that the contained value is passed to the supplied callable with the value category of `*this`. + +```cpp +template +etl::optional transform(F&& f) +``` +**Description** +If `*this` contains a value, returns an `etl::optional` containing the result of invoking `f` +with the contained value, otherwise returns an empty `etl::optional`. + +`U` is the result type of `f`, with any reference and cv qualification removed. + +### Note +Unlike `std::optional::transform`, which only removes cv qualification, a callable returning a +reference is accepted, and the referenced value is copied into the resulting optional. + +--- + +```cpp +template +U and_then(F&& f) +``` +**Description** +If `*this` contains a value, returns the result of invoking `f` with the contained value, +otherwise returns an empty `U`. + +`U` is the result type of `f`, with any reference and cv qualification removed, and must be an +`etl::optional`. The overload takes no part in overload resolution otherwise. + +--- + +```cpp +template +U or_else(F&& f) +``` +**Description** +If `*this` contains a value, returns a copy of `*this`, otherwise returns the result of invoking +`f` with no arguments. + +`U` is the result type of `f`, with any reference and cv qualification removed, and must be an +`etl::optional` whose `value_type` is `T`. The overload takes no part in overload resolution +otherwise. + +### Example + +```cpp +etl::optional opt = 6; + +etl::optional result = opt.and_then([](int i) { return i > 0 ? etl::optional(i * 2) : etl::optional(); }) + .or_else([]() { return etl::optional(1); }) + .transform([](int i) { return double(i) / 2.0; }); +// result contains 6.0 +``` + ## Non-member functions ```cpp @@ -269,3 +327,15 @@ 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 +struct etl::is_optional; + +template +inline constexpr bool etl::is_optional_v; // C++17 and above +``` +**Description** +`true` if `T` is an `etl::optional`, otherwise `false`. Any cv qualification on `T` is ignored. diff --git a/include/etl/optional.h b/include/etl/optional.h index e4372453..d0c10b44 100644 --- a/include/etl/optional.h +++ b/include/etl/optional.h @@ -50,6 +50,11 @@ namespace etl template class optional; + //***************************************************************************** + /// Determines if a type is an etl::optional. + /// Ignores any cv qualification on the type being tested. + ///\ingroup utilities + //***************************************************************************** template struct is_optional : etl::false_type { @@ -60,6 +65,26 @@ namespace etl { }; + template + struct is_optional > : etl::true_type + { + }; + + template + struct is_optional > : etl::true_type + { + }; + + template + struct is_optional > : etl::true_type + { + }; + +#if ETL_USING_CPP17 + template + inline constexpr bool is_optional_v = etl::is_optional::value; +#endif + //***************************************************************************** /// A null option type. ///\ingroup utilities diff --git a/test/test_optional.cpp b/test/test_optional.cpp index 9d95f8a1..a6ffc5a7 100644 --- a/test/test_optional.cpp +++ b/test/test_optional.cpp @@ -1815,6 +1815,137 @@ namespace CHECK_TRUE(result_from_empty.has_value()); CHECK_EQUAL(123, result_from_empty.value()); } + + //************************************************************************* + TEST(test_is_optional) + { + CHECK_TRUE((etl::is_optional>::value)); + CHECK_TRUE((etl::is_optional>::value)); + CHECK_TRUE((etl::is_optional>::value)); + CHECK_TRUE((etl::is_optional>::value)); + CHECK_TRUE((etl::is_optional>::value)); + + CHECK_FALSE((etl::is_optional::value)); + CHECK_FALSE((etl::is_optional::value)); + CHECK_FALSE((etl::is_optional*>::value)); + CHECK_FALSE((etl::is_optional&>::value)); + + #if ETL_USING_CPP17 + CHECK_TRUE((etl::is_optional_v>)); + CHECK_TRUE((etl::is_optional_v>)); + CHECK_FALSE((etl::is_optional_v)); + #endif + } + + //************************************************************************* + TEST(test_transform_const_rvalue) + { + const etl::optional engaged(42); + const etl::optional empty; + + etl::optional doubled = etl::move(engaged).transform([](const int&& i) { return i * 2; }); + CHECK_TRUE(doubled.has_value()); + CHECK_EQUAL(84, doubled.value()); + CHECK_TRUE(engaged.has_value()); + CHECK_EQUAL(42, engaged.value()); + + etl::optional from_empty = etl::move(empty).transform([](const int&& i) { return i * 2; }); + CHECK_FALSE(from_empty.has_value()); + } + + //************************************************************************* + TEST(test_and_then_const_rvalue) + { + const etl::optional engaged(42); + const etl::optional empty; + + etl::optional incremented = etl::move(engaged).and_then([](const int&& i) { return etl::optional(i + 1); }); + CHECK_TRUE(incremented.has_value()); + CHECK_EQUAL(43, incremented.value()); + + etl::optional from_empty = etl::move(empty).and_then([](const int&& i) { return etl::optional(i + 1); }); + CHECK_FALSE(from_empty.has_value()); + } + + //************************************************************************* + TEST(test_or_else_const_rvalue) + { + const etl::optional engaged(42); + const etl::optional empty; + + etl::optional from_engaged = etl::move(engaged).or_else([]() { return etl::optional(99); }); + CHECK_TRUE(from_engaged.has_value()); + CHECK_EQUAL(42, from_engaged.value()); + + etl::optional from_empty = etl::move(empty).or_else([]() { return etl::optional(99); }); + CHECK_TRUE(from_empty.has_value()); + CHECK_EQUAL(99, from_empty.value()); + } + + //************************************************************************* + /// Unlike std::optional, a callable returning a reference is accepted. + /// The reference is stripped from the result type and the value is copied. + //************************************************************************* + TEST(test_transform_callable_returning_reference) + { + etl::optional engaged(42); + + auto result = engaged.transform([](int& i) -> int& { return i; }); + + CHECK_TRUE((etl::is_same, decltype(result)>::value)); + CHECK_TRUE(result.has_value()); + CHECK_EQUAL(42, result.value()); + + // The result is a copy, not an alias of the source. + result.value() = 1; + CHECK_EQUAL(42, engaged.value()); + } + + //************************************************************************* + TEST(test_and_then_callable_returning_reference) + { + etl::optional engaged(42); + etl::optional empty; + + auto f = [](int i) -> etl::optional& + { + static etl::optional shared; + shared = i + 1; + return shared; + }; + + auto result = engaged.and_then(f); + + CHECK_TRUE((etl::is_same, decltype(result)>::value)); + CHECK_TRUE(result.has_value()); + CHECK_EQUAL(43, result.value()); + + auto from_empty = empty.and_then(f); + CHECK_FALSE(from_empty.has_value()); + } + + //************************************************************************* + TEST(test_or_else_callable_returning_reference) + { + etl::optional engaged(42); + etl::optional empty; + + auto f = []() -> etl::optional& + { + static etl::optional shared(99); + return shared; + }; + + auto from_engaged = engaged.or_else(f); + + CHECK_TRUE((etl::is_same, decltype(from_engaged)>::value)); + CHECK_TRUE(from_engaged.has_value()); + CHECK_EQUAL(42, from_engaged.value()); + + auto from_empty = empty.or_else(f); + CHECK_TRUE(from_empty.has_value()); + CHECK_EQUAL(99, from_empty.value()); + } #endif } } // namespace From 1a1f259ccb1c99b9edc1789a3ecb18b1dce8ef79 Mon Sep 17 00:00:00 2001 From: Konstantinos Kanavouras Date: Fri, 28 Aug 2026 10:41:00 +0200 Subject: [PATCH 53/77] Remove spurious link in documentation (#1576) * Update _index.md * Update _index.md * Cherry pick the Docker fixes for PowerPC (#1563) Co-authored-by: John Wellbelove * Remove spurious link --------- Co-authored-by: John Wellbelove Co-authored-by: John Wellbelove --- docs/containers/lists/intrusive-forward-list.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/containers/lists/intrusive-forward-list.md b/docs/containers/lists/intrusive-forward-list.md index 5f1ee051..09b8a30d 100644 --- a/docs/containers/lists/intrusive-forward-list.md +++ b/docs/containers/lists/intrusive-forward-list.md @@ -8,7 +8,7 @@ weight: 4 Similar to: `std::forward_list` {{< /callout >}} -An intrusive forward list.https://www.messenger.com/t/837634551/ +An intrusive forward list. ```cpp template From 49a9ebde788339a3021323ee5fe63a78213cfb0e Mon Sep 17 00:00:00 2001 From: Sergei Date: Fri, 28 Aug 2026 12:21:00 +0300 Subject: [PATCH 54/77] Fixed building and unit tests run under XCode 2027 Beta 6 (#1572) * Update _index.md * Update _index.md * Cherry pick the Docker fixes for PowerPC (#1563) Co-authored-by: John Wellbelove * Fixed building and unit tests run under XCode 2027 Beta 6 (AppleClang 21.0.0.21000333): - Compilation failures: - `etl::ideque::iterator::operator[]` index type should be `difference_type` (instead of `size_t`). - random-access `etl::poly_span::iterator` is missing `operator[]`. - Unit tests failures of `test_multimap` suit: - fixed `equal_range`-related tests to properly verify result range (via new `Check_Equal` overload helper). - fixed `find`-related tests to not depend on particular order of `second` values (via new `Check_ContainsPair` helper). See note 1,2) at [cppreference](https://cppreference.com/cpp/container/multimap/find) about order. * PR review fixes. --------- Co-authored-by: John Wellbelove Co-authored-by: John Wellbelove --- include/etl/deque.h | 15 +-- include/etl/poly_span.h | 10 ++ test/test_deque.cpp | 5 +- test/test_multimap.cpp | 208 ++++++++++++++++++++-------------------- 4 files changed, 122 insertions(+), 116 deletions(-) diff --git a/include/etl/deque.h b/include/etl/deque.h index ffa689a9..c3ff33d9 100644 --- a/include/etl/deque.h +++ b/include/etl/deque.h @@ -348,16 +348,7 @@ namespace etl } //*************************************************** - reference operator[](size_t i) - { - iterator result(*this); - result += i; - - return *result; - } - - //*************************************************** - const_reference operator[](size_t i) const + reference operator[](difference_type i) const { iterator result(*this); result += i; @@ -607,10 +598,10 @@ namespace etl } //*************************************************** - const_reference operator[](size_t i) const + const_reference operator[](difference_type i) const { const_iterator result(*this); - result += static_cast(i); + result += i; return *result; } diff --git a/include/etl/poly_span.h b/include/etl/poly_span.h index 0c73ffd4..544befa5 100644 --- a/include/etl/poly_span.h +++ b/include/etl/poly_span.h @@ -77,6 +77,7 @@ namespace etl typedef ptrdiff_t difference_type; typedef TBase* pointer; typedef TBase& reference; + typedef const TBase& const_reference; typedef ETL_OR_STD::random_access_iterator_tag iterator_category; //***************************************** @@ -114,6 +115,15 @@ namespace etl return ptr; } + //*************************************************** + reference operator[](difference_type i) const + { + iterator result(*this); + result += i; + + return *result; + } + //***************************************** iterator& operator++() { diff --git a/test/test_deque.cpp b/test/test_deque.cpp index 796b21ac..d03580bd 100644 --- a/test/test_deque.cpp +++ b/test/test_deque.cpp @@ -2389,9 +2389,10 @@ namespace #include "etl/private/diagnostic_uninitialized_push.h" // Verify operator[] returns values matching sequential access - for (size_t i = 0; i < cdata.size(); ++i) + DataNDC::difference_type it_idx = 0; + for (size_t i = 0; i < cdata.size(); ++i, ++it_idx) { - CHECK(cit[i] == cdata[i]); + CHECK(cit[it_idx] == cdata[i]); } // Verify const_iterator operator[] returns const_reference diff --git a/test/test_multimap.cpp b/test/test_multimap.cpp index 4273932b..3cc897ae 100644 --- a/test/test_multimap.cpp +++ b/test/test_multimap.cpp @@ -103,6 +103,34 @@ namespace return true; } + template + bool Check_Equal(R1 range1, R2 range2) + { + while (range1.first != range1.second) + { + if (range2.first == range2.second) + { + return false; + } + auto& pair1 = *range1.first; + auto& pair2 = *range2.first; + if ((pair1.first != pair2.first) || (pair1.second != pair2.second)) + { + return false; + } + + ++range1.first; + ++range2.first; + } + + return range2.first == range2.second; + } + template + bool Check_ContainsPair(T1 begin, T1 end, const T2& pair) + { + return std::find_if(begin, end, [&pair](typename T1::reference item) { return (item.first == pair.first) && (item.second == pair.second); }) + != end; + } #include "etl/private/diagnostic_null_dereference_push.h" //************************************************************************* @@ -694,13 +722,7 @@ namespace ETL_OR_STD::pair compare_result = compare_data.equal_range("1"); // Check that both return the same return results - CHECK(compare_result.first->first == data_result.first->first); - CHECK(compare_result.first->second == data_result.first->second); - CHECK(compare_result.second->first == data_result.second->first); - CHECK(compare_result.second->second == data_result.second->second); - - bool isEqual = Check_Equal(data.begin(), data.end(), compare_data.begin()); - + const bool isEqual = Check_Equal(data_result, compare_result); CHECK(isEqual); } @@ -718,13 +740,7 @@ namespace ETL_OR_STD::pair compare_result = compare_data.equal_range("1"); // Check that both return the same return results - CHECK(compare_result.first->first == data_result.first->first); - CHECK(compare_result.first->second == data_result.first->second); - CHECK(compare_result.second->first == data_result.second->first); - CHECK(compare_result.second->second == data_result.second->second); - - bool isEqual = Check_Equal(data.begin(), data.end(), compare_data.begin()); - + const bool isEqual = Check_Equal(data_result, compare_result); CHECK(isEqual); } @@ -739,13 +755,7 @@ namespace ETL_OR_STD::pair compare_result = compare_data.equal_range("2"); // Check that both return the same return results - CHECK(compare_result.first->first == data_result.first->first); - CHECK(compare_result.first->second == data_result.first->second); - CHECK(compare_result.second->first == data_result.second->first); - CHECK(compare_result.second->second == data_result.second->second); - - bool isEqual = Check_Equal(data.begin(), data.end(), compare_data.begin()); - + const bool isEqual = Check_Equal(data_result, compare_result); CHECK(isEqual); } @@ -763,13 +773,7 @@ namespace ETL_OR_STD::pair compare_result = compare_data.equal_range("2"); // Check that both return the same return results - CHECK(compare_result.first->first == data_result.first->first); - CHECK(compare_result.first->second == data_result.first->second); - CHECK(compare_result.second->first == data_result.second->first); - CHECK(compare_result.second->second == data_result.second->second); - - bool isEqual = Check_Equal(data.begin(), data.end(), compare_data.begin()); - + const bool isEqual = Check_Equal(data_result, compare_result); CHECK(isEqual); } @@ -1022,47 +1026,47 @@ namespace Compare_Data compare_data(initial_data.begin(), initial_data.end()); Data data(initial_data.begin(), initial_data.end()); - Data::iterator i_data = data.find("0"); - Compare_Data::iterator i_compare = compare_data.find("0"); + Data::iterator i_data = data.find("0"); + auto i_compare = compare_data.equal_range("0"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find("1"); - i_compare = compare_data.find("1"); + i_compare = compare_data.equal_range("1"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find("2"); - i_compare = compare_data.find("2"); + i_compare = compare_data.equal_range("2"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find("3"); - i_compare = compare_data.find("3"); + i_compare = compare_data.equal_range("3"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find("."); - i_compare = compare_data.find("."); + i_compare = compare_data.equal_range("."); // Check that both return successful return results - CHECK(data.end() == i_data); - CHECK(compare_data.end() == i_compare); + CHECK(i_data == data.end()); + CHECK(i_compare.first == i_compare.second); i_data = data.find("A"); - i_compare = compare_data.find("A"); + i_compare = compare_data.equal_range("A"); // Check that both return successful return results - CHECK(data.end() == i_data); - CHECK(compare_data.end() == i_compare); + CHECK(i_data == data.end()); + CHECK(i_compare.first == i_compare.second); } //************************************************************************* @@ -1075,46 +1079,46 @@ namespace CMap compare_data(initial_data.begin(), initial_data.end()); EMap::iterator i_data = data.find(Key("0")); - CMap::iterator i_compare = compare_data.find("0"); + auto i_compare = compare_data.equal_range("0"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find(Key("1")); - i_compare = compare_data.find("1"); + i_compare = compare_data.equal_range("1"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find(Key("2")); - i_compare = compare_data.find("2"); + i_compare = compare_data.equal_range("2"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find(Key("3")); - i_compare = compare_data.find("3"); + i_compare = compare_data.equal_range("3"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find(Key(".")); - i_compare = compare_data.find("."); + i_compare = compare_data.equal_range("."); // Check that both return successful return results - CHECK(data.end() == i_data); - CHECK(compare_data.end() == i_compare); + CHECK(i_data == data.end()); + CHECK(i_compare.first == i_compare.second); i_data = data.find(Key("A")); - i_compare = compare_data.find("A"); + i_compare = compare_data.equal_range("A"); // Check that both return successful return results - CHECK(data.end() == i_data); - CHECK(compare_data.end() == i_compare); + CHECK(i_data == data.end()); + CHECK(i_compare.first == i_compare.second); } //************************************************************************* @@ -1123,47 +1127,47 @@ namespace const Compare_Data compare_data(initial_data.begin(), initial_data.end()); const Data data(initial_data.begin(), initial_data.end()); - Data::const_iterator i_data = data.find("0"); - Compare_Data::const_iterator i_compare = compare_data.find("0"); + Data::const_iterator i_data = data.find("0"); + auto i_compare = compare_data.equal_range("0"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find("1"); - i_compare = compare_data.find("1"); + i_compare = compare_data.equal_range("1"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find("2"); - i_compare = compare_data.find("2"); + i_compare = compare_data.equal_range("2"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find("3"); - i_compare = compare_data.find("3"); + i_compare = compare_data.equal_range("3"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find("."); - i_compare = compare_data.find("."); + i_compare = compare_data.equal_range("."); // Check that both return successful return results - CHECK(data.end() == i_data); - CHECK(compare_data.end() == i_compare); + CHECK(i_data == data.end()); + CHECK(i_compare.first == i_compare.second); i_data = data.find("A"); - i_compare = compare_data.find("A"); + i_compare = compare_data.equal_range("A"); // Check that both return successful return results - CHECK(data.end() == i_data); - CHECK(compare_data.end() == i_compare); + CHECK(i_data == data.end()); + CHECK(i_compare.first == i_compare.second); } //************************************************************************* @@ -1176,46 +1180,46 @@ namespace const CMap compare_data(initial_data.begin(), initial_data.end()); EMap::const_iterator i_data = data.find(Key("0")); - CMap::const_iterator i_compare = compare_data.find("0"); + auto i_compare = compare_data.equal_range("0"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find(Key("1")); - i_compare = compare_data.find("1"); + i_compare = compare_data.equal_range("1"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find(Key("2")); - i_compare = compare_data.find("2"); + i_compare = compare_data.equal_range("2"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find(Key("3")); - i_compare = compare_data.find("3"); + i_compare = compare_data.equal_range("3"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find(Key(".")); - i_compare = compare_data.find("."); + i_compare = compare_data.equal_range("."); // Check that both return successful return results - CHECK(data.end() == i_data); - CHECK(compare_data.end() == i_compare); + CHECK(i_data == data.end()); + CHECK(i_compare.first == i_compare.second); i_data = data.find(Key("A")); - i_compare = compare_data.find("A"); + i_compare = compare_data.equal_range("A"); // Check that both return successful return results - CHECK(data.end() == i_data); - CHECK(compare_data.end() == i_compare); + CHECK(i_data == data.end()); + CHECK(i_compare.first == i_compare.second); } //************************************************************************* From fe4db13587bfdc1b0b0bdb857c7dde4534c9e862 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Fri, 28 Aug 2026 12:49:01 +0200 Subject: [PATCH 55/77] Make etl::variant trivially copyable (#1574) --- include/etl/private/variant_variadic.h | 171 ++++++++++++++++--------- test/test_variant_variadic.cpp | 29 +++++ 2 files changed, 140 insertions(+), 60 deletions(-) diff --git a/include/etl/private/variant_variadic.h b/include/etl/private/variant_variadic.h index b2b8771e..5f6444b2 100644 --- a/include/etl/private/variant_variadic.h +++ b/include/etl/private/variant_variadic.h @@ -555,113 +555,94 @@ namespace etl } }; - //*************************************************************************** - /// variant_base specialisation for trivially destructible types. - /// Uses variadic_union storage. Destructor is trivial (defaulted), making - /// the variant a literal type eligible for constexpr / ROM placement. - /// No operation function pointer is needed since destroy/copy/move are - /// all handled without indirection for trivially destructible types. - //*************************************************************************** + //******************************************* + /// Trait: are all types trivially copyable? + //******************************************* template - struct variant_base + struct are_all_trivially_copyable : etl::conjunction...> + { + }; + + //*************************************************************************** + /// Storage and copy/move operations used by the trivially destructible + /// variant_base specialisation. + /// The general case defines the copy and move operations in terms of the + /// operations of the currently active alternative. + //*************************************************************************** + template + struct variant_trivially_destructible_base { variadic_union data; size_t type_id; - constexpr variant_base() noexcept + constexpr variant_trivially_destructible_base() noexcept : data() , type_id(variant_npos) { } - constexpr variant_base(size_t id) noexcept + constexpr variant_trivially_destructible_base(size_t id) noexcept : data() , type_id(id) { } template - constexpr variant_base(etl::in_place_index_t, T&& value, - size_t id) noexcept(etl::is_nothrow_constructible, T>::value) + constexpr variant_trivially_destructible_base(etl::in_place_index_t, T&& value, + size_t id) noexcept(etl::is_nothrow_constructible, T>::value) : data(etl::in_place_index_t{}, etl::forward(value)) , type_id(id) { } - variant_base(const variant_base& other) noexcept(etl::conjunction...>::value) + variant_trivially_destructible_base(const variant_trivially_destructible_base& other) noexcept( + etl::conjunction...>::value) : data() , type_id(other.type_id) { - copy_construct_from(other, is_all_trivially_copyable{}); + copy_construct_from(other); } - variant_base(variant_base&& other) noexcept(etl::conjunction...>::value) + variant_trivially_destructible_base(variant_trivially_destructible_base&& other) noexcept( + etl::conjunction...>::value) : data() , type_id(other.type_id) { - move_construct_from(other, is_all_trivially_copyable{}); + move_construct_from(other); } - variant_base& operator=(const variant_base& other) noexcept(etl::conjunction...>::value - && etl::conjunction...>::value) + variant_trivially_destructible_base& + operator=(const variant_trivially_destructible_base& other) noexcept(etl::conjunction...>::value + && etl::conjunction...>::value) { if (this != &other) { - copy_assign_from(other, is_all_trivially_copyable{}); + copy_assign_from(other); } return *this; } - variant_base& operator=(variant_base&& other) noexcept(etl::conjunction...>::value - && etl::conjunction...>::value) + variant_trivially_destructible_base& + operator=(variant_trivially_destructible_base&& other) noexcept(etl::conjunction...>::value + && etl::conjunction...>::value) { if (this != &other) { - move_assign_from(other, is_all_trivially_copyable{}); + move_assign_from(other); } return *this; } - ~variant_base() = default; + ~variant_trivially_destructible_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. + // Dispatch to the active alternative's own copy/move operations. + // 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::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) + void copy_construct_from(const variant_trivially_destructible_base& other) { if (other.type_id != variant_npos) { @@ -669,7 +650,7 @@ namespace etl } } - void move_construct_from(variant_base& other, etl::false_type) + void move_construct_from(variant_trivially_destructible_base& other) { if (other.type_id != variant_npos) { @@ -677,7 +658,7 @@ namespace etl } } - void copy_assign_from(const variant_base& other, etl::false_type) + void copy_assign_from(const variant_trivially_destructible_base& other) { if ((type_id != variant_npos) && (type_id == other.type_id)) { @@ -698,7 +679,7 @@ namespace etl } } - void move_assign_from(variant_base& other, etl::false_type) + void move_assign_from(variant_trivially_destructible_base& other) { if ((type_id != variant_npos) && (type_id == other.type_id)) { @@ -720,6 +701,76 @@ namespace etl } }; + //*************************************************************************** + /// Specialisation for when all of the types are trivially copyable. + /// All of the copy and move operations are defaulted, so that they are + /// trivial and the variant itself is trivially copyable. See P0602R4. + //*************************************************************************** + template + struct variant_trivially_destructible_base + { + variadic_union data; + size_t type_id; + + constexpr variant_trivially_destructible_base() noexcept + : data() + , type_id(variant_npos) + { + } + + constexpr variant_trivially_destructible_base(size_t id) noexcept + : data() + , type_id(id) + { + } + + template + constexpr variant_trivially_destructible_base(etl::in_place_index_t, T&& value, + size_t id) noexcept(etl::is_nothrow_constructible, T>::value) + : data(etl::in_place_index_t{}, etl::forward(value)) + , type_id(id) + { + } + + variant_trivially_destructible_base(const variant_trivially_destructible_base&) = default; + variant_trivially_destructible_base(variant_trivially_destructible_base&&) = default; + variant_trivially_destructible_base& operator=(const variant_trivially_destructible_base&) = default; + variant_trivially_destructible_base& operator=(variant_trivially_destructible_base&&) = default; + ~variant_trivially_destructible_base() = default; + }; + + //*************************************************************************** + /// variant_base specialisation for trivially destructible types. + /// Uses variadic_union storage. Destructor is trivial (defaulted), making + /// the variant a literal type eligible for constexpr / ROM placement. + /// No operation function pointer is needed since destroy/copy/move are + /// all handled without indirection for trivially destructible types. + /// The copy and move operations are inherited so that they are trivial + /// when every alternative is trivially copyable. + //*************************************************************************** + template + struct variant_base : public variant_trivially_destructible_base::value, TTypes...> + { + typedef variant_trivially_destructible_base::value, TTypes...> base_t; + + constexpr variant_base() noexcept + : base_t() + { + } + + constexpr variant_base(size_t id) noexcept + : base_t(id) + { + } + + template + constexpr variant_base(etl::in_place_index_t, T&& value, + size_t id) noexcept(etl::is_nothrow_constructible, T>::value) + : base_t(etl::in_place_index_t{}, etl::forward(value), id) + { + } + }; + //******************************************* /// Trait: are all types copy constructible? //******************************************* diff --git a/test/test_variant_variadic.cpp b/test/test_variant_variadic.cpp index 0680f79a..8e81a606 100644 --- a/test/test_variant_variadic.cpp +++ b/test/test_variant_variadic.cpp @@ -1324,6 +1324,35 @@ namespace CHECK(etl::get(variant_1).move_assigned); } + //************************************************************************* + // Issue #1567: the variant propagates trivial copyability from its + // alternatives, as required by P0602R4. + TEST(test_trivially_copyable) + { + // Fundamental types are trivially copyable in every ETL configuration. + CHECK((std::is_trivially_copyable >::value)); + CHECK((std::is_trivially_copy_constructible >::value)); + CHECK((std::is_trivially_move_constructible >::value)); + CHECK((std::is_trivially_copy_assignable >::value)); + CHECK((std::is_trivially_move_assignable >::value)); + + // An alternative that is not trivially copyable prevents propagation. + CHECK((!std::is_trivially_copyable >::value)); + + // The variant still copies and moves correctly. + typedef etl::variant trivial_variant; + + trivial_variant variant_1(1.5); + trivial_variant variant_2(variant_1); + CHECK_EQUAL(2U, variant_2.index()); + CHECK_EQUAL(1.5, etl::get(variant_2)); + + trivial_variant variant_3; + variant_3 = variant_2; + CHECK_EQUAL(2U, variant_3.index()); + CHECK_EQUAL(1.5, etl::get(variant_3)); + } + //************************************************************************* // Issue #1512: even though the copy operations are disabled at compile // time, a move-only variant must still move construct / move assign From fa238db580f67a146a5f110dc00779b7a1678955 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Mon, 31 Aug 2026 20:08:25 +0200 Subject: [PATCH 56/77] Fix sanitizer builds (#1579) Besides, remove unused ASAN_OPTIONS from github workflows. Further, activate sanitizers for C++23 workflows, at the places where we also activated -O3 optimization. Not activating everywhere for CI efficiency reasons for now. Note that the sanitizers are activated on clang C++23 workflows only, since there is a remaining issue with GCC in delegate_cpp11. --- .github/workflows/clang-c++11.yml | 3 - .github/workflows/clang-c++14.yml | 3 - .github/workflows/clang-c++17.yml | 3 - .github/workflows/clang-c++20.yml | 8 --- .github/workflows/clang-c++23.yml | 32 +++++----- .github/workflows/clang-c++26.yml | 9 --- .github/workflows/gcc-c++11.yml | 2 - .github/workflows/gcc-c++14.yml | 2 - .github/workflows/gcc-c++17.yml | 2 - .github/workflows/gcc-c++20.yml | 4 -- .github/workflows/gcc-c++23.yml | 4 -- .github/workflows/gcc-c++26.yml | 4 -- include/etl/expected.h | 6 ++ include/etl/inplace_function.h | 6 ++ include/etl/invoke.h | 4 ++ include/etl/message_packet.h | 9 +++ include/etl/not_null.h | 21 ++++++- include/etl/nullptr.h | 22 +++++++ include/etl/optional.h | 6 ++ include/etl/private/delegate_cpp11.h | 2 +- include/etl/private/variant_variadic.h | 19 ++++++ .../etl/profiles/determine_builtin_support.h | 15 +++++ include/etl/to_arithmetic.h | 4 +- include/etl/type_traits.h | 5 +- test/test_delegate.cpp | 59 +++++++++++++++++++ test/test_optional.cpp | 8 +++ 26 files changed, 196 insertions(+), 66 deletions(-) diff --git a/.github/workflows/clang-c++11.yml b/.github/workflows/clang-c++11.yml index 6ca727ef..39c0a0a4 100644 --- a/.github/workflows/clang-c++11.yml +++ b/.github/workflows/clang-c++11.yml @@ -50,7 +50,6 @@ jobs: run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=11 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -79,7 +78,6 @@ jobs: export CXX=clang++ export CXXFLAGS="-stdlib=libc++" export LDFLAGS="-stdlib=libc++" - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=11 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -101,7 +99,6 @@ jobs: run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=11 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" diff --git a/.github/workflows/clang-c++14.yml b/.github/workflows/clang-c++14.yml index 3ddb0cfa..e7909302 100644 --- a/.github/workflows/clang-c++14.yml +++ b/.github/workflows/clang-c++14.yml @@ -50,7 +50,6 @@ jobs: run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=14 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -79,7 +78,6 @@ jobs: export CXX=clang++ export CXXFLAGS="-stdlib=libc++" export LDFLAGS="-stdlib=libc++" - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=14 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -101,7 +99,6 @@ jobs: run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=14 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" diff --git a/.github/workflows/clang-c++17.yml b/.github/workflows/clang-c++17.yml index ccec532b..54b05957 100644 --- a/.github/workflows/clang-c++17.yml +++ b/.github/workflows/clang-c++17.yml @@ -50,7 +50,6 @@ jobs: run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -79,7 +78,6 @@ jobs: export CXX=clang++ export CXXFLAGS="-stdlib=libc++" export LDFLAGS="-stdlib=libc++" - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -101,7 +99,6 @@ jobs: run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" diff --git a/.github/workflows/clang-c++20.yml b/.github/workflows/clang-c++20.yml index 0d3fa3f7..eb6d6bf5 100644 --- a/.github/workflows/clang-c++20.yml +++ b/.github/workflows/clang-c++20.yml @@ -62,7 +62,6 @@ jobs: export CXX=clang++-17 export CXXFLAGS="-stdlib=libc++" export LDFLAGS="-stdlib=libc++" - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=20 ./ clang-17 --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -91,7 +90,6 @@ jobs: run: | export CC=clang-17 export CXX=clang++-17 - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=20 ./ clang-17 --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -120,7 +118,6 @@ jobs: run: | export CC=clang-17 export CXX=clang++-17 - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./ clang-17 --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -149,7 +146,6 @@ jobs: run: | export CC=clang-17 export CXX=clang++-17 - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./ clang-17 --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -171,7 +167,6 @@ jobs: run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=20 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -193,7 +188,6 @@ jobs: run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=20 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -215,7 +209,6 @@ jobs: run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -237,7 +230,6 @@ jobs: run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" diff --git a/.github/workflows/clang-c++23.yml b/.github/workflows/clang-c++23.yml index d25a346c..97ba5af3 100644 --- a/.github/workflows/clang-c++23.yml +++ b/.github/workflows/clang-c++23.yml @@ -57,12 +57,19 @@ jobs: export CXX=clang++ export CXXFLAGS="-stdlib=libc++" export LDFLAGS="-stdlib=libc++" - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ + cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 -DETL_ENABLE_SANITIZER=ON ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests + # The system libc++/libc++abi are not built with ASan. Exception objects + # (e.g. std::runtime_error's reference counted string) are allocated with + # operator new inside libc++.so, but released with free() inside + # libc++abi.so, which AddressSanitizer reports as an alloc-dealloc-mismatch. + # This is a libc++ packaging issue, not an ETL defect, so the check is + # disabled for this job only. + env: + ASAN_OPTIONS: alloc_dealloc_mismatch=0 run: ./test/etl_tests -v build-clang-cpp23-linux-no-stl: @@ -79,8 +86,7 @@ jobs: run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ + cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 -DETL_ENABLE_SANITIZER=ON ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -101,8 +107,7 @@ jobs: run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ + cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 -DETL_ENABLE_SANITIZER=ON ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -123,8 +128,7 @@ jobs: run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ + cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 -DETL_ENABLE_SANITIZER=ON ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -145,8 +149,7 @@ jobs: run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ + cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 -DETL_ENABLE_SANITIZER=ON ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -167,8 +170,7 @@ jobs: run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ + cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 -DETL_ENABLE_SANITIZER=ON ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -189,8 +191,7 @@ jobs: run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ + cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 -DETL_ENABLE_SANITIZER=ON ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -211,8 +212,7 @@ jobs: run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ + cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 -DETL_ENABLE_SANITIZER=ON ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" diff --git a/.github/workflows/clang-c++26.yml b/.github/workflows/clang-c++26.yml index e118c66a..5905289a 100644 --- a/.github/workflows/clang-c++26.yml +++ b/.github/workflows/clang-c++26.yml @@ -55,7 +55,6 @@ jobs: export CXX=clang++ && \ export CXXFLAGS=-stdlib=libc++ && \ export LDFLAGS=-stdlib=libc++ && \ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=26 ./ && \ clang --version && \ make -j \$(getconf _NPROCESSORS_ONLN) && \ @@ -77,7 +76,6 @@ jobs: cd /workspaces/etl && \ export CC=clang && \ export CXX=clang++ && \ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=26 ./ && \ clang --version && \ make -j \$(getconf _NPROCESSORS_ONLN) && \ @@ -99,7 +97,6 @@ jobs: cd /workspaces/etl && \ export CC=clang && \ export CXX=clang++ && \ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=26 ./ && \ clang --version && \ make -j \$(getconf _NPROCESSORS_ONLN) && \ @@ -121,7 +118,6 @@ jobs: cd /workspaces/etl && \ export CC=clang && \ export CXX=clang++ && \ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./ && \ clang --version && \ make -j \$(getconf _NPROCESSORS_ONLN) && \ @@ -143,7 +139,6 @@ jobs: cd /workspaces/etl && \ export CC=clang && \ export CXX=clang++ && \ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./ && \ clang --version && \ make -j \$(getconf _NPROCESSORS_ONLN) && \ @@ -163,7 +158,6 @@ jobs: run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=26 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -185,7 +179,6 @@ jobs: run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=26 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -207,7 +200,6 @@ jobs: run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -229,7 +221,6 @@ jobs: run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" diff --git a/.github/workflows/gcc-c++11.yml b/.github/workflows/gcc-c++11.yml index 4d33a909..71a5809e 100644 --- a/.github/workflows/gcc-c++11.yml +++ b/.github/workflows/gcc-c++11.yml @@ -49,7 +49,6 @@ jobs: - name: Build run: | git fetch - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=11 ./ @@ -72,7 +71,6 @@ jobs: - name: Build run: | git fetch - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=11 ./ diff --git a/.github/workflows/gcc-c++14.yml b/.github/workflows/gcc-c++14.yml index bc7cca67..0cb6dc2c 100644 --- a/.github/workflows/gcc-c++14.yml +++ b/.github/workflows/gcc-c++14.yml @@ -48,7 +48,6 @@ jobs: - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=14 ./ @@ -70,7 +69,6 @@ jobs: - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=14 ./ diff --git a/.github/workflows/gcc-c++17.yml b/.github/workflows/gcc-c++17.yml index 1378fc69..4968a87d 100644 --- a/.github/workflows/gcc-c++17.yml +++ b/.github/workflows/gcc-c++17.yml @@ -48,7 +48,6 @@ jobs: - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./ @@ -70,7 +69,6 @@ jobs: - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./ diff --git a/.github/workflows/gcc-c++20.yml b/.github/workflows/gcc-c++20.yml index 6e004456..68be97d8 100644 --- a/.github/workflows/gcc-c++20.yml +++ b/.github/workflows/gcc-c++20.yml @@ -48,7 +48,6 @@ jobs: - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=20 ./ @@ -70,7 +69,6 @@ jobs: - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=20 ./ @@ -92,7 +90,6 @@ jobs: - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./ @@ -114,7 +111,6 @@ jobs: - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./ diff --git a/.github/workflows/gcc-c++23.yml b/.github/workflows/gcc-c++23.yml index a7e29e56..65cf7dc4 100644 --- a/.github/workflows/gcc-c++23.yml +++ b/.github/workflows/gcc-c++23.yml @@ -48,7 +48,6 @@ jobs: - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc-14 export CXX=g++-14 cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ @@ -70,7 +69,6 @@ jobs: - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc-14 export CXX=g++-14 cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ @@ -92,7 +90,6 @@ jobs: - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc-14 export CXX=g++-14 cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ @@ -114,7 +111,6 @@ jobs: - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc-14 export CXX=g++-14 cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ diff --git a/.github/workflows/gcc-c++26.yml b/.github/workflows/gcc-c++26.yml index 63677261..38506641 100644 --- a/.github/workflows/gcc-c++26.yml +++ b/.github/workflows/gcc-c++26.yml @@ -50,7 +50,6 @@ jobs: run: | docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ cd /workspaces/etl && \ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ export CC=gcc && \ export CXX=g++ && \ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=26 ./ && \ @@ -72,7 +71,6 @@ jobs: run: | docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ cd /workspaces/etl && \ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ export CC=gcc && \ export CXX=g++ && \ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=26 ./ && \ @@ -94,7 +92,6 @@ jobs: run: | docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ cd /workspaces/etl && \ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ export CC=gcc && \ export CXX=g++ && \ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./ && \ @@ -116,7 +113,6 @@ jobs: run: | docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ cd /workspaces/etl && \ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ export CC=gcc && \ export CXX=g++ && \ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./ && \ diff --git a/include/etl/expected.h b/include/etl/expected.h index 1feb1487..49b99aad 100644 --- a/include/etl/expected.h +++ b/include/etl/expected.h @@ -1435,6 +1435,11 @@ namespace etl return lhs.error() == rhs.error(); } +#include "private/diagnostic_uninitialized_push.h" + //******************************************* + // The error is only read when the expected does not have a value, in which + // case it is always fully constructed. GCC can emit a false positive + // -Wmaybe-uninitialized when these are inlined at high optimisation levels. //******************************************* template ETL_CONSTEXPR14 bool operator==(const etl::expected& lhs, const etl::expected& rhs) @@ -1460,6 +1465,7 @@ namespace etl } return lhs.error() == rhs.error(); } +#include "private/diagnostic_pop.h" //******************************************* template diff --git a/include/etl/inplace_function.h b/include/etl/inplace_function.h index 63760d15..e9211702 100644 --- a/include/etl/inplace_function.h +++ b/include/etl/inplace_function.h @@ -1126,8 +1126,13 @@ namespace etl { } + #include "private/diagnostic_uninitialized_push.h" //************************************************************************* // clone_from + // + // GCC can emit a false positive -Wmaybe-uninitialized for the source + // storage when this is inlined into a copy constructor at high + // optimisation levels. The source object is always fully constructed. //************************************************************************* template void clone_from(const etl::inplace_function& other) @@ -1139,6 +1144,7 @@ namespace etl vtable->copy(&other.storage, &storage); } } + #include "private/diagnostic_pop.h" //************************************************************************* // move_from diff --git a/include/etl/invoke.h b/include/etl/invoke.h index 52ad388c..d2644748 100644 --- a/include/etl/invoke.h +++ b/include/etl/invoke.h @@ -39,6 +39,8 @@ SOFTWARE. #if ETL_USING_CPP11 + #include "private/diagnostic_array_bounds_push.h" + namespace etl { //**************************************************************************** @@ -387,5 +389,7 @@ namespace etl #endif } // namespace etl + #include "private/diagnostic_pop.h" + #endif // ETL_USING_CPP11 #endif // ETL_INVOKE_INCLUDED diff --git a/include/etl/message_packet.h b/include/etl/message_packet.h index fdaed2b3..de56fb9c 100644 --- a/include/etl/message_packet.h +++ b/include/etl/message_packet.h @@ -305,6 +305,7 @@ namespace etl (add_new_message_type(etl::move(msg)) || ...); } + #include "private/diagnostic_array_bounds_push.h" #include "private/diagnostic_uninitialized_push.h" //******************************************** /// Only enabled for types that are in the typelist. @@ -318,6 +319,9 @@ namespace etl } #include "private/diagnostic_pop.h" + #include "private/diagnostic_pop.h" + + #include "private/diagnostic_array_bounds_push.h" #include "private/diagnostic_uninitialized_push.h" //******************************************** template @@ -336,6 +340,9 @@ namespace etl } #include "private/diagnostic_pop.h" + #include "private/diagnostic_pop.h" + + #include "private/diagnostic_array_bounds_push.h" #include "private/diagnostic_uninitialized_push.h" //******************************************** template @@ -352,6 +359,8 @@ namespace etl return false; } } + #include "private/diagnostic_pop.h" + #include "private/diagnostic_pop.h" typename etl::aligned_storage::type data; diff --git a/include/etl/not_null.h b/include/etl/not_null.h index dc1ada2f..de0ebe22 100644 --- a/include/etl/not_null.h +++ b/include/etl/not_null.h @@ -35,6 +35,7 @@ SOFTWARE. #include "error_handler.h" #include "exception.h" #include "memory.h" +#include "nullptr.h" #include "type_traits.h" namespace etl @@ -95,7 +96,16 @@ namespace etl ETL_CONSTEXPR14 explicit not_null(underlying_type ptr_) : ptr(ptr_) { - ETL_ASSERT(ptr_ != ETL_NULLPTR, ETL_ERROR(not_null_contains_null)); + if (etl::is_constant_evaluated()) + { + // ETL_IS_FOLDED_NULL keeps the compile time rejection of null working + // when the undefined behaviour sanitizer instruments the comparison. + ETL_ASSERT(!ETL_IS_FOLDED_NULL(ptr_), ETL_ERROR(not_null_contains_null)); + } + else + { + ETL_ASSERT(ptr_ != ETL_NULLPTR, ETL_ERROR(not_null_contains_null)); + } } //********************************* @@ -112,7 +122,14 @@ namespace etl //********************************* ETL_CONSTEXPR14 not_null& operator=(underlying_type rhs) { - ETL_ASSERT_OR_RETURN_VALUE(rhs != ETL_NULLPTR, ETL_ERROR(not_null_contains_null), *this); + if (etl::is_constant_evaluated()) + { + ETL_ASSERT_OR_RETURN_VALUE(!ETL_IS_FOLDED_NULL(rhs), ETL_ERROR(not_null_contains_null), *this); + } + else + { + ETL_ASSERT_OR_RETURN_VALUE(rhs != ETL_NULLPTR, ETL_ERROR(not_null_contains_null), *this); + } ptr = rhs; diff --git a/include/etl/nullptr.h b/include/etl/nullptr.h index 581305ae..57f1c8bd 100644 --- a/include/etl/nullptr.h +++ b/include/etl/nullptr.h @@ -78,4 +78,26 @@ namespace etl #endif } // namespace etl +//***************************************************************************** +/// True when the compiler can fold the comparison at compile time and it yields +/// null. +/// +/// Normally the comparison folds for any pointer that is a constant expression, +/// so this is equivalent to a plain null check. Under GCC's undefined behaviour +/// sanitizer, however, the comparison is instrumented and only folds for a null +/// pointer constant, not for the address of an object or function. +/// __builtin_constant_p filters out the latter, which would otherwise make the +/// enclosing expression non-constant and reject perfectly valid code. +/// +/// Only meaningful during constant evaluation. At run time +/// __builtin_constant_p is false for any pointer that is not a compile time +/// constant, so this reports false for a genuine null pointer. Callers must +/// use a plain comparison against ETL_NULLPTR on the run time path. +//***************************************************************************** +#if ETL_USING_BUILTIN_CONSTANT_P + #define ETL_IS_FOLDED_NULL(p) (__builtin_constant_p((p) == ETL_NULLPTR) && ((p) == ETL_NULLPTR)) +#else + #define ETL_IS_FOLDED_NULL(p) ((p) == ETL_NULLPTR) +#endif + #endif diff --git a/include/etl/optional.h b/include/etl/optional.h index d0c10b44..f675354e 100644 --- a/include/etl/optional.h +++ b/include/etl/optional.h @@ -703,6 +703,11 @@ namespace etl } #endif +#include "private/diagnostic_uninitialized_push.h" + //******************************* + // GCC may warn that 'valid' is used uninitialised when the storage of a + // nested optional is inspected through an inlined destructor chain. + // The flag is always initialised by the constructors, so this is a false positive. //******************************* ETL_CONSTEXPR20_STL void destroy() @@ -713,6 +718,7 @@ namespace etl valid = false; } } +#include "private/diagnostic_pop.h" //******************************* union union_type diff --git a/include/etl/private/delegate_cpp11.h b/include/etl/private/delegate_cpp11.h index eafbde77..c351da9c 100644 --- a/include/etl/private/delegate_cpp11.h +++ b/include/etl/private/delegate_cpp11.h @@ -492,7 +492,7 @@ namespace etl //************************************************************************* /// Assignment //************************************************************************* - delegate& operator=(const delegate& rhs) = default; + ETL_CONSTEXPR14 delegate& operator=(const delegate& rhs) = default; //************************************************************************* /// Create from Lambda or Functor. diff --git a/include/etl/private/variant_variadic.h b/include/etl/private/variant_variadic.h index 5f6444b2..aaedb064 100644 --- a/include/etl/private/variant_variadic.h +++ b/include/etl/private/variant_variadic.h @@ -94,6 +94,15 @@ namespace etl template struct variant_operations { + #include "diagnostic_uninitialized_push.h" + //************************************************************************* + // destroy + // + // GCC can emit a false positive -Wmaybe-uninitialized when this is inlined + // into a variant destructor at high optimisation levels, as it cannot + // prove that the branch for an inactive alternative is never taken. + // The alternative identified by type_id is always fully constructed. + //************************************************************************* static void destroy(char* data, size_t type_id) { if (type_id == Index) @@ -105,6 +114,7 @@ namespace etl variant_operations::destroy(data, type_id); } } + #include "diagnostic_pop.h" static void copy(char* dst, const char* src, size_t type_id) { @@ -156,6 +166,14 @@ namespace etl private: + #include "diagnostic_uninitialized_push.h" + //************************************************************************* + // The *_impl functions below read the alternative identified by type_id, + // which is always fully constructed. GCC can emit a false positive + // -Wmaybe-uninitialized when these are inlined at high optimisation + // levels, as it cannot prove that the branch for an inactive alternative + // is never taken. + //************************************************************************* static void copy_impl(char* dst, const char* src, etl::true_type) { ::new (dst) THead(*reinterpret_cast(src)); @@ -183,6 +201,7 @@ namespace etl } static void move_assign_impl(char*, const char*, etl::false_type) {} + #include "diagnostic_pop.h" }; //******************************************* diff --git a/include/etl/profiles/determine_builtin_support.h b/include/etl/profiles/determine_builtin_support.h index 3db1a474..6340e6e3 100644 --- a/include/etl/profiles/determine_builtin_support.h +++ b/include/etl/profiles/determine_builtin_support.h @@ -232,6 +232,10 @@ SOFTWARE. #define ETL_USING_BUILTIN_IS_CONSTANT_EVALUATED __has_builtin(__builtin_is_constant_evaluated) #endif + #if !defined(ETL_USING_BUILTIN_CONSTANT_P) + #define ETL_USING_BUILTIN_CONSTANT_P __has_builtin(__builtin_constant_p) + #endif + #if !defined(ETL_USING_BUILTIN_BIT_CAST) #define ETL_USING_BUILTIN_BIT_CAST __has_builtin(__builtin_bit_cast) #endif @@ -444,6 +448,16 @@ SOFTWARE. #define ETL_USING_BUILTIN_BUILTIN_IS_CPP_TRIVIALLY_RELOCATABLE 0 #endif +// __builtin_constant_p predates __has_builtin on GCC and Clang, so enable it for +// those compilers when __has_builtin was not available to detect it. +#if !defined(ETL_USING_BUILTIN_CONSTANT_P) && (defined(__GNUC__) || defined(__clang__)) + #define ETL_USING_BUILTIN_CONSTANT_P 1 +#endif + +#if !defined(ETL_USING_BUILTIN_CONSTANT_P) + #define ETL_USING_BUILTIN_CONSTANT_P 0 +#endif + #if !defined(ETL_USING_BUILTIN_BIT_CAST) #define ETL_USING_BUILTIN_BIT_CAST 0 #endif @@ -470,6 +484,7 @@ namespace etl static ETL_CONSTANT bool using_builtin_is_trivially_copyable = (ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE == 1); static ETL_CONSTANT bool using_builtin_underlying_type = (ETL_USING_BUILTIN_UNDERLYING_TYPE == 1); static ETL_CONSTANT bool using_builtin_is_constant_evaluated = (ETL_USING_BUILTIN_IS_CONSTANT_EVALUATED == 1); + static ETL_CONSTANT bool using_builtin_constant_p = (ETL_USING_BUILTIN_CONSTANT_P == 1); static ETL_CONSTANT bool using_builtin_memcpy = (ETL_USING_BUILTIN_MEMCPY == 1); static ETL_CONSTANT bool using_builtin_memmove = (ETL_USING_BUILTIN_MEMMOVE == 1); static ETL_CONSTANT bool using_builtin_memset = (ETL_USING_BUILTIN_MEMSET == 1); diff --git a/include/etl/to_arithmetic.h b/include/etl/to_arithmetic.h index 941fc09b..bba59f88 100644 --- a/include/etl/to_arithmetic.h +++ b/include/etl/to_arithmetic.h @@ -713,7 +713,9 @@ namespace etl const uvalue_t uvalue = static_cast(accumulator_result.value()); // Convert from the accumulator type to the desired type. - result = (is_negative ? static_cast(static_cast(0) - static_cast(uvalue)) : etl::bit_cast(uvalue)); + // The negation is performed on the unsigned type to avoid signed overflow + // when the value is the most negative value for the type. + result = (is_negative ? etl::bit_cast(static_cast(static_cast(0) - uvalue)) : etl::bit_cast(uvalue)); } } } diff --git a/include/etl/type_traits.h b/include/etl/type_traits.h index 0c9da447..87b7607b 100644 --- a/include/etl/type_traits.h +++ b/include/etl/type_traits.h @@ -3603,8 +3603,9 @@ namespace etl template struct is_nothrow_relocatable { - // In builtins mode, conservatively use trivially_relocatable as the definition - static ETL_CONSTANT bool value = etl::is_trivially_relocatable::value; + static ETL_CONSTANT bool value = etl::is_trivially_relocatable::value + || (etl::is_nothrow_move_constructible::type>::value + && etl::is_nothrow_destructible::type>::value); }; #elif defined(ETL_USER_DEFINED_TYPE_TRAITS) && !defined(ETL_USE_TYPE_TRAITS_BUILTINS) diff --git a/test/test_delegate.cpp b/test/test_delegate.cpp index c4e870bd..60a8965e 100644 --- a/test/test_delegate.cpp +++ b/test/test_delegate.cpp @@ -2183,6 +2183,65 @@ namespace } #endif + #if ETL_USING_CPP17 + //************************************************************************* + // Verify that copy assigning a delegate, and comparing against a default + // constructed one, are usable in a constant expression. + //************************************************************************* + TEST(test_constexpr_copy_assignment_and_clear) + { + using delegate_type = etl::delegate; + + // Copy assignment from a bound delegate. + constexpr auto copy_assigned = []() constexpr + { + const delegate_type source(&free_void); + delegate_type d; + d = source; + return d; + }; + + constexpr delegate_type d1 = copy_assigned(); + + static_assert(d1.is_valid(), "a copy assigned delegate should be valid"); + static_assert(d1 == delegate_type(&free_void), "a copy assigned delegate should compare equal to its source"); + + // Copy assignment from a default constructed delegate, and comparing an + // unbound delegate against another one. Neither may inspect the null stub + // pointer, which the undefined behaviour sanitizer makes non-constant. + constexpr auto cleared = []() constexpr + { + delegate_type d(&free_void); + d = delegate_type(); + return d; + }; + + constexpr delegate_type d2 = cleared(); + + static_assert(!d2.is_valid(), "a delegate assigned from a default constructed one should be invalid"); + static_assert(d2 == delegate_type(), "an unbound delegate should compare equal to a default constructed one"); + static_assert(d2 != d1, "an unbound delegate should not compare equal to a bound one"); + + // The same operations must still behave identically at run time. + delegate_type d3; + d3 = d1; + CHECK_TRUE(d3.is_valid()); + CHECK(d3 == d1); + + d3 = delegate_type(); + CHECK_FALSE(d3.is_valid()); + CHECK(d3 == delegate_type()); + + // Assigning a null function pointer clears the delegate at run time. + // This is deliberately not checked in a constant expression: the null + // test cannot be folded there when the sanitizer instruments it. + delegate_type d4(&free_void); + d4 = static_cast(ETL_NULLPTR); + CHECK_FALSE(d4.is_valid()); + CHECK(d4 == delegate_type()); + } + #endif + TEST(test_delegate_clear_equals_default_constructed) { using delegate_type = etl::delegate; diff --git a/test/test_optional.cpp b/test/test_optional.cpp index a6ffc5a7..905de3cb 100644 --- a/test/test_optional.cpp +++ b/test/test_optional.cpp @@ -35,6 +35,12 @@ SOFTWARE. #include #include +// GCC can emit false positive -Wmaybe-uninitialized warnings inside the test data +// types when optional's storage is inspected through inlined, never-taken branches. +// The diagnostic is raised late (after inlining), so the suppression has to cover the +// whole translation unit. It is popped at the end of the file. +#include "etl/private/diagnostic_uninitialized_push.h" + #include "data.h" #include "etl/algorithm.h" #include "etl/optional.h" @@ -1949,3 +1955,5 @@ namespace #endif } } // namespace + +#include "etl/private/diagnostic_pop.h" From 06e54f6b23a2fc69e4e722f29756d7a506a5d08e Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Mon, 31 Aug 2026 20:09:16 +0200 Subject: [PATCH 57/77] Add missing type traits (#1573) etl::is_scoped_enum etl::is_trivially_default_constructible etl::is_trivially_move_constructible etl::is_trivially_move_assignable etl::is_trivially_assignable etl::is_layout_compatible etl::is_pointer_interconvertible_base_of Co-authored-by: John Wellbelove --- docs/types/type-traits.md | 124 +++++++ .../etl/profiles/determine_builtin_support.h | 41 +++ include/etl/type_traits.h | 325 +++++++++++++++++- test/test_type_traits.cpp | 197 +++++++++++ 4 files changed, 682 insertions(+), 5 deletions(-) diff --git a/docs/types/type-traits.md b/docs/types/type-traits.md index 98326be6..dc95aca1 100644 --- a/docs/types/type-traits.md +++ b/docs/types/type-traits.md @@ -42,6 +42,8 @@ Not all traits have been defined as some rely on compiler intrinsics that are no `is_aggregate` `is_trivial` `is_standard_layout` +`is_layout_compatible` +`is_pointer_interconvertible_base_of` `is_destructible` `is_nothrow_destructible` `has_virtual_destructor` @@ -72,6 +74,7 @@ Not all traits have been defined as some rely on compiler intrinsics that are no `declvar` `20.28.0` `common_type` `is_enum` `20.30.0` +`is_scoped_enum` `20.50.0` `underlying_type` `20.42.0`  Unless the ETL is set to use builtins, the user must specialise the template for their enumerations. @@ -87,8 +90,12 @@ The following will be defined according to the C++ standard and user defined mac `is_move_constructible` `is_trivially_constructible` `is_trivially_copy_constructible` +`is_trivially_default_constructible` +`is_trivially_move_constructible` `is_trivially_destructible` +`is_trivially_assignable` `is_trivially_copy_assignable` +`is_trivially_move_assignable` `is_trivially_copyable` ## Scenario 1 @@ -875,3 +882,120 @@ template inline constexpr bool is_scalar_v = etl::is_scalar::value; ``` C++17 + +## is_scoped_enum +From: `20.50.0` + +```cpp +template +struct is_scoped_enum +``` +Checks if `T` is a scoped enumeration type; that is, an enumeration type that is not implicitly convertible to an integer. +The result is found in the member `value`. + +```cpp +template +inline constexpr bool is_scoped_enum_v = etl::is_scoped_enum::value; +``` +C++17 + +## is_trivially_default_constructible +From: `20.50.0` + +```cpp +template +struct is_trivially_default_constructible +``` +Checks if `T` is default constructible and the default construction is trivial. +The result is found in the member `value`. + +```cpp +template +inline constexpr bool is_trivially_default_constructible_v = etl::is_trivially_default_constructible::value; +``` +C++17 + +## is_trivially_move_constructible +From: `20.50.0` + +```cpp +template +struct is_trivially_move_constructible +``` +Checks if `T` is move constructible and the move construction is trivial. +The result is found in the member `value`. + +```cpp +template +inline constexpr bool is_trivially_move_constructible_v = etl::is_trivially_move_constructible::value; +``` +C++17 + +## is_trivially_move_assignable +From: `20.50.0` + +```cpp +template +struct is_trivially_move_assignable +``` +Checks if `T` is move assignable and the move assignment is trivial. +The result is found in the member `value`. + +```cpp +template +inline constexpr bool is_trivially_move_assignable_v = etl::is_trivially_move_assignable::value; +``` +C++17 + +## is_trivially_assignable + +```cpp +template +struct is_trivially_assignable +``` +Checks if an expression of type `T2` can be assigned to an lvalue expression of type `T1`, and that the assignment is trivial. +The result is found in the member `value`. + See Scenarios 1 to 4 above for how this trait is defined when the STL or the compiler built-ins are unavailable. + +```cpp +template +inline constexpr bool is_trivially_assignable_v = etl::is_trivially_assignable::value; +``` +C++17 + +## is_layout_compatible +From: `20.50.0` + +```cpp +template +struct is_layout_compatible +``` +Checks if `T` and `U` are layout-compatible types. +The result is found in the member `value`. + When using the STL with C++20 or above, this is defined as `std::is_layout_compatible`. + Otherwise it requires compiler support (the `__is_layout_compatible` intrinsic). When neither is available, this trait is not defined, as there is no portable way of determining layout compatibility. + +```cpp +template +inline constexpr bool is_layout_compatible_v = etl::is_layout_compatible::value; +``` +C++17 + +## is_pointer_interconvertible_base_of +From: `20.50.0` + +```cpp +template +struct is_pointer_interconvertible_base_of +``` +Checks if `TBase` is a pointer-interconvertible base class of `TDerived`; that is, every object of type `TDerived` has its `TBase` base class subobject at the same address as the complete object. +The result is found in the member `value`. + When using the STL with C++20 or above, this is defined as `std::is_pointer_interconvertible_base_of`. + Otherwise it requires compiler support (the `__is_pointer_interconvertible_base_of` intrinsic). When neither is available, this trait is not defined, as there is no portable way of determining pointer interconvertibility. + Note that, as required by the standard, the program is ill-formed if `TDerived` is a complete non-union class type and `TBase` is an incomplete class type. The trait cannot be used to probe possibly-incomplete types. + +```cpp +template +inline constexpr bool is_pointer_interconvertible_base_of_v = etl::is_pointer_interconvertible_base_of::value; +``` +C++17 diff --git a/include/etl/profiles/determine_builtin_support.h b/include/etl/profiles/determine_builtin_support.h index 6340e6e3..ad681fb5 100644 --- a/include/etl/profiles/determine_builtin_support.h +++ b/include/etl/profiles/determine_builtin_support.h @@ -53,6 +53,10 @@ SOFTWARE. #define ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE 1 #endif + #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE) + #define ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE 1 + #endif + #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE) #define ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE 1 #endif @@ -77,6 +81,14 @@ SOFTWARE. #define ETL_USING_BUILTIN_IS_TRIVIAL 1 #endif + #if !defined(ETL_USING_BUILTIN_IS_LAYOUT_COMPATIBLE) + #define ETL_USING_BUILTIN_IS_LAYOUT_COMPATIBLE 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_POINTER_INTERCONVERTIBLE_BASE_OF) + #define ETL_USING_BUILTIN_IS_POINTER_INTERCONVERTIBLE_BASE_OF 1 + #endif + #if !defined(ETL_USING_BUILTIN_IS_UNION) #define ETL_USING_BUILTIN_IS_UNION 1 #endif @@ -168,6 +180,10 @@ SOFTWARE. #define ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE (__has_builtin(__has_trivial_constructor) || __has_builtin(__is_trivially_constructible)) #endif + #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE) + #define ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE __has_builtin(__is_trivially_assignable) + #endif + #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE) #define ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE (__has_builtin(__has_trivial_destructor) || __has_builtin(__is_trivially_destructible)) #endif @@ -192,6 +208,14 @@ SOFTWARE. #define ETL_USING_BUILTIN_IS_TRIVIAL __has_builtin(__is_trivial) #endif + #if !defined(ETL_USING_BUILTIN_IS_LAYOUT_COMPATIBLE) + #define ETL_USING_BUILTIN_IS_LAYOUT_COMPATIBLE __has_builtin(__is_layout_compatible) + #endif + + #if !defined(ETL_USING_BUILTIN_IS_POINTER_INTERCONVERTIBLE_BASE_OF) + #define ETL_USING_BUILTIN_IS_POINTER_INTERCONVERTIBLE_BASE_OF __has_builtin(__is_pointer_interconvertible_base_of) + #endif + #if !defined(ETL_USING_BUILTIN_IS_UNION) #define ETL_USING_BUILTIN_IS_UNION __has_builtin(__is_union) #endif @@ -283,6 +307,10 @@ SOFTWARE. #define ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE 1 #endif + #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE) + #define ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE 1 + #endif + #if !defined(ETL_USING_BUILTIN_IS_STANDARD_LAYOUT) #define ETL_USING_BUILTIN_IS_STANDARD_LAYOUT 1 #endif @@ -352,6 +380,10 @@ SOFTWARE. #define ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE 0 #endif +#if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE) + #define ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE 0 +#endif + #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE) #define ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE 0 #endif @@ -376,6 +408,14 @@ SOFTWARE. #define ETL_USING_BUILTIN_IS_TRIVIAL 0 #endif +#if !defined(ETL_USING_BUILTIN_IS_LAYOUT_COMPATIBLE) + #define ETL_USING_BUILTIN_IS_LAYOUT_COMPATIBLE 0 +#endif + +#if !defined(ETL_USING_BUILTIN_IS_POINTER_INTERCONVERTIBLE_BASE_OF) + #define ETL_USING_BUILTIN_IS_POINTER_INTERCONVERTIBLE_BASE_OF 0 +#endif + #if !defined(ETL_USING_BUILTIN_IS_UNION) #define ETL_USING_BUILTIN_IS_UNION 0 #endif @@ -480,6 +520,7 @@ namespace etl static ETL_CONSTANT bool using_builtin_is_nothrow_constructible = (ETL_USING_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE == 1); static ETL_CONSTANT bool using_builtin_is_nothrow_assignable = (ETL_USING_BUILTIN_IS_NOTHROW_ASSIGNABLE == 1); static ETL_CONSTANT bool using_builtin_is_trivially_constructible = (ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE == 1); + static ETL_CONSTANT bool using_builtin_is_trivially_assignable = (ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE == 1); static ETL_CONSTANT bool using_builtin_is_trivially_destructible = (ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE == 1); static ETL_CONSTANT bool using_builtin_is_trivially_copyable = (ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE == 1); static ETL_CONSTANT bool using_builtin_underlying_type = (ETL_USING_BUILTIN_UNDERLYING_TYPE == 1); diff --git a/include/etl/type_traits.h b/include/etl/type_traits.h index 87b7607b..ea9823a7 100644 --- a/include/etl/type_traits.h +++ b/include/etl/type_traits.h @@ -916,6 +916,38 @@ namespace etl #endif #endif + //*************************************************************************** + /// is_layout_compatible + /// Requires compiler support. Without it there is no portable way to + /// determine whether two types are layout compatible, so the trait is not defined. + #if ETL_USING_BUILTIN_IS_LAYOUT_COMPATIBLE + template + struct is_layout_compatible : etl::bool_constant<__is_layout_compatible(T, U)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_layout_compatible_v = etl::is_layout_compatible::value; + #endif + #endif + + //*************************************************************************** + /// is_pointer_interconvertible_base_of + /// Requires compiler support. Without it there is no portable way to + /// determine pointer interconvertibility, so the trait is not defined. + #if ETL_USING_BUILTIN_IS_POINTER_INTERCONVERTIBLE_BASE_OF + template + struct is_pointer_interconvertible_base_of : etl::bool_constant<__is_pointer_interconvertible_base_of(TBase, TDerived)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_pointer_interconvertible_base_of_v = etl::is_pointer_interconvertible_base_of::value; + #endif + #endif + //*************************************************************************** /// conditional template @@ -1667,6 +1699,20 @@ namespace etl inline constexpr bool is_nothrow_convertible_v = etl::is_nothrow_convertible::value; #endif + //*************************************************************************** + /// is_scoped_enum + ///\ingroup type_traits + /// An enumeration is scoped if it is not implicitly convertible to an integer. + template + struct is_scoped_enum : etl::bool_constant::value && !etl::is_convertible::value> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_scoped_enum_v = etl::is_scoped_enum::value; + #endif + //*************************************************************************** /// is_destructible /// Requires either compiler support or C++11. Without them there is no @@ -2337,6 +2383,52 @@ namespace etl #endif #endif + //*************************************************************************** + /// is_layout_compatible + ///\ingroup type_traits + #if ETL_USING_CPP20 && defined(__cpp_lib_is_layout_compatible) + template + struct is_layout_compatible : std::is_layout_compatible + { + }; + + template + inline constexpr bool is_layout_compatible_v = std::is_layout_compatible_v; + #elif ETL_USING_BUILTIN_IS_LAYOUT_COMPATIBLE + template + struct is_layout_compatible : etl::bool_constant<__is_layout_compatible(T, U)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_layout_compatible_v = etl::is_layout_compatible::value; + #endif + #endif + + //*************************************************************************** + /// is_pointer_interconvertible_base_of + ///\ingroup type_traits + #if ETL_USING_CPP20 && defined(__cpp_lib_is_pointer_interconvertible) + template + struct is_pointer_interconvertible_base_of : std::is_pointer_interconvertible_base_of + { + }; + + template + inline constexpr bool is_pointer_interconvertible_base_of_v = std::is_pointer_interconvertible_base_of_v; + #elif ETL_USING_BUILTIN_IS_POINTER_INTERCONVERTIBLE_BASE_OF + template + struct is_pointer_interconvertible_base_of : etl::bool_constant<__is_pointer_interconvertible_base_of(TBase, TDerived)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_pointer_interconvertible_base_of_v = etl::is_pointer_interconvertible_base_of::value; + #endif + #endif + //*************************************************************************** /// is_destructible ///\ingroup type_traits @@ -2711,6 +2803,27 @@ namespace etl inline constexpr bool is_enum_v = etl::is_enum::value; #endif + //*************************************************************************** + /// is_scoped_enum + ///\ingroup type_traits + /// An enumeration is scoped if it is not implicitly convertible to an integer. + #if ETL_USING_CPP23 && defined(__cpp_lib_is_scoped_enum) + template + struct is_scoped_enum : std::is_scoped_enum + { + }; + #else + template + struct is_scoped_enum : etl::bool_constant::value && !std::is_convertible::value> + { + }; + #endif + + #if ETL_USING_CPP17 + template + inline constexpr bool is_scoped_enum_v = etl::is_scoped_enum::value; + #endif + #endif //*************************************************************************** @@ -3295,6 +3408,26 @@ namespace etl #else template using is_trivially_copy_constructible = etl::bool_constant::value || etl::is_pointer::value>; + #endif + + //********************************************* + // is_trivially_default_constructible + #if ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED + template + using is_trivially_default_constructible = std::is_trivially_default_constructible; + #else + template + using is_trivially_default_constructible = etl::bool_constant::value || etl::is_pointer::value>; + #endif + + //********************************************* + // is_trivially_move_constructible + #if ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED + template + using is_trivially_move_constructible = std::is_trivially_move_constructible; + #else + template + using is_trivially_move_constructible = etl::bool_constant::value || etl::is_pointer::value>; #endif //********************************************* @@ -3307,14 +3440,46 @@ namespace etl using is_trivially_destructible = etl::bool_constant::value || etl::is_pointer::value>; #endif + //********************************************* + // is_trivially_assignable + #if ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED + template + using is_trivially_assignable = std::is_trivially_assignable; + #elif ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE + template + using is_trivially_assignable = etl::bool_constant<__is_trivially_assignable(T1, T2)>; + #else + template + using is_trivially_assignable = etl::bool_constant<(etl::is_arithmetic::value || etl::is_pointer::value) + && (etl::is_arithmetic::value || etl::is_pointer::value)>; + #endif + //********************************************* // is_trivially_copy_assignable #if ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED template using is_trivially_copy_assignable = std::is_trivially_copy_assignable; + #elif ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE + template + using is_trivially_copy_assignable = + etl::is_trivially_assignable< typename etl::add_lvalue_reference::type, typename etl::add_lvalue_reference::type>; #else template using is_trivially_copy_assignable = etl::bool_constant::value || etl::is_pointer::value>; + #endif + + //********************************************* + // is_trivially_move_assignable + #if ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED + template + using is_trivially_move_assignable = std::is_trivially_move_assignable; + #elif ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE + template + using is_trivially_move_assignable = + etl::is_trivially_assignable< typename etl::add_lvalue_reference::type, typename etl::add_rvalue_reference::type>; + #else + template + using is_trivially_move_assignable = etl::bool_constant::value || etl::is_pointer::value>; #endif //********************************************* @@ -3550,6 +3715,27 @@ namespace etl { }; + //********************************************* + // is_trivially_default_constructible + template + struct is_trivially_default_constructible : public is_trivially_constructible + { + }; + + //********************************************* + // is_trivially_move_constructible + #if ETL_USING_CPP11 + template + struct is_trivially_move_constructible : public is_trivially_constructible< T, typename add_rvalue_reference::type> + { + }; + #else + template + struct is_trivially_move_constructible : public etl::bool_constant::value || etl::is_pointer::value> + { + }; + #endif + //********************************************* // is_trivially_destructible template @@ -3562,18 +3748,37 @@ namespace etl #endif }; + //********************************************* + // is_trivially_assignable + template + struct is_trivially_assignable + { + static ETL_CONSTANT bool value = __is_trivially_assignable(T1, T2); + }; + //********************************************* // is_trivially_copy_assignable template struct is_trivially_copy_assignable + : public etl::is_trivially_assignable< typename etl::add_lvalue_reference::type, typename etl::add_lvalue_reference::type> { - #if defined(ETL_COMPILER_GCC) - static ETL_CONSTANT bool value = __has_trivial_copy(T); - #else - static ETL_CONSTANT bool value = __is_trivially_copyable(T); - #endif }; + //********************************************* + // is_trivially_move_assignable + #if ETL_USING_CPP11 + template + struct is_trivially_move_assignable + : public etl::is_trivially_assignable< typename etl::add_lvalue_reference::type, typename etl::add_rvalue_reference::type> + { + }; + #else + template + struct is_trivially_move_assignable : public etl::bool_constant::value || etl::is_pointer::value> + { + }; + #endif + //********************************************* // is_trivially_copyable template @@ -3826,6 +4031,32 @@ namespace etl template struct is_trivially_copy_constructible; + //********************************************* + // is_trivially_default_constructible + template ::value || etl::is_pointer::value> + struct is_trivially_default_constructible; + + template + struct is_trivially_default_constructible : public etl::true_type + { + }; + + template + struct is_trivially_default_constructible; + + //********************************************* + // is_trivially_move_constructible + template ::value || etl::is_pointer::value> + struct is_trivially_move_constructible; + + template + struct is_trivially_move_constructible : public etl::true_type + { + }; + + template + struct is_trivially_move_constructible; + //********************************************* // is_trivially_destructible template ::value || etl::is_pointer::value> @@ -3839,6 +4070,21 @@ namespace etl template struct is_trivially_destructible; + //********************************************* + // is_trivially_assignable + template ::value || etl::is_pointer::value) && (etl::is_arithmetic::value || etl::is_pointer::value)> + struct is_trivially_assignable; + + template + struct is_trivially_assignable : public etl::true_type + { + }; + + template + struct is_trivially_assignable; + //********************************************* // is_trivially_copy_assignable template ::value || etl::is_pointer::value> @@ -3852,6 +4098,19 @@ namespace etl template struct is_trivially_copy_assignable; + //********************************************* + // is_trivially_move_assignable + template ::value || etl::is_pointer::value> + struct is_trivially_move_assignable; + + template + struct is_trivially_move_assignable : public etl::true_type + { + }; + + template + struct is_trivially_move_assignable; + //********************************************* // is_trivially_copyable template ::value || etl::is_pointer::value> @@ -4106,6 +4365,20 @@ namespace etl { }; + //********************************************* + // is_trivially_default_constructible + template + struct is_trivially_default_constructible : public etl::bool_constant::value || etl::is_pointer::value> + { + }; + + //********************************************* + // is_trivially_move_constructible + template + struct is_trivially_move_constructible : public etl::bool_constant::value || etl::is_pointer::value> + { + }; + //********************************************* // is_trivially_destructible template @@ -4113,10 +4386,40 @@ namespace etl { }; + //********************************************* + // is_trivially_assignable + template + #if ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE + struct is_trivially_assignable : public etl::bool_constant<__is_trivially_assignable(T1, T2)> + #else + struct is_trivially_assignable + : public etl::bool_constant< (etl::is_arithmetic::value || etl::is_pointer::value) + && (etl::is_arithmetic::value || etl::is_pointer::value)> + #endif + { + }; + //********************************************* // is_trivially_copy_assignable template + #if ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE + struct is_trivially_copy_assignable + : public etl::is_trivially_assignable< typename etl::add_lvalue_reference::type, typename etl::add_lvalue_reference::type> + #else struct is_trivially_copy_assignable : public etl::bool_constant::value || etl::is_pointer::value> + #endif + { + }; + + //********************************************* + // is_trivially_move_assignable + template + #if ETL_USING_CPP11 && ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE + struct is_trivially_move_assignable + : public etl::is_trivially_assignable< typename etl::add_lvalue_reference::type, typename etl::add_rvalue_reference::type> + #else + struct is_trivially_move_assignable : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; @@ -4233,12 +4536,24 @@ namespace etl template inline constexpr bool is_trivially_copy_constructible_v = etl::is_trivially_copy_constructible::value; + template + inline constexpr bool is_trivially_default_constructible_v = etl::is_trivially_default_constructible::value; + + template + inline constexpr bool is_trivially_move_constructible_v = etl::is_trivially_move_constructible::value; + template inline constexpr bool is_trivially_destructible_v = etl::is_trivially_destructible::value; + template + inline constexpr bool is_trivially_assignable_v = etl::is_trivially_assignable::value; + template inline constexpr bool is_trivially_copy_assignable_v = etl::is_trivially_copy_assignable::value; + template + inline constexpr bool is_trivially_move_assignable_v = etl::is_trivially_move_assignable::value; + template inline constexpr bool is_trivially_copyable_v = etl::is_trivially_copyable::value; diff --git a/test/test_type_traits.cpp b/test/test_type_traits.cpp index ab2e7c4c..0c031435 100644 --- a/test/test_type_traits.cpp +++ b/test/test_type_traits.cpp @@ -847,6 +847,104 @@ namespace #endif } + //************************************************************************* +#if ETL_USING_BUILTIN_IS_LAYOUT_COMPATIBLE || (ETL_USING_STL && ETL_USING_CPP20 && defined(__cpp_lib_is_layout_compatible)) + TEST(test_is_layout_compatible) + { + struct A + { + int a; + }; + + struct B + { + const int b; + }; + + struct C + { + int c1; + int c2; + }; + + struct D + { + char d; + }; + + CHECK((etl::is_layout_compatible::value)); + CHECK((etl::is_layout_compatible::value)); + CHECK((etl::is_layout_compatible::value)); + CHECK((etl::is_layout_compatible::value)); + CHECK((!etl::is_layout_compatible::value)); + CHECK((!etl::is_layout_compatible::value)); + CHECK((!etl::is_layout_compatible::value)); + + #if ETL_USING_STL && ETL_USING_CPP20 && defined(__cpp_lib_is_layout_compatible) + CHECK((etl::is_layout_compatible::value == std::is_layout_compatible::value)); + CHECK((etl::is_layout_compatible::value == std::is_layout_compatible::value)); + #endif + + #if ETL_USING_CPP17 + CHECK((etl::is_layout_compatible_v)); + CHECK((!etl::is_layout_compatible_v)); + #endif + } +#endif + + //************************************************************************* +#if ETL_USING_BUILTIN_IS_POINTER_INTERCONVERTIBLE_BASE_OF || (ETL_USING_STL && ETL_USING_CPP20 && defined(__cpp_lib_is_pointer_interconvertible)) + TEST(test_is_pointer_interconvertible_base_of) + { + struct Base + { + int b; + }; + + struct Derived : public Base + { + }; + + struct VirtualDerived : virtual public Base + { + }; + + struct Polymorphic + { + virtual ~Polymorphic() {} + + int p; + }; + + struct PolymorphicDerived : public Polymorphic + { + }; + + struct Unrelated + { + int u; + }; + + CHECK((etl::is_pointer_interconvertible_base_of::value)); + CHECK((etl::is_pointer_interconvertible_base_of::value)); + CHECK((!etl::is_pointer_interconvertible_base_of::value)); + CHECK((!etl::is_pointer_interconvertible_base_of::value)); + CHECK((!etl::is_pointer_interconvertible_base_of::value)); + CHECK((!etl::is_pointer_interconvertible_base_of::value)); + + #if ETL_USING_STL && ETL_USING_CPP20 && defined(__cpp_lib_is_pointer_interconvertible) + CHECK((etl::is_pointer_interconvertible_base_of::value == std::is_pointer_interconvertible_base_of::value)); + CHECK((etl::is_pointer_interconvertible_base_of::value + == std::is_pointer_interconvertible_base_of::value)); + #endif + + #if ETL_USING_CPP17 + CHECK((etl::is_pointer_interconvertible_base_of_v)); + CHECK((!etl::is_pointer_interconvertible_base_of_v)); + #endif + } +#endif + //************************************************************************* TEST(test_is_destructible) { @@ -2378,6 +2476,33 @@ namespace } #undef CHECK_EQUAL_FOR_TYPE + //************************************************************************* + TEST(test_is_scoped_enum) + { + CHECK_FALSE((etl::is_scoped_enum::value)); + CHECK_FALSE((etl::is_scoped_enum::value)); + CHECK_FALSE((etl::is_scoped_enum::value)); + CHECK_FALSE((etl::is_scoped_enum::value)); + CHECK_FALSE((etl::is_scoped_enum::value)); + CHECK_FALSE((etl::is_scoped_enum::value)); + CHECK_FALSE((etl::is_scoped_enum::value)); + + CHECK_TRUE((etl::is_scoped_enum::value)); + CHECK_TRUE((etl::is_scoped_enum::value)); + CHECK_FALSE((etl::is_scoped_enum::value)); + +#if ETL_USING_CPP17 + CHECK_FALSE((etl::is_scoped_enum_v)); + CHECK_TRUE((etl::is_scoped_enum_v)); +#endif + +#if ETL_USING_CPP23 && defined(__cpp_lib_is_scoped_enum) + CHECK_EQUAL((std::is_scoped_enum::value), (etl::is_scoped_enum::value)); + CHECK_EQUAL((std::is_scoped_enum::value), (etl::is_scoped_enum::value)); + CHECK_EQUAL((std::is_scoped_enum::value), (etl::is_scoped_enum::value)); +#endif + } + //************************************************************************* TEST(test_integral_constants) { @@ -2755,6 +2880,42 @@ namespace #endif } + //************************************************************************* + TEST(test_is_trivially_default_constructible) + { +#if (!(defined(ETL_COMPILER_GCC) && defined(ETL_USE_TYPE_TRAITS_BUILTINS))) + #if ETL_USING_STL || defined(ETL_USE_TYPE_TRAITS_BUILTINS) || defined(ETL_USER_DEFINED_TYPE_TRAITS) + CHECK((etl::is_trivially_default_constructible::value) == (std::is_trivially_default_constructible::value)); + CHECK((etl::is_trivially_default_constructible::value) == (std::is_trivially_default_constructible::value)); + CHECK((etl::is_trivially_default_constructible::value) == (std::is_trivially_default_constructible::value)); + CHECK((etl::is_trivially_default_constructible::value) == (std::is_trivially_default_constructible::value)); + + #if ETL_USING_CPP17 + CHECK((etl::is_trivially_default_constructible_v) == (std::is_trivially_default_constructible_v)); + CHECK((etl::is_trivially_default_constructible_v) == (std::is_trivially_default_constructible_v)); + #endif + #endif +#endif + } + + //************************************************************************* + TEST(test_is_trivially_move_constructible) + { +#if (!(defined(ETL_COMPILER_GCC) && defined(ETL_USE_TYPE_TRAITS_BUILTINS))) + #if ETL_USING_CPP11 && (ETL_USING_STL || defined(ETL_USE_TYPE_TRAITS_BUILTINS) || defined(ETL_USER_DEFINED_TYPE_TRAITS)) + CHECK((etl::is_trivially_move_constructible::value) == (std::is_trivially_move_constructible::value)); + CHECK((etl::is_trivially_move_constructible::value) == (std::is_trivially_move_constructible::value)); + CHECK((etl::is_trivially_move_constructible::value) == (std::is_trivially_move_constructible::value)); + CHECK((etl::is_trivially_move_constructible::value) == (std::is_trivially_move_constructible::value)); + + #if ETL_USING_CPP17 + CHECK((etl::is_trivially_move_constructible_v) == (std::is_trivially_move_constructible_v)); + CHECK((etl::is_trivially_move_constructible_v) == (std::is_trivially_move_constructible_v)); + #endif + #endif +#endif + } + //************************************************************************* TEST(test_is_trivially_destructible) { @@ -2771,6 +2932,25 @@ namespace #endif } + //************************************************************************* + TEST(test_is_trivially_assignable) + { +#if ETL_USING_CPP11 && !defined(ETL_USER_DEFINED_TYPE_TRAITS) \ + && (ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED || ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE) + CHECK((etl::is_trivially_assignable::value) == (std::is_trivially_assignable::value)); + CHECK((etl::is_trivially_assignable::value) == (std::is_trivially_assignable::value)); + CHECK((etl::is_trivially_assignable::value) == (std::is_trivially_assignable::value)); + CHECK((etl::is_trivially_assignable::value) == (std::is_trivially_assignable::value)); + CHECK((etl::is_trivially_assignable::value) + == (std::is_trivially_assignable::value)); + + #if ETL_USING_CPP17 + CHECK((etl::is_trivially_assignable_v) == (std::is_trivially_assignable_v)); + CHECK((etl::is_trivially_assignable_v) == (std::is_trivially_assignable_v)); + #endif +#endif + } + //************************************************************************* TEST(test_is_trivially_copy_assignable) { @@ -2789,6 +2969,23 @@ namespace #endif } + //************************************************************************* + TEST(test_is_trivially_move_assignable) + { +#if ETL_USING_CPP11 && !defined(ETL_USER_DEFINED_TYPE_TRAITS) \ + && (ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED || ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE) + CHECK((etl::is_trivially_move_assignable::value) == (std::is_trivially_move_assignable::value)); + CHECK((etl::is_trivially_move_assignable::value) == (std::is_trivially_move_assignable::value)); + CHECK((etl::is_trivially_move_assignable::value) == (std::is_trivially_move_assignable::value)); + CHECK((etl::is_trivially_move_assignable::value) == (std::is_trivially_move_assignable::value)); + + #if ETL_USING_CPP17 + CHECK((etl::is_trivially_move_assignable_v) == (std::is_trivially_move_assignable_v)); + CHECK((etl::is_trivially_move_assignable_v) == (std::is_trivially_move_assignable_v)); + #endif +#endif + } + //************************************************************************* TEST(test_is_trivially_copyable) { From c896d2d7ddf2e68e69fa0e4fba8d323be39f330b Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Tue, 1 Sep 2026 20:16:24 +0200 Subject: [PATCH 58/77] Support variable number of arguments to inject at runtime (#1580) Further, switching internal member from array_view to span to be closer to standard, and more constexpr friendly. Also adjusting to C++11 template syntax. Co-authored-by: John Wellbelove --- include/etl/format.h | 22 +++++++++++++++------- test/test_format.cpp | 23 +++++++++++++++++++++++ 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/include/etl/format.h b/include/etl/format.h index 8820ac6f..830e64a6 100644 --- a/include/etl/format.h +++ b/include/etl/format.h @@ -35,7 +35,6 @@ SOFTWARE. #include "algorithm.h" #include "array.h" -#include "array_view.h" #include "error_handler.h" #include "limits.h" #include "math.h" @@ -627,7 +626,7 @@ namespace etl }; template - struct has_formatter_parse&>().parse(etl::declval()))> > + struct has_formatter_parse&>().parse(etl::declval()))>> : etl::true_type { }; @@ -642,7 +641,7 @@ namespace etl template struct has_formatter_format&>().format( - etl::declval(), etl::declval&>()))> > : etl::true_type + etl::declval(), etl::declval&>()))>> : etl::true_type { }; @@ -806,7 +805,7 @@ namespace etl // Converting constructor for user-defined types that provide an // etl::formatter specialisation. The value is stored type-erased in a // handle, matching the std::basic_format_arg behaviour for custom types. - template ::value> > + template ::value>> basic_format_arg(const T& v) : data(handle(v)) { @@ -863,7 +862,7 @@ namespace etl return _args.get(i); } - etl::array_view> get() + etl::span> get() { return _args; } @@ -884,6 +883,15 @@ namespace etl { } + // non-standard + // Constructs from externally owned storage, for cases where the number of + // arguments is only known at runtime and so no format_arg_store + // can be formed. The referenced storage must outlive this object. + explicit basic_format_args(etl::span> args_) + : _args(args_) + { + } + basic_format_args(const basic_format_args& other) : _args(other._args) { @@ -908,7 +916,7 @@ namespace etl private: - etl::array_view> _args; + etl::span> _args; }; namespace private_format @@ -2019,7 +2027,7 @@ namespace etl } // for user-defined types routed through basic_format_arg::handle - void operator()(typename basic_format_arg >::handle h) + void operator()(typename basic_format_arg>::handle h) { h.format(parse_ctx, fmt_ctx); } diff --git a/test/test_format.cpp b/test/test_format.cpp index 480345b5..bfd4c848 100644 --- a/test/test_format.cpp +++ b/test/test_format.cpp @@ -950,6 +950,29 @@ namespace CHECK_EQUAL("010", test_format(s, "{:#o}", 8)); } + //************************************************************************* + // Arguments whose count is only known at runtime cannot be held in a + // format_arg_store, so basic_format_args can also be built + // directly over caller owned storage. + TEST(test_vformat_to_runtime_sized_args) + { + etl::string<100> s; + + using iterator_t = etl::back_insert_iterator>; + using arg_t = etl::format_arg; + + etl::array storage = {arg_t(1), arg_t("two"), arg_t(3), arg_t('x')}; + + // Only the first three of the four slots are used. + etl::vformat_to(etl::back_inserter(s), "{} {} {}", etl::format_args(etl::span(storage.data(), 3U))); + CHECK_EQUAL("1 two 3", s); + + // An empty argument list is also valid. + s.clear(); + etl::vformat_to(etl::back_inserter(s), "no args", etl::format_args(etl::span())); + CHECK_EQUAL("no args", s); + } + //************************************************************************* TEST(test_format_custom_type) { From 2f338411a3e63baebb267edd2f9acf23716b3280 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Tue, 1 Sep 2026 21:25:31 +0200 Subject: [PATCH 59/77] Fix symbols to be exportable from C++20 named modules (#1582) Fixes #1543 Co-authored-by: John Wellbelove --- include/etl/atomic/atomic_std.h | 12 +++++----- include/etl/basic_format_spec.h | 32 +++++++++++++------------- include/etl/index_of_type.h | 2 +- include/etl/private/dynamic_extent.h | 5 +++- include/etl/private/variant_variadic.h | 2 +- include/etl/type_list.h | 2 +- 6 files changed, 29 insertions(+), 26 deletions(-) diff --git a/include/etl/atomic/atomic_std.h b/include/etl/atomic/atomic_std.h index 06fcef6c..76a22c93 100644 --- a/include/etl/atomic/atomic_std.h +++ b/include/etl/atomic/atomic_std.h @@ -135,12 +135,12 @@ namespace etl using memory_order = std::memory_order; - static ETL_CONSTANT etl::memory_order memory_order_relaxed = std::memory_order_relaxed; - static ETL_CONSTANT etl::memory_order memory_order_consume = std::memory_order_consume; - static ETL_CONSTANT etl::memory_order memory_order_acquire = std::memory_order_acquire; - static ETL_CONSTANT etl::memory_order memory_order_release = std::memory_order_release; - static ETL_CONSTANT etl::memory_order memory_order_acq_rel = std::memory_order_acq_rel; - static ETL_CONSTANT etl::memory_order memory_order_seq_cst = std::memory_order_seq_cst; + ETL_INLINE_VAR ETL_CONSTANT etl::memory_order memory_order_relaxed = std::memory_order_relaxed; + ETL_INLINE_VAR ETL_CONSTANT etl::memory_order memory_order_consume = std::memory_order_consume; + ETL_INLINE_VAR ETL_CONSTANT etl::memory_order memory_order_acquire = std::memory_order_acquire; + ETL_INLINE_VAR ETL_CONSTANT etl::memory_order memory_order_release = std::memory_order_release; + ETL_INLINE_VAR ETL_CONSTANT etl::memory_order memory_order_acq_rel = std::memory_order_acq_rel; + ETL_INLINE_VAR ETL_CONSTANT etl::memory_order memory_order_seq_cst = std::memory_order_seq_cst; using atomic_bool = etl::atomic; using atomic_char = etl::atomic; diff --git a/include/etl/basic_format_spec.h b/include/etl/basic_format_spec.h index dc198f54..cd8b3e33 100644 --- a/include/etl/basic_format_spec.h +++ b/include/etl/basic_format_spec.h @@ -135,65 +135,65 @@ namespace etl //*************************************************************************** // Stream formatting manipulators. //*************************************************************************** - static ETL_CONSTEXPR private_basic_format_spec::base_spec setbase(uint32_t base) + inline ETL_CONSTEXPR private_basic_format_spec::base_spec setbase(uint32_t base) { return private_basic_format_spec::base_spec(base); } //********************************* - static ETL_CONSTEXPR private_basic_format_spec::width_spec setw(uint32_t width) + inline ETL_CONSTEXPR private_basic_format_spec::width_spec setw(uint32_t width) { return private_basic_format_spec::width_spec(width); } //********************************* template - static ETL_CONSTEXPR private_basic_format_spec::fill_spec setfill(TChar fill) + ETL_CONSTEXPR private_basic_format_spec::fill_spec setfill(TChar fill) { return private_basic_format_spec::fill_spec(fill); } //********************************* - static ETL_CONSTEXPR private_basic_format_spec::precision_spec setprecision(uint32_t precision) + inline ETL_CONSTEXPR private_basic_format_spec::precision_spec setprecision(uint32_t precision) { return private_basic_format_spec::precision_spec(precision); } //********************************* - static ETL_CONSTANT private_basic_format_spec::base_spec bin(2U); + ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::base_spec bin(2U); //********************************* - static ETL_CONSTANT private_basic_format_spec::base_spec oct(8U); + ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::base_spec oct(8U); //********************************* - static ETL_CONSTANT private_basic_format_spec::base_spec dec(10U); + ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::base_spec dec(10U); //********************************* - static ETL_CONSTANT private_basic_format_spec::base_spec hex(16U); + ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::base_spec hex(16U); //********************************* - static ETL_CONSTANT private_basic_format_spec::left_spec left = private_basic_format_spec::left_spec(); + ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::left_spec left = private_basic_format_spec::left_spec(); //********************************* - static ETL_CONSTANT private_basic_format_spec::right_spec right = private_basic_format_spec::right_spec(); + ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::right_spec right = private_basic_format_spec::right_spec(); //********************************* - static ETL_CONSTANT private_basic_format_spec::boolalpha_spec boolalpha(true); + ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::boolalpha_spec boolalpha(true); //********************************* - static ETL_CONSTANT private_basic_format_spec::boolalpha_spec noboolalpha(false); + ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::boolalpha_spec noboolalpha(false); //********************************* - static ETL_CONSTANT private_basic_format_spec::uppercase_spec uppercase(true); + ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::uppercase_spec uppercase(true); //********************************* - static ETL_CONSTANT private_basic_format_spec::uppercase_spec nouppercase(false); + ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::uppercase_spec nouppercase(false); //********************************* - static ETL_CONSTANT private_basic_format_spec::showbase_spec showbase(true); + ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::showbase_spec showbase(true); //********************************* - static ETL_CONSTANT private_basic_format_spec::showbase_spec noshowbase(false); + ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::showbase_spec noshowbase(false); //*************************************************************************** /// basic_format_spec diff --git a/include/etl/index_of_type.h b/include/etl/index_of_type.h index 2d799832..7471ef22 100644 --- a/include/etl/index_of_type.h +++ b/include/etl/index_of_type.h @@ -41,7 +41,7 @@ namespace etl //*************************************************************************** /// Defines a no-position constant. //*************************************************************************** - static ETL_CONSTANT size_t index_of_type_npos = etl::integral_limits::max; + ETL_INLINE_VAR ETL_CONSTANT size_t index_of_type_npos = etl::integral_limits::max; //*************************************************************************** /// Finds the index of a type in a variadic type parameter. diff --git a/include/etl/private/dynamic_extent.h b/include/etl/private/dynamic_extent.h index afcc9687..cd34d3c1 100644 --- a/include/etl/private/dynamic_extent.h +++ b/include/etl/private/dynamic_extent.h @@ -36,7 +36,10 @@ SOFTWARE. namespace etl { - static ETL_CONSTANT size_t dynamic_extent = etl::integral_limits::max; + // ETL_INLINE_VAR gives external linkage in C++17 and later, so that etl::span, + // which references dynamic_extent in its public interface, can be exported from + // a C++20 named module without exposing a TU-local entity. See issue #1543. + ETL_INLINE_VAR ETL_CONSTANT size_t dynamic_extent = etl::integral_limits::max; } #endif diff --git a/include/etl/private/variant_variadic.h b/include/etl/private/variant_variadic.h index aaedb064..924d73a3 100644 --- a/include/etl/private/variant_variadic.h +++ b/include/etl/private/variant_variadic.h @@ -314,7 +314,7 @@ namespace etl } // namespace private_variant /// Definition of variant_npos. - constexpr size_t variant_npos = etl::integral_limits::max; + ETL_INLINE_VAR constexpr size_t variant_npos = etl::integral_limits::max; //*********************************** // variant. Forward declaration diff --git a/include/etl/type_list.h b/include/etl/type_list.h index 7cfdfaa0..0a79c964 100644 --- a/include/etl/type_list.h +++ b/include/etl/type_list.h @@ -44,7 +44,7 @@ namespace etl //*************************************************************************** /// Defines a no-position constant. //*************************************************************************** - static ETL_CONSTANT size_t type_list_npos = etl::integral_limits::max; + ETL_INLINE_VAR ETL_CONSTANT size_t type_list_npos = etl::integral_limits::max; //*************************************************************************** // Type list forward declaration. From 452b6269f30ffe1d836349b7deea74156ff8d37e Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Wed, 2 Sep 2026 08:54:41 +0100 Subject: [PATCH 60/77] Add search to the Hugo website --- hugo/hugo.toml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/hugo/hugo.toml b/hugo/hugo.toml index ae907872..a4ae3632 100644 --- a/hugo/hugo.toml +++ b/hugo/hugo.toml @@ -48,30 +48,36 @@ includeFiles = [".htaccess"] defaultOpen = false autoCollapse = true +[[menus.main]] + name = "Search" + weight = 1 + [menus.main.params] + type = "search" + [[menus.main]] name = "Home" pageRef = "/docs" - weight = 1 + weight = 2 [[menus.main]] name = "Github" url = "https://github.com/ETLCPP/etl.git" - weight = 2 + weight = 3 [[menus.main]] name = "Slack" url = "https://slofile.com/slack/etlcpp" - weight = 3 + weight = 4 [[menus.main]] name = "Arduino" url = "https://github.com/ETLCPP/etl-arduino/archive/master.zip" - weight = 4 + weight = 5 [[menus.main]] name = "Blog" pageRef = "/docs/blog" - weight = 5 + weight = 6 [[menus.main]] name = "About" From ed5219283b0ceafae9e3d3634ced915a4de50e3e Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Wed, 2 Sep 2026 09:58:40 +0200 Subject: [PATCH 61/77] Detect builtins (#1581) Even without ETL_USE_TYPE_TRAITS_BUILTINS in the profile, we get compiler builtins detected. Makes detection more consistent now. Previously, some type trait builtins were detected automatically, and some others needed to be activated via ETL_USE_TYPE_TRAITS_BUILTINS in the profile explicitly. Replaces: #1174 Co-authored-by: John Wellbelove --- include/etl/memory.h | 118 ++++++++++++++---- .../etl/profiles/determine_builtin_support.h | 26 +++- include/etl/type_traits.h | 80 +++++++++++- test/test_memory.cpp | 80 ++++++++++++ test/test_type_traits.cpp | 40 +++++- 5 files changed, 310 insertions(+), 34 deletions(-) diff --git a/include/etl/memory.h b/include/etl/memory.h index f923a6ce..742766c5 100644 --- a/include/etl/memory.h +++ b/include/etl/memory.h @@ -53,6 +53,44 @@ SOFTWARE. namespace etl { + namespace private_memory + { + //************************************************************************* + /// Checks whether a type can be copied to uninitialised storage by using + /// assignment instead of placement new. + //************************************************************************* + template + struct is_trivially_copy_assignable_to_uninitialised_storage + : etl::bool_constant::value && etl::is_trivially_copy_constructible::value + && etl::is_trivially_copy_assignable::value> + { + }; + +#if ETL_USING_CPP11 + //************************************************************************* + /// Checks whether a type can be moved to uninitialised storage by using + /// assignment instead of placement new. + //************************************************************************* + template + struct is_trivially_move_assignable_to_uninitialised_storage + : etl::bool_constant::value && etl::is_trivially_move_constructible::value + && etl::is_trivially_move_assignable::value> + { + }; +#endif + + //************************************************************************* + /// Checks whether a value-initialised type can be copied to uninitialised + /// storage by using assignment instead of placement new. + //************************************************************************* + template + struct is_trivially_value_assignable_to_uninitialised_storage + : etl::bool_constant::value && etl::is_trivially_copyable::value + && etl::is_trivially_copy_assignable::value> + { + }; + } // namespace private_memory + //***************************************************************************** /// Obtain the address represented by p without forming a reference to the /// object pointed to by p. Defined when not using the STL or C++20 @@ -428,7 +466,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, TOutputIterator>::type + typename etl::enable_if< + etl::private_memory::is_trivially_copy_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_fill(TOutputIterator o_begin, TOutputIterator o_end, const T& value) { etl::fill(o_begin, o_end, value); @@ -442,8 +482,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< !etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, - TOutputIterator>::type + typename etl::enable_if< + !etl::private_memory::is_trivially_copy_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_fill(TOutputIterator o_begin, TOutputIterator o_end, const T& value) { typedef typename etl::iterator_traits::value_type value_type; @@ -464,7 +505,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, TOutputIterator>::type + typename etl::enable_if< + etl::private_memory::is_trivially_copy_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_fill(TOutputIterator o_begin, TOutputIterator o_end, const T& value, TCounter& count) { count += static_cast(etl::distance(o_begin, o_end)); @@ -481,8 +524,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< !etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, - TOutputIterator>::type + typename etl::enable_if< + !etl::private_memory::is_trivially_copy_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_fill(TOutputIterator o_begin, TOutputIterator o_end, const T& value, TCounter& count) { count += static_cast(etl::distance(o_begin, o_end)); @@ -577,7 +621,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, TOutputIterator>::type + typename etl::enable_if< + etl::private_memory::is_trivially_copy_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_copy(TInputIterator i_begin, TInputIterator i_end, TOutputIterator o_begin) { return etl::copy(i_begin, i_end, o_begin); @@ -589,8 +635,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< !etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, - TOutputIterator>::type + typename etl::enable_if< + !etl::private_memory::is_trivially_copy_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_copy(TInputIterator i_begin, TInputIterator i_end, TOutputIterator o_begin) { typedef typename etl::iterator_traits::value_type value_type; @@ -614,7 +661,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, TOutputIterator>::type + typename etl::enable_if< + etl::private_memory::is_trivially_copy_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_copy(TInputIterator i_begin, TInputIterator i_end, TOutputIterator o_begin, TCounter& count) { TOutputIterator o_end = etl::copy(i_begin, i_end, o_begin); @@ -630,8 +679,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< !etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, - TOutputIterator>::type + typename etl::enable_if< + !etl::private_memory::is_trivially_copy_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_copy(TInputIterator i_begin, TInputIterator i_end, TOutputIterator o_begin, TCounter& count) { TOutputIterator o_end = etl::uninitialized_copy(i_begin, i_end, o_begin); @@ -910,7 +960,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, TOutputIterator>::type + typename etl::enable_if< + etl::private_memory::is_trivially_move_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_move(TInputIterator i_begin, TInputIterator i_end, TOutputIterator o_begin) { return etl::move(i_begin, i_end, o_begin); @@ -922,8 +974,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< !etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, - TOutputIterator>::type + typename etl::enable_if< + !etl::private_memory::is_trivially_move_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_move(TInputIterator i_begin, TInputIterator i_end, TOutputIterator o_begin) { typedef typename etl::iterator_traits::value_type value_type; @@ -947,7 +1000,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, TOutputIterator>::type + typename etl::enable_if< + etl::private_memory::is_trivially_move_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_move(TInputIterator i_begin, TInputIterator i_end, TOutputIterator o_begin, TCounter& count) { TOutputIterator o_end = etl::move(i_begin, i_end, o_begin); @@ -963,8 +1018,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< !etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, - TOutputIterator>::type + typename etl::enable_if< + !etl::private_memory::is_trivially_move_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_move(TInputIterator i_begin, TInputIterator i_end, TOutputIterator o_begin, TCounter& count) { TOutputIterator o_end = etl::uninitialized_move(i_begin, i_end, o_begin); @@ -1037,7 +1093,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, TOutputIterator>::type + typename etl::enable_if< + etl::private_memory::is_trivially_move_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_move_n(TInputIterator i_begin, TSize n, TOutputIterator o_begin) { return etl::move(i_begin, i_begin + n, o_begin); @@ -1049,8 +1107,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< !etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, - TOutputIterator>::type + typename etl::enable_if< + !etl::private_memory::is_trivially_move_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_move_n(TInputIterator i_begin, TSize n, TOutputIterator o_begin) { typedef typename etl::iterator_traits::value_type value_type; @@ -1074,7 +1133,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, TOutputIterator>::type + typename etl::enable_if< + etl::private_memory::is_trivially_move_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_move_n(TInputIterator i_begin, TSize n, TOutputIterator o_begin, TCounter& count) { TOutputIterator o_end = etl::move(i_begin, i_begin + n, o_begin); @@ -1090,8 +1151,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< !etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, - TOutputIterator>::type + typename etl::enable_if< + !etl::private_memory::is_trivially_move_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_move_n(TInputIterator i_begin, TSize n, TOutputIterator o_begin, TCounter& count) { TOutputIterator o_end = etl::uninitialized_move(i_begin, i_begin + n, o_begin); @@ -1527,7 +1589,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, void>::type + typename etl::enable_if< + etl::private_memory::is_trivially_value_assignable_to_uninitialised_storage::value_type>::value, + void>::type uninitialized_value_construct(TOutputIterator o_begin, TOutputIterator o_end) { typedef typename etl::iterator_traits::value_type value_type; @@ -1541,7 +1605,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< !etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, void>::type + typename etl::enable_if< + !etl::private_memory::is_trivially_value_assignable_to_uninitialised_storage::value_type>::value, + void>::type uninitialized_value_construct(TOutputIterator o_begin, TOutputIterator o_end) { typedef typename etl::iterator_traits::value_type value_type; diff --git a/include/etl/profiles/determine_builtin_support.h b/include/etl/profiles/determine_builtin_support.h index ad681fb5..994cbadf 100644 --- a/include/etl/profiles/determine_builtin_support.h +++ b/include/etl/profiles/determine_builtin_support.h @@ -53,6 +53,10 @@ SOFTWARE. #define ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE 1 #endif + #if !defined(ETL_USING_BUILTIN_HAS_TRIVIAL_CONSTRUCTOR) + #define ETL_USING_BUILTIN_HAS_TRIVIAL_CONSTRUCTOR 1 + #endif + #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE) #define ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE 1 #endif @@ -61,6 +65,10 @@ SOFTWARE. #define ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE 1 #endif + #if !defined(ETL_USING_BUILTIN_HAS_TRIVIAL_DESTRUCTOR) + #define ETL_USING_BUILTIN_HAS_TRIVIAL_DESTRUCTOR 1 + #endif + #if !defined(ETL_USING_BUILTIN_IS_DESTRUCTIBLE) #define ETL_USING_BUILTIN_IS_DESTRUCTIBLE 1 #endif @@ -177,7 +185,14 @@ SOFTWARE. #endif #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE) - #define ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE (__has_builtin(__has_trivial_constructor) || __has_builtin(__is_trivially_constructible)) + #define ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE __has_builtin(__is_trivially_constructible) + #endif + + // The legacy __has_trivial_constructor builtin. Deprecated by GCC 12 and + // removed by GCC 15, so it must be detected separately from the standard + // __is_trivially_constructible builtin. + #if !defined(ETL_USING_BUILTIN_HAS_TRIVIAL_CONSTRUCTOR) + #define ETL_USING_BUILTIN_HAS_TRIVIAL_CONSTRUCTOR __has_builtin(__has_trivial_constructor) #endif #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE) @@ -185,7 +200,14 @@ SOFTWARE. #endif #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE) - #define ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE (__has_builtin(__has_trivial_destructor) || __has_builtin(__is_trivially_destructible)) + #define ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE __has_builtin(__is_trivially_destructible) + #endif + + // The legacy __has_trivial_destructor builtin. Deprecated by GCC 12 and + // removed by GCC 15, so it must be detected separately from the standard + // __is_trivially_destructible builtin. + #if !defined(ETL_USING_BUILTIN_HAS_TRIVIAL_DESTRUCTOR) + #define ETL_USING_BUILTIN_HAS_TRIVIAL_DESTRUCTOR __has_builtin(__has_trivial_destructor) #endif #if !defined(ETL_USING_BUILTIN_IS_DESTRUCTIBLE) diff --git a/include/etl/type_traits.h b/include/etl/type_traits.h index ea9823a7..b01b308f 100644 --- a/include/etl/type_traits.h +++ b/include/etl/type_traits.h @@ -4193,8 +4193,9 @@ namespace etl }; #if ETL_USING_CPP11 - //*************************************************************************** - /// is_constructible + //*************************************************************************** + /// is_constructible + #if !ETL_USING_BUILTIN_IS_CONSTRUCTIBLE namespace private_type_traits { template @@ -4207,11 +4208,19 @@ namespace etl { }; } // namespace private_type_traits + #endif - //********************************************* - // is_constructible + //********************************************* + // is_constructible + #if ETL_USING_BUILTIN_IS_CONSTRUCTIBLE + template + struct is_constructible : public etl::bool_constant<__is_constructible(T, TArgs...)> + { + }; + #else template using is_constructible = private_type_traits::is_constructible_, T, TArgs...>; + #endif //********************************************* // is_copy_constructible @@ -4304,49 +4313,79 @@ namespace etl //********************************************* // is_nothrow_constructible template + #if ETL_USING_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE + struct is_nothrow_constructible : public etl::bool_constant<__is_nothrow_constructible(T, TArgs...)> + #else struct is_nothrow_constructible : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; //********************************************* // is_nothrow_default_constructible template + #if ETL_USING_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE + struct is_nothrow_default_constructible : public etl::is_nothrow_constructible + #else struct is_nothrow_default_constructible : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; //********************************************* // is_nothrow_copy_constructible template + #if ETL_USING_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE + struct is_nothrow_copy_constructible : public etl::is_nothrow_constructible::type> + #else struct is_nothrow_copy_constructible : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; //********************************************* // is_nothrow_move_constructible template + #if ETL_USING_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE + struct is_nothrow_move_constructible : public etl::is_nothrow_constructible::type> + #else struct is_nothrow_move_constructible : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; //********************************************* // is_nothrow_assignable template + #if ETL_USING_BUILTIN_IS_NOTHROW_ASSIGNABLE + struct is_nothrow_assignable : public etl::bool_constant<__is_nothrow_assignable(T, U)> + #else struct is_nothrow_assignable : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; //********************************************* // is_nothrow_copy_assignable template + #if ETL_USING_BUILTIN_IS_NOTHROW_ASSIGNABLE + struct is_nothrow_copy_assignable + : public etl::is_nothrow_assignable::type, typename etl::add_lvalue_reference::type> + #else struct is_nothrow_copy_assignable : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; //********************************************* // is_nothrow_move_assignable template + #if ETL_USING_BUILTIN_IS_NOTHROW_ASSIGNABLE + struct is_nothrow_move_assignable + : public etl::is_nothrow_assignable::type, typename etl::add_rvalue_reference::type> + #else struct is_nothrow_move_assignable : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; #endif @@ -4354,35 +4393,68 @@ namespace etl //********************************************* // is_trivially_constructible template + #if ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE + struct is_trivially_constructible : public etl::bool_constant<__is_trivially_constructible(T)> + #elif ETL_USING_CPP11 && ETL_USING_BUILTIN_HAS_TRIVIAL_CONSTRUCTOR + struct is_trivially_constructible : public etl::bool_constant<__has_trivial_constructor(T) && etl::is_constructible::value> + #else struct is_trivially_constructible : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; //********************************************* // is_trivially_copy_constructible template + #if ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE + struct is_trivially_copy_constructible + : public etl::bool_constant<__is_trivially_constructible(T, typename etl::add_lvalue_reference::type)> + #elif ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE + // A trivially copyable type may still have a deleted copy constructor, so the + // copy constructor must be checked for as well. + struct is_trivially_copy_constructible : public etl::bool_constant<__is_trivially_copyable(T) && etl::is_copy_constructible::value> + #else struct is_trivially_copy_constructible : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; //********************************************* // is_trivially_default_constructible template + #if ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE || ETL_USING_BUILTIN_HAS_TRIVIAL_CONSTRUCTOR + struct is_trivially_default_constructible : public etl::is_trivially_constructible + #else struct is_trivially_default_constructible : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; //********************************************* // is_trivially_move_constructible template + #if ETL_USING_CPP11 && ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE + struct is_trivially_move_constructible : public etl::bool_constant<__is_trivially_constructible(T, typename etl::add_rvalue_reference::type)> + #elif ETL_USING_CPP11 && ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE + // A trivially copyable type may still have a deleted move constructor, so the + // move constructor must be checked for as well. + struct is_trivially_move_constructible : public etl::bool_constant<__is_trivially_copyable(T) && etl::is_move_constructible::value> + #else struct is_trivially_move_constructible : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; //********************************************* // is_trivially_destructible template + #if ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE + struct is_trivially_destructible : public etl::bool_constant<__is_trivially_destructible(T)> + #elif ETL_USING_CPP11 && ETL_USING_BUILTIN_HAS_TRIVIAL_DESTRUCTOR + struct is_trivially_destructible : public etl::bool_constant<__has_trivial_destructor(T) && etl::is_destructible::value> + #else struct is_trivially_destructible : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; diff --git a/test/test_memory.cpp b/test/test_memory.cpp index 70f03493..a8fda0fa 100644 --- a/test/test_memory.cpp +++ b/test/test_memory.cpp @@ -138,6 +138,33 @@ namespace } }; +#if ETL_USING_CPP11 + // A trivially copyable type that can be copy constructed but not copy + // assigned. It must use the placement-new uninitialized algorithm path. + struct CopyConstructOnly + { + int value; + + CopyConstructOnly() = default; + + CopyConstructOnly(const CopyConstructOnly&) = default; + CopyConstructOnly& operator=(const CopyConstructOnly&) = delete; + }; + + // A trivially copyable type that can be move constructed but not move + // assigned. It must use the placement-new uninitialized algorithm path. + struct MoveConstructOnly + { + int value; + + MoveConstructOnly() = default; + + MoveConstructOnly(const MoveConstructOnly&) = delete; + MoveConstructOnly(MoveConstructOnly&&) = default; + MoveConstructOnly& operator=(MoveConstructOnly&&) = delete; + }; +#endif + //*********************************** template struct NoDelete @@ -546,6 +573,59 @@ namespace CHECK_EQUAL(0U, count); } +#if ETL_USING_CPP11 + //************************************************************************* + TEST(test_uninitialized_algorithms_use_construction_when_assignment_is_deleted) + { + CopyConstructOnly copy_source[2]; + copy_source[0].value = 1; + copy_source[1].value = 2; + + alignas(CopyConstructOnly) unsigned char copy_storage[sizeof(CopyConstructOnly) * 2U]; + CopyConstructOnly* copy_destination = reinterpret_cast(copy_storage); + + etl::uninitialized_copy(copy_source, copy_source + 2, copy_destination); + CHECK_EQUAL(1, copy_destination[0].value); + CHECK_EQUAL(2, copy_destination[1].value); + + alignas(CopyConstructOnly) unsigned char fill_storage[sizeof(CopyConstructOnly) * 2U]; + CopyConstructOnly* fill_destination = reinterpret_cast(fill_storage); + + etl::uninitialized_fill(fill_destination, fill_destination + 2, copy_source[0]); + CHECK_EQUAL(1, fill_destination[0].value); + CHECK_EQUAL(1, fill_destination[1].value); + + alignas(CopyConstructOnly) unsigned char value_storage[sizeof(CopyConstructOnly) * 2U]; + CopyConstructOnly* value_destination = reinterpret_cast(value_storage); + + etl::uninitialized_value_construct(value_destination, value_destination + 2); + CHECK_EQUAL(0, value_destination[0].value); + CHECK_EQUAL(0, value_destination[1].value); + + MoveConstructOnly move_source[2]; + move_source[0].value = 3; + move_source[1].value = 4; + + alignas(MoveConstructOnly) unsigned char move_storage[sizeof(MoveConstructOnly) * 2U]; + MoveConstructOnly* move_destination = reinterpret_cast(move_storage); + + etl::uninitialized_move(move_source, move_source + 2, move_destination); + CHECK_EQUAL(3, move_destination[0].value); + CHECK_EQUAL(4, move_destination[1].value); + + MoveConstructOnly move_n_source[2]; + move_n_source[0].value = 5; + move_n_source[1].value = 6; + + alignas(MoveConstructOnly) unsigned char move_n_storage[sizeof(MoveConstructOnly) * 2U]; + MoveConstructOnly* move_n_destination = reinterpret_cast(move_n_storage); + + etl::uninitialized_move_n(move_n_source, 2U, move_n_destination); + CHECK_EQUAL(5, move_n_destination[0].value); + CHECK_EQUAL(6, move_n_destination[1].value); + } +#endif + //************************************************************************* TEST(test_uninitialized_default_construct_n_trivial) { diff --git a/test/test_type_traits.cpp b/test/test_type_traits.cpp index 0c031435..844b3511 100644 --- a/test/test_type_traits.cpp +++ b/test/test_type_traits.cpp @@ -150,6 +150,29 @@ namespace NotDefaultConstructible& operator=(NotDefaultConstructible&&) = delete; }; + // The legacy compiler builtin for trivial construction does not test + // accessibility, unlike is_trivially_constructible. + struct PrivateTrivialDefaultConstructor + { + private: + + PrivateTrivialDefaultConstructor() = default; + }; + + // The legacy compiler builtin for trivial destruction does not test + // accessibility or deletion, unlike is_trivially_destructible. + struct PrivateTrivialDestructor + { + private: + + ~PrivateTrivialDestructor() = default; + }; + + struct DeletedTrivialDestructor + { + ~DeletedTrivialDestructor() = delete; + }; + // A function to test etl::type_identity. template T type_identity_test_add(T first, typename etl::type_identity::type second) @@ -2851,7 +2874,8 @@ namespace //************************************************************************* TEST(test_is_trivially_constructible) { -#if ETL_USING_STL || defined(ETL_USE_TYPE_TRAITS_BUILTINS) || defined(ETL_USER_DEFINED_TYPE_TRAITS) +#if ETL_USING_STL || defined(ETL_USE_TYPE_TRAITS_BUILTINS) || defined(ETL_USER_DEFINED_TYPE_TRAITS) || ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE \ + || ETL_USING_BUILTIN_HAS_TRIVIAL_CONSTRUCTOR #if ETL_USING_CPP17 CHECK((etl::is_trivially_constructible_v) == (std::is_trivially_constructible_v)); CHECK((etl::is_trivially_constructible_v) == (std::is_trivially_constructible_v)); @@ -2861,6 +2885,11 @@ namespace CHECK((etl::is_trivially_constructible::value) == (std::is_trivially_constructible::value)); CHECK((etl::is_trivially_constructible::value) == (std::is_trivially_constructible::value)); #endif + #if ETL_USING_CPP11 + CHECK(!etl::is_trivially_constructible::value); + CHECK((etl::is_trivially_constructible::value) + == (std::is_trivially_constructible::value)); + #endif #endif } @@ -2919,7 +2948,8 @@ namespace //************************************************************************* TEST(test_is_trivially_destructible) { -#if ETL_USING_STL || defined(ETL_USE_TYPE_TRAITS_BUILTINS) || defined(ETL_USER_DEFINED_TYPE_TRAITS) +#if ETL_USING_STL || defined(ETL_USE_TYPE_TRAITS_BUILTINS) || defined(ETL_USER_DEFINED_TYPE_TRAITS) || ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE \ + || ETL_USING_BUILTIN_HAS_TRIVIAL_DESTRUCTOR #if ETL_USING_CPP17 CHECK((etl::is_trivially_destructible_v) == (std::is_trivially_destructible_v)); CHECK((etl::is_trivially_destructible_v) == (std::is_trivially_destructible_v)); @@ -2929,6 +2959,12 @@ namespace CHECK((etl::is_trivially_destructible::value) == (std::is_trivially_destructible::value)); CHECK((etl::is_trivially_destructible::value) == (std::is_trivially_destructible::value)); #endif + #if ETL_USING_CPP11 + CHECK(!etl::is_trivially_destructible::value); + CHECK(!etl::is_trivially_destructible::value); + CHECK((etl::is_trivially_destructible::value) == (std::is_trivially_destructible::value)); + CHECK((etl::is_trivially_destructible::value) == (std::is_trivially_destructible::value)); + #endif #endif } From e25a44195ca7a6192d2f78b0629ad1bf29fc7cfa Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Thu, 3 Sep 2026 12:36:00 +0200 Subject: [PATCH 62/77] Fix size type of string_view (#1586) Fixes #1577 Rename variables to align to snake_case convention --- include/etl/string_view.h | 66 +++++++++++++++++++-------------------- test/test_string_view.cpp | 18 +++++++++++ 2 files changed, 51 insertions(+), 33 deletions(-) diff --git a/include/etl/string_view.h b/include/etl/string_view.h index 81446bcf..2cb20e37 100644 --- a/include/etl/string_view.h +++ b/include/etl/string_view.h @@ -128,10 +128,7 @@ namespace etl typedef const T* const_iterator; typedef ETL_OR_STD::reverse_iterator const_reverse_iterator; - enum - { - npos = etl::integral_limits::max - }; + static ETL_CONSTANT size_type npos = etl::integral_limits::max; //************************************************************************* /// Default constructor. @@ -172,7 +169,7 @@ namespace etl //************************************************************************* /// Construct from pointer/size. //************************************************************************* - ETL_CONSTEXPR basic_string_view(const T* begin_, size_t size_) ETL_NOEXCEPT + ETL_CONSTEXPR basic_string_view(const T* begin_, size_type size_) ETL_NOEXCEPT : mbegin(begin_) , mend(begin_ + size_) { @@ -304,15 +301,15 @@ namespace etl //************************************************************************* /// Returns the size of the array. //************************************************************************* - ETL_CONSTEXPR size_t size() const ETL_NOEXCEPT + ETL_CONSTEXPR size_type size() const ETL_NOEXCEPT { - return static_cast(mend - mbegin); + return static_cast(mend - mbegin); } //************************************************************************* /// Returns the size of the array. //************************************************************************* - ETL_CONSTEXPR size_t length() const ETL_NOEXCEPT + ETL_CONSTEXPR size_type length() const ETL_NOEXCEPT { return size(); } @@ -320,7 +317,7 @@ namespace etl //************************************************************************* /// Returns the maximum possible size of the array. //************************************************************************* - ETL_CONSTEXPR size_t max_size() const ETL_NOEXCEPT + ETL_CONSTEXPR size_type max_size() const ETL_NOEXCEPT { return size(); } @@ -347,7 +344,7 @@ namespace etl //************************************************************************* /// Assign from iterator and size. //************************************************************************* - ETL_CONSTEXPR14 void assign(const_pointer begin_, size_t size_) ETL_NOEXCEPT + ETL_CONSTEXPR14 void assign(const_pointer begin_, size_type size_) ETL_NOEXCEPT { mbegin = begin_; mend = begin_ + size_; @@ -358,7 +355,7 @@ namespace etl /// If asserts or exceptions are enabled, throws an etl::string_view_bounds /// if the index is out of bounds. //************************************************************************* - ETL_CONSTEXPR const_reference operator[](size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) + ETL_CONSTEXPR const_reference operator[](size_type i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_INDEX_OPERATOR return i < size() ? mbegin[i] : throw(ETL_ERROR(string_view_bounds)); @@ -371,7 +368,7 @@ namespace etl //************************************************************************* /// Returns a const reference to the indexed value. //************************************************************************* - const_reference at(size_t i) const + const_reference at(size_type i) const { ETL_ASSERT((mbegin != ETL_NULLPTR && mend != ETL_NULLPTR), ETL_ERROR(string_view_uninitialised)); ETL_ASSERT(i < size(), ETL_ERROR(string_view_bounds)); @@ -394,7 +391,7 @@ namespace etl //************************************************************************* ETL_CONSTEXPR14 size_type copy(T* destination, size_type count, size_type position = 0) const ETL_NOEXCEPT { - size_t n = 0UL; + size_type n = 0UL; if (position < size()) { @@ -415,7 +412,7 @@ namespace etl if (position < size()) { - size_t n = etl::min(count, size() - position); + size_type n = etl::min(count, size() - position); view = basic_string_view(mbegin + position, mbegin + position + n); } @@ -526,9 +523,9 @@ namespace etl ETL_CONSTEXPR14 bool starts_with(const T* text) const ETL_NOEXCEPT { - size_t lengthtext = TTraits::length(text); + size_type length_text = TTraits::length(text); - return (size() >= lengthtext) && (compare(0, lengthtext, text) == 0); + return (size() >= length_text) && (compare(0, length_text, text) == 0); } //************************************************************************* @@ -546,10 +543,10 @@ namespace etl ETL_CONSTEXPR14 bool ends_with(const T* text) const { - size_t lengthtext = TTraits::length(text); - size_t lengthview = size(); + size_type length_text = TTraits::length(text); + size_type length_view = size(); - return (lengthview >= lengthtext) && (compare(lengthview - lengthtext, lengthtext, text) == 0); + return (length_view >= length_text) && (compare(length_view - length_text, length_text, text) == 0); } //************************************************************************* @@ -633,15 +630,15 @@ namespace etl //************************************************************************* ETL_CONSTEXPR14 size_type find_first_of(etl::basic_string_view view, size_type position = 0) const ETL_NOEXCEPT { - const size_t lengthtext = size(); + const size_type length_text = size(); - if (position < lengthtext) + if (position < length_text) { - for (size_t i = position; i < lengthtext; ++i) + for (size_type i = position; i < length_text; ++i) { - const size_t lengthview = view.size(); + const size_type length_view = view.size(); - for (size_t j = 0UL; j < lengthview; ++j) + for (size_type j = 0UL; j < length_view; ++j) { if (mbegin[i] == view[j]) { @@ -685,9 +682,9 @@ namespace etl while (it != rend()) { - const size_t viewlength = view.size(); + const size_type view_length = view.size(); - for (size_t j = 0UL; j < viewlength; ++j) + for (size_type j = 0UL; j < view_length; ++j) { if (mbegin[position] == view[j]) { @@ -722,17 +719,17 @@ namespace etl //************************************************************************* ETL_CONSTEXPR14 size_type find_first_not_of(etl::basic_string_view view, size_type position = 0) const ETL_NOEXCEPT { - const size_t lengthtext = size(); + const size_type length_text = size(); - if (position < lengthtext) + if (position < length_text) { - for (size_t i = position; i < lengthtext; ++i) + for (size_type i = position; i < length_text; ++i) { bool found = false; - const size_t viewlength = view.size(); + const size_type view_length = view.size(); - for (size_t j = 0UL; j < viewlength; ++j) + for (size_type j = 0UL; j < view_length; ++j) { if (mbegin[i] == view[j]) { @@ -784,9 +781,9 @@ namespace etl { bool found = false; - const size_t viewlength = view.size(); + const size_type view_length = view.size(); - for (size_t j = 0UL; j < viewlength; ++j) + for (size_type j = 0UL; j < view_length; ++j) { if (mbegin[position] == view[j]) { @@ -900,6 +897,9 @@ namespace etl const_pointer mend; }; + template + ETL_CONSTANT typename basic_string_view::size_type basic_string_view::npos; + typedef etl::basic_string_view string_view; typedef etl::basic_string_view wstring_view; typedef etl::basic_string_view u8string_view; diff --git a/test/test_string_view.cpp b/test/test_string_view.cpp index 1d016160..ad234834 100644 --- a/test/test_string_view.cpp +++ b/test/test_string_view.cpp @@ -1262,5 +1262,23 @@ namespace CHECK_EQUAL(std_pos, etl_pos); } } + + //************************************************************************* + TEST(test_npos_type_and_value) + { + typedef etl::string_view::size_type size_type; + + // npos must have the same type as size_type, not an anonymous enum. + CHECK((etl::is_same::value)); + CHECK_EQUAL(etl::integral_limits::max, etl::string_view::npos); + + // npos must compare equal to the equivalent std::string_view constant. + CHECK_EQUAL(std::string::npos, etl::string_view::npos); + + // substr with the default count must return the whole remaining view. + etl::string_view view("Hello World"); + CHECK_EQUAL(std::string("World"), std::string(view.substr(6).data(), view.substr(6).size())); + CHECK_EQUAL(5U, view.substr(6, etl::string_view::npos).size()); + } } } // namespace From a547336322430a5e0069b5679f1172e9a3f1fb4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Sch=C3=A4pers?= Date: Thu, 3 Sep 2026 13:27:56 +0200 Subject: [PATCH 63/77] Set default compare for iconst_map (#1560) This is consistent with (i)flat_map, and allows to just write iconst_map. Add tests, also fix iconst_set. --- include/etl/const_map.h | 2 +- include/etl/const_set.h | 2 +- test/test_const_map.cpp | 3 +++ test/test_const_multimap.cpp | 3 +++ test/test_const_multiset.cpp | 3 +++ test/test_const_set.cpp | 2 ++ test/test_flat_map.cpp | 2 ++ test/test_flat_multimap.cpp | 2 ++ test/test_flat_multiset.cpp | 2 ++ test/test_flat_set.cpp | 2 ++ 10 files changed, 21 insertions(+), 2 deletions(-) diff --git a/include/etl/const_map.h b/include/etl/const_map.h index 747cc5ed..6bcab37d 100644 --- a/include/etl/const_map.h +++ b/include/etl/const_map.h @@ -48,7 +48,7 @@ SOFTWARE. namespace etl { - template + template > class iconst_map { public: diff --git a/include/etl/const_set.h b/include/etl/const_set.h index e734de43..29841dba 100644 --- a/include/etl/const_set.h +++ b/include/etl/const_set.h @@ -48,7 +48,7 @@ SOFTWARE. namespace etl { - template + template > class iconst_set { public: diff --git a/test/test_const_map.cpp b/test/test_const_map.cpp index a3dc8f14..ee8b5aff 100644 --- a/test/test_const_map.cpp +++ b/test/test_const_map.cpp @@ -112,6 +112,9 @@ namespace using mapped_type = Data::mapped_type; using const_iterator = Data::const_iterator; + static_assert(etl::is_same, etl::iconst_map>>::value, + "default compare is not less"); + SUITE(test_const_map) { //************************************************************************* diff --git a/test/test_const_multimap.cpp b/test/test_const_multimap.cpp index 3e181311..02bd030e 100644 --- a/test/test_const_multimap.cpp +++ b/test/test_const_multimap.cpp @@ -111,6 +111,9 @@ namespace using mapped_type = Data::mapped_type; using const_iterator = Data::const_iterator; + static_assert(etl::is_same, etl::iconst_multimap>>::value, + "default compare is not less"); + SUITE(test_const_multimap) { //************************************************************************* diff --git a/test/test_const_multiset.cpp b/test/test_const_multiset.cpp index 62310317..8baf4198 100644 --- a/test/test_const_multiset.cpp +++ b/test/test_const_multiset.cpp @@ -114,6 +114,9 @@ namespace using key_type = Data::key_type; using const_iterator = Data::const_iterator; + static_assert(etl::is_same, etl::iconst_multiset>>::value, + "default compare is not less"); + SUITE(test_const_multiset) { //************************************************************************* diff --git a/test/test_const_set.cpp b/test/test_const_set.cpp index 47f78e0f..0383e852 100644 --- a/test/test_const_set.cpp +++ b/test/test_const_set.cpp @@ -112,6 +112,8 @@ namespace using key_type = Data::key_type; using const_iterator = Data::const_iterator; + static_assert(etl::is_same, etl::iconst_set>>::value, "default compare is not less"); + SUITE(test_const_set) { //************************************************************************* diff --git a/test/test_flat_map.cpp b/test/test_flat_map.cpp index ae9ddbdd..431c8980 100644 --- a/test/test_flat_map.cpp +++ b/test/test_flat_map.cpp @@ -211,6 +211,8 @@ namespace // return os; //} + static_assert(etl::is_same, etl::iflat_map>>::value, "default compare is not less"); + SUITE(test_flat_map) { NDC N0 = NDC("A"); diff --git a/test/test_flat_multimap.cpp b/test/test_flat_multimap.cpp index d1f3ccd9..01c1eba8 100644 --- a/test/test_flat_multimap.cpp +++ b/test/test_flat_multimap.cpp @@ -239,6 +239,8 @@ namespace return true; } + static_assert(etl::is_same, etl::iflat_multimap>>::value, "default compare is not less"); + SUITE(test_flat_multimap) { //************************************************************************* diff --git a/test/test_flat_multiset.cpp b/test/test_flat_multiset.cpp index 2d91748e..a0625d1a 100644 --- a/test/test_flat_multiset.cpp +++ b/test/test_flat_multiset.cpp @@ -241,6 +241,8 @@ namespace std::vector int_data; + static_assert(etl::is_same, etl::iflat_multiset>>::value, "default compare is not less"); + //************************************************************************* struct SetupFixture { diff --git a/test/test_flat_set.cpp b/test/test_flat_set.cpp index 7930c72d..f90fde74 100644 --- a/test/test_flat_set.cpp +++ b/test/test_flat_set.cpp @@ -261,6 +261,8 @@ namespace return (lhs.value < rhs.k.value); } + static_assert(etl::is_same, etl::iflat_set>>::value, "default compare is not less"); + //************************************************************************* struct SetupFixture { From 75c07eca5461b41128aac07e01bec564fe32e7bd Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Thu, 3 Sep 2026 14:38:51 +0200 Subject: [PATCH 64/77] Absolute and casting updates (#1583) --- docs/utilities/utility.md | 46 +++++++ include/etl/absolute.h | 19 ++- include/etl/utility.h | 24 ++++ test/test_math_functions.cpp | 104 +++++++++++++--- test/test_utility.cpp | 234 +++++++++++++++++++++++++++++++++++ 5 files changed, 400 insertions(+), 27 deletions(-) diff --git a/docs/utilities/utility.md b/docs/utilities/utility.md index c4f76c5f..48c0c3e3 100644 --- a/docs/utilities/utility.md +++ b/docs/utilities/utility.md @@ -605,3 +605,49 @@ struct nontype_t; ```cpp using FortyTwo = etl::nontype_t<42> ``` + +--- + +## to_unsigned + +```cpp +template +ETL_NODISCARD +ETL_CONSTEXPR14 +typename etl::make_unsigned::type + to_unsigned(T value) ETL_NOEXCEPT +``` + +**Description** +Casts `value` to the unsigned type. + +**Example** + +```cpp +int8_t a = -50; +auto b = etl::to_unsigned(a); +``` +`b` is of type `uint8_t` and contains the value `206`. + +--- + +## to_signed + +```cpp +template +ETL_NODISCARD +ETL_CONSTEXPR14 +typename etl::make_signed::type + to_signed(T value) ETL_NOEXCEPT +``` + +**Description** +Casts `value` to the signed type. + +**Example** + +```cpp +uint8_t a = 206; +auto b = etl::to_signed(a); +``` +`b` is of type `int8_t` and contains the value `-50`. diff --git a/include/etl/absolute.h b/include/etl/absolute.h index bfa68642..14988ffa 100644 --- a/include/etl/absolute.h +++ b/include/etl/absolute.h @@ -58,14 +58,13 @@ namespace etl // For signed types. //*************************************************************************** template - ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if< etl::is_signed::value && etl::is_integral::value, T>::type absolute(T value) + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value && etl::is_integral::value, T>::type absolute(T value) { return (value == etl::integral_limits::min) ? etl::private_absolute::signed_min_error() : static_cast((value < T(0)) ? -value : value); } template - ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if< etl::is_signed::value && !etl::is_integral::value, T>::type absolute(T value) - ETL_NOEXCEPT + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value && !etl::is_integral::value, T>::type absolute(T value) ETL_NOEXCEPT { return (value < T(0)) ? -value : value; } @@ -84,14 +83,13 @@ namespace etl // Returns the result as the unsigned type. //*************************************************************************** template - ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value, typename etl::make_unsigned::type>::type absolute_unsigned(T value) - ETL_NOEXCEPT + ETL_NODISCARD + ETL_CONSTEXPR typename etl::enable_if::value && etl::is_integral::value, typename etl::make_unsigned::type>::type + absolute_unsigned(T value) ETL_NOEXCEPT { typedef typename etl::make_unsigned::type TReturn; - return (value == etl::integral_limits::min) ? (etl::integral_limits::max / 2U) + 1U - : (value < T(0)) ? TReturn(-value) - : TReturn(value); + return (value < T(0)) ? 0U - static_cast(value) : static_cast(value); } //*************************************************************************** @@ -99,9 +97,10 @@ namespace etl // Returns the result as the unsigned type. //*************************************************************************** template - ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value, T>::type absolute_unsigned(T value) ETL_NOEXCEPT + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value && etl::is_integral::value, T>::type absolute_unsigned(T value) + ETL_NOEXCEPT { - return etl::absolute(value); + return value; } } // namespace etl diff --git a/include/etl/utility.h b/include/etl/utility.h index f11ccca8..6d93aa1f 100644 --- a/include/etl/utility.h +++ b/include/etl/utility.h @@ -1384,6 +1384,30 @@ namespace etl static constexpr T value = Value; }; #endif + + //*************************************************************************** + /// Returns 'value' cast to an unsigned integer. + ///\param value The integer to cast. + ///\return The unsigned integer. + //*************************************************************************** + template + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value, typename etl::make_unsigned::type>::type to_unsigned(T value) + ETL_NOEXCEPT + { + return static_cast::type>(value); + } + + //*************************************************************************** + /// Returns 'value' cast to a signed integer. + ///\param value The integer to cast. + ///\return The signed integer. + //*************************************************************************** + template + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value, typename etl::make_signed::type>::type to_signed(T value) + ETL_NOEXCEPT + { + return static_cast::type>(value); + } } // namespace etl #endif diff --git a/test/test_math_functions.cpp b/test/test_math_functions.cpp index 3c672e42..05171bb0 100644 --- a/test/test_math_functions.cpp +++ b/test/test_math_functions.cpp @@ -556,7 +556,28 @@ namespace } //************************************************************************* - TEST(test_absolute_unsigned) + TEST(test_absolute_unsigned_int8_t) + { + // Edge cases for int8_t : min is -128, which has no positive int8_t + // counterpart, but is representable in uint8_t (128). + CHECK_EQUAL(uint8_t(0), etl::absolute_unsigned(int8_t(0))); + CHECK_EQUAL(uint8_t(127), etl::absolute_unsigned(int8_t(127))); + CHECK_EQUAL(uint8_t(127), etl::absolute_unsigned(int8_t(-127))); + CHECK_EQUAL(uint8_t(128), etl::absolute_unsigned(int8_t(-128))); // INT8_MIN edge case + + CHECK_EQUAL(uint8_t(0), etl::absolute_unsigned(uint8_t(0))); + CHECK_EQUAL(uint8_t(255), etl::absolute_unsigned(uint8_t(255))); + } + + //************************************************************************* + TEST(test_absolute_unsigned_uint8_t) + { + CHECK_EQUAL(uint8_t(0), etl::absolute_unsigned(uint8_t(0))); + CHECK_EQUAL(uint8_t(255), etl::absolute_unsigned(uint8_t(255))); + } + + //************************************************************************* + TEST(test_absolute_unsigned_int16_t) { CHECK_EQUAL(uint16_t(0), etl::absolute_unsigned(int16_t(0))); CHECK_EQUAL(uint16_t(32767), etl::absolute_unsigned(int16_t(32767))); @@ -568,23 +589,72 @@ namespace } //************************************************************************* - TEST(test_absolute_unsigned_constexpr) + TEST(test_absolute_unsigned_uint16_t) { - constexpr uint16_t absolute1 = etl::absolute(int16_t(0)); - constexpr uint16_t absolute2 = etl::absolute(int16_t(32767)); - constexpr uint16_t absolute3 = etl::absolute(int16_t(-32767)); - // constexpr uint16_t absolute4 = etl::absolute(int16_t(-32768)); // - // Compile error - - CHECK_EQUAL(uint16_t(0), absolute1); - CHECK_EQUAL(uint16_t(32767), absolute2); - CHECK_EQUAL(uint16_t(32767), absolute3); - - constexpr uint16_t absolute5 = etl::absolute(uint16_t(0)); - constexpr uint16_t absolute6 = etl::absolute(uint16_t(65535)); - - CHECK_EQUAL(uint16_t(0), absolute5); - CHECK_EQUAL(uint16_t(65535), absolute6); + CHECK_EQUAL(uint16_t(0), etl::absolute_unsigned(uint16_t(0))); + CHECK_EQUAL(uint16_t(65535), etl::absolute_unsigned(uint16_t(65535))); } + + //************************************************************************* + TEST(test_absolute_unsigned_int32_t) + { + CHECK_EQUAL(uint32_t(0), etl::absolute_unsigned(int32_t(0))); + CHECK_EQUAL(uint32_t(2147483647), etl::absolute_unsigned(int32_t(2147483647))); + CHECK_EQUAL(uint32_t(2147483647), etl::absolute_unsigned(int32_t(-2147483647))); + CHECK_EQUAL(uint32_t(2147483648u), etl::absolute_unsigned(int32_t(-2147483647 - 1))); // INT32_MIN edge case + + CHECK_EQUAL(uint32_t(0), etl::absolute_unsigned(uint32_t(0))); + CHECK_EQUAL(uint32_t(4294967295u), etl::absolute_unsigned(uint32_t(4294967295u))); + } + + //************************************************************************* + TEST(test_absolute_unsigned_uint32_t) + { + CHECK_EQUAL(uint32_t(0), etl::absolute_unsigned(uint32_t(0))); + CHECK_EQUAL(uint32_t(4294967295u), etl::absolute_unsigned(uint32_t(4294967295u))); + } + + //************************************************************************* + TEST(test_absolute_unsigned_int64_t) + { + CHECK_EQUAL(uint64_t(0), etl::absolute_unsigned(int64_t(0))); + CHECK_EQUAL(uint64_t(9223372036854775807LL), etl::absolute_unsigned(int64_t(9223372036854775807LL))); + CHECK_EQUAL(uint64_t(9223372036854775807LL), etl::absolute_unsigned(int64_t(-9223372036854775807LL))); + CHECK_EQUAL(uint64_t(9223372036854775808ULL), etl::absolute_unsigned(int64_t(-9223372036854775807LL - 1))); // INT64_MIN edge case + + CHECK_EQUAL(uint64_t(0), etl::absolute_unsigned(uint64_t(0))); + CHECK_EQUAL(uint64_t(18446744073709551615ULL), etl::absolute_unsigned(uint64_t(18446744073709551615ULL))); + } + + //************************************************************************* + TEST(test_absolute_unsigned_uint64_t) + { + CHECK_EQUAL(uint64_t(0), etl::absolute_unsigned(uint64_t(0))); + CHECK_EQUAL(uint64_t(18446744073709551615ULL), etl::absolute_unsigned(uint64_t(18446744073709551615ULL))); + } + +#if ETL_USING_CPP14 + //************************************************************************* + TEST(test_absolute_unsigned_constexpr14) + { + // Unlike etl::absolute, absolute_unsigned can represent INT_MIN safely, + // so this should be usable at compile time even for the minimum value. + constexpr uint16_t r1 = etl::absolute_unsigned(int16_t(0)); + constexpr uint16_t r2 = etl::absolute_unsigned(int16_t(32767)); + constexpr uint16_t r3 = etl::absolute_unsigned(int16_t(-32767)); + constexpr uint16_t r4 = etl::absolute_unsigned(int16_t(-32768)); // No compile error, unlike etl::absolute + + CHECK_EQUAL(uint16_t(0), r1); + CHECK_EQUAL(uint16_t(32767), r2); + CHECK_EQUAL(uint16_t(32767), r3); + CHECK_EQUAL(uint16_t(32768), r4); + + constexpr uint16_t r5 = etl::absolute_unsigned(uint16_t(0)); + constexpr uint16_t r6 = etl::absolute_unsigned(uint16_t(65535)); + + CHECK_EQUAL(uint16_t(0), r5); + CHECK_EQUAL(uint16_t(65535), r6); + } +#endif } } // namespace diff --git a/test/test_utility.cpp b/test/test_utility.cpp index 2093a355..124cff1f 100644 --- a/test/test_utility.cpp +++ b/test/test_utility.cpp @@ -1299,5 +1299,239 @@ namespace CHECK(true); } #endif + + //************************************************************************* + TEST(test_to_unsigned_from_int8_t) + { + auto umin = etl::to_unsigned(etl::integral_limits::min); + auto umax = etl::to_unsigned(etl::integral_limits::max); + auto uzero = etl::to_unsigned(int8_t(0)); + auto uplus = etl::to_unsigned(int8_t(50)); + auto uminus = etl::to_unsigned(int8_t(-50)); + + auto umin_expected = static_cast(etl::integral_limits::min); + auto umax_expected = static_cast(etl::integral_limits::max); + auto uzero_expected = static_cast(0); + auto uplus_expected = static_cast(50); + auto uminus_expected = static_cast(-50); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(int(umin_expected), int(umin)); + CHECK_EQUAL(int(umax_expected), int(umax)); + CHECK_EQUAL(int(uzero_expected), int(uzero)); + CHECK_EQUAL(int(uplus_expected), int(uplus)); + CHECK_EQUAL(int(uminus_expected), int(uminus)); + } + + //************************************************************************* + TEST(test_to_unsigned_from_int16_t) + { + auto umin = etl::to_unsigned(etl::integral_limits::min); + auto umax = etl::to_unsigned(etl::integral_limits::max); + auto uzero = etl::to_unsigned(int16_t(0)); + auto uplus = etl::to_unsigned(int16_t(50)); + auto uminus = etl::to_unsigned(int16_t(-50)); + + auto umin_expected = static_cast(etl::integral_limits::min); + auto umax_expected = static_cast(etl::integral_limits::max); + auto uzero_expected = static_cast(0); + auto uplus_expected = static_cast(50); + auto uminus_expected = static_cast(-50); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(umin_expected, umin); + CHECK_EQUAL(umax_expected, umax); + CHECK_EQUAL(uzero_expected, uzero); + CHECK_EQUAL(uplus_expected, uplus); + CHECK_EQUAL(uminus_expected, uminus); + } + + //************************************************************************* + TEST(test_to_unsigned_from_int32_t) + { + auto umin = etl::to_unsigned(etl::integral_limits::min); + auto umax = etl::to_unsigned(etl::integral_limits::max); + auto uzero = etl::to_unsigned(int32_t(0)); + auto uplus = etl::to_unsigned(int32_t(50)); + auto uminus = etl::to_unsigned(int32_t(-50)); + + auto umin_expected = static_cast(etl::integral_limits::min); + auto umax_expected = static_cast(etl::integral_limits::max); + auto uzero_expected = static_cast(0); + auto uplus_expected = static_cast(50); + auto uminus_expected = static_cast(-50); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(umin_expected, umin); + CHECK_EQUAL(umax_expected, umax); + CHECK_EQUAL(uzero_expected, uzero); + CHECK_EQUAL(uplus_expected, uplus); + CHECK_EQUAL(uminus_expected, uminus); + } + + //************************************************************************* + TEST(test_to_unsigned_from_int64_t) + { + auto umin = etl::to_unsigned(etl::integral_limits::min); + auto umax = etl::to_unsigned(etl::integral_limits::max); + auto uzero = etl::to_unsigned(int64_t(0)); + auto uplus = etl::to_unsigned(int64_t(50)); + auto uminus = etl::to_unsigned(int64_t(-50)); + + auto umin_expected = static_cast(etl::integral_limits::min); + auto umax_expected = static_cast(etl::integral_limits::max); + auto uzero_expected = static_cast(0); + auto uplus_expected = static_cast(50); + auto uminus_expected = static_cast(-50); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(umin_expected, umin); + CHECK_EQUAL(umax_expected, umax); + CHECK_EQUAL(uzero_expected, uzero); + CHECK_EQUAL(uplus_expected, uplus); + CHECK_EQUAL(uminus_expected, uminus); + } + + //************************************************************************* + TEST(test_to_signed_from_uint8_t) + { + auto smin = etl::to_signed(etl::integral_limits::min); + auto smax = etl::to_signed(etl::integral_limits::max); + auto splus = etl::to_signed(uint8_t(50)); + auto sminus = etl::to_signed(uint8_t(206)); + + auto smin_expected = static_cast(etl::integral_limits::min); + auto smax_expected = static_cast(etl::integral_limits::max); + auto splus_expected = static_cast(50); + auto sminus_expected = static_cast(206); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(int(smin_expected), int(smin)); + CHECK_EQUAL(int(smax_expected), int(smax)); + CHECK_EQUAL(int(splus_expected), int(splus)); + CHECK_EQUAL(int(sminus_expected), int(sminus)); + } + + //************************************************************************* + TEST(test_to_signed_from_uint16_t) + { + auto smin = etl::to_signed(etl::integral_limits::min); + auto smax = etl::to_signed(etl::integral_limits::max); + auto splus = etl::to_signed(uint16_t(50)); + auto sminus = etl::to_signed(uint16_t(65486)); + + auto smin_expected = static_cast(etl::integral_limits::min); + auto smax_expected = static_cast(etl::integral_limits::max); + auto splus_expected = static_cast(50); + auto sminus_expected = static_cast(65486); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(smin_expected, smin); + CHECK_EQUAL(smax_expected, smax); + CHECK_EQUAL(splus_expected, splus); + CHECK_EQUAL(sminus_expected, sminus); + } + + //************************************************************************* + TEST(test_to_signed_from_uint32_t) + { + auto smin = etl::to_signed(etl::integral_limits::min); + auto smax = etl::to_signed(etl::integral_limits::max); + auto splus = etl::to_signed(uint32_t(50)); + auto sminus = etl::to_signed(uint32_t(4294967246U)); + + auto smin_expected = static_cast(etl::integral_limits::min); + auto smax_expected = static_cast(etl::integral_limits::max); + auto splus_expected = static_cast(50); + auto sminus_expected = static_cast(4294967246U); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(smin_expected, smin); + CHECK_EQUAL(smax_expected, smax); + CHECK_EQUAL(splus_expected, splus); + CHECK_EQUAL(sminus_expected, sminus); + } + + //************************************************************************* + TEST(test_to_signed_from_uint64_t) + { + auto smin = etl::to_signed(etl::integral_limits::min); + auto smax = etl::to_signed(etl::integral_limits::max); + auto splus = etl::to_signed(uint64_t(50)); + auto sminus = etl::to_signed(uint64_t(18446744073709551566ULL)); + + auto smin_expected = static_cast(etl::integral_limits::min); + auto smax_expected = static_cast(etl::integral_limits::max); + auto splus_expected = static_cast(50); + auto sminus_expected = static_cast(18446744073709551566ULL); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(smin_expected, smin); + CHECK_EQUAL(smax_expected, smax); + CHECK_EQUAL(splus_expected, splus); + CHECK_EQUAL(sminus_expected, sminus); + } + + //************************************************************************* + TEST(test_to_unsigned_constexpr) + { + const int8_t value = static_cast(-50); + + constexpr auto Size = etl::to_unsigned(value); + + CHECK_EQUAL(static_cast(value), Size); + + int a[Size] = {0}; // Use Size in a context that requires a constant expression + (void)a; // Avoid unused variable warning + } + + //************************************************************************* + TEST(test_to_signed_constexpr) + { + const uint8_t value = static_cast(50); + + constexpr auto Size = etl::to_signed(value); + + CHECK_EQUAL(static_cast(value), Size); + + int a[Size] = {0}; // Use Size in a context that requires a constant expression + (void)a; // Avoid unused variable warning + } } } // namespace From c8f610a69da407b88f49ba45ddc5e79bf4877e64 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Sat, 5 Sep 2026 09:08:56 +0200 Subject: [PATCH 65/77] Optimize format.h (#1588) Dispatch only the argument types which are actually stated. For everything else, don't generate the code anymore. Detemplatize parse_format_spec(). Collapse long double and double to double on targets where they are same size. This helps prevent duplicate code generation. --- include/etl/format.h | 264 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 246 insertions(+), 18 deletions(-) diff --git a/include/etl/format.h b/include/etl/format.h index 830e64a6..288454fd 100644 --- a/include/etl/format.h +++ b/include/etl/format.h @@ -652,6 +652,197 @@ namespace etl struct is_formattable : etl::bool_constant::value && has_formatter_format::value> { }; + + //************************************************************************* + // Compile-time argument mask. + // + // basic_format_arg stores its value in a variant with a fixed set of + // alternatives, and etl::visit dispatches over every one of them. That + // would instantiate etl::formatter for all alternatives - including the + // whole floating point formatting chain - even for a call that only ever + // passes an int. + // + // The mask records which alternatives can actually occur for a given pack + // of argument types. format_visitor turns the unreachable alternatives into + // empty functions, so their formatters are never instantiated and the + // linker never sees the code. + // + // A single table, arg_type_mask, drives both halves: it builds the mask + // from the argument pack, and it tells the visitor which bit an alternative + // corresponds to. + // + // Soundness: the mask is only ever narrowed for argument types that are + // recognised explicitly below. Anything else - including user-defined types + // and pointers other than void* - falls back to mask_all, which reproduces + // the unmasked behaviour exactly. + //************************************************************************* + typedef unsigned int arg_mask_t; + + static ETL_CONSTANT arg_mask_t mask_monostate = 1U << 0; + static ETL_CONSTANT arg_mask_t mask_bool = 1U << 1; + static ETL_CONSTANT arg_mask_t mask_char = 1U << 2; + static ETL_CONSTANT arg_mask_t mask_int = 1U << 3; + static ETL_CONSTANT arg_mask_t mask_uint = 1U << 4; + static ETL_CONSTANT arg_mask_t mask_llong = 1U << 5; + static ETL_CONSTANT arg_mask_t mask_ullong = 1U << 6; + static ETL_CONSTANT arg_mask_t mask_float = 1U << 7; + static ETL_CONSTANT arg_mask_t mask_double = 1U << 8; + static ETL_CONSTANT arg_mask_t mask_ldouble = 1U << 9; + static ETL_CONSTANT arg_mask_t mask_cstring = 1U << 10; + static ETL_CONSTANT arg_mask_t mask_string_view = 1U << 11; + static ETL_CONSTANT arg_mask_t mask_voidp = 1U << 12; + static ETL_CONSTANT arg_mask_t mask_all = ~static_cast(0); + + // Maps a type onto the set of alternatives a value of that type can be + // stored as. Mirrors the converting constructors of basic_format_arg. + // + // This serves both directions of the problem. For a user argument type it + // gives the alternatives that argument may end up in, which is how the + // per-call mask is built. For a variant alternative it degenerates to that + // alternative's own bit, which is how format_visitor tests reachability. + // + // The primary template is deliberately conservative: an unrecognised type + // yields mask_all, so it is never wrongly excluded. + template + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_all; + }; + + // Not constructible as a user argument; listed so that the table covers + // every variant alternative. Every mask contains this bit regardless, as + // the args_mask recursion bottoms out at mask_monostate. + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_monostate; + }; + + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_bool; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_char; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_int; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_uint; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_int; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_uint; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_int; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_uint; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_llong; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_ullong; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_llong; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_ullong; + }; + #if ETL_USING_FORMAT_FLOATING_POINT + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_float; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_double; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_ldouble; + }; + #endif + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_cstring; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_cstring; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_string_view; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_voidp; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_voidp; + }; + + // etl::string, etl::istring and friends are stored as a string_view. + template + struct arg_type_mask, T>::value>> + { + static ETL_CONSTANT arg_mask_t value = mask_string_view; + }; + + // The empty alternative is always reachable, so monostate is always set. + template + struct args_mask; + + template <> + struct args_mask<> + { + static ETL_CONSTANT arg_mask_t value = mask_monostate; + }; + + template + struct args_mask + { + static ETL_CONSTANT arg_mask_t value = arg_type_mask>::value | args_mask::value; + }; } // namespace private_format template @@ -1206,11 +1397,11 @@ namespace etl return false; } - template - void parse_format_spec(format_parse_context& parse_ctx, format_context& fmt_context) + // Not templated on the output iterator: the parse phase only reads the + // parse context and writes the format spec, so a single instantiation is + // shared by every output iterator type. + inline void parse_format_spec(format_parse_context& parse_ctx, format_spec_t& format_spec) { - auto& format_spec = fmt_context.format_spec; - format_spec = format_spec_t(); // reset format_spec to defaults format_spec.index = parse_num(parse_ctx); // optional explicit index @@ -1536,6 +1727,19 @@ namespace etl } #if ETL_USING_FORMAT_FLOATING_POINT + //*********************************** + // On many targets (e.g. ARM EABI, MSVC) 'long double' has exactly the same + // representation as 'double'. There, formatting a long double through the + // 'double' instantiation is value-preserving, and avoids emitting a second, + // byte-identical copy of the whole floating point formatting chain. + // On targets where the two differ (e.g. x86 80-bit) 'long double' is kept. + typedef etl::conditional_t<(sizeof(long double) == sizeof(double)) + && (etl::numeric_limits::digits == etl::numeric_limits::digits) + && (etl::numeric_limits::max_exponent == etl::numeric_limits::max_exponent) + && (etl::numeric_limits::min_exponent == etl::numeric_limits::min_exponent), + double, long double> + long_double_format_type; + #if ETL_NOT_USING_FORMAT_LONG_DOUBLE_MATH //*********************************** // Math function wrappers to handle toolchains that don't provide @@ -2004,7 +2208,7 @@ namespace etl } #endif - template + template struct format_visitor { using output_iterator = OutputIt; @@ -2019,11 +2223,12 @@ namespace etl template void operator()(T value) { - formatter f; - format_parse_context::iterator it = f.parse(parse_ctx); - parse_ctx.advance_to(it); - OutputIt fit = f.format(value, fmt_ctx); - fmt_ctx.advance_to(fit); + // T is a variant alternative here, so arg_type_mask is that + // alternative's own bit. + // Tag dispatch rather than 'if constexpr' so that the formatter for an + // unreachable alternative is not instantiated in C++11/14 either. + typedef etl::conditional_t<(arg_type_mask::value & Mask) != 0, etl::true_type, etl::false_type> reachable; + dispatch(value, reachable()); } // for user-defined types routed through basic_format_arg::handle @@ -2034,6 +2239,25 @@ namespace etl format_parse_context& parse_ctx; format_context& fmt_ctx; + + private: + + template + void dispatch(T value, etl::true_type) + { + formatter f; + format_parse_context::iterator it = f.parse(parse_ctx); + parse_ctx.advance_to(it); + OutputIt fit = f.format(value, fmt_ctx); + fmt_ctx.advance_to(fit); + } + + // This alternative cannot be reached for the argument types of this call, + // so no formatter is instantiated for it. + template + void dispatch(T, etl::false_type) + { + } }; template @@ -2645,7 +2869,8 @@ namespace etl template typename format_context::iterator format(long double arg, format_context& fmt_ctx) { - return private_format::format_aligned_floating(arg, fmt_ctx); + typedef private_format::long_double_format_type type; + return private_format::format_aligned_floating(static_cast(arg), fmt_ctx); } }; #endif @@ -2715,12 +2940,15 @@ namespace etl } }; - template + // Mask is a compile-time set of the basic_format_arg alternatives that the + // caller can actually supply. It only removes unreachable code paths; the + // default of mask_all formats every alternative, as before. + template OutputIt vformat_to(OutputIt out, etl::string_view fmt, format_args args) { - format_parse_context parse_context(fmt, args.size()); - format_context fmt_context(out, args); - private_format::format_visitor v(parse_context, fmt_context); + format_parse_context parse_context(fmt, args.size()); + format_context fmt_context(out, args); + private_format::format_visitor v(parse_context, fmt_context); while (parse_context.begin() != parse_context.end()) { @@ -2736,7 +2964,7 @@ namespace etl } else { - private_format::parse_format_spec(parse_context, fmt_context); + private_format::parse_format_spec(parse_context, fmt_context.format_spec); // Resolve nested replacement fields for width/precision private_format::resolve_nested_replacements(fmt_context.format_spec, args); @@ -2774,14 +3002,14 @@ namespace etl OutputIt format_to(OutputIt out, format_string fmt, Args&&... args) { auto the_args{make_format_args(args...)}; - return vformat_to(etl::move(out), fmt.get(), format_args(the_args)); + return vformat_to::value>(etl::move(out), fmt.get(), format_args(the_args)); } template , class... Args> OutputIt format_to_n(OutputIt out, size_t n, format_string fmt, Args&&... args) { auto the_args{make_format_args(args...)}; - return vformat_to(WrapperIt(out, n), fmt.get(), format_args(the_args)).get(); + return vformat_to::value>(WrapperIt(out, n), fmt.get(), format_args(the_args)).get(); } // non std in the following, specific to etl From dd05595860fc23cb3dc82fcbb668bc74b546030e Mon Sep 17 00:00:00 2001 From: Ayesha Noor Date: Sat, 5 Sep 2026 16:22:48 +0500 Subject: [PATCH 66/77] Fix MSVC C4244 warning in format specifiers (#1589) Co-authored-by: John Wellbelove --- include/etl/basic_format_spec.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/etl/basic_format_spec.h b/include/etl/basic_format_spec.h index cd8b3e33..1ad83322 100644 --- a/include/etl/basic_format_spec.h +++ b/include/etl/basic_format_spec.h @@ -137,13 +137,13 @@ namespace etl //*************************************************************************** inline ETL_CONSTEXPR private_basic_format_spec::base_spec setbase(uint32_t base) { - return private_basic_format_spec::base_spec(base); + return private_basic_format_spec::base_spec(static_cast(base)); } //********************************* inline ETL_CONSTEXPR private_basic_format_spec::width_spec setw(uint32_t width) { - return private_basic_format_spec::width_spec(width); + return private_basic_format_spec::width_spec(static_cast(width)); } //********************************* @@ -156,20 +156,20 @@ namespace etl //********************************* inline ETL_CONSTEXPR private_basic_format_spec::precision_spec setprecision(uint32_t precision) { - return private_basic_format_spec::precision_spec(precision); + return private_basic_format_spec::precision_spec(static_cast(precision)); } //********************************* - ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::base_spec bin(2U); + static ETL_CONSTANT private_basic_format_spec::base_spec bin(static_cast(2U)); //********************************* - ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::base_spec oct(8U); + static ETL_CONSTANT private_basic_format_spec::base_spec oct(static_cast(8U)); //********************************* - ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::base_spec dec(10U); + static ETL_CONSTANT private_basic_format_spec::base_spec dec(static_cast(10U)); //********************************* - ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::base_spec hex(16U); + static ETL_CONSTANT private_basic_format_spec::base_spec hex(static_cast(16U)); //********************************* ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::left_spec left = private_basic_format_spec::left_spec(); From ae9207dd0affb1e029a36754d069f31544f88825 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Thu, 3 Sep 2026 15:29:10 +0100 Subject: [PATCH 67/77] Changed version from 20.50.0 to 20.49.0 --- docs/types/type-traits.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/types/type-traits.md b/docs/types/type-traits.md index dc95aca1..cf718739 100644 --- a/docs/types/type-traits.md +++ b/docs/types/type-traits.md @@ -74,7 +74,7 @@ Not all traits have been defined as some rely on compiler intrinsics that are no `declvar` `20.28.0` `common_type` `is_enum` `20.30.0` -`is_scoped_enum` `20.50.0` +`is_scoped_enum` `20.49.0` `underlying_type` `20.42.0`  Unless the ETL is set to use builtins, the user must specialise the template for their enumerations. @@ -884,7 +884,7 @@ inline constexpr bool is_scalar_v = etl::is_scalar::value; C++17 ## is_scoped_enum -From: `20.50.0` +From: `20.49.0` ```cpp template @@ -900,7 +900,7 @@ inline constexpr bool is_scoped_enum_v = etl::is_scoped_enum::value; C++17 ## is_trivially_default_constructible -From: `20.50.0` +From: `20.49.0` ```cpp template @@ -916,7 +916,7 @@ inline constexpr bool is_trivially_default_constructible_v = etl::is_trivially_d C++17 ## is_trivially_move_constructible -From: `20.50.0` +From: `20.49.0` ```cpp template @@ -932,7 +932,7 @@ inline constexpr bool is_trivially_move_constructible_v = etl::is_trivially_move C++17 ## is_trivially_move_assignable -From: `20.50.0` +From: `20.49.0` ```cpp template @@ -964,7 +964,7 @@ inline constexpr bool is_trivially_assignable_v = etl::is_trivially_assignable @@ -982,7 +982,7 @@ inline constexpr bool is_layout_compatible_v = etl::is_layout_compatible:: C++17 ## is_pointer_interconvertible_base_of -From: `20.50.0` +From: `20.49.0` ```cpp template From 53a05760e071d40904eb5f1bb6c6107ee8339235 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sat, 5 Sep 2026 12:30:57 +0100 Subject: [PATCH 68/77] Version updates --- arduino/library-arduino.json | 2 +- arduino/library-arduino.properties | 2 +- hugo/assets/version.txt | 2 +- include/etl/version.h | 4 ++-- library.json | 2 +- library.properties | 2 +- version.txt | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/arduino/library-arduino.json b/arduino/library-arduino.json index 8bdb2a1b..3d192f5b 100644 --- a/arduino/library-arduino.json +++ b/arduino/library-arduino.json @@ -1,6 +1,6 @@ { "name": "Embedded Template Library ETL", - "version": "20.48.1", + "version": "20.49.0", "authors": { "name": "John Wellbelove", "email": "john.wellbelove@etlcpp.com" diff --git a/arduino/library-arduino.properties b/arduino/library-arduino.properties index 55f4e119..ede330f3 100644 --- a/arduino/library-arduino.properties +++ b/arduino/library-arduino.properties @@ -1,5 +1,5 @@ name=Embedded Template Library ETL -version=20.48.1 +version=20.49.0 author= John Wellbelove maintainer=John Wellbelove license=MIT diff --git a/hugo/assets/version.txt b/hugo/assets/version.txt index 09bbfc15..e38061bf 100644 --- a/hugo/assets/version.txt +++ b/hugo/assets/version.txt @@ -1 +1 @@ -20.48.1 +20.49.0 diff --git a/include/etl/version.h b/include/etl/version.h index a5e76179..cb9275a1 100644 --- a/include/etl/version.h +++ b/include/etl/version.h @@ -39,8 +39,8 @@ SOFTWARE. ///\ingroup utilities #define ETL_VERSION_MAJOR 20 -#define ETL_VERSION_MINOR 48 -#define ETL_VERSION_PATCH 1 +#define ETL_VERSION_MINOR 49 +#define ETL_VERSION_PATCH 0 #define ETL_VERSION \ ETL_STRING(ETL_VERSION_MAJOR) \ diff --git a/library.json b/library.json index f597d53f..78ab105b 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "Embedded Template Library", - "version": "20.48.1", + "version": "20.49.0", "authors": { "name": "John Wellbelove", "email": "john.wellbelove@etlcpp.com" diff --git a/library.properties b/library.properties index 813e2468..815c694d 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Embedded Template Library -version=20.48.1 +version=20.49.0 author= John Wellbelove maintainer=John Wellbelove license=MIT diff --git a/version.txt b/version.txt index 09bbfc15..e38061bf 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -20.48.1 +20.49.0 From 75cd310fd4d13ecd54588ae40749c60c3218a94f Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sun, 6 Sep 2026 20:29:32 +0100 Subject: [PATCH 69/77] Updated menus for Home page for documentation --- docs/_index.md | 14 ++++++++++++++ hugo/hugo.toml | 13 ++++--------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/docs/_index.md b/docs/_index.md index a1886a77..472fb0ac 100644 --- a/docs/_index.md +++ b/docs/_index.md @@ -51,6 +51,20 @@ type: hextra-home ## Version This documents version **{{< version >}}**. +## Get the ETL +
+
+ Github +
+ Arduino +
+ Conan +
+ PlatformIO +
+
+ + ## Motivation C++ is a powerful language for embedded systems development, with templates offering a great deal of flexibility and type safety. While the C++ Standard Library provides a wealth of well-tested functionality, it’s often not well suited to environments with strict deterministic behavior and limited resources. diff --git a/hugo/hugo.toml b/hugo/hugo.toml index a4ae3632..7ce20b6b 100644 --- a/hugo/hugo.toml +++ b/hugo/hugo.toml @@ -65,20 +65,15 @@ includeFiles = [".htaccess"] weight = 3 [[menus.main]] - name = "Slack" - url = "https://slofile.com/slack/etlcpp" + name = "Blog" + pageRef = "/docs/blog" weight = 4 [[menus.main]] - name = "Arduino" - url = "https://github.com/ETLCPP/etl-arduino/archive/master.zip" + name = "Slack" + url = "https://slofile.com/slack/etlcpp" weight = 5 -[[menus.main]] - name = "Blog" - pageRef = "/docs/blog" - weight = 6 - [[menus.main]] name = "About" pageRef = "/docs/about" From 043094129a14e50ca06e169e06b7a05a9a12692b Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sun, 6 Sep 2026 20:35:56 +0100 Subject: [PATCH 70/77] Added sponsors page to documentation --- docs/about.md | 61 ++++++++++++++++++++++++------------------------ docs/sponsors.md | 20 ++++++++++++++++ 2 files changed, 50 insertions(+), 31 deletions(-) create mode 100644 docs/sponsors.md diff --git a/docs/about.md b/docs/about.md index 6068aea1..4c2bf907 100644 --- a/docs/about.md +++ b/docs/about.md @@ -1,6 +1,6 @@ --- title: "About" -weight: 3002 +weight: 3003 --- ## About me @@ -12,41 +12,40 @@ Most systems I have worked on have required high speed and deterministic perform Acting as a mentor for colleagues has often been a significant, though unofficial, part of my role. ## Why write this library? -I wrote this library, and all the others I have written over the -years, because I'm lazy. +The truth behind this library, and every other tool I've built over the years, is simple: -Yes, lazy! +I’m lazy. -One of the things I really hate when writing software is having to do the same, or something almost the same, over and over again. The first thing I think when presented with a problem that requires a specific set of functionality is "Is this a specific case of a more generic problem?". Surprisingly, I can say "yes" more often than you would expect. Even if not all of the problem can be seen as generic, there are almost certainly parts that are. In every job I've had I've left an extensive code library behind me. +One of my biggest frustrations in software engineering is having to write the same code multiple times. Whenever I'm faced with a new problem, the very first question I ask is: *"Is this just a specific instance of a more generic problem?"* -Reinventing the wheel every time is a bad idea, for many reasons. +More often than not, it is. Even if the entire problem isn't generic, core pieces of it usually are. I build tools to solve those pieces once and for all. It's a philosophy that has defined my career; at every company I've worked for, I've left behind a legacy of extensive code libraries that teams still use today. -- **Compile-Time Features:** -Templated compile-time constants -Template-based design pattern base classes (e.g., Visitor, Observer) -Type-safe smart enumerations -Type-safe typedefs and constants -- **Code Bloat** -Multiple instances of slight variations of a theme results in an increase in code size due to no commonality of functionality. -Testing, more testing or no testing. -Are all the variants tested to the same degree? -Are some tested at all? -- **Variable functionality** -Not all the variants are going to have the same level of functionality, or the same API. -Ad-hoc solutions are invariably only going to solve the little bit of the problem that was needed at the time. -This goes against the YAGNI principle, but... -*I think that YAGNI can often be just another way of saying ISEP.* -*I believe in GIRFT.* -*I have over three decades of empirical proof.* -- **No collective knowledge base** -Without commonality every new variant has to be learnt. The underlying principles may be understood (i.e. Linked list), but each implementation has to be understood separately, along with its particular caveats and foibles. -Documentation is likely to be patchy, if it exists at all. -- **Octopus code** -The application is liable to have a close coupling with the solution. For example, I've often seen code using linked lists directly accessing the node pointers. Ad-hoc solutions are liable to have lazy (the bad kind) implementations. +Reinventing the wheel every time is a bad idea. When teams rely on fragmented, ad-hoc solutions, a predictable set of problems always follows: ->YAGNI - You Aren't Going To Need It ->ISEP - It's Somebody Else's Problem ->GIRFT - Get It Right First Time +* **Code Bloat:** Multiple variations of the same theme increase code size because there is no shared functionality. +* **Patchy Testing:** Are all the variants tested to the same degree? Are some tested at all? Usually, it's a roll of the dice. +* **Variable Functionality:** Not all variants share the same level of features or even the same API. Ad-hoc solutions invariably only solve the tiny sliver of the problem that was needed at the exact moment they were written. + +This directly conflicts with the popular **YAGNI** principle, but I think **YAGNI** can often be just another way of saying **ISEP** (It's Somebody Else's Problem). + +Personally, I believe in **GIRFT** (Get It Right First Time). I have over three decades of empirical proof to back it up. + +Without a centralized, generalized library to solve these core problems, you also run into: + +* **No Collective Knowledge Base:** +Without commonality, every new variant has to be relearned from scratch. Even if the underlying concepts are understood, each separate implementation brings its own caveats, foibles, and patchy documentation. +* **Octopus Code:** +Applications end up tightly coupled to the underlying solution. I've often seen code directly accessing the underlying implementation. Ad-hoc solutions are highly vulnerable to this kind of lazy engineering *(the bad kind)*. + +### The Antidote +To prevent this, this library provides robust, centralized building blocks out of the box: +* **Compile-Time Features:** Templated compile-time constants. +* **Design Patterns:** Template-based design pattern base classes (such as Visitor and Observer). +* **Type Safety:** Type-safe smart enumerations, typedefs, and constants. + +--- + +**YAGNI** - *You Aren't Going To Need It* ## Why not use 'C'? **Greenspun's Tenth Rule** has an unofficial C corollary: diff --git a/docs/sponsors.md b/docs/sponsors.md new file mode 100644 index 00000000..25d7fb00 --- /dev/null +++ b/docs/sponsors.md @@ -0,0 +1,20 @@ +--- +title: "Sponsors of the ETL" +weight: 3002 +--- + +A special thank you to all of our past and current sponsors. Your generosity directly funds the continuous development and maintenance of the ETL. + +While this tool is openly available, keeping it secure and production-ready takes significant time. A portion of our user base includes global enterprise teams using this ETL for mission-critical pipelines. If your organization relies on this tool daily, please consider supporting its sustainability. + +*I know enterprise procurement can be tricky. If your company requires custom invoicing or formal paperwork to approve open-source funding, please visit our +[Is the ETL free?](https://www.etlcpp.com/docs/is-the-etl-free) page for hassle-free corporate setup guidelines.* + +## Companies +### Inspiro +**Partner in Embedded Systems & IoT** +*Are you developing a product where intelligence is required through microelectronics and software? Inspiro will be happy to partner with you in the development of embedded systems and Internet of Things (IoT) applications. We add intelligence to your product by applying microelectronics, embedded software & programmable logic (VHDL/FPGA) and connected cloud services (IoT).* + +[Website](https://www.inspiro.nl/) + +## Individuals From d5156f9a1a55574dd585f4820c870beb5c8a1773 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Mon, 7 Sep 2026 14:42:44 +0200 Subject: [PATCH 71/77] Fix false positive warning from GCC 16 (#1591) --- include/etl/const_map.h | 5 +++++ include/etl/const_multimap.h | 5 +++++ include/etl/const_multiset.h | 5 +++++ include/etl/const_set.h | 5 +++++ 4 files changed, 20 insertions(+) diff --git a/include/etl/const_map.h b/include/etl/const_map.h index 6bcab37d..f0c50be2 100644 --- a/include/etl/const_map.h +++ b/include/etl/const_map.h @@ -505,6 +505,7 @@ namespace etl static_assert((etl::is_default_constructible::value), "key_type must be default constructible"); static_assert((etl::is_default_constructible::value), "mapped_type must be default constructible"); + #include "private/diagnostic_uninitialized_push.h" //************************************************************************* ///\brief Construct a const_map from a variadic list of elements. /// Static asserts if the elements are not constructible into @@ -512,6 +513,9 @@ namespace etl /// Static asserts if the number of elements is greater than the capacity of /// the const_map. //************************************************************************* + // The base class is passed the address of 'element_list' before that member has been + // initialised. The base class only stores the address and never reads through it during + // construction, so the compiler's 'may be used uninitialized' warning is a false positive. template ETL_CONSTEXPR14 explicit const_map(TElements&&... elements) ETL_NOEXCEPT : iconst_map(element_list, sizeof...(elements), Size) @@ -521,6 +525,7 @@ namespace etl "All elements must be constructible into value_type"); static_assert(sizeof...(elements) <= Size, "Number of elements exceeds capacity"); } + #include "private/diagnostic_pop.h" private: diff --git a/include/etl/const_multimap.h b/include/etl/const_multimap.h index 824abf77..1ec056a3 100644 --- a/include/etl/const_multimap.h +++ b/include/etl/const_multimap.h @@ -454,6 +454,7 @@ namespace etl static_assert((etl::is_default_constructible::value), "key_type must be default constructible"); static_assert((etl::is_default_constructible::value), "mapped_type must be default constructible"); + #include "private/diagnostic_uninitialized_push.h" //************************************************************************* ///\brief Construct a const_map from a variadic list of elements. /// Static asserts if the element type is not constructible. @@ -462,6 +463,9 @@ namespace etl /// Static asserts if the number of elements is greater than the capacity of /// the const_map. //************************************************************************* + // The base class is passed the address of 'element_list' before that member has been + // initialised. The base class only stores the address and never reads through it during + // construction, so the compiler's 'may be used uninitialized' warning is a false positive. template ETL_CONSTEXPR14 explicit const_multimap(TElements&&... elements) ETL_NOEXCEPT : iconst_multimap(element_list, sizeof...(elements), Size) @@ -471,6 +475,7 @@ namespace etl "All elements must be constructible into value_type"); static_assert(sizeof...(elements) <= Size, "Number of elements exceeds capacity"); } + #include "private/diagnostic_pop.h" private: diff --git a/include/etl/const_multiset.h b/include/etl/const_multiset.h index 28e9567d..f0bb70fb 100644 --- a/include/etl/const_multiset.h +++ b/include/etl/const_multiset.h @@ -409,6 +409,7 @@ namespace etl static_assert((etl::is_default_constructible::value), "key_type must be default constructible"); + #include "private/diagnostic_uninitialized_push.h" //************************************************************************* ///\brief Construct a const_set from a variadic list of elements. /// Static asserts if the element type is not constructible. @@ -416,6 +417,9 @@ namespace etl /// Static asserts if the number of elements is greater than the capacity of /// the const_set. //************************************************************************* + // The base class is passed the address of 'element_list' before that member has been + // initialised. The base class only stores the address and never reads through it during + // construction, so the compiler's 'may be used uninitialized' warning is a false positive. template ETL_CONSTEXPR14 explicit const_multiset(TElements&&... elements) ETL_NOEXCEPT : iconst_multiset(element_list, sizeof...(elements), Size) @@ -424,6 +428,7 @@ namespace etl static_assert((etl::are_all_same...>::value), "All elements must be value_type"); static_assert(sizeof...(elements) <= Size, "Number of elements exceeds capacity"); } + #include "private/diagnostic_pop.h" private: diff --git a/include/etl/const_set.h b/include/etl/const_set.h index 29841dba..92bbad2c 100644 --- a/include/etl/const_set.h +++ b/include/etl/const_set.h @@ -403,12 +403,16 @@ namespace etl static_assert((etl::is_default_constructible::value), "key_type must be default constructible"); + #include "private/diagnostic_uninitialized_push.h" //************************************************************************* ///\brief Construct a const_set from a variadic list of elements. /// Static asserts if the elements are not of type value_type. /// Static asserts if the number of elements is greater than the capacity of /// the const_set. //************************************************************************* + // The base class is passed the address of 'element_list' before that member has been + // initialised. The base class only stores the address and never reads through it during + // construction, so the compiler's 'may be used uninitialized' warning is a false positive. template ETL_CONSTEXPR14 explicit const_set(TElements&&... elements) ETL_NOEXCEPT : iconst_set(element_list, sizeof...(elements), Size) @@ -417,6 +421,7 @@ namespace etl static_assert((etl::are_all_same...>::value), "All elements must be key_type"); static_assert(sizeof...(elements) <= Size, "Number of elements exceeds capacity"); } + #include "private/diagnostic_pop.h" private: From d06cd9f303e07986272a54676d086bb3dee8c43f Mon Sep 17 00:00:00 2001 From: Marco Nilsson Date: Mon, 7 Sep 2026 16:23:20 +0200 Subject: [PATCH 72/77] Allow expected to be constructed from a convertible value or expected (#1587) --- docs/utilities/expected.md | 77 ++++++ include/etl/expected.h | 205 +++++++++++++++ include/etl/private/variant_variadic.h | 18 ++ test/test_expected.cpp | 332 +++++++++++++++++++++++++ 4 files changed, 632 insertions(+) diff --git a/docs/utilities/expected.md b/docs/utilities/expected.md index 8443a782..ab021424 100644 --- a/docs/utilities/expected.md +++ b/docs/utilities/expected.md @@ -236,6 +236,72 @@ Since: C++11 --- +```cpp +template +ETL_CONSTEXPR14 expected(U&& value) +``` +**Description** +Construct the value type from a value implicitly convertible to it. +Not selected when U is an etl::expected, etl::unexpected, etl::in_place_t, etl::unexpect_t or value_type itself. +Since: C++11 + +--- + +```cpp +template +ETL_CONSTEXPR14 explicit expected(U&& value) +``` +**Description** +Construct the value type from a value that is only explicitly convertible to it. +Constrained as above. +Since: C++11 + +--- + +```cpp +template +expected(const etl::expected& other) +``` +**Description** +Copy construct from an etl::expected whose value and error types are both implicitly convertible. +Not selected when value_type can be constructed from, or etl::unexpected built from, the source etl::expected itself. +Since: C++11 + +--- + +```cpp +template +explicit expected(const etl::expected& other) +``` +**Description** +Copy construct from an etl::expected whose value or error type is only explicitly convertible. +Constrained as above. +Since: C++11 + +--- + +```cpp +template +expected(etl::expected&& other) +``` +**Description** +Move construct from an etl::expected whose value and error types are both implicitly convertible. +Constrained as above. +Since: C++11 + +--- + +```cpp +template +explicit expected(etl::expected&& other) +``` +**Description** +Move construct from an etl::expected whose value or error type is only explicitly convertible. +Constrained as above. +Since: C++11 + +--- + ```cpp ETL_CONSTEXPR14 expected(const expected& other) ETL_NOEXCEPT ``` @@ -353,6 +419,17 @@ Since: C++11 --- +```cpp +template +expected& operator =(U&& value) +``` +**Description** +Assign from a value convertible to value_type. +Constrained like the value constructor. +Since: C++11 + +--- + ```cpp expected& operator =(const unexpected_type& error) ``` diff --git a/include/etl/expected.h b/include/etl/expected.h index 49b99aad..092434dc 100644 --- a/include/etl/expected.h +++ b/include/etl/expected.h @@ -249,6 +249,92 @@ namespace etl static const unexpect_t unexpect; #endif +#if ETL_USING_CPP11 + namespace private_expected + { + //***************************************************************************** + // Detects etl::unexpected, which the value constructors must not accept as a value. + //***************************************************************************** + template + struct is_unexpected : etl::false_type + { + }; + + template + struct is_unexpected > : etl::true_type + { + }; + + //***************************************************************************** + // True when TValue can be built from the source expected, which must be wrapped rather than unwrapped. + //***************************************************************************** + template + struct constructible_from_expected + : etl::integral_constant< + bool, etl::is_constructible&>::value || etl::is_constructible >::value + || etl::is_constructible&>::value + || etl::is_constructible >::value || etl::is_convertible&, TValue>::value + || etl::is_convertible&&, TValue>::value || etl::is_convertible&, TValue>::value + || etl::is_convertible&&, TValue>::value> + { + }; + + //***************************************************************************** + // True when TError can be built from the source expected, which must be wrapped rather than unwrapped. + //***************************************************************************** + template + struct unexpected_constructible_from_expected + : etl::integral_constant, etl::expected&>::value + || etl::is_constructible, etl::expected >::value + || etl::is_constructible, const etl::expected&>::value + || etl::is_constructible, const etl::expected >::value> + { + }; + + //***************************************************************************** + // Constraints shared by the implicit and explicit value constructors. + //***************************************************************************** + template + struct is_value_constructor_candidate + : etl::integral_constant::type, etl::expected >::value + && !etl::is_same::type, TValue>::value + && !etl::is_same::type, etl::in_place_t>::value + && !etl::is_same::type, etl::unexpect_t>::value + && !is_unexpected::type>::value && etl::is_constructible::value> + { + }; + + //***************************************************************************** + // Constraints on the source expected that hold whatever its value category. + //***************************************************************************** + template + struct is_expected_conversion_allowed + : etl::integral_constant, etl::expected >::value + && !constructible_from_expected::value + && !unexpected_constructible_from_expected::value> + { + }; + + //***************************************************************************** + // UArgument and GArgument are the source members as the constructor receives them. + //***************************************************************************** + template + struct is_expected_conversion_constructible + : etl::integral_constant::value && etl::is_constructible::value> + { + }; + + //***************************************************************************** + // A converting constructor is implicit only when both the value and the error convert implicitly. + //***************************************************************************** + template + struct is_expected_conversion_implicit + : etl::integral_constant::value && etl::is_convertible::value> + { + }; + } // namespace private_expected +#endif + //***************************************************************************** /// Expected type. //***************************************************************************** @@ -293,6 +379,114 @@ namespace etl } #endif +#if ETL_USING_CPP11 + //******************************************* + /// Constructor from a value convertible to value_type. + //******************************************* + template ::value && etl::is_convertible::value, int>::type = 0> + ETL_CONSTEXPR14 expected(U&& value_) + : storage(etl::in_place_index_t(), etl::forward(value_)) + { + } + + //******************************************* + /// Constructor from a value explicitly convertible to value_type. + //******************************************* + template ::value + && !etl::is_convertible::value, + int>::type = 0> + ETL_CONSTEXPR14 explicit expected(U&& value_) + : storage(etl::in_place_index_t(), etl::forward(value_)) + { + } + + //******************************************* + /// Copy constructor from an expected with convertible value and error types. + //******************************************* + template ::value + && private_expected::is_expected_conversion_constructible::value + && private_expected::is_expected_conversion_implicit::value, + int>::type = 0> + expected(const etl::expected& other) + : storage(private_variant::valueless_t()) + { + if (other.has_value()) + { + storage.template emplace(other.value()); + } + else + { + storage.template emplace(other.error()); + } + } + + //******************************************* + /// Copy constructor from an expected with explicitly convertible value or error types. + //******************************************* + template ::value + && private_expected::is_expected_conversion_constructible::value + && !private_expected::is_expected_conversion_implicit::value, + int>::type = 0> + explicit expected(const etl::expected& other) + : storage(private_variant::valueless_t()) + { + if (other.has_value()) + { + storage.template emplace(other.value()); + } + else + { + storage.template emplace(other.error()); + } + } + + //******************************************* + /// Move constructor from an expected with convertible value and error types. + //******************************************* + template ::value + && private_expected::is_expected_conversion_constructible::value + && private_expected::is_expected_conversion_implicit::value, + int>::type = 0> + expected(etl::expected&& other) + : storage(private_variant::valueless_t()) + { + if (other.has_value()) + { + storage.template emplace(etl::move(other.value())); + } + else + { + storage.template emplace(etl::move(other.error())); + } + } + + //******************************************* + /// Move constructor from an expected with explicitly convertible value or error types. + //******************************************* + template ::value + && private_expected::is_expected_conversion_constructible::value + && !private_expected::is_expected_conversion_implicit::value, + int>::type = 0> + explicit expected(etl::expected&& other) + : storage(private_variant::valueless_t()) + { + if (other.has_value()) + { + storage.template emplace(etl::move(other.value())); + } + else + { + storage.template emplace(etl::move(other.error())); + } + } +#endif + //******************************************* /// Copy constructor //******************************************* @@ -451,6 +645,17 @@ namespace etl return *this; } + + //******************************************* + /// Assign from a value convertible to value_type. + //******************************************* + template ::value, int>::type = 0> + expected& operator=(U&& value) + { + storage.template emplace(etl::forward(value)); + + return *this; + } #endif //******************************************* diff --git a/include/etl/private/variant_variadic.h b/include/etl/private/variant_variadic.h index 924d73a3..4161aa31 100644 --- a/include/etl/private/variant_variadic.h +++ b/include/etl/private/variant_variadic.h @@ -69,6 +69,14 @@ namespace etl { namespace private_variant { + //******************************************* + /// Tag for constructing a variant with no alternative. + //******************************************* + struct valueless_t + { + explicit ETL_CONSTEXPR valueless_t() {} + }; + //******************************************* /// Switch-based dispatch for destroy/copy/move. /// Replaces the per-instance function pointer with @@ -1034,6 +1042,16 @@ namespace etl } #include "diagnostic_pop.h" + //*************************************************************************** + /// Construct with no alternative. + /// The caller is expected to emplace one; until it does the variant is valueless. + /// Lets a caller choose the alternative at run time without a temporary variant. + //*************************************************************************** + ETL_CONSTEXPR14 explicit variant(private_variant::valueless_t) ETL_NOEXCEPT + : base_type() + { + } + //*************************************************************************** /// Construct from arguments. //*************************************************************************** diff --git a/test/test_expected.cpp b/test/test_expected.cpp index b7bee13d..d0027f08 100644 --- a/test/test_expected.cpp +++ b/test/test_expected.cpp @@ -120,6 +120,147 @@ namespace std::string e; }; + struct ExplicitValue + { + std::string v; + + ExplicitValue() {} + + explicit ExplicitValue(const std::string& v_) + : v(v_) + { + } + }; + + struct ExplicitError + { + std::string e; + + ExplicitError() = default; + + explicit ExplicitError(const std::string& e_) + : e(e_) + { + } + }; + + struct MoveOnlyValue + { + MoveOnlyValue() + : v(0) + { + } + + explicit MoveOnlyValue(int v_) + : v(v_) + { + } + + MoveOnlyValue(MoveOnlyValue&&) = default; + MoveOnlyValue& operator=(MoveOnlyValue&&) = default; + + MoveOnlyValue(const MoveOnlyValue&) = delete; + MoveOnlyValue& operator=(const MoveOnlyValue&) = delete; + + int v; + }; + + struct FromMoveOnlyValue + { + FromMoveOnlyValue() + : v(0) + { + } + + FromMoveOnlyValue(MoveOnlyValue&& other) + : v(other.v) + { + } + + int v; + }; + + struct NoMoveValue + { + explicit NoMoveValue(int v_) + : v(v_) + { + } + + NoMoveValue(const NoMoveValue&) = default; + NoMoveValue(NoMoveValue&&) = delete; + + int v; + }; + + struct FromInt + { + FromInt() + : v(0) + { + } + + FromInt(int v_) + : v(v_) + { + } + + int v; + }; + + struct ErrorFromExpected + { + ErrorFromExpected() {} + + ErrorFromExpected(int) {} + + ErrorFromExpected(const etl::expected&) {} + }; + + struct ImmobileValue + { + static int destructions; + + explicit ImmobileValue(int v_) + : v(v_) + { + } + + ImmobileValue(const ImmobileValue&) = delete; + ImmobileValue(ImmobileValue&&) = delete; + + ~ImmobileValue() + { + ++destructions; + } + + int v; + }; + + int ImmobileValue::destructions = 0; + + struct ImmobileError + { + static int destructions; + + ImmobileError(int e_) + : e(e_) + { + } + + ImmobileError(const ImmobileError&) = delete; + ImmobileError(ImmobileError&&) = delete; + + ~ImmobileError() + { + ++destructions; + } + + int e; + }; + + int ImmobileError::destructions = 0; + using Expected = etl::expected; using ExpectedV = etl::expected; using ExpectedM = etl::expected; @@ -1651,5 +1792,196 @@ namespace CHECK_FALSE(result.has_value()); CHECK_EQUAL("original_transformed", result.error().e); } + + //************************************************************************* + TEST(test_constructor_from_value_convertible_to_value_type) + { + const Expected result = std::string("converted"); + + CHECK_TRUE(result.has_value()); + CHECK_EQUAL("converted", result.value().v); + } + + //************************************************************************* + TEST(test_constructor_from_expected_with_convertible_types) + { + const etl::expected source(std::string("converted")); + + const Expected result(source); + + CHECK_TRUE(result.has_value()); + CHECK_EQUAL("converted", result.value().v); + } + + //************************************************************************* + TEST(test_constructor_from_expected_with_convertible_types_carries_error) + { + const etl::expected source(etl::unexpected(std::string("failed"))); + + const Expected result(source); + + CHECK_FALSE(result.has_value()); + CHECK_EQUAL("failed", result.error().e); + } + + //************************************************************************* + TEST(test_value_type_constructible_from_expected_is_not_unwrapped) + { + struct WrapsExpected + { + WrapsExpected() + : wrapped(false) + { + } + + WrapsExpected(const etl::expected&) + : wrapped(true) + { + } + + explicit WrapsExpected(int) + : wrapped(false) + { + } + + bool wrapped; + }; + + const etl::expected source(7); + + const etl::expected result(source); + + CHECK_TRUE(result.has_value()); + CHECK_TRUE(result.value().wrapped); + } + + //************************************************************************* + TEST(test_explicit_value_conversion_is_not_implicit) + { + using ExplicitExpected = etl::expected; + + CHECK_TRUE((etl::is_constructible::value)); + CHECK_FALSE((etl::is_convertible::value)); + + CHECK_TRUE((etl::is_convertible::value)); + } + + //************************************************************************* + TEST(test_explicit_value_conversion_is_available_by_direct_initialisation) + { + const etl::expected result(std::string("converted")); + + CHECK_TRUE(result.has_value()); + CHECK_EQUAL("converted", result.value().v); + } + + //************************************************************************* + TEST(test_explicit_error_conversion_is_not_implicit) + { + using Source = etl::expected; + using ExplicitExpected = etl::expected; + + CHECK_TRUE((etl::is_constructible::value)); + CHECK_FALSE((etl::is_convertible::value)); + + CHECK_TRUE((etl::is_convertible::value)); + } + + //************************************************************************* + TEST(test_explicit_error_conversion_carries_the_error) + { + const etl::expected source(etl::unexpected(std::string("failed"))); + + const etl::expected result(source); + + CHECK_FALSE(result.has_value()); + CHECK_EQUAL("failed", result.error().e); + } + + //************************************************************************* + TEST(test_move_constructor_from_expected_with_convertible_types) + { + etl::expected source(MoveOnlyValue(1)); + + const etl::expected result(etl::move(source)); + + CHECK_TRUE(result.has_value()); + CHECK_EQUAL(1, result.value().v); + } + + //************************************************************************* + TEST(test_move_constructor_from_expected_with_convertible_types_carries_error) + { + etl::expected source(etl::unexpected(Error("failed"))); + + const etl::expected result(etl::move(source)); + + CHECK_FALSE(result.has_value()); + CHECK_EQUAL("failed", result.error().e); + } + + //************************************************************************* + TEST(test_constructor_from_value_does_not_require_a_move) + { + const etl::expected result(1); + + CHECK_TRUE(result.has_value()); + CHECK_EQUAL(1, result.value().v); + } + + //************************************************************************* + TEST(test_error_type_constructible_from_expected_is_not_converted) + { + using Source = etl::expected; + + CHECK_FALSE((etl::is_constructible, const Source&>::value)); + CHECK_FALSE((etl::is_constructible, Source&&>::value)); + + CHECK_TRUE((etl::is_constructible, const Source&>::value)); + } + + //************************************************************************* + TEST(test_assign_from_value_convertible_to_value_type) + { + Expected result; + + result = std::string("converted"); + + CHECK_TRUE(result.has_value()); + CHECK_EQUAL("converted", result.value().v); + } + //************************************************************************* + TEST(test_conversion_into_an_immobile_value_constructs_in_place) + { + ImmobileValue::destructions = 0; + + { + const etl::expected source(7); + + const etl::expected result(source); + + CHECK_TRUE(result.has_value()); + CHECK_EQUAL(7, result.value().v); + } + + CHECK_EQUAL(1, ImmobileValue::destructions); + } + + //************************************************************************* + TEST(test_conversion_into_an_immobile_error_constructs_in_place) + { + ImmobileError::destructions = 0; + + { + const etl::expected source(etl::unexpected(9)); + + const etl::expected result(source); + + CHECK_FALSE(result.has_value()); + CHECK_EQUAL(9, result.error().e); + } + + CHECK_EQUAL(1, ImmobileError::destructions); + } } } // namespace From ee87ea90aaed9d8c051ec5e3a0dc3dfd04f55e92 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Mon, 7 Sep 2026 15:39:37 +0100 Subject: [PATCH 73/77] Added sponsors page to documentation --- docs/sponsors.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/sponsors.md b/docs/sponsors.md index 25d7fb00..2f5ed2ae 100644 --- a/docs/sponsors.md +++ b/docs/sponsors.md @@ -7,8 +7,7 @@ A special thank you to all of our past and current sponsors. Your generosity dir While this tool is openly available, keeping it secure and production-ready takes significant time. A portion of our user base includes global enterprise teams using this ETL for mission-critical pipelines. If your organization relies on this tool daily, please consider supporting its sustainability. -*I know enterprise procurement can be tricky. If your company requires custom invoicing or formal paperwork to approve open-source funding, please visit our -[Is the ETL free?](https://www.etlcpp.com/docs/is-the-etl-free) page for hassle-free corporate setup guidelines.* +*I know enterprise procurement can be tricky. If your company requires custom invoicing or formal paperwork to approve open-source funding, please visit our [Is the ETL free?](https://www.etlcpp.com/docs/is-the-etl-free) page for hassle-free corporate setup guidelines.* ## Companies ### Inspiro From 11623e686257d73de5e14579d32b51877eadda70 Mon Sep 17 00:00:00 2001 From: Drew Rife Date: Tue, 8 Sep 2026 05:21:33 -0400 Subject: [PATCH 74/77] Implement clamped_value utility (#1565) * feat: add clamped_value utility * style: format clamped_value include list * feat: enhance clamped_value with runtime bounds and comparison operators and DRY up the implementation * refactor: call set in runtime ctor instead of duplicated the clamping logic * Add C++20 floating-point clamped_value support * Document and stress clamped_value edge cases * Change ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) to ETL_NOEXCEPT * Change ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) to ETL_NOEXCEPT * Revert changes --------- Co-authored-by: John Wellbelove --- docs/utilities/clamped-value.md | 170 +++++ include/etl/clamped_value.h | 956 ++++++++++++++++++++++++ test/CMakeLists.txt | 1 + test/maincpp03check.cpp | 1 + test/meson.build | 1 + test/syntax_check/CMakeLists.txt | 1 + test/syntax_check/clamped_value.h.t.cpp | 36 + test/test_clamped_value.cpp | 515 +++++++++++++ test/vs2022/etl.vcxproj | 5 +- test/vs2022/etl.vcxproj.filters | 11 +- 10 files changed, 1695 insertions(+), 2 deletions(-) create mode 100644 docs/utilities/clamped-value.md create mode 100644 include/etl/clamped_value.h create mode 100644 test/syntax_check/clamped_value.h.t.cpp create mode 100644 test/test_clamped_value.cpp diff --git a/docs/utilities/clamped-value.md b/docs/utilities/clamped-value.md new file mode 100644 index 00000000..18fba437 --- /dev/null +++ b/docs/utilities/clamped-value.md @@ -0,0 +1,170 @@ +--- +title: "clamped_value" +--- + +{{< callout type="info">}} + Header: `clamped_value.h` + Since: `TBC` +{{< /callout >}} + +Provides a value constrained between a minimum and maximum value. Values are +clamped using `etl::clamp`. The type supports compile-time bounds to minimise +storage and runtime bounds when the range must be changed. Integral types are +supported in all language modes. Floating-point types are supported in C++20 +when `ETL_HAS_FLOATING_POINT_CLAMPED_VALUE` is `1`. By default, the macro is +enabled when `__cpp_nontype_template_args` is at least `201911L`. + +## Availability + +`ETL_HAS_FLOATING_POINT_CLAMPED_VALUE` is always defined by this header as +either `0` or `1`. It defaults to `1` only when C++20 is enabled and the +compiler supports floating-point non-type template arguments. A platform +profile or build may define the macro before including the header to override +automatic detection. + +When the macro is `0`, `T` must be an integral type. When it is `1`, `float`, +`double`, and `long double` may also be used. + +```cpp +template +class clamped_value; +``` + +```cpp +etl::clamped_value value_ct; // Fixed range [2, 7]. + +etl::clamped_value value_rt(2, 7, 5); // Runtime range and initial value. + +#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE +etl::clamped_value float_ct(0.5f); +etl::clamped_value float_rt(-1.5, 2.5, 0.5); +#endif +``` + +## Types + +`difference_type` is the signed counterpart of `T` for integral values. For +floating-point values, `difference_type` is `T`, allowing fractional steps. +`limits_type` and `difference_limits_type` provide the corresponding +`etl::numeric_limits` specializations. + +## Constructors + +```cpp +clamped_value value; +``` + +Creates a compile-time clamped value initialized to `Min`. + +```cpp +clamped_value value(min, max); +clamped_value value(min, max, initial); +``` + +Creates a runtime clamped value. The two-argument form initializes the value +to `min`; the three-argument form clamps `initial` to the range. + +`min` must not be greater than `max`. As with `cyclic_value`, zero-initialized +`Min` and `Max` select the runtime-bound specialization; use a non-zero +compile-time bound when a fixed compile-time range is needed. NaN bounds and +initial values are rejected. Infinite bounds and values are permitted. + +Because zero-initialized `Min` and `Max` select runtime bounds, a fixed +compile-time range `[0, 0]` cannot be represented by this dispatch scheme. + +## Modifiers + +```cpp +void set(T value); +void set(T min, T max); +void to_min(); +void to_max(); +void advance(difference_type n) ETL_NOEXCEPT; +``` + +`set(T)` clamps the current value. The runtime `set(min, max)` changes the +range and resets the value to the new minimum. `to_min()` and `to_max()` move +the value directly to a bound. For integral values, `advance` uses the signed +counterpart of `T` as its step type. For floating-point values, the step type +is `T` and may be fractional. Steps saturate in constant time at the minimum +or maximum rather than wrapping. NaN steps are rejected. + +Positive steps move toward `max`; negative steps move toward `min`. Infinite +steps saturate at a finite bound. Infinite values and bounds remain valid. + +```cpp +etl::clamped_value value(0.25f); +value.advance(0.5f); // 0.75f +value.advance(10.0f); // 1.0f +value.advance(-0.5f); // 0.5f +``` + +Increment and decrement move by one and also saturate, including when a +floating-point bound is less than one unit away: + +```cpp +++value; // Remains Max when already at Max. +--value; // Remains Min when already at Min. +``` + +## Access + +```cpp +ETL_NODISCARD T get() const noexcept; +ETL_NODISCARD T min() const noexcept; +ETL_NODISCARD T max() const noexcept; +``` + +Gets the current value and its bounds. Both compile-time and runtime +specializations provide these accessors as const-qualified member functions. + +## Operators + +```cpp +ETL_NODISCARD operator T() const noexcept; +clamped_value& operator++() & noexcept; +clamped_value operator++(int) noexcept; +clamped_value& operator--() & noexcept; +clamped_value operator--(int) noexcept; +clamped_value& operator=(T value) & noexcept; +clamped_value& operator+=(difference_type n) & noexcept; +clamped_value& operator-=(difference_type n) & noexcept; +``` + +Conversion and assignment to the underlying type are supported. Assignment is +clamped to the configured range. The lvalue reference qualification on +assignment prevents assignment through temporary values where supported. For +floating-point values, assignment and compound-assignment reject NaN. + +Prefix increment and decrement return a reference to the updated object. +Postfix increment and decrement return a copy of the value before it was +updated. All four operations move by one and saturate at the applicable bound. + +## Operations + +```cpp +ETL_CONSTEXPR14 void swap(clamped_value& other); +ETL_CONSTEXPR14 void swap(clamped_value& lhs, clamped_value& rhs); +``` + +Swaps clamped values. Runtime values also swap their bounds. + +Equality, inequality, and relational comparisons are provided. Comparisons +against arithmetic values use the implicit conversion to `T`. Equality +compares the current values; runtime bounds do not affect equality. Runtime +ranges must satisfy `min <= max`; invalid ranges trigger an ETL assertion. The +default runtime specialization uses the full representable range of `T`. + +Floating-point comparisons use the underlying values directly; no approximate +or tolerance-based comparison is applied. + +## Error Handling + +Runtime ranges with `min > max` trigger an ETL assertion. NaN values, bounds, +assignments, and steps also trigger an ETL assertion. Depending on the ETL +error-handler configuration, an assertion may throw `etl::exception`, invoke a +configured handler, or terminate. Operations that validate input are only +`noexcept` when exceptions are disabled. diff --git a/include/etl/clamped_value.h b/include/etl/clamped_value.h new file mode 100644 index 00000000..5448548a --- /dev/null +++ b/include/etl/clamped_value.h @@ -0,0 +1,956 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2026 John Wellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef ETL_CLAMPED_VALUE_INCLUDED +#define ETL_CLAMPED_VALUE_INCLUDED + +#include "platform.h" +#include "absolute.h" +#include "algorithm.h" +#include "error_handler.h" +#include "limits.h" +#include "math.h" +#include "static_assert.h" +#include "type_traits.h" +#include "utility.h" + +///\def ETL_HAS_FLOATING_POINT_CLAMPED_VALUE +/// Set to `1` when clamped_value supports floating-point types. +/// Floating-point support requires C++20 non-type template arguments. +/// The macro may be defined by the build or platform profile to override +/// automatic detection. +#if !defined(ETL_HAS_FLOATING_POINT_CLAMPED_VALUE) + #if ETL_USING_CPP20 && defined(__cpp_nontype_template_args) && (__cpp_nontype_template_args >= 201911L) + #define ETL_HAS_FLOATING_POINT_CLAMPED_VALUE 1 + #else + #define ETL_HAS_FLOATING_POINT_CLAMPED_VALUE 0 + #endif +#endif + +///\defgroup clamped_value clamped_value +/// Provides a value that is clamped between two limits. +/// Integral types are supported in all language modes. Floating-point types +/// are supported when ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`. +/// \ingroup utilities + +namespace etl +{ + /// \cond INTERNAL + namespace private_clamped_value + { + //************************************************************************* + /// Type policy for clamped_value. + /// Integral values use a signed difference type and unsigned saturation + /// arithmetic. Floating-point values use `T` as the difference type. + //************************************************************************* + template ::value> + struct traits; + + template + struct traits + { + typedef typename etl::make_signed::type difference_type; + typedef typename etl::make_unsigned::type unsigned_type; + typedef etl::numeric_limits limits_type; + typedef etl::numeric_limits difference_limits_type; + }; + + template + struct traits + { + ETL_STATIC_ASSERT(ETL_HAS_FLOATING_POINT_CLAMPED_VALUE&& etl::is_floating_point::value, + "clamped_value requires an integral type, or a floating-point type when supported"); + + typedef T difference_type; + typedef etl::numeric_limits limits_type; + typedef etl::numeric_limits difference_limits_type; + }; + + template ::value> + struct arithmetic; + + //************************************************************************* + /// Saturating arithmetic policy for integral values. + //************************************************************************* + template + struct arithmetic + { + typedef typename traits::unsigned_type unsigned_type; + + ETL_NODISCARD + static ETL_CONSTEXPR14 T advance(T value, T min_value, T max_value, typename traits::difference_type n) ETL_NOEXCEPT + { + if (n == 0) + { + return value; + } + + const unsigned_type current = static_cast(value); + const unsigned_type step = etl::absolute_unsigned(n); + + if ((n > 0) && (value < max_value)) + { + const unsigned_type distance = static_cast(max_value) - current; + + value = (step >= distance) ? max_value : static_cast(current + step); + } + else if ((n < 0) && (value > min_value)) + { + const unsigned_type distance = current - static_cast(min_value); + + value = (step >= distance) ? min_value : static_cast(current - step); + } + + return value; + } + + ETL_NODISCARD + static ETL_CONSTEXPR14 T subtract(T value, T min_value, T max_value, typename traits::difference_type n) ETL_NOEXCEPT + { + if (n == traits::difference_limits_type::lowest()) ETL_UNLIKELY + { + value = advance(value, min_value, max_value, traits::difference_limits_type::max()); + return advance(value, min_value, max_value, 1); + } + + return advance(value, min_value, max_value, static_cast::difference_type>(-n)); + } + }; + +#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE + //************************************************************************* + /// Saturating arithmetic policy for floating-point values. + //************************************************************************* + template + struct arithmetic + { + ETL_NODISCARD + static ETL_CONSTEXPR14 T advance(T value, T min_value, T max_value, T n) ETL_NOEXCEPT + { + if ((n > T(0)) && (value < max_value)) + { + const T distance = max_value - value; + value = (n >= distance) ? max_value : static_cast(value + n); + } + else if ((n < T(0)) && (value > min_value)) + { + const T distance = value - min_value; + value = (-n >= distance) ? min_value : static_cast(value + n); + } + + return value; + } + + ETL_NODISCARD + static ETL_CONSTEXPR14 T subtract(T value, T min_value, T max_value, T n) ETL_NOEXCEPT + { + return advance(value, min_value, max_value, -n); + } + }; +#endif + + //************************************************************************* + /// Advances a value and saturates it at the supplied bounds. + ///\param value The current value. + ///\param min_value The minimum value. + ///\param max_value The maximum value. + ///\param n The number of steps. + ///\return The advanced value. + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 T advance(T value, T min_value, T max_value, typename traits::difference_type n) ETL_NOEXCEPT + { + return arithmetic::advance(value, min_value, max_value, n); + } + + //************************************************************************* + /// Subtracts a value and saturates it at the supplied bounds. + /// Handles the lowest integral difference without signed overflow. + ///\param value The current value. + ///\param min_value The minimum value. + ///\param max_value The maximum value. + ///\param n The number of steps to subtract. + ///\return The value after subtraction. + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 T subtract(T value, T min_value, T max_value, typename traits::difference_type n) ETL_NOEXCEPT + { + return arithmetic::subtract(value, min_value, max_value, n); + } + + //************************************************************************* + /// Verifies that a clamped value, bound, or difference is not NaN. + /// Integral values always pass validation. + ///\param value The value to validate. + //************************************************************************* + template + ETL_CONSTEXPR14 void validate(T value) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + { + ETL_ASSERT(!etl::is_nan(value), ETL_ERROR_GENERIC("clamped_value: NaN is not supported")); + } + + //************************************************************************* + /// Validates a value and its bounds before clamping. + ///\param value The value to clamp. + ///\param min_value The minimum permitted value. + ///\param max_value The maximum permitted value. + ///\return The value clamped to the supplied range. + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 T validated_clamp(T value, T min_value, T max_value) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + { + validate(value); + validate(min_value); + validate(max_value); + ETL_ASSERT(min_value <= max_value, ETL_ERROR_GENERIC("clamped_value: invalid range")); + return etl::clamp(value, min_value, max_value); + } + } // namespace private_clamped_value + /// \endcond + +#include "private/diagnostic_float_equal_push.h" + template + class clamped_value; +#include "private/diagnostic_pop.h" + + //*************************************************************************** + /// Provides a value that is clamped between two compile-time limits. + /// Supports incrementing, decrementing and arbitrary advance. + ///@tparam T An integral type, or a floating-point type when + /// ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`. + ///@tparam Min The minimum value of the range. + ///@tparam Max The maximum value of the range. + ///\ingroup clamped_value + //*************************************************************************** + template + class clamped_value + { + public: + + /// The type used to advance or subtract from the stored value. + typedef typename private_clamped_value::traits::difference_type difference_type; + + /// Numeric limits for the stored value type. + typedef typename private_clamped_value::traits::limits_type limits_type; + + /// Numeric limits for difference_type. + typedef typename private_clamped_value::traits::difference_limits_type difference_limits_type; + + /// \cond INTERNAL + ETL_STATIC_ASSERT(Min <= Max, "clamped_value minimum must not exceed maximum"); + /// \endcond + + //************************************************************************* + /// Default constructor. + /// The initial value is set to the minimum value. + //************************************************************************* + ETL_CONSTEXPR clamped_value() ETL_NOEXCEPT + : value(Min) + { + } + + //************************************************************************* + /// Constructor. + /// Set to an initial value. + /// Clamped to the range. + /// NaN is rejected for floating-point values. + ///\param initial The initial value. + //************************************************************************* + ETL_CONSTEXPR14 explicit clamped_value(T initial) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + : value(private_clamped_value::validated_clamp(initial, Min, Max)) + { + } + + //************************************************************************* + /// Copy constructor. + //************************************************************************* + ETL_CONSTEXPR clamped_value(const clamped_value& other) ETL_NOEXCEPT + : value(other.value) + { + } + + //************************************************************************* + /// Assignment operator. + ///\param other The value to copy. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator=(const clamped_value& other) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + if (this == &other) + { + return *this; + } + + set(other.value); + return *this; + } + + //************************************************************************* + /// Sets the value. + /// Clamps to the Min/Max range. + /// NaN is rejected for floating-point values. + ///\param value_ The value. + //************************************************************************* + ETL_CONSTEXPR14 void set(T value_) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + { + value = private_clamped_value::validated_clamp(value_, Min, Max); + } + + //************************************************************************* + /// Resets the value to the minimum in the range. + //************************************************************************* + ETL_CONSTEXPR14 void to_min() ETL_NOEXCEPT + { + value = Min; + } + + //************************************************************************* + /// Resets the value to the maximum in the range. + //************************************************************************* + ETL_CONSTEXPR14 void to_max() ETL_NOEXCEPT + { + value = Max; + } + + //************************************************************************* + /// Advances the value by a number of steps. + /// Saturates at the Min/Max range. + /// Floating-point steps may be fractional. NaN is rejected. + ///\param n The number of steps. + //************************************************************************* + ETL_CONSTEXPR14 void advance(difference_type n) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + { + private_clamped_value::validate(n); + value = private_clamped_value::advance(value, Min, Max, n); + } + + //************************************************************************* + /// Conversion operator. + /// \return The value of the underlying type. + //************************************************************************* + ETL_NODISCARD ETL_CONSTEXPR14 operator T() const ETL_NOEXCEPT + { + return value; + } + + //************************************************************************* + /// ++ operator. + /// Advances by one. + /// Saturates at Max. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator++() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + value = private_clamped_value::advance(value, Min, Max, difference_type(1)); + return *this; + } + + //************************************************************************* + /// ++ operator. + /// Saturates at Max. + ///\return The value before incrementing. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value operator++(int) ETL_NOEXCEPT + { + clamped_value temp(*this); + ++(*this); + return temp; + } + + //************************************************************************* + /// -- operator. + /// Subtracts one. + /// Saturates at Min. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator--() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + value = private_clamped_value::advance(value, Min, Max, difference_type(-1)); + return *this; + } + + //************************************************************************* + /// -- operator. + /// Saturates at Min. + ///\return The value before decrementing. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value operator--(int) ETL_NOEXCEPT + { + clamped_value temp(*this); + --(*this); + return temp; + } + + //************************************************************************* + /// = operator. + /// Clamps to the Min/Max range. + ///\param value_ The value to assign. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator=(T value_) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + { + set(value_); + return *this; + } + + //************************************************************************* + /// Adds a number of steps and clamps to the range. + /// Floating-point steps may be fractional. NaN is rejected. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator+=(difference_type n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + { + advance(n); + return *this; + } + + //************************************************************************* + /// Subtracts a number of steps and clamps to the range. + /// Floating-point steps may be fractional. NaN is rejected. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator-=(difference_type n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + { + private_clamped_value::validate(n); + value = private_clamped_value::subtract(value, Min, Max, n); + return *this; + } + + //************************************************************************* + /// Gets the value. + ///\return The current value. + //************************************************************************* + ETL_NODISCARD ETL_CONSTEXPR T get() const ETL_NOEXCEPT + { + return value; + } + + //************************************************************************* + /// Gets the minimum value. + ///\return The minimum value. + //************************************************************************* + ETL_NODISCARD ETL_CONSTEXPR T min() const ETL_NOEXCEPT + { + return Min; + } + + //************************************************************************* + /// Gets the maximum value. + ///\return The maximum value. + //************************************************************************* + ETL_NODISCARD ETL_CONSTEXPR T max() const ETL_NOEXCEPT + { + return Max; + } + + //************************************************************************* + /// Swaps the values. + ///\param other The value to swap with. + //************************************************************************* + ETL_CONSTEXPR14 void swap(clamped_value& other) ETL_NOEXCEPT + { + using ETL_OR_STD::swap; + swap(value, other.value); + } + + //************************************************************************* + /// Swaps the values. + ///\param lhs The first value. + ///\param rhs The second value. + //************************************************************************* + friend ETL_CONSTEXPR14 void swap(clamped_value& lhs, clamped_value& rhs) ETL_NOEXCEPT + { + lhs.swap(rhs); + } + + //************************************************************************* + /// Operator ==. + ///\param lhs The left-hand value. + ///\param rhs The right-hand value. + ///\return `true` if the values are equal. + //************************************************************************* + friend ETL_CONSTEXPR bool operator==(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { +#include "private/diagnostic_float_equal_push.h" + return lhs.value == rhs.value; +#include "private/diagnostic_pop.h" + } + + //************************************************************************* + /// Operator !=. + ///\param lhs The left-hand value. + ///\param rhs The right-hand value. + ///\return `true` if the values are not equal. + //************************************************************************* + friend ETL_CONSTEXPR bool operator!=(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { + return !(lhs == rhs); + } + + //************************************************************************* + /// Operator <. + ///\param lhs The left-hand operand. + ///\param rhs The right-hand operand. + ///\return `true` if lhs is less than rhs. + /// @{ + friend ETL_CONSTEXPR bool operator<(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { + return lhs.value < rhs.value; + } + + /// @} + + //************************************************************************* + /// Operator <=. + ///\param lhs The left-hand operand. + ///\param rhs The right-hand operand. + ///\return `true` if lhs is less than or equal to rhs. + /// @{ + friend ETL_CONSTEXPR bool operator<=(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { + return !(rhs < lhs); + } + + /// @} + + //************************************************************************* + /// Operator >. + ///\param lhs The left-hand operand. + ///\param rhs The right-hand operand. + ///\return `true` if lhs is greater than rhs. + /// @{ + friend ETL_CONSTEXPR bool operator>(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { + return rhs < lhs; + } + + /// @} + + //************************************************************************* + /// Operator >=. + ///\param lhs The left-hand operand. + ///\param rhs The right-hand operand. + ///\return `true` if lhs is greater than or equal to rhs. + /// @{ + friend ETL_CONSTEXPR bool operator>=(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { + return !(lhs < rhs); + } + + /// @} + + private: + + T value; + }; + + //*************************************************************************** + /// Provides a value that is clamped between two runtime limits. + /// Supports incrementing, decrementing and arbitrary advance. + ///@tparam T An integral type, or a floating-point type when + /// ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`. + ///@tparam Min Ignored for this specialisation. + ///@tparam Max Ignored for this specialisation. + ///\ingroup clamped_value + //*************************************************************************** + template + class clamped_value + { + public: + + /// The type used to advance or subtract from the stored value. + typedef typename private_clamped_value::traits::difference_type difference_type; + + /// Numeric limits for the stored value type. + typedef typename private_clamped_value::traits::limits_type limits_type; + + /// Numeric limits for difference_type. + typedef typename private_clamped_value::traits::difference_limits_type difference_limits_type; + + //************************************************************************* + /// Default constructor. + /// Sets the value to the minimum representable value of `T` and the + /// bounds to the full range of `T`. + //************************************************************************* + ETL_CONSTEXPR clamped_value() ETL_NOEXCEPT + : value(limits_type::lowest()) + , min_value(limits_type::lowest()) + , max_value(limits_type::max()) + { + } + + //************************************************************************* + /// Constructor. + /// Sets the value to the minimum of the range. + /// Reversed bounds and NaN bounds are rejected. + ///\param min_ The minimum value. + ///\param max_ The maximum value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value(T min_, T max_) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + : value(min_) + , min_value(min_) + , max_value(max_) + { + private_clamped_value::validate(min_); + private_clamped_value::validate(max_); + ETL_ASSERT(min_ <= max_, ETL_ERROR_GENERIC("clamped_value: invalid range")); + } + + //************************************************************************* + /// Constructor. + /// Set to an initial value. + /// Clamped to the range. + /// Reversed bounds and NaN values or bounds are rejected. + ///\param min_ The minimum value. + ///\param max_ The maximum value. + ///\param initial The initial value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value(T min_, T max_, T initial) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + : value(private_clamped_value::validated_clamp(initial, min_, max_)) + , min_value(min_) + , max_value(max_) + { + } + + //************************************************************************* + /// Copy constructor. + ///\param other The value to copy. + //************************************************************************* + ETL_CONSTEXPR clamped_value(const clamped_value& other) ETL_NOEXCEPT + : value(other.value) + , min_value(other.min_value) + , max_value(other.max_value) + { + } + + //************************************************************************* + /// Sets the range. + /// Sets the value to the minimum of the range. + /// Reversed bounds and NaN bounds are rejected. + ///\param min_ The minimum value. + ///\param max_ The maximum value. + //************************************************************************* + ETL_CONSTEXPR14 void set(T min_, T max_) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + { + private_clamped_value::validate(min_); + private_clamped_value::validate(max_); + ETL_ASSERT(min_ <= max_, ETL_ERROR_GENERIC("clamped_value: invalid range")); + min_value = min_; + max_value = max_; + value = min_; + } + + //************************************************************************* + /// Sets the value. + /// Clamps to the runtime Min/Max range. + /// NaN is rejected for floating-point values. + ///\param value_ The value. + //************************************************************************* + ETL_CONSTEXPR14 void set(T value_) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + { + value = private_clamped_value::validated_clamp(value_, min_value, max_value); + } + + //************************************************************************* + /// Resets the value to the minimum in the range. + //************************************************************************* + ETL_CONSTEXPR14 void to_min() ETL_NOEXCEPT + { + value = min_value; + } + + //************************************************************************* + /// Resets the value to the maximum in the range. + //************************************************************************* + ETL_CONSTEXPR14 void to_max() ETL_NOEXCEPT + { + value = max_value; + } + + //************************************************************************* + /// Advances the value by a number of steps. + /// Saturates at the runtime Min/Max range. + /// Floating-point steps may be fractional. NaN is rejected. + ///\param n The number of steps. + //************************************************************************* + ETL_CONSTEXPR14 void advance(difference_type n) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + { + private_clamped_value::validate(n); + value = private_clamped_value::advance(value, min_value, max_value, n); + } + + //************************************************************************* + /// Conversion operator. + /// \return The value of the underlying type. + //************************************************************************* + ETL_NODISCARD ETL_CONSTEXPR14 operator T() const ETL_NOEXCEPT + { + return value; + } + + //************************************************************************* + /// ++ operator. + /// Advances by one. + /// Saturates at the maximum. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator++() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + value = private_clamped_value::advance(value, min_value, max_value, difference_type(1)); + return *this; + } + + //************************************************************************* + /// ++ operator. + /// Saturates at the maximum. + ///\return The value before incrementing. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value operator++(int) ETL_NOEXCEPT + { + clamped_value temp(*this); + ++(*this); + return temp; + } + + //************************************************************************* + /// -- operator. + /// Subtracts one. + /// Saturates at the minimum. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator--() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + value = private_clamped_value::advance(value, min_value, max_value, difference_type(-1)); + return *this; + } + + //************************************************************************* + /// -- operator. + /// Saturates at the minimum. + ///\return The value before decrementing. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value operator--(int) ETL_NOEXCEPT + { + clamped_value temp(*this); + --(*this); + return temp; + } + + //************************************************************************* + /// = operator. + /// Clamps to the runtime Min/Max range. + ///\param value_ The value to assign. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator=(T value_) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + { + set(value_); + return *this; + } + + //************************************************************************* + /// = operator. + /// Copies the value and runtime bounds. + ///\param other The value to copy. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator=(const clamped_value& other) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + if (this == &other) + { + return *this; + } + + value = other.value; + min_value = other.min_value; + max_value = other.max_value; + return *this; + } + + //************************************************************************* + /// Adds a number of steps and clamps to the range. + /// Floating-point steps may be fractional. NaN is rejected. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator+=(difference_type n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + { + advance(n); + return *this; + } + + //************************************************************************* + /// Subtracts a number of steps and clamps to the range. + /// Floating-point steps may be fractional. NaN is rejected. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator-=(difference_type n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + { + private_clamped_value::validate(n); + value = private_clamped_value::subtract(value, min_value, max_value, n); + return *this; + } + + //************************************************************************* + /// Gets the value. + ///\return The current value. + //************************************************************************* + ETL_NODISCARD ETL_CONSTEXPR T get() const ETL_NOEXCEPT + { + return value; + } + + //************************************************************************* + /// Gets the minimum value. + ///\return The minimum value. + //************************************************************************* + ETL_NODISCARD ETL_CONSTEXPR T min() const ETL_NOEXCEPT + { + return min_value; + } + + //************************************************************************* + /// Gets the maximum value. + ///\return The maximum value. + //************************************************************************* + ETL_NODISCARD ETL_CONSTEXPR T max() const ETL_NOEXCEPT + { + return max_value; + } + + //************************************************************************* + /// Swaps the values and runtime bounds. + ///\param other The value to swap with. + //************************************************************************* + ETL_CONSTEXPR14 void swap(clamped_value& other) ETL_NOEXCEPT + { + using ETL_OR_STD::swap; + swap(value, other.value); + swap(min_value, other.min_value); + swap(max_value, other.max_value); + } + + //************************************************************************* + /// Swaps the values and runtime bounds. + ///\param lhs The first value. + ///\param rhs The second value. + //************************************************************************* + friend ETL_CONSTEXPR14 void swap(clamped_value& lhs, clamped_value& rhs) ETL_NOEXCEPT + { + lhs.swap(rhs); + } + + //************************************************************************* + /// Operator ==. + ///\param lhs The left-hand value. + ///\param rhs The right-hand value. + ///\return `true` if the values are equal. + //************************************************************************* + friend ETL_CONSTEXPR bool operator==(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { +#include "private/diagnostic_float_equal_push.h" + return lhs.value == rhs.value; +#include "private/diagnostic_pop.h" + } + + //************************************************************************* + /// Operator !=. + ///\param lhs The left-hand value. + ///\param rhs The right-hand value. + ///\return `true` if the values differ. + //************************************************************************* + friend ETL_CONSTEXPR bool operator!=(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { + return !(lhs == rhs); + } + + //************************************************************************* + /// Operator <. + ///\param lhs The left-hand operand. + ///\param rhs The right-hand operand. + ///\return `true` if lhs is less than rhs. + /// @{ + friend ETL_CONSTEXPR bool operator<(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { + return lhs.value < rhs.value; + } + /// @} + + //************************************************************************* + /// Operator <=. + ///\param lhs The left-hand operand. + ///\param rhs The right-hand operand. + ///\return `true` if lhs is less than or equal to rhs. + /// @{ + friend ETL_CONSTEXPR bool operator<=(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { + return !(rhs < lhs); + } + + /// @} + + //************************************************************************* + /// Operator >. + ///\param lhs The left-hand operand. + ///\param rhs The right-hand operand. + ///\return `true` if lhs is greater than rhs. + /// @{ + friend ETL_CONSTEXPR bool operator>(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { + return rhs < lhs; + } + + /// @} + + //************************************************************************* + /// Operator >=. + ///\param lhs The left-hand operand. + ///\param rhs The right-hand operand. + ///\return `true` if lhs is greater than or equal to rhs. + /// @{ + friend ETL_CONSTEXPR bool operator>=(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { + return !(lhs < rhs); + } + + /// @} + + private: + + T value; + T min_value; + T max_value; + }; +} // namespace etl + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fbbf091e..56699a41 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -163,6 +163,7 @@ add_executable(etl_tests test_crc8_opensafety.cpp test_crc8_rohc.cpp test_crc8_wcdma.cpp + test_clamped_value.cpp test_cyclic_value.cpp test_debounce.cpp test_delegate.cpp diff --git a/test/maincpp03check.cpp b/test/maincpp03check.cpp index a1745c6e..2c5c9936 100644 --- a/test/maincpp03check.cpp +++ b/test/maincpp03check.cpp @@ -46,6 +46,7 @@ SOFTWARE. #include "etl/callback_timer.h" #include "etl/char_traits.h" #include "etl/checksum.h" +#include "etl/clamped_value.h" #include "etl/combinations.h" #include "etl/compare.h" #include "etl/constant.h" diff --git a/test/meson.build b/test/meson.build index add9b29b..b4bc601d 100644 --- a/test/meson.build +++ b/test/meson.build @@ -151,6 +151,7 @@ etl_test_sources = files( 'test_crc8_opensafety.cpp', 'test_crc8_rohc.cpp', 'test_crc8_wcdma.cpp', + 'test_clamped_value.cpp', 'test_cyclic_value.cpp', 'test_debounce.cpp', 'test_delegate.cpp', diff --git a/test/syntax_check/CMakeLists.txt b/test/syntax_check/CMakeLists.txt index 3546cfa3..3c4e8e54 100644 --- a/test/syntax_check/CMakeLists.txt +++ b/test/syntax_check/CMakeLists.txt @@ -202,6 +202,7 @@ target_sources(tests PRIVATE crc8_nrsc5.h.t.cpp crc8_rohc.h.t.cpp crc8_wcdma.h.t.cpp + clamped_value.h.t.cpp cyclic_value.h.t.cpp debounce.h.t.cpp debug_count.h.t.cpp diff --git a/test/syntax_check/clamped_value.h.t.cpp b/test/syntax_check/clamped_value.h.t.cpp new file mode 100644 index 00000000..a09da520 --- /dev/null +++ b/test/syntax_check/clamped_value.h.t.cpp @@ -0,0 +1,36 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2026 John Wellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include + +#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE +etl::clamped_value clamped_float_compile_time; +etl::clamped_value clamped_double_run_time; +#endif diff --git a/test/test_clamped_value.cpp b/test/test_clamped_value.cpp new file mode 100644 index 00000000..d74dfb13 --- /dev/null +++ b/test/test_clamped_value.cpp @@ -0,0 +1,515 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2026 John Wellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "unit_test_framework.h" + +#include "etl/clamped_value.h" + +#include +#include + +namespace +{ + SUITE(test_clamped_value) + { + //************************************************************************* + TEST(test_compile_time_initialisation) + { + etl::clamped_value value; + const etl::clamped_value const_value(value); + int converted_value = value; + int converted_const_value = const_value; + CHECK_EQUAL(2, value.get()); + CHECK_EQUAL(2, value.min()); + CHECK_EQUAL(7, value.max()); + CHECK_EQUAL(2, converted_value); + CHECK_EQUAL(2, converted_const_value); + } + + //************************************************************************* + TEST(test_compile_time_bounds_accessors_are_const) + { + const etl::clamped_value value(4); + CHECK_EQUAL(2, value.min()); + CHECK_EQUAL(7, value.max()); + } + + //************************************************************************* + TEST(test_run_time_initialisation) + { + etl::clamped_value value(2, 7, 5); + etl::clamped_value default_value; + etl::clamped_value range_value(2, 7); + const etl::clamped_value const_value(value); + int converted_value = value; + int converted_const_value = const_value; + CHECK_EQUAL(5, value.get()); + CHECK_EQUAL(2, value.min()); + CHECK_EQUAL(7, value.max()); + CHECK_EQUAL(etl::numeric_limits::lowest(), default_value.min()); + CHECK_EQUAL(etl::numeric_limits::max(), default_value.max()); + CHECK_EQUAL(etl::numeric_limits::lowest(), default_value.get()); + CHECK_EQUAL(2, range_value.get()); + CHECK_EQUAL(5, converted_value); + CHECK_EQUAL(5, converted_const_value); + } + + //************************************************************************* + TEST(test_set_and_assignment_clamp_values) + { + etl::clamped_value compile_time(1); + etl::clamped_value run_time(2, 7, 8); + CHECK_EQUAL(2, compile_time.get()); + CHECK_EQUAL(7, run_time.get()); + compile_time = 8; + run_time.set(1); + CHECK_EQUAL(7, compile_time.get()); + CHECK_EQUAL(2, run_time.get()); + } + + //************************************************************************* + TEST(test_increment_and_decrement_saturate) + { + etl::clamped_value compile_time(7); + etl::clamped_value run_time(2, 7, 2); + ++compile_time; + --run_time; + CHECK_EQUAL(7, compile_time.get()); + CHECK_EQUAL(2, run_time.get()); + compile_time.to_min(); + run_time.to_max(); + --compile_time; + ++run_time; + CHECK_EQUAL(2, compile_time.get()); + CHECK_EQUAL(7, run_time.get()); + } + + //************************************************************************* + TEST(test_postfix_operators) + { + etl::clamped_value value(4); + etl::clamped_value previous_increment = value++; + CHECK_EQUAL(4, previous_increment.get()); + CHECK_EQUAL(5, value.get()); + etl::clamped_value previous_decrement = value--; + CHECK_EQUAL(5, previous_decrement.get()); + CHECK_EQUAL(4, value.get()); + } + + //************************************************************************* + TEST(test_advance_saturates) + { + etl::clamped_value value; + value.advance(100); + CHECK_EQUAL(7, value.get()); + value.advance(-100); + CHECK_EQUAL(2, value.get()); + } + + //************************************************************************* + TEST(test_set_run_time_bounds) + { + etl::clamped_value value(2, 7, 6); + value.set(3, 8); + CHECK_EQUAL(3, value.get()); + CHECK_EQUAL(3, value.min()); + CHECK_EQUAL(8, value.max()); + } + + //************************************************************************* + TEST(test_copy_equality_and_swap_compile_time) + { + etl::clamped_value value1(4); + etl::clamped_value value2(value1); + CHECK(value1 == value2); + value2 = 6; + CHECK(value1 != value2); + swap(value1, value2); + CHECK_EQUAL(6, value1.get()); + CHECK_EQUAL(4, value2.get()); + } + + //************************************************************************* + TEST(test_compile_time_instances_with_different_bounds) + { + etl::clamped_value value1(3); + etl::clamped_value value2(3); + CHECK(value1 == value2); + } + + //************************************************************************* + TEST(test_runtime_equality_uses_current_value) + { + etl::clamped_value equal_value1(2, 7, 3); + etl::clamped_value equal_value2(2, 8, 3); + etl::clamped_value different_value(2, 8, 4); + CHECK(equal_value1 == equal_value2); + CHECK(!(equal_value1 != equal_value2)); + CHECK(equal_value1 != different_value); + } + + //************************************************************************* + TEST(test_copy_assignment_and_self_assignment_run_time) + { + etl::clamped_value value1(2, 7, 5); + etl::clamped_value value2(3, 8, 6); + value1 = value2; + CHECK_EQUAL(6, value1.get()); + CHECK_EQUAL(3, value1.min()); + CHECK_EQUAL(8, value1.max()); + etl::clamped_value* value1_pointer = &value1; + value1 = *value1_pointer; + CHECK_EQUAL(6, value1.get()); + CHECK_EQUAL(3, value1.min()); + CHECK_EQUAL(8, value1.max()); + } + + //************************************************************************* + TEST(test_unsigned_boundary_values) + { + etl::clamped_value value(2U); + ++value; + CHECK_EQUAL(2U, value.get()); + value.to_min(); + --value; + CHECK_EQUAL(0U, value.get()); + } + + //************************************************************************* + TEST(test_narrow_signed_range_spanning_lowest_value) + { + etl::clamped_value value(-50); + + value += 10; + CHECK_EQUAL(-40, value.get()); + value -= 10; + CHECK_EQUAL(-50, value.get()); + value.advance(INT8_MIN); + CHECK_EQUAL(INT8_MIN, value.get()); + value.advance(INT8_MAX); + CHECK_EQUAL(-10, value.get()); + } + + //************************************************************************* + TEST(test_runtime_postfix_and_advance) + { + etl::clamped_value value(2, 7, 4); + etl::clamped_value previous_increment = value++; + CHECK_EQUAL(4, previous_increment.get()); + CHECK_EQUAL(5, value.get()); + etl::clamped_value previous_decrement = value--; + CHECK_EQUAL(5, previous_decrement.get()); + CHECK_EQUAL(4, value.get()); + value.advance(0); + CHECK_EQUAL(4, value.get()); + value.advance(3); + CHECK_EQUAL(7, value.get()); + value.advance(-3); + CHECK_EQUAL(4, value.get()); + } + + //************************************************************************* + TEST(test_wide_and_negative_ranges) + { + etl::clamped_value negative(-4); + etl::clamped_value wide(0); + etl::clamped_value unsigned_value(2U, 7U, 4U); + + negative.advance(100); + wide.advance(INT64_MAX); + unsigned_value.advance(3); + + CHECK_EQUAL(-2, negative.get()); + CHECK_EQUAL(INT64_C(7), wide.get()); + CHECK_EQUAL(7U, unsigned_value.get()); + } + + //************************************************************************* + TEST(test_compound_and_comparison_operators) + { + etl::clamped_value value(4); + etl::clamped_value other(6); + + value += 10; + CHECK_EQUAL(7, value.get()); + value -= 10; + CHECK_EQUAL(2, value.get()); + CHECK(value < other); + CHECK(value < 6); + CHECK(4 < other); + CHECK(value <= other); + CHECK(value <= 4); + CHECK(4 <= other); + CHECK(other > value); + CHECK(other > 4); + CHECK(6 > value); + CHECK(other >= value); + CHECK(other >= 6); + CHECK(6 >= value); + CHECK(value == 2); + CHECK(2 == value); + CHECK(value != 6); + CHECK(6 != value); + } + + //************************************************************************* + TEST(test_mixed_type_comparisons_are_not_ambiguous) + { + etl::clamped_value narrow(10); + etl::clamped_value value(10); + + CHECK(narrow < 20); + CHECK(value < 20L); + CHECK(20L > value); + CHECK(value <= 20L); + CHECK(20L >= value); + } + + //************************************************************************* + TEST(test_runtime_comparison_operator_overloads) + { + etl::clamped_value value(2, 7, 4); + etl::clamped_value other(2, 8, 6); + + CHECK(value < other); + CHECK(value < 6); + CHECK(4 < other); + CHECK(value <= other); + CHECK(value <= 4); + CHECK(4 <= other); + CHECK(other > value); + CHECK(other > 4); + CHECK(6 > value); + CHECK(other >= value); + CHECK(other >= 6); + CHECK(6 >= value); + } + + //************************************************************************* + TEST(test_advance_extreme_values) + { + etl::clamped_value compile_time(4); + etl::clamped_value run_time(2, 7, 4); + compile_time.advance(INT_MAX); + run_time.advance(INT_MIN); + CHECK_EQUAL(7, compile_time.get()); + CHECK_EQUAL(2, run_time.get()); + } + + //************************************************************************* + TEST(test_subtract_lowest_difference_saturates) + { + etl::clamped_value compile_time(0); + etl::clamped_value run_time(-10, 10, 0); + + compile_time -= etl::clamped_value::difference_limits_type::lowest(); + run_time -= etl::clamped_value::difference_limits_type::lowest(); + + CHECK_EQUAL(10, compile_time.get()); + CHECK_EQUAL(10, run_time.get()); + } + + //************************************************************************* + TEST(test_runtime_compound_assignment_and_swap) + { + etl::clamped_value value1(-4, 8, 2); + etl::clamped_value value2(10, 20, 15); + + value1 += 20; + value2 -= 20; + value1.swap(value2); + + CHECK_EQUAL(10, value1.get()); + CHECK_EQUAL(10, value1.min()); + CHECK_EQUAL(20, value1.max()); + CHECK_EQUAL(8, value2.get()); + CHECK_EQUAL(-4, value2.min()); + CHECK_EQUAL(8, value2.max()); + } + + //************************************************************************* + TEST(test_runtime_reversed_bounds_are_rejected) + { + CHECK_THROW((etl::clamped_value(7, 2)), etl::exception); + CHECK_THROW((etl::clamped_value(7, 2, 4)), etl::exception); + + etl::clamped_value value; + CHECK_THROW(value.set(7, 2), etl::exception); + } + +#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE + //************************************************************************* + TEST(test_compile_time_floating_clamps_and_advances) + { + etl::clamped_value value(4.0f); + CHECK_CLOSE(2.5f, value.get(), 0.0001f); + + value = -4.0f; + CHECK_CLOSE(-1.5f, value.get(), 0.0001f); + + value += 0.75f; + CHECK_CLOSE(-0.75f, value.get(), 0.0001f); + value -= 0.5f; + CHECK_CLOSE(-1.25f, value.get(), 0.0001f); + value.advance(10.0f); + CHECK_CLOSE(2.5f, value.get(), 0.0001f); + } + + //************************************************************************* + TEST(test_runtime_floating_clamps_and_advances) + { + etl::clamped_value value(-1.5, 2.5, 4.0); + CHECK_CLOSE(2.5, value.get(), 0.0001); + + value = -4.0; + value += 0.75; + value -= 0.5; + CHECK_CLOSE(-1.25, value.get(), 0.0001); + + value.advance(-10.0); + CHECK_CLOSE(-1.5, value.get(), 0.0001); + } + + //************************************************************************* + TEST(test_floating_increment_and_decrement_saturate_at_fractional_bounds) + { + etl::clamped_value compile_time(0.25f); + etl::clamped_value run_time(-0.5f, 0.5f, -0.25f); + + ++compile_time; + --run_time; + + CHECK_CLOSE(0.5f, compile_time.get(), 0.0001f); + CHECK_CLOSE(-0.5f, run_time.get(), 0.0001f); + } + + //************************************************************************* + TEST(test_floating_comparison_and_swap) + { + etl::clamped_value compile_time1(-0.5); + etl::clamped_value compile_time2(1.5); + etl::clamped_value run_time1(-3.0, 3.0, -1.0); + etl::clamped_value run_time2(4.0, 8.0, 6.0); + + CHECK(compile_time1 < compile_time2); + CHECK(compile_time1 < 1.0); + swap(compile_time1, compile_time2); + swap(run_time1, run_time2); + + CHECK_CLOSE(1.5, compile_time1.get(), 0.0001); + CHECK_CLOSE(6.0, run_time1.get(), 0.0001); + CHECK_CLOSE(4.0, run_time1.min(), 0.0001); + CHECK_CLOSE(-1.0, run_time2.get(), 0.0001); + } + + //************************************************************************* + TEST(test_floating_infinity_saturates) + { + const double infinity = etl::numeric_limits::infinity(); + etl::clamped_value value(0.0); + etl::clamped_value infinite_range(-infinity, infinity, 0.0); + + value += infinity; + CHECK_CLOSE(2.0, value.get(), 0.0001); + value -= infinity; + CHECK_CLOSE(-2.0, value.get(), 0.0001); + + infinite_range = infinity; + CHECK(etl::is_infinity(infinite_range.get())); + } + + //************************************************************************* + TEST(test_floating_finite_overflow_is_prevented) + { + const double maximum = etl::numeric_limits::max(); + etl::clamped_value value(-maximum, maximum, maximum / 2.0); + + value += maximum; + CHECK_EQUAL(maximum, value.get()); + + value = -maximum / 2.0; + value -= maximum; + CHECK_EQUAL(-maximum, value.get()); + + value = -maximum; + value += maximum; + CHECK_EQUAL(0.0, value.get()); + value += maximum; + CHECK_EQUAL(maximum, value.get()); + } + + //************************************************************************* + TEST(test_floating_nan_is_rejected) + { + const double nan = etl::numeric_limits::quiet_NaN(); + CHECK_THROW((etl::clamped_value(nan, 1.0)), etl::exception); + CHECK_THROW((etl::clamped_value(-1.0, nan)), etl::exception); + CHECK_THROW((etl::clamped_value(-1.0, 1.0, nan)), etl::exception); + + etl::clamped_value value(-1.0, 1.0, 0.0); + CHECK_THROW(value.set(nan, 1.0), etl::exception); + CHECK_THROW(value.set(-1.0, nan), etl::exception); + CHECK_THROW(value = nan, etl::exception); + CHECK_THROW(value.advance(nan), etl::exception); + CHECK_THROW(value += nan, etl::exception); + CHECK_THROW(value -= nan, etl::exception); + } + + //************************************************************************* + TEST(test_default_floating_range) + { + etl::clamped_value value; + CHECK_EQUAL(etl::numeric_limits::lowest(), value.min()); + CHECK_EQUAL(etl::numeric_limits::max(), value.max()); + CHECK_EQUAL(etl::numeric_limits::lowest(), value.get()); + } +#endif + +#if ETL_USING_CPP14 + //************************************************************************* + TEST(test_clamped_value_constexpr_ctor) + { + constexpr etl::clamped_value value; + static_assert(value.get() == 0, "constexpr clamped_value constructor"); + CHECK(true); + } + + #if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE + //************************************************************************* + TEST(test_floating_clamped_value_constexpr) + { + constexpr etl::clamped_value value(0.5); + static_assert((value.get() > 0.49) && (value.get() < 0.51), "constexpr floating clamped_value constructor"); + CHECK(true); + } + #endif +#endif + } +} // namespace diff --git a/test/vs2022/etl.vcxproj b/test/vs2022/etl.vcxproj index 503563eb..7a69c37e 100644 --- a/test/vs2022/etl.vcxproj +++ b/test/vs2022/etl.vcxproj @@ -3764,6 +3764,7 @@ + @@ -6303,6 +6304,7 @@ true true + true true @@ -10446,6 +10448,7 @@ + @@ -11804,4 +11807,4 @@ - \ No newline at end of file + diff --git a/test/vs2022/etl.vcxproj.filters b/test/vs2022/etl.vcxproj.filters index f20f1a7a..10afee24 100644 --- a/test/vs2022/etl.vcxproj.filters +++ b/test/vs2022/etl.vcxproj.filters @@ -261,6 +261,9 @@ ETL\Utilities + + ETL\Utilities + ETL\Utilities @@ -2087,6 +2090,9 @@ Tests\Algorithms + + Tests\Algorithms + Tests\Algorithms @@ -2909,6 +2915,9 @@ Tests\Syntax Checks\Source + + Tests\Syntax Checks\Source + Tests\Syntax Checks\Source @@ -4115,4 +4124,4 @@ Resource Files\Images - \ No newline at end of file + From 2696828f27b93e0a6232d5189ab038ce91b1774a Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Tue, 8 Sep 2026 15:37:23 +0100 Subject: [PATCH 75/77] Updated release notes Removed redundant ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) from clamped_value.h --- .gitignore | 1 + docs/releases/_index.md | 72 +++++++++++++++++++++++++++++++++++++ include/etl/clamped_value.h | 41 ++++++++++----------- test/CMakeLists.txt | 2 ++ test/test_clamped_value.cpp | 2 +- 5 files changed, 97 insertions(+), 21 deletions(-) diff --git a/.gitignore b/.gitignore index 60d4e469..b5cc3865 100644 --- a/.gitignore +++ b/.gitignore @@ -422,3 +422,4 @@ test/build-coverage include/etl/iterator.h~RFfd5eda.TMP test/vs2022/Build docs/Messaging +test/vs2026/Build diff --git a/docs/releases/_index.md b/docs/releases/_index.md index aa737d96..e7767e21 100644 --- a/docs/releases/_index.md +++ b/docs/releases/_index.md @@ -5,6 +5,78 @@ weight: 2002 Click on the links to see the original Github page. +## 20.49.0 + +**Pull requests:** + +[#1502](https://github.com/ETLCPP/etl/pull/1502) Remove the documentation page for `etl::type_id` +[#1503](https://github.com/ETLCPP/etl/pull/1503) Add `difference_type` to span +[#1504](https://github.com/ETLCPP/etl/pull/1504) Add missing `array` `get<>` overloads, and noexcept/constexpr +[#1505](https://github.com/ETLCPP/etl/pull/1505) Support structured bindings for array +[#1506](https://github.com/ETLCPP/etl/pull/1506) Add safe integer comparisons +[#1507](https://github.com/ETLCPP/etl/pull/1507) Various fixes and cleanup +[#1508](https://github.com/ETLCPP/etl/pull/1508) Remove redundant `reinterpret_cast` +[#1509](https://github.com/ETLCPP/etl/pull/1509) Add `etl::apply` +[#1510](https://github.com/ETLCPP/etl/pull/1510) Fix `variant` `emplace` to be valueless after a throwing construction +[#1511](https://github.com/ETLCPP/etl/pull/1511) Fix deep const-cast in `ivector` pointer repair for const pointee types +[#1513](https://github.com/ETLCPP/etl/pull/1513) `string_view`: clamp n in `remove_prefix` and remove_suffix +[#1514](https://github.com/ETLCPP/etl/pull/1514) Fix `variant` copy operations enabled with non-copyable alternatives +[#1515](https://github.com/ETLCPP/etl/pull/1515) Clean up the use of type traits by using C++11 forms wherever possible +[#1516](https://github.com/ETLCPP/etl/pull/1516) Restore .devcontainer/context directory removed by #1422 +[#1517](https://github.com/ETLCPP/etl/pull/1517) Update Github Actions to checkout@v6.0.3 +[#1519](https://github.com/ETLCPP/etl/pull/1519) fix(devcontainer): resolve missing packages in bullseye image +[#1522](https://github.com/ETLCPP/etl/pull/1522) Fix ODR violation in `optional` +[#1524](https://github.com/ETLCPP/etl/pull/1524) Fixed signed/unsigned mismatch warnings in VS2022 +[#1525](https://github.com/ETLCPP/etl/pull/1525) Fix `operator[]` for const `iota_view` and repeat_view +[#1526](https://github.com/ETLCPP/etl/pull/1526) Fix `make_array` and `make_deque` rejecting const-qualified lvalues of the element type +[#1528](https://github.com/ETLCPP/etl/pull/1528) Change `make_list` to take its arguments by forwarding reference +[#1529](https://github.com/ETLCPP/etl/pull/1529) Fix `etl::common_type` decaying both arguments to `T2` +[#1530](https://github.com/ETLCPP/etl/pull/1530) Correct the documented make_xxx factory signatures and examples +[#1534](https://github.com/ETLCPP/etl/pull/1534) Prevent usage of `N` as template value parameter +[#1535](https://github.com/ETLCPP/etl/pull/1535) Make `etl_profile` Incompatible by Default +[#1537](https://github.com/ETLCPP/etl/pull/1537) Fix ambiguous `byte_stream_reader` construction from `etl::span` +[#1538](https://github.com/ETLCPP/etl/pull/1538) Fix `expected::operator*() const&` and `&&` failing to compile +[#1540](https://github.com/ETLCPP/etl/pull/1540) Fix flat_map/reference_flat_map to use random-access iterators in lower_bound +[#1542](https://github.com/ETLCPP/etl/pull/1542) Remove redundant `noexcept` expr- #1542 +[#1544](https://github.com/ETLCPP/etl/pull/1544) Various cleanup +[#1545](https://github.com/ETLCPP/etl/pull/1545) Add etl::`is_standard_layout` and other type traits according to std +[#1546](https://github.com/ETLCPP/etl/pull/1546) Fix `delegate` clear through assignment +[#1548](https://github.com/ETLCPP/etl/pull/1548) `delegate`: restore argument deduction behaviour- #1548 +[#1550](https://github.com/ETLCPP/etl/pull/1550) Add span deduction guide for types with `data()` and `size()` +[#1552](https://github.com/ETLCPP/etl/pull/1552) Add Macro for Inline Control +[#1553](https://github.com/ETLCPP/etl/pull/1553) Adopt the Library Fundamentals TS `make_array` design for the sequence factories +[#1554](https://github.com/ETLCPP/etl/pull/1554) Fix PowerPC Dockerfile to use Snapshot consistently +[#1555](https://github.com/ETLCPP/etl/pull/1555) Constexpr for unaligned_type +[#1556](https://github.com/ETLCPP/etl/pull/1556) `circular_buffer`: add initialization and assignment overloads +[#1557](https://github.com/ETLCPP/etl/pull/1557) CI: cancel superseded workflow runs +[#1558](https://github.com/ETLCPP/etl/pull/1558) CI: Filter compiler workflows by path +[#1559](https://github.com/ETLCPP/etl/pull/1559) Remove redundant `ETL_USING_STL` in tests +[#1560](https://github.com/ETLCPP/etl/pull/1560) Set default compare for `iconst_map` +[#1561](https://github.com/ETLCPP/etl/pull/1561) Allow easier construction of const_(multi_)map +[#1562](https://github.com/ETLCPP/etl/pull/1562) Various cleanup +[#1563](https://github.com/ETLCPP/etl/pull/1563) Cherry pick the Docker fixes for PowerPC +[#1564](https://github.com/ETLCPP/etl/pull/1564) Update `etl::expected` documentation +[#1565](https://github.com/ETLCPP/etl/pull/1565) Implement clamped_value utility +[#1566](https://github.com/ETLCPP/etl/pull/1566) Fix guards in `array.h` and `test/test_array.h` +[#1568](https://github.com/ETLCPP/etl/pull/1568) For optional monadic operations, add docs, tests, and complete type +[#1569](https://github.com/ETLCPP/etl/pull/1569) Add missing `etl::ranges::distance` overload +[#1570](https://github.com/ETLCPP/etl/pull/1570) Fix `FreeRTOS.h/semphr.h` include paths for ESP-IDF (fixes #1439) +[#1571](https://github.com/ETLCPP/etl/pull/1571) Drop Debian 11 (bullseye) support +[#1572](https://github.com/ETLCPP/etl/pull/1572) Fixed building and unit tests run under XCode 2027 Beta 6 +[#1573](https://github.com/ETLCPP/etl/pull/1573) Add missing type traits +[#1574](https://github.com/ETLCPP/etl/pull/1574) Make `etl::variant` trivially copyable +[#1576](https://github.com/ETLCPP/etl/pull/1576) Remove spurious link in documentation +[#1578](https://github.com/ETLCPP/etl/pull/1578) Fix implicit conversion in binary rotations +[#1579](https://github.com/ETLCPP/etl/pull/1579) Fix sanitizer builds +[#1580](https://github.com/ETLCPP/etl/pull/1580) Support variable number of arguments to inject at runtime +[#1581](https://github.com/ETLCPP/etl/pull/1581) Detect builtins +[#1582](https://github.com/ETLCPP/etl/pull/1582) Fix symbols to be exportable from C++20 named modules +[#1583](https://github.com/ETLCPP/etl/pull/1583) absolute and casting updates +[#1586](https://github.com/ETLCPP/etl/pull/1586) Fix size type of `string_view` +[#1588](https://github.com/ETLCPP/etl/pull/1588) Optimize `format.h` +[#1589](https://github.com/ETLCPP/etl/pull/1589) Fix MSVC C4244 warning in format specifiers +[#1591](https://github.com/ETLCPP/etl/pull/1591) Fix false positive warning from GCC 16 + ## 20.48.1 **Pull requests:** diff --git a/include/etl/clamped_value.h b/include/etl/clamped_value.h index 5448548a..d587bde4 100644 --- a/include/etl/clamped_value.h +++ b/include/etl/clamped_value.h @@ -105,25 +105,26 @@ namespace etl typedef typename traits::unsigned_type unsigned_type; ETL_NODISCARD - static ETL_CONSTEXPR14 T advance(T value, T min_value, T max_value, typename traits::difference_type n) ETL_NOEXCEPT + static + ETL_CONSTEXPR14 T advance(T value, T min_value, T max_value, typename traits::difference_type n) ETL_NOEXCEPT { if (n == 0) { return value; } - const unsigned_type current = static_cast(value); + const unsigned_type current = etl::to_unsigned(value); //static_cast(value); const unsigned_type step = etl::absolute_unsigned(n); if ((n > 0) && (value < max_value)) { - const unsigned_type distance = static_cast(max_value) - current; + const unsigned_type distance = etl::to_unsigned(max_value) - current; //static_cast(max_value) - current; value = (step >= distance) ? max_value : static_cast(current + step); } else if ((n < 0) && (value > min_value)) { - const unsigned_type distance = current - static_cast(min_value); + const unsigned_type distance = current - etl::to_unsigned(min_value); //static_cast(min_value); value = (step >= distance) ? min_value : static_cast(current - step); } @@ -211,7 +212,7 @@ namespace etl ///\param value The value to validate. //************************************************************************* template - ETL_CONSTEXPR14 void validate(T value) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 void validate(T value) { ETL_ASSERT(!etl::is_nan(value), ETL_ERROR_GENERIC("clamped_value: NaN is not supported")); } @@ -224,7 +225,7 @@ namespace etl ///\return The value clamped to the supplied range. //************************************************************************* template - ETL_NODISCARD ETL_CONSTEXPR14 T validated_clamp(T value, T min_value, T max_value) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + ETL_NODISCARD ETL_CONSTEXPR14 T validated_clamp(T value, T min_value, T max_value) { validate(value); validate(min_value); @@ -283,7 +284,7 @@ namespace etl /// NaN is rejected for floating-point values. ///\param initial The initial value. //************************************************************************* - ETL_CONSTEXPR14 explicit clamped_value(T initial) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 explicit clamped_value(T initial) : value(private_clamped_value::validated_clamp(initial, Min, Max)) { } @@ -318,7 +319,7 @@ namespace etl /// NaN is rejected for floating-point values. ///\param value_ The value. //************************************************************************* - ETL_CONSTEXPR14 void set(T value_) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 void set(T value_) { value = private_clamped_value::validated_clamp(value_, Min, Max); } @@ -345,7 +346,7 @@ namespace etl /// Floating-point steps may be fractional. NaN is rejected. ///\param n The number of steps. //************************************************************************* - ETL_CONSTEXPR14 void advance(difference_type n) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 void advance(difference_type n) { private_clamped_value::validate(n); value = private_clamped_value::advance(value, Min, Max, n); @@ -414,7 +415,7 @@ namespace etl ///\param value_ The value to assign. ///\return A reference to this value. //************************************************************************* - ETL_CONSTEXPR14 clamped_value& operator=(T value_) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 clamped_value& operator=(T value_) ETL_LVALUE_REF_QUALIFIER { set(value_); return *this; @@ -426,7 +427,7 @@ namespace etl ///\param n The number of steps. ///\return A reference to this value. //************************************************************************* - ETL_CONSTEXPR14 clamped_value& operator+=(difference_type n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 clamped_value& operator+=(difference_type n) ETL_LVALUE_REF_QUALIFIER { advance(n); return *this; @@ -438,7 +439,7 @@ namespace etl ///\param n The number of steps. ///\return A reference to this value. //************************************************************************* - ETL_CONSTEXPR14 clamped_value& operator-=(difference_type n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 clamped_value& operator-=(difference_type n) ETL_LVALUE_REF_QUALIFIER { private_clamped_value::validate(n); value = private_clamped_value::subtract(value, Min, Max, n); @@ -615,7 +616,7 @@ namespace etl ///\param min_ The minimum value. ///\param max_ The maximum value. //************************************************************************* - ETL_CONSTEXPR14 clamped_value(T min_, T max_) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 clamped_value(T min_, T max_) : value(min_) , min_value(min_) , max_value(max_) @@ -634,7 +635,7 @@ namespace etl ///\param max_ The maximum value. ///\param initial The initial value. //************************************************************************* - ETL_CONSTEXPR14 clamped_value(T min_, T max_, T initial) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 clamped_value(T min_, T max_, T initial) : value(private_clamped_value::validated_clamp(initial, min_, max_)) , min_value(min_) , max_value(max_) @@ -659,7 +660,7 @@ namespace etl ///\param min_ The minimum value. ///\param max_ The maximum value. //************************************************************************* - ETL_CONSTEXPR14 void set(T min_, T max_) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 void set(T min_, T max_) { private_clamped_value::validate(min_); private_clamped_value::validate(max_); @@ -675,7 +676,7 @@ namespace etl /// NaN is rejected for floating-point values. ///\param value_ The value. //************************************************************************* - ETL_CONSTEXPR14 void set(T value_) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 void set(T value_) { value = private_clamped_value::validated_clamp(value_, min_value, max_value); } @@ -702,7 +703,7 @@ namespace etl /// Floating-point steps may be fractional. NaN is rejected. ///\param n The number of steps. //************************************************************************* - ETL_CONSTEXPR14 void advance(difference_type n) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 void advance(difference_type n) { private_clamped_value::validate(n); value = private_clamped_value::advance(value, min_value, max_value, n); @@ -771,7 +772,7 @@ namespace etl ///\param value_ The value to assign. ///\return A reference to this value. //************************************************************************* - ETL_CONSTEXPR14 clamped_value& operator=(T value_) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 clamped_value& operator=(T value_) ETL_LVALUE_REF_QUALIFIER { set(value_); return *this; @@ -802,7 +803,7 @@ namespace etl ///\param n The number of steps. ///\return A reference to this value. //************************************************************************* - ETL_CONSTEXPR14 clamped_value& operator+=(difference_type n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 clamped_value& operator+=(difference_type n) ETL_LVALUE_REF_QUALIFIER { advance(n); return *this; @@ -814,7 +815,7 @@ namespace etl ///\param n The number of steps. ///\return A reference to this value. //************************************************************************* - ETL_CONSTEXPR14 clamped_value& operator-=(difference_type n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 clamped_value& operator-=(difference_type n) ETL_LVALUE_REF_QUALIFIER { private_clamped_value::validate(n); value = private_clamped_value::subtract(value, min_value, max_value, n); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 56699a41..e1661909 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -498,6 +498,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") PRIVATE -fno-omit-frame-pointer -fno-common +# -fmax-errors=10 -pedantic-errors -Wall -Wextra @@ -518,6 +519,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") PRIVATE -fno-omit-frame-pointer -fno-common +# -ferror-limit=10 -pedantic-errors -Wall -Wextra diff --git a/test/test_clamped_value.cpp b/test/test_clamped_value.cpp index d74dfb13..40ecc270 100644 --- a/test/test_clamped_value.cpp +++ b/test/test_clamped_value.cpp @@ -501,7 +501,7 @@ namespace CHECK(true); } - #if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE + #if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE && (ETL_USING_MICROSOFT_COMPILER && (_MSC_VER >= 1950)) //************************************************************************* TEST(test_floating_clamped_value_constexpr) { From ffea27142ae6dbf00e85b15c4c1fba2e96494a93 Mon Sep 17 00:00:00 2001 From: Drew Rife Date: Tue, 8 Sep 2026 14:45:31 -0400 Subject: [PATCH 76/77] Support runtime floating-point clamped_value before C++20 (#1595) --- docs/utilities/clamped-value.md | 43 ++++++++-------- include/etl/clamped_value.h | 44 +++++++++++++---- test/syntax_check/clamped_value.h.t.cpp | 7 ++- test/test_clamped_value.cpp | 66 ++++++++++++++++--------- 4 files changed, 105 insertions(+), 55 deletions(-) diff --git a/docs/utilities/clamped-value.md b/docs/utilities/clamped-value.md index 18fba437..5bae0543 100644 --- a/docs/utilities/clamped-value.md +++ b/docs/utilities/clamped-value.md @@ -10,26 +10,27 @@ title: "clamped_value" Provides a value constrained between a minimum and maximum value. Values are clamped using `etl::clamp`. The type supports compile-time bounds to minimise storage and runtime bounds when the range must be changed. Integral types are -supported in all language modes. Floating-point types are supported in C++20 -when `ETL_HAS_FLOATING_POINT_CLAMPED_VALUE` is `1`. By default, the macro is -enabled when `__cpp_nontype_template_args` is at least `201911L`. +supported in all language modes. Floating-point types with runtime bounds are +supported in C++11 and later when `ETL_HAS_FLOATING_POINT_CLAMPED_VALUE` is +`1`. Floating-point compile-time bounds require C++20 support for +floating-point non-type template arguments. ## Availability `ETL_HAS_FLOATING_POINT_CLAMPED_VALUE` is always defined by this header as -either `0` or `1`. It defaults to `1` only when C++20 is enabled and the -compiler supports floating-point non-type template arguments. A platform -profile or build may define the macro before including the header to override -automatic detection. +either `0` or `1`. It defaults to `1` in C++11 and later. The related +`ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE` macro defaults to `1` only +when C++20 is enabled and the compiler supports floating-point non-type +template arguments. A platform profile or build may define either macro before +including the header to override automatic detection. -When the macro is `0`, `T` must be an integral type. When it is `1`, `float`, -`double`, and `long double` may also be used. +When `ETL_HAS_FLOATING_POINT_CLAMPED_VALUE` is `0`, `T` must be an integral +type. When it is `1`, `float`, `double`, and `long double` may also be used with +runtime bounds. ```cpp -template +// C++11 and later +template class clamped_value; ``` @@ -39,8 +40,11 @@ etl::clamped_value value_ct; // Fixed range [2, 7]. etl::clamped_value value_rt(2, 7, 5); // Runtime range and initial value. #if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE +etl::clamped_value float_rt(-1.5, 2.5, 0.5); +#endif + +#if ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE etl::clamped_value float_ct(0.5f); -etl::clamped_value float_rt(-1.5, 2.5, 0.5); #endif ``` @@ -67,13 +71,10 @@ clamped_value value(min, max, initial); Creates a runtime clamped value. The two-argument form initializes the value to `min`; the three-argument form clamps `initial` to the range. -`min` must not be greater than `max`. As with `cyclic_value`, zero-initialized -`Min` and `Max` select the runtime-bound specialization; use a non-zero -compile-time bound when a fixed compile-time range is needed. NaN bounds and -initial values are rejected. Infinite bounds and values are permitted. - -Because zero-initialized `Min` and `Max` select runtime bounds, a fixed -compile-time range `[0, 0]` cannot be represented by this dispatch scheme. +`min` must not be greater than `max`. Omitting the template bounds selects the +runtime-bound specialization. Supplying `Min` and `Max` selects fixed +compile-time bounds. NaN bounds and initial values are rejected. Infinite +bounds and values are permitted. ## Modifiers diff --git a/include/etl/clamped_value.h b/include/etl/clamped_value.h index d587bde4..d7f3dcf9 100644 --- a/include/etl/clamped_value.h +++ b/include/etl/clamped_value.h @@ -43,21 +43,32 @@ SOFTWARE. ///\def ETL_HAS_FLOATING_POINT_CLAMPED_VALUE /// Set to `1` when clamped_value supports floating-point types. -/// Floating-point support requires C++20 non-type template arguments. +/// Runtime floating-point support requires C++11 variadic templates. /// The macro may be defined by the build or platform profile to override /// automatic detection. #if !defined(ETL_HAS_FLOATING_POINT_CLAMPED_VALUE) - #if ETL_USING_CPP20 && defined(__cpp_nontype_template_args) && (__cpp_nontype_template_args >= 201911L) + #if ETL_USING_CPP11 #define ETL_HAS_FLOATING_POINT_CLAMPED_VALUE 1 #else #define ETL_HAS_FLOATING_POINT_CLAMPED_VALUE 0 #endif #endif +///\def ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE +/// Set to `1` when clamped_value supports floating-point compile-time bounds. +#if !defined(ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE) + #if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE && ETL_USING_CPP20 && defined(__cpp_nontype_template_args) && (__cpp_nontype_template_args >= 201911L) + #define ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE 1 + #else + #define ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE 0 + #endif +#endif + ///\defgroup clamped_value clamped_value /// Provides a value that is clamped between two limits. /// Integral types are supported in all language modes. Floating-point types -/// are supported when ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`. +/// with runtime bounds are supported when +/// ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`. /// \ingroup utilities namespace etl @@ -113,18 +124,18 @@ namespace etl return value; } - const unsigned_type current = etl::to_unsigned(value); //static_cast(value); + const unsigned_type current = etl::to_unsigned(value); // static_cast(value); const unsigned_type step = etl::absolute_unsigned(n); if ((n > 0) && (value < max_value)) { - const unsigned_type distance = etl::to_unsigned(max_value) - current; //static_cast(max_value) - current; + const unsigned_type distance = etl::to_unsigned(max_value) - current; // static_cast(max_value) - current; value = (step >= distance) ? max_value : static_cast(current + step); } else if ((n < 0) && (value > min_value)) { - const unsigned_type distance = current - etl::to_unsigned(min_value); //static_cast(min_value); + const unsigned_type distance = current - etl::to_unsigned(min_value); // static_cast(min_value); value = (step >= distance) ? min_value : static_cast(current - step); } @@ -236,22 +247,32 @@ namespace etl } // namespace private_clamped_value /// \endcond -#include "private/diagnostic_float_equal_push.h" +#if ETL_USING_CPP11 + template + class clamped_value; +#else // ETL_NOT_USING_CPP11 + #include "private/diagnostic_float_equal_push.h" template class clamped_value; -#include "private/diagnostic_pop.h" + #include "private/diagnostic_pop.h" +#endif //*************************************************************************** /// Provides a value that is clamped between two compile-time limits. /// Supports incrementing, decrementing and arbitrary advance. ///@tparam T An integral type, or a floating-point type when - /// ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`. + /// ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE is `1`. ///@tparam Min The minimum value of the range. ///@tparam Max The maximum value of the range. ///\ingroup clamped_value //*************************************************************************** +#if ETL_USING_CPP11 + template + class clamped_value +#else // ETL_NOT_USING_CPP11 template class clamped_value +#endif { public: @@ -583,8 +604,13 @@ namespace etl ///@tparam Max Ignored for this specialisation. ///\ingroup clamped_value //*************************************************************************** +#if ETL_USING_CPP11 + template + class clamped_value +#else // ETL_NOT_USING_CPP11 template class clamped_value +#endif { public: diff --git a/test/syntax_check/clamped_value.h.t.cpp b/test/syntax_check/clamped_value.h.t.cpp index a09da520..7651ff8c 100644 --- a/test/syntax_check/clamped_value.h.t.cpp +++ b/test/syntax_check/clamped_value.h.t.cpp @@ -31,6 +31,9 @@ SOFTWARE. #include #if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE -etl::clamped_value clamped_float_compile_time; -etl::clamped_value clamped_double_run_time; +etl::clamped_value clamped_double_run_time; +#endif + +#if ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE +etl::clamped_value clamped_float_compile_time; #endif diff --git a/test/test_clamped_value.cpp b/test/test_clamped_value.cpp index 40ecc270..cdf81420 100644 --- a/test/test_clamped_value.cpp +++ b/test/test_clamped_value.cpp @@ -61,6 +61,17 @@ namespace CHECK_EQUAL(7, value.max()); } +#if ETL_USING_CPP11 + //************************************************************************* + TEST(test_compile_time_zero_range) + { + etl::clamped_value value(1); + CHECK_EQUAL(0, value.get()); + CHECK_EQUAL(0, value.min()); + CHECK_EQUAL(0, value.max()); + } +#endif + //************************************************************************* TEST(test_run_time_initialisation) { @@ -364,7 +375,7 @@ namespace CHECK_THROW(value.set(7, 2), etl::exception); } -#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE +#if ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE //************************************************************************* TEST(test_compile_time_floating_clamps_and_advances) { @@ -381,7 +392,9 @@ namespace value.advance(10.0f); CHECK_CLOSE(2.5f, value.get(), 0.0001f); } +#endif +#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE //************************************************************************* TEST(test_runtime_floating_clamps_and_advances) { @@ -396,45 +409,52 @@ namespace value.advance(-10.0); CHECK_CLOSE(-1.5, value.get(), 0.0001); } +#endif +#if ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE //************************************************************************* - TEST(test_floating_increment_and_decrement_saturate_at_fractional_bounds) + TEST(test_compile_time_floating_increment_saturates_at_fractional_bound) { etl::clamped_value compile_time(0.25f); - etl::clamped_value run_time(-0.5f, 0.5f, -0.25f); ++compile_time; - --run_time; CHECK_CLOSE(0.5f, compile_time.get(), 0.0001f); - CHECK_CLOSE(-0.5f, run_time.get(), 0.0001f); + } +#endif + +#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE + //************************************************************************* + TEST(test_runtime_floating_decrement_saturates_at_fractional_bound) + { + etl::clamped_value value(-0.5f, 0.5f, -0.25f); + + --value; + + CHECK_CLOSE(-0.5f, value.get(), 0.0001f); } //************************************************************************* - TEST(test_floating_comparison_and_swap) + TEST(test_runtime_floating_comparison_and_swap) { - etl::clamped_value compile_time1(-0.5); - etl::clamped_value compile_time2(1.5); - etl::clamped_value run_time1(-3.0, 3.0, -1.0); - etl::clamped_value run_time2(4.0, 8.0, 6.0); + etl::clamped_value value1(-3.0, 3.0, -1.0); + etl::clamped_value value2(4.0, 8.0, 6.0); - CHECK(compile_time1 < compile_time2); - CHECK(compile_time1 < 1.0); - swap(compile_time1, compile_time2); - swap(run_time1, run_time2); + CHECK(value1 < value2); + CHECK(value1 < 1.0); + swap(value1, value2); - CHECK_CLOSE(1.5, compile_time1.get(), 0.0001); - CHECK_CLOSE(6.0, run_time1.get(), 0.0001); - CHECK_CLOSE(4.0, run_time1.min(), 0.0001); - CHECK_CLOSE(-1.0, run_time2.get(), 0.0001); + CHECK_CLOSE(6.0, value1.get(), 0.0001); + CHECK_CLOSE(4.0, value1.min(), 0.0001); + CHECK_CLOSE(-1.0, value2.get(), 0.0001); } //************************************************************************* - TEST(test_floating_infinity_saturates) + TEST(test_runtime_floating_infinity_saturates) { - const double infinity = etl::numeric_limits::infinity(); - etl::clamped_value value(0.0); - etl::clamped_value infinite_range(-infinity, infinity, 0.0); + const double infinity = etl::numeric_limits::infinity(); + etl::clamped_value value(-2.0, 2.0, 0.0); + etl::clamped_value infinite_range(-infinity, infinity, 0.0); value += infinity; CHECK_CLOSE(2.0, value.get(), 0.0001); @@ -501,7 +521,7 @@ namespace CHECK(true); } - #if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE && (ETL_USING_MICROSOFT_COMPILER && (_MSC_VER >= 1950)) + #if ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE && (ETL_USING_MICROSOFT_COMPILER && (_MSC_VER >= 1950)) //************************************************************************* TEST(test_floating_clamped_value_constexpr) { From aef7e493a080535fbd777c5fc3a5dcafaab28fd8 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Tue, 8 Sep 2026 19:50:09 +0100 Subject: [PATCH 77/77] Updated release notes --- docs/releases/_index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/releases/_index.md b/docs/releases/_index.md index e7767e21..ad4bd76c 100644 --- a/docs/releases/_index.md +++ b/docs/releases/_index.md @@ -76,6 +76,7 @@ Click on the links to see the original Github page. [#1588](https://github.com/ETLCPP/etl/pull/1588) Optimize `format.h` [#1589](https://github.com/ETLCPP/etl/pull/1589) Fix MSVC C4244 warning in format specifiers [#1591](https://github.com/ETLCPP/etl/pull/1591) Fix false positive warning from GCC 16 +[#1595](https://github.com/ETLCPP/etl/pull/1595) Support runtime floating-point clamped_value before C++20 ## 20.48.1