From 232c948674cff1b4b72c2d6b3acfa7342d096dd1 Mon Sep 17 00:00:00 2001 From: Hossein <37206671+HosseinSagha@users.noreply.github.com> Date: Sat, 12 Sep 2026 19:22:39 +1000 Subject: [PATCH 01/16] Exclude operator <=> from chrono when STL not available (#1602) * Exclude operator <=> from chrono when STL not available * apply the fix to tests * chore: rerun pipeline --- include/etl/private/chrono/day.h | 2 +- include/etl/private/chrono/duration.h | 2 +- include/etl/private/chrono/month.h | 2 +- include/etl/private/chrono/month_day.h | 4 ++-- include/etl/private/chrono/time_point.h | 2 +- include/etl/private/chrono/year.h | 2 +- include/etl/private/chrono/year_month.h | 2 +- include/etl/private/chrono/year_month_day.h | 4 ++-- test/test_chrono_day.cpp | 2 +- test/test_chrono_duration.cpp | 2 +- test/test_chrono_month.cpp | 2 +- test/test_chrono_month_day.cpp | 2 +- test/test_chrono_time_point.cpp | 2 +- test/test_chrono_year.cpp | 2 +- test/test_chrono_year_month.cpp | 2 +- test/test_chrono_year_month_day.cpp | 2 +- 16 files changed, 18 insertions(+), 18 deletions(-) diff --git a/include/etl/private/chrono/day.h b/include/etl/private/chrono/day.h index a929c601..1f903067 100644 --- a/include/etl/private/chrono/day.h +++ b/include/etl/private/chrono/day.h @@ -211,7 +211,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 +#if ETL_USING_CPP20 && ETL_USING_STL [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::day& d1, const etl::chrono::day& d2) ETL_NOEXCEPT { diff --git a/include/etl/private/chrono/duration.h b/include/etl/private/chrono/duration.h index bc8d6ba5..cf48e170 100644 --- a/include/etl/private/chrono/duration.h +++ b/include/etl/private/chrono/duration.h @@ -482,7 +482,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 +#if ETL_USING_CPP20 && ETL_USING_STL template [[nodiscard]] constexpr auto operator<=>(const etl::chrono::duration& lhs, const etl::chrono::duration& rhs) ETL_NOEXCEPT diff --git a/include/etl/private/chrono/month.h b/include/etl/private/chrono/month.h index 662bb632..40c2e594 100644 --- a/include/etl/private/chrono/month.h +++ b/include/etl/private/chrono/month.h @@ -224,7 +224,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 +#if ETL_USING_CPP20 && ETL_USING_STL [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::month& d1, const etl::chrono::month& d2) ETL_NOEXCEPT { diff --git a/include/etl/private/chrono/month_day.h b/include/etl/private/chrono/month_day.h index dcefbff6..f4f77628 100644 --- a/include/etl/private/chrono/month_day.h +++ b/include/etl/private/chrono/month_day.h @@ -178,7 +178,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 +#if ETL_USING_CPP20 && ETL_USING_STL [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::month_day& lhs, const etl::chrono::month_day& rhs) ETL_NOEXCEPT { @@ -298,7 +298,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 +#if ETL_USING_CPP20 && ETL_USING_STL [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::month_day_last& mdl1, const etl::chrono::month_day_last& mdl2) ETL_NOEXCEPT { diff --git a/include/etl/private/chrono/time_point.h b/include/etl/private/chrono/time_point.h index f90f1c61..a0e79bb1 100644 --- a/include/etl/private/chrono/time_point.h +++ b/include/etl/private/chrono/time_point.h @@ -353,7 +353,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 +#if ETL_USING_CPP20 && ETL_USING_STL template [[nodiscard]] constexpr auto operator<=>(const etl::chrono::time_point& lhs, const etl::chrono::time_point& rhs) diff --git a/include/etl/private/chrono/year.h b/include/etl/private/chrono/year.h index 49ad3530..e6cb3416 100644 --- a/include/etl/private/chrono/year.h +++ b/include/etl/private/chrono/year.h @@ -240,7 +240,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 +#if ETL_USING_CPP20 && ETL_USING_STL [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::year& y1, const etl::chrono::year& y2) ETL_NOEXCEPT { diff --git a/include/etl/private/chrono/year_month.h b/include/etl/private/chrono/year_month.h index 16b3a431..44bb9fd1 100644 --- a/include/etl/private/chrono/year_month.h +++ b/include/etl/private/chrono/year_month.h @@ -234,7 +234,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 +#if ETL_USING_CPP20 && ETL_USING_STL [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::year_month& lhs, const etl::chrono::year_month& rhs) ETL_NOEXCEPT { diff --git a/include/etl/private/chrono/year_month_day.h b/include/etl/private/chrono/year_month_day.h index a0f42474..91088c3b 100644 --- a/include/etl/private/chrono/year_month_day.h +++ b/include/etl/private/chrono/year_month_day.h @@ -426,7 +426,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 +#if ETL_USING_CPP20 && ETL_USING_STL [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::year_month_day& lhs, const etl::chrono::year_month_day& rhs) ETL_NOEXCEPT { @@ -722,7 +722,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 +#if ETL_USING_CPP20 && ETL_USING_STL [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::year_month_day_last& lhs, const etl::chrono::year_month_day_last& rhs) ETL_NOEXCEPT { diff --git a/test/test_chrono_day.cpp b/test/test_chrono_day.cpp index 706a3de9..47ab3fc8 100644 --- a/test/test_chrono_day.cpp +++ b/test/test_chrono_day.cpp @@ -278,7 +278,7 @@ namespace CHECK_TRUE(day10 >= day10); CHECK_TRUE(day20 >= day10); -#if ETL_USING_CPP20 +#if ETL_USING_CPP20 && ETL_USING_STL CHECK_TRUE((day10 <=> day10) == 0); CHECK_TRUE((day10 <=> day20) < 0); CHECK_TRUE((day20 <=> day10) > 0); diff --git a/test/test_chrono_duration.cpp b/test/test_chrono_duration.cpp index 81ef8a0b..4122cdd5 100644 --- a/test/test_chrono_duration.cpp +++ b/test/test_chrono_duration.cpp @@ -1045,7 +1045,7 @@ namespace CHECK_EQUAL(1, s1.count()); // 10 seconds % (-3) seconds = 1 second } -#if ETL_USING_CPP20 +#if ETL_USING_CPP20 && ETL_USING_STL //************************************************************************* TEST(test_duration_spaceship_operator) { diff --git a/test/test_chrono_month.cpp b/test/test_chrono_month.cpp index 46522d4f..a4e61491 100644 --- a/test/test_chrono_month.cpp +++ b/test/test_chrono_month.cpp @@ -303,7 +303,7 @@ namespace CHECK_TRUE(month1 >= month1); CHECK_TRUE(month2 >= month1); -#if ETL_USING_CPP20 +#if ETL_USING_CPP20 && ETL_USING_STL CHECK_TRUE((month1 <=> month1) == 0); CHECK_TRUE((month1 <=> month2) < 0); CHECK_TRUE((month2 <=> month1) > 0); diff --git a/test/test_chrono_month_day.cpp b/test/test_chrono_month_day.cpp index fd6ea597..7e7bf9b5 100644 --- a/test/test_chrono_month_day.cpp +++ b/test/test_chrono_month_day.cpp @@ -89,7 +89,7 @@ namespace CHECK_FALSE(md.ok()); // Invalid month_day } -#if ETL_USING_CPP20 +#if ETL_USING_CPP20 && ETL_USING_STL //************************************************************************* TEST(test_month_day_spaceship_operator) { diff --git a/test/test_chrono_time_point.cpp b/test/test_chrono_time_point.cpp index 0bbea37f..a4866f0c 100644 --- a/test/test_chrono_time_point.cpp +++ b/test/test_chrono_time_point.cpp @@ -365,7 +365,7 @@ namespace CHECK_FALSE(tp10d >= tp20h); CHECK_FALSE(tp10h >= tp20d); -#if ETL_USING_CPP20 +#if ETL_USING_CPP20 && ETL_USING_STL CHECK_TRUE((tp10h <=> tp10h) == 0); CHECK_TRUE((tp10h <=> tp20h) < 0); CHECK_TRUE((tp20h <=> tp10h) > 0); diff --git a/test/test_chrono_year.cpp b/test/test_chrono_year.cpp index 8e3c9f1c..2b8dad29 100644 --- a/test/test_chrono_year.cpp +++ b/test/test_chrono_year.cpp @@ -284,7 +284,7 @@ namespace CHECK_FALSE(year10 == year20); CHECK_TRUE(year10 != year20); -#if ETL_USING_CPP20 +#if ETL_USING_CPP20 && ETL_USING_STL CHECK_TRUE((year10 <=> year10) == 0); CHECK_TRUE((year10 <=> year20) < 0); CHECK_TRUE((year20 <=> year10) > 0); diff --git a/test/test_chrono_year_month.cpp b/test/test_chrono_year_month.cpp index e23bdf6d..73065121 100644 --- a/test/test_chrono_year_month.cpp +++ b/test/test_chrono_year_month.cpp @@ -89,7 +89,7 @@ namespace CHECK_FALSE(ym.ok()); // Invalid year_month } -#if ETL_USING_CPP20 +#if ETL_USING_CPP20 && ETL_USING_STL //************************************************************************* TEST(test_year_month_spaceship_operator) { diff --git a/test/test_chrono_year_month_day.cpp b/test/test_chrono_year_month_day.cpp index 8f57f822..e44ee967 100644 --- a/test/test_chrono_year_month_day.cpp +++ b/test/test_chrono_year_month_day.cpp @@ -98,7 +98,7 @@ namespace CHECK_FALSE(ymd.ok()); // Invalid year_month_day } -#if ETL_USING_CPP20 +#if ETL_USING_CPP20 && ETL_USING_STL //************************************************************************* TEST(test_year_month_day_spaceship_operator) { From eb793d2b8231cb554d651b11efd7fbf9a50260f7 Mon Sep 17 00:00:00 2001 From: Marco Nilsson Date: Sun, 13 Sep 2026 11:33:42 +0200 Subject: [PATCH 02/16] Help the compiler optimize unaligned_type. (#1598) * Help the compiler optimize unaligned_type. This fixes a regression in 266b0f99 when the type was made constexpr. When switching to a loop to construct the value the compiler was unable to detect what was going on. We now unroll the loop at compile time into what's essentially a hand rolled shift and OR operation which the compiler detects and replaces with a byte swap. Tested on gcc (x86+arm) and clang (x86). Introduce a static value_from which can be used to convert a value from an existing buffer. There were previously three options for this, all bad or awkward in different ways: - val = etl::be_uint32_t(address) Copies to temporary storage. - val = *reinterpret_cast(address); Unreadable. - etl::be_uint32_t::copy_store_to_value(address, output); Requires an output parameter. The new addition is: - val = etl::be_uint32_t::value_from(address); Additionally, update the documentation with value_from and the missing, existing constructors. * Please clang-format. --------- Co-authored-by: John Wellbelove --- docs/types/unaligned-type.md | 66 +++++++++++++++-- include/etl/unaligned_type.h | 135 +++++++++++++++++++++++------------ test/test_unaligned_type.cpp | 105 +++++++++++++++++++++++++++ 3 files changed, 256 insertions(+), 50 deletions(-) diff --git a/docs/types/unaligned-type.md b/docs/types/unaligned-type.md index 0f6689a9..28c9f85f 100644 --- a/docs/types/unaligned-type.md +++ b/docs/types/unaligned-type.md @@ -166,6 +166,50 @@ Constructs with the supplied value. --- +```cpp +unaligned_type(const void* address) +``` +**Description** +Constructs from the `Size` bytes at the address. +From: `20.39.5` + +--- + +```cpp +unaligned_type(const void* address, size_t buffer_size) +``` +**Description** +Constructs from the `Size` bytes at the address. +`buffer_size` must be greater than or equal to `Size`. This is a precondition, +checked by `ETL_ASSERT`. If checks are disabled then a smaller buffer size +results in a read beyond the end of the buffer. +From: `20.39.5` + +--- + +```cpp +ETL_CONSTEXPR14 unaligned_type(const unsigned char* address) +``` +**Description** +Constructs from the `Size` bytes at the address. +Unlike the `const void*` overload, this requires no `reinterpret_cast` and so +may be used in a constexpr context. +From: `20.48.1` + +--- + +```cpp +ETL_CONSTEXPR14 unaligned_type(const unsigned char* address, size_t buffer_size) +``` +**Description** +Constructs from the `Size` bytes at the address. +`buffer_size` must be greater than or equal to `Size`. This is a precondition, +checked by `ETL_ASSERT`. If checks are disabled then a smaller buffer size +results in a read beyond the end of the buffer. +From: `20.48.1` + +--- + ```cpp template unaligned_type(const unaligned_type& other) @@ -176,10 +220,11 @@ The endianness is converted, if necessary. ## Member types ```cpp -using pointer = char*; -using const_pointer = const char*; -using iterator = char*; -using const_iterator = const char*; +using storage_type = unsigned char; +using pointer = unsigned char*; +using const_pointer = const unsigned char*; +using iterator = unsigned char*; +using const_iterator = const unsigned char*; using reverse_iterator = etl::reverse_iterator; using const_reverse_iterator = etl::reverse_iterator; ``` @@ -306,7 +351,7 @@ Const reverse iterator to the end of the storage. --- ```cpp -char& operator[](int i) +storage_type& operator[](int i) ``` **Description** Index operator. @@ -314,7 +359,7 @@ Index operator. --- ```cpp -ETL_CONSTEXPR14 const char& operator[](int i) const +ETL_CONSTEXPR14 const storage_type& operator[](int i) const ``` **Description** Const index operator. @@ -345,6 +390,15 @@ Gets the value. --- +```cpp +static ETL_CONSTEXPR14 T value_from(const_pointer address) +``` +**Description** +Gets the value of the `Size` bytes at the address. +Unlike construction from an address, the bytes are not copied to storage first. + +--- + ```cpp template ETL_CONSTEXPR14 unaligned_type& operator =(const unaligned_type& other) diff --git a/include/etl/unaligned_type.h b/include/etl/unaligned_type.h index cba1874f..6d8ef74c 100644 --- a/include/etl/unaligned_type.h +++ b/include/etl/unaligned_type.h @@ -335,6 +335,48 @@ namespace etl return ((Endian == ETL_ENDIAN_LITTLE) ? index : (Size - 1U - index)) * 8U; } + //************************************************************************* + /// Gathers the bytes of a store into a value, and scatters a value back out. + /// Unrolled at source level, so byte swap recognition sees no loop. + //************************************************************************* + template + struct gather_bytes + { + static ETL_CONSTEXPR14 TValue from(const unsigned char* store) + { + return static_cast(gather_bytes::from(store) + | (static_cast(store[Index]) << byte_shift(Index))); + } + }; + + template + struct gather_bytes + { + static ETL_CONSTEXPR14 TValue from(const unsigned char* store) + { + return static_cast(static_cast(store[0]) << byte_shift(0)); + } + }; + + template + struct scatter_bytes + { + static ETL_CONSTEXPR14 void into(TValue value, unsigned char* store) + { + scatter_bytes::into(value, store); + store[Index] = static_cast(value >> byte_shift(Index)); + } + }; + + template + struct scatter_bytes + { + static ETL_CONSTEXPR14 void into(TValue value, unsigned char* store) + { + store[0] = static_cast(value >> byte_shift(0)); + } + }; + #if ETL_USING_BUILTIN_BIT_CAST //************************************************************************* /// Unsigned integer type of a given byte size, used as a constexpr-capable @@ -402,28 +444,23 @@ namespace etl // latter is not defined for 'bool'. typedef typename etl::unsigned_type::type unsigned_t; - unsigned_t uvalue = static_cast(value); + private_unaligned_type::scatter_bytes::into(static_cast(value), store); + } - for (size_t i = 0UL; i < Size_; ++i) - { - store[i] = static_cast(uvalue >> private_unaligned_type::byte_shift(i)); - } + //******************************* + template + static ETL_CONSTEXPR14 T value_from(const_pointer store) + { + typedef typename etl::unsigned_type::type unsigned_t; + + return static_cast(private_unaligned_type::gather_bytes::from(store)); } //******************************* template static ETL_CONSTEXPR14 void copy_store_to_value(const_pointer store, T & value) { - typedef typename etl::unsigned_type::type unsigned_t; - - unsigned_t uvalue = unsigned_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 = static_cast(uvalue); + value = value_from(store); } //******************************* @@ -462,12 +499,9 @@ namespace etl { typedef typename private_unaligned_type::uint_of_size::type uint_t; - uint_t uvalue = etl::bit_cast(value); + const 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)); - } + private_unaligned_type::scatter_bytes::into(uvalue, store); } #endif @@ -510,12 +544,7 @@ namespace etl { 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))); - } + const uint_t uvalue = private_unaligned_type::gather_bytes::from(store); value = etl::bit_cast(uvalue); } @@ -553,6 +582,22 @@ namespace etl do_copy_store_to_value(store, value, use_bit_cast_t()); } + //******************************* + template + static +#if ETL_USING_BUILTIN_BIT_CAST + ETL_CONSTEXPR14 +#endif + T + value_from(const_pointer store) + { + T value = T(); + + copy_store_to_value(store, value); + + return value; + } + //******************************* // This is pure byte manipulation (copy + optional reversal), with no // floating point arithmetic involved, so it is always constexpr-capable @@ -714,11 +759,7 @@ namespace etl //************************************************************************* ETL_CONSTEXPR14 operator T() const { - T value = T(); - - unaligned_copy::copy_store_to_value(this->storage, value); - - return value; + return value_from(this->storage); } //************************************************************************* @@ -726,11 +767,16 @@ namespace etl //************************************************************************* ETL_CONSTEXPR14 T value() const { - T value = T(); + return value_from(this->storage); + } - unaligned_copy::copy_store_to_value(this->storage, value); - - return value; + //************************************************************************* + /// Get the value directly from a byte buffer. + /// Unlike construction from an address, the bytes are not copied to storage. + //************************************************************************* + static ETL_CONSTEXPR14 T value_from(const_pointer address) + { + return unaligned_copy::template value_from(address); } }; ETL_END_PACKED @@ -898,11 +944,7 @@ namespace etl //************************************************************************* operator T() const { - T value = T(); - - unaligned_copy::copy_store_to_value(this->storage, value); - - return value; + return value_from(this->storage); } //************************************************************************* @@ -910,11 +952,16 @@ namespace etl //************************************************************************* T value() const { - T value = T(); + return value_from(this->storage); + } - unaligned_copy::copy_store_to_value(this->storage, value); - - return value; + //************************************************************************* + /// Get the value directly from a byte buffer. + /// Unlike construction from an address, the bytes are not copied to storage. + //************************************************************************* + static T value_from(const_pointer address) + { + return unaligned_copy::template value_from(address); } //************************************************************************* diff --git a/test/test_unaligned_type.cpp b/test/test_unaligned_type.cpp index ad1e50c9..578d0cfb 100644 --- a/test/test_unaligned_type.cpp +++ b/test/test_unaligned_type.cpp @@ -282,6 +282,92 @@ namespace CHECK_THROW(etl::be_uint8_t be_byte_empty(buffer.data(), 0U), etl::unaligned_type_buffer_size); } + //************************************************************************* + // 'value_from' reads a value straight out of a byte buffer, without first + // copying the bytes into an unaligned_type's own storage. + //************************************************************************* + TEST(test_value_from_integral) + { + const std::array buffer = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0}; + + CHECK_EQUAL(uint16_t(0x1234), etl::be_uint16_t::value_from(buffer.data())); + CHECK_EQUAL(uint16_t(0x3412), etl::le_uint16_t::value_from(buffer.data())); + + CHECK_EQUAL(uint32_t(0x12345678), etl::be_uint32_t::value_from(buffer.data())); + CHECK_EQUAL(uint32_t(0x78563412), etl::le_uint32_t::value_from(buffer.data())); + + CHECK_EQUAL(uint64_t(0x123456789ABCDEF0), etl::be_uint64_t::value_from(buffer.data())); + CHECK_EQUAL(uint64_t(0xF0DEBC9A78563412), etl::le_uint64_t::value_from(buffer.data())); + } + + //************************************************************************* + /// The address need not be aligned for the value's type. + //************************************************************************* + TEST(test_value_from_unaligned_address) + { + const std::array buffer = {0xFF, 0x12, 0x34, 0x56, 0x78, 0xFF}; + + CHECK_EQUAL(uint32_t(0x12345678), etl::be_uint32_t::value_from(buffer.data() + 1)); + CHECK_EQUAL(uint32_t(0x78563412), etl::le_uint32_t::value_from(buffer.data() + 1)); + } + + //************************************************************************* + /// 'value_from' must agree with constructing from the same address and + /// then reading the value back out. + //************************************************************************* + TEST(test_value_from_matches_construction) + { + const std::array buffer = {0xEE, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}; + + CHECK_EQUAL(etl::be_uint16_t(buffer.data()).value(), etl::be_uint16_t::value_from(buffer.data())); + CHECK_EQUAL(etl::le_uint16_t(buffer.data()).value(), etl::le_uint16_t::value_from(buffer.data())); + CHECK_EQUAL(etl::be_uint32_t(buffer.data()).value(), etl::be_uint32_t::value_from(buffer.data())); + CHECK_EQUAL(etl::le_uint32_t(buffer.data()).value(), etl::le_uint32_t::value_from(buffer.data())); + CHECK_EQUAL(etl::be_uint64_t(buffer.data()).value(), etl::be_uint64_t::value_from(buffer.data())); + CHECK_EQUAL(etl::le_uint64_t(buffer.data()).value(), etl::le_uint64_t::value_from(buffer.data())); + } + + //************************************************************************* + /// Boundary values must survive the round trip through the store. + //************************************************************************* + TEST(test_value_from_boundary_values) + { + const std::array zeroes = {0x00, 0x00, 0x00, 0x00}; + const std::array ones = {0xFF, 0xFF, 0xFF, 0xFF}; + + CHECK_EQUAL(uint32_t(0), etl::be_uint32_t::value_from(zeroes.data())); + CHECK_EQUAL(uint32_t(0), etl::le_uint32_t::value_from(zeroes.data())); + + CHECK_EQUAL(etl::integral_limits::max, etl::be_uint32_t::value_from(ones.data())); + CHECK_EQUAL(etl::integral_limits::max, etl::le_uint32_t::value_from(ones.data())); + } + + //************************************************************************* + /// Signed types are reconstructed through their unsigned representation. + //************************************************************************* + TEST(test_value_from_negative_values) + { + const std::array buffer = {0xFF, 0xFF, 0xFF, 0xFE}; + + CHECK_EQUAL(int32_t(-2), etl::be_int32_t::value_from(buffer.data())); + CHECK_EQUAL(int16_t(-1), etl::be_int16_t::value_from(buffer.data())); + } + + //************************************************************************* + /// The floating point specialisation provides 'value_from' too. + //************************************************************************* + TEST(test_value_from_float) + { + const float f = 3.1415927f; + const double d = 2.718281828459045; + + etl::be_float_t be_f(f); + etl::le_double_t le_d(d); + + CHECK_EQUAL(f, etl::be_float_t::value_from(be_f.data())); + CHECK_EQUAL(d, etl::le_double_t::value_from(le_d.data())); + } + //************************************************************************* // The following tests demonstrate the 'decode' direction: given a raw byte // buffer (e.g. as received from a file, network socket or memory-mapped @@ -625,6 +711,25 @@ namespace CHECK_EQUAL(0x12345678U, be_v.value()); } + //************************************************************************* + /// 'value_from' is usable at compile time for the same reason: it reads + /// the buffer through the 'const unsigned char*' path. + //************************************************************************* + TEST(test_constexpr_value_from_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 uint32_t le_v = etl::le_uint32_t::value_from(le_buffer); + constexpr uint32_t be_v = etl::be_uint32_t::value_from(be_buffer); + + static_assert(le_v == 0x12345678U, "le_uint32_t constexpr value_from"); + static_assert(be_v == 0x12345678U, "be_uint32_t constexpr value_from"); + + CHECK_EQUAL(0x12345678U, le_v); + CHECK_EQUAL(0x12345678U, be_v); + } + #if ETL_USING_BUILTIN_BIT_CAST //************************************************************************* TEST(test_constexpr_decode_buffer_float) From 2e2c5d01999f2a9807f756fdf84b4a39fc492cc9 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Thu, 17 Sep 2026 12:38:18 +0200 Subject: [PATCH 03/16] Implement spaceship operator <=> for No-STL case (#1604) This was disabled explicitly in recent commit 232c948674cff1b4b72c2d6b3acfa7342d096dd1 to prevent using the header from STL. Now, re-enabling by implementing an ETL version of it in compare.h --- docs/utilities/compare.md | 103 ++++++- include/etl/chrono.h | 1 + include/etl/compare.h | 295 +++++++++++++++++++- include/etl/private/chrono/day.h | 4 +- include/etl/private/chrono/duration.h | 32 ++- include/etl/private/chrono/month.h | 4 +- include/etl/private/chrono/month_day.h | 6 +- include/etl/private/chrono/time_point.h | 2 +- include/etl/private/chrono/year.h | 4 +- include/etl/private/chrono/year_month.h | 2 +- include/etl/private/chrono/year_month_day.h | 4 +- test/test_chrono_day.cpp | 2 +- test/test_chrono_duration.cpp | 33 ++- test/test_chrono_month.cpp | 2 +- test/test_chrono_month_day.cpp | 8 +- test/test_chrono_month_day_last.cpp | 14 + test/test_chrono_time_point.cpp | 4 +- test/test_chrono_year.cpp | 2 +- test/test_chrono_year_month.cpp | 12 +- test/test_chrono_year_month_day.cpp | 16 +- test/test_chrono_year_month_day_last.cpp | 17 ++ test/test_compare.cpp | 148 ++++++++++ 22 files changed, 667 insertions(+), 48 deletions(-) diff --git a/docs/utilities/compare.md b/docs/utilities/compare.md index f6062c73..54d904e5 100644 --- a/docs/utilities/compare.md +++ b/docs/utilities/compare.md @@ -7,23 +7,32 @@ title: "compare" Since: `TBC` {{< /callout >}} -A helper class that defines `<=`, `>`, `>=` in terms of `etl::less` or a user supplied compare type. +A helper for deriving comparison operations from a strict weak ordering. +`etl::compare` exposes boolean predicates and a three-state comparison result. +`etl::three_way_compare` returns `-1`, `0`, or `1` using the same comparison policy. ```cpp template > struct compare ``` +```cpp +template > +ETL_CONSTEXPR14 int three_way_compare(const T& lhs, const T& rhs) +``` + ## Types ```cpp first_argument_type; second_argument_type; -result_type; +cmp_result; ``` +`cmp_result` has the values `Less`, `Equal`, and `Greater`. + ## Members ```cpp -static result_type lt(first_argument_type lhs, second_argument_type rhs) +static bool lt(first_argument_type lhs, second_argument_type rhs) ``` **Return** `true` if `lhs < rhs`, otherwise `false`. @@ -31,7 +40,7 @@ static result_type lt(first_argument_type lhs, second_argument_type rhs) --- ```cpp -static result_type lte(first_argument_type lhs, second_argument_type rhs) +static bool lte(first_argument_type lhs, second_argument_type rhs) ``` **Return** `true` if `lhs <= rhs`, otherwise `false`. @@ -39,7 +48,7 @@ static result_type lte(first_argument_type lhs, second_argument_type rhs) --- ```cpp -static result_type gt(first_argument_type lhs, second_argument_type rhs) +static bool gt(first_argument_type lhs, second_argument_type rhs) ``` **Return** `true` if `lhs > rhs`, otherwise `false`. @@ -47,14 +56,74 @@ static result_type gt(first_argument_type lhs, second_argument_type rhs) --- ```cpp -static result_type gte(first_argument_type lhs, second_argument_type rhs) +static bool gte(first_argument_type lhs, second_argument_type rhs) ``` **Return** `true` if `lhs >= rhs`, otherwise `false`. +--- + +```cpp +static bool eq(first_argument_type lhs, second_argument_type rhs) +``` +**Return** +`true` if `lhs == rhs`, otherwise `false`. + +--- + +```cpp +static bool ne(first_argument_type lhs, second_argument_type rhs) +``` +**Return** +`true` if `lhs != rhs`, otherwise `false`. + +--- + +```cpp +static cmp_result cmp(first_argument_type lhs, second_argument_type rhs) +``` +**Return** +`Less`, `Equal`, or `Greater`. + +## Three-way comparison + +`etl::three_way_compare(lhs, rhs)` returns the same ordering as `compare::cmp(lhs, rhs)`, +but as an `int` with the values `-1`, `0`, or `1`. + +## C++20 comparison categories + +When compiling as C++20, `compare.h` makes the comparison categories available in the `etl` +namespace, so ETL and user code can name them regardless of the STL configuration. + +```cpp +class strong_ordering; +class weak_ordering; +class partial_ordering; +``` + +* When the STL is available these are aliases for `std::strong_ordering`, `std::weak_ordering` + and `std::partial_ordering`. +* When the STL is not available, `compare.h` provides its own minimal implementations that + follow the standard semantics: the named values `less`, `equivalent`, `greater` + (plus `equal` for `strong_ordering` and `unordered` for `partial_ordering`), comparison + against the literal `0` only, and the implicit conversions + `strong_ordering` → `weak_ordering` → `partial_ordering`. + +An `unordered` result compares `false` against `0` for all of `<`, `<=`, `==`, `>=` and `>`. + +```cpp +ETL_CONSTEXPR strong_ordering make_strong_ordering(int value) ETL_NOEXCEPT; +ETL_CONSTEXPR weak_ordering make_weak_ordering(int value) ETL_NOEXCEPT; +ETL_CONSTEXPR partial_ordering make_partial_ordering(int value) ETL_NOEXCEPT; +``` + +These helpers convert a three-way comparison result, such as the one returned by +`etl::three_way_compare`, into a comparison category. Negative values map to `less`, positive +values to `greater`, and zero to `equal`/`equivalent`. `unordered` cannot be expressed as an +integer result, so use `etl::partial_ordering::unordered` directly for that case. + ## Example 1 ```cpp -using std::less struct Test { int a; @@ -84,6 +153,11 @@ bool operator >=(const Test& lhs, const Test& rhs) { return Compare::gte(lhs, rhs); } + +int operator <=>(const Test& lhs, const Test& rhs) +{ + return etl::three_way_compare(lhs, rhs); +} ``` ## Example 2 @@ -124,6 +198,11 @@ bool operator >=(const Test& lhs, const Test& rhs) { return Compare::gte(lhs, rhs); } + +int operator <=>(const Test& lhs, const Test& rhs) +{ + return etl::three_way_compare(lhs, rhs); +} ``` ## Example 3 @@ -158,3 +237,13 @@ bool operator >=(const Test& lhs, const Test& rhs) return Test::gte(lhs, rhs); } ``` + +## Example 4 +C++20, with or without the STL. + +```cpp +etl::strong_ordering operator <=>(const Test& lhs, const Test& rhs) +{ + return etl::make_strong_ordering(etl::three_way_compare(lhs, rhs)); +} +``` diff --git a/include/etl/chrono.h b/include/etl/chrono.h index b452ca4e..ab360108 100644 --- a/include/etl/chrono.h +++ b/include/etl/chrono.h @@ -37,6 +37,7 @@ SOFTWARE. #if ETL_USING_CPP11 + #include "compare.h" #include "hash.h" #include "integral_limits.h" #include "type_traits.h" diff --git a/include/etl/compare.h b/include/etl/compare.h index 918465bd..f909291c 100644 --- a/include/etl/compare.h +++ b/include/etl/compare.h @@ -35,6 +35,10 @@ SOFTWARE. #include "functional.h" #include "parameter_type.h" +#if ETL_USING_CPP20 && ETL_USING_STL + #include +#endif + //***************************************************************************** ///\defgroup compare compare /// Comparisons only using less than operator @@ -42,6 +46,295 @@ SOFTWARE. //***************************************************************************** namespace etl { +#if ETL_USING_CPP20 + #if ETL_USING_STL + //*************************************************************************** + /// The STL supplies the C++20 comparison categories, so simply expose them + /// through the etl namespace. This allows ETL code and user code to refer to + /// etl::strong_ordering (etc.) regardless of the STL configuration. + //*************************************************************************** + using std::partial_ordering; + using std::strong_ordering; + using std::weak_ordering; + #else + namespace private_compare + { + //************************************************************************* + /// A type that can only be constructed from the literal 0. + /// Used to constrain the comparison-category operators to comparisons + /// against the literal 0, as required by the standard. + //************************************************************************* + struct only_literal_zero + { + ETL_CONSTEXPR only_literal_zero(only_literal_zero*) ETL_NOEXCEPT {} + }; + + typedef signed char ordering_value_type; + + inline constexpr ordering_value_type ordering_less = -1; + inline constexpr ordering_value_type ordering_equivalent = 0; + inline constexpr ordering_value_type ordering_greater = 1; + inline constexpr ordering_value_type ordering_unordered = 2; + } // namespace private_compare + + //*************************************************************************** + /// Minimal partial-ordering category for C++20 three-way comparisons when the + /// STL is unavailable. + /// Unlike the other categories this one has an 'unordered' state, for which + /// every comparison against 0 is false. + //*************************************************************************** + class partial_ordering + { + public: + + static const partial_ordering less; + static const partial_ordering equivalent; + static const partial_ordering greater; + static const partial_ordering unordered; + + friend ETL_CONSTEXPR bool operator==(partial_ordering lhs, partial_ordering rhs) ETL_NOEXCEPT = default; + + friend ETL_CONSTEXPR bool operator==(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value == private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator<(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value == private_compare::ordering_less; + } + friend ETL_CONSTEXPR bool operator>(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value == private_compare::ordering_greater; + } + friend ETL_CONSTEXPR bool operator<=(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return (v.value == private_compare::ordering_less) || (v.value == private_compare::ordering_equivalent); + } + friend ETL_CONSTEXPR bool operator>=(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return (v.value == private_compare::ordering_greater) || (v.value == private_compare::ordering_equivalent); + } + + friend ETL_CONSTEXPR bool operator<(private_compare::only_literal_zero, partial_ordering v) ETL_NOEXCEPT + { + return v > 0; + } + friend ETL_CONSTEXPR bool operator>(private_compare::only_literal_zero, partial_ordering v) ETL_NOEXCEPT + { + return v < 0; + } + friend ETL_CONSTEXPR bool operator<=(private_compare::only_literal_zero, partial_ordering v) ETL_NOEXCEPT + { + return v >= 0; + } + friend ETL_CONSTEXPR bool operator>=(private_compare::only_literal_zero, partial_ordering v) ETL_NOEXCEPT + { + return v <= 0; + } + + private: + + explicit ETL_CONSTEXPR partial_ordering(private_compare::ordering_value_type value_) ETL_NOEXCEPT + : value(value_) + { + } + + friend class weak_ordering; + friend class strong_ordering; + + private_compare::ordering_value_type value; + }; + + inline constexpr partial_ordering partial_ordering::less(private_compare::ordering_less); + inline constexpr partial_ordering partial_ordering::equivalent(private_compare::ordering_equivalent); + inline constexpr partial_ordering partial_ordering::greater(private_compare::ordering_greater); + inline constexpr partial_ordering partial_ordering::unordered(private_compare::ordering_unordered); + + //*************************************************************************** + /// Minimal weak-ordering category for C++20 three-way comparisons when the + /// STL is unavailable. + //*************************************************************************** + class weak_ordering + { + public: + + static const weak_ordering less; + static const weak_ordering equivalent; + static const weak_ordering greater; + + ETL_CONSTEXPR operator partial_ordering() const ETL_NOEXCEPT + { + return partial_ordering(value); + } + + friend ETL_CONSTEXPR bool operator==(weak_ordering lhs, weak_ordering rhs) ETL_NOEXCEPT = default; + + friend ETL_CONSTEXPR bool operator==(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value == private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator<(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value < private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator>(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value > private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator<=(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value <= private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator>=(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value >= private_compare::ordering_equivalent; + } + + friend ETL_CONSTEXPR bool operator<(private_compare::only_literal_zero, weak_ordering v) ETL_NOEXCEPT + { + return v > 0; + } + friend ETL_CONSTEXPR bool operator>(private_compare::only_literal_zero, weak_ordering v) ETL_NOEXCEPT + { + return v < 0; + } + friend ETL_CONSTEXPR bool operator<=(private_compare::only_literal_zero, weak_ordering v) ETL_NOEXCEPT + { + return v >= 0; + } + friend ETL_CONSTEXPR bool operator>=(private_compare::only_literal_zero, weak_ordering v) ETL_NOEXCEPT + { + return v <= 0; + } + + private: + + explicit ETL_CONSTEXPR weak_ordering(private_compare::ordering_value_type value_) ETL_NOEXCEPT + : value(value_) + { + } + + friend class strong_ordering; + + private_compare::ordering_value_type value; + }; + + inline constexpr weak_ordering weak_ordering::less(private_compare::ordering_less); + inline constexpr weak_ordering weak_ordering::equivalent(private_compare::ordering_equivalent); + inline constexpr weak_ordering weak_ordering::greater(private_compare::ordering_greater); + + //*************************************************************************** + /// Minimal strong-ordering category for C++20 three-way comparisons when the + /// STL is unavailable. + //*************************************************************************** + class strong_ordering + { + public: + + static const strong_ordering less; + static const strong_ordering equal; + static const strong_ordering equivalent; + static const strong_ordering greater; + + ETL_CONSTEXPR operator partial_ordering() const ETL_NOEXCEPT + { + return partial_ordering(value); + } + + ETL_CONSTEXPR operator weak_ordering() const ETL_NOEXCEPT + { + return weak_ordering(value); + } + + friend ETL_CONSTEXPR bool operator==(strong_ordering lhs, strong_ordering rhs) ETL_NOEXCEPT = default; + + friend ETL_CONSTEXPR bool operator==(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value == private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator<(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value < private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator>(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value > private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator<=(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value <= private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator>=(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value >= private_compare::ordering_equivalent; + } + + friend ETL_CONSTEXPR bool operator<(private_compare::only_literal_zero, strong_ordering v) ETL_NOEXCEPT + { + return v > 0; + } + friend ETL_CONSTEXPR bool operator>(private_compare::only_literal_zero, strong_ordering v) ETL_NOEXCEPT + { + return v < 0; + } + friend ETL_CONSTEXPR bool operator<=(private_compare::only_literal_zero, strong_ordering v) ETL_NOEXCEPT + { + return v >= 0; + } + friend ETL_CONSTEXPR bool operator>=(private_compare::only_literal_zero, strong_ordering v) ETL_NOEXCEPT + { + return v <= 0; + } + + private: + + explicit ETL_CONSTEXPR strong_ordering(private_compare::ordering_value_type value_) ETL_NOEXCEPT + : value(value_) + { + } + + private_compare::ordering_value_type value; + }; + + inline constexpr strong_ordering strong_ordering::less(private_compare::ordering_less); + inline constexpr strong_ordering strong_ordering::equal(private_compare::ordering_equivalent); + inline constexpr strong_ordering strong_ordering::equivalent(private_compare::ordering_equivalent); + inline constexpr strong_ordering strong_ordering::greater(private_compare::ordering_greater); + #endif + + //*************************************************************************** + /// Converts a three-way comparison result to an etl::strong_ordering. + /// Negative values map to 'less', positive values to 'greater' and zero to + /// 'equal'. + //*************************************************************************** + ETL_NODISCARD ETL_CONSTEXPR inline etl::strong_ordering make_strong_ordering(int value) ETL_NOEXCEPT + { + return (value < 0) ? etl::strong_ordering::less : (value > 0) ? etl::strong_ordering::greater : etl::strong_ordering::equal; + } + + //*************************************************************************** + /// Converts a three-way comparison result to an etl::weak_ordering. + /// Negative values map to 'less', positive values to 'greater' and zero to + /// 'equivalent'. + //*************************************************************************** + ETL_NODISCARD ETL_CONSTEXPR inline etl::weak_ordering make_weak_ordering(int value) ETL_NOEXCEPT + { + return (value < 0) ? etl::weak_ordering::less : (value > 0) ? etl::weak_ordering::greater : etl::weak_ordering::equivalent; + } + + //*************************************************************************** + /// Converts a three-way comparison result to an etl::partial_ordering. + /// Negative values map to 'less', positive values to 'greater' and zero to + /// 'equivalent'. + /// Note that 'unordered' cannot be expressed by an integer comparison result, + /// so use etl::partial_ordering::unordered directly for that case. + //*************************************************************************** + ETL_NODISCARD ETL_CONSTEXPR inline etl::partial_ordering make_partial_ordering(int value) ETL_NOEXCEPT + { + return (value < 0) ? etl::partial_ordering::less : (value > 0) ? etl::partial_ordering::greater : etl::partial_ordering::equivalent; + } +#endif + //*************************************************************************** /// Defines <=, >, >=, ==, !=, <=> in terms of < /// Default implementation of TLess is etl::less @@ -105,7 +398,7 @@ namespace etl #endif ETL_CONSTEXPR14 int three_way_compare(const T& lhs, const T& rhs) { - return etl::compare::cmp(lhs, rhs); + return etl::compare::cmp(lhs, rhs); } } // namespace etl diff --git a/include/etl/private/chrono/day.h b/include/etl/private/chrono/day.h index 1f903067..bbca6a59 100644 --- a/include/etl/private/chrono/day.h +++ b/include/etl/private/chrono/day.h @@ -211,11 +211,11 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::day& d1, const etl::chrono::day& d2) ETL_NOEXCEPT { - return (static_cast(d1) <=> static_cast(d2)); + return etl::make_strong_ordering(d1.compare(d2)); } #endif diff --git a/include/etl/private/chrono/duration.h b/include/etl/private/chrono/duration.h index cf48e170..fc0b6cb3 100644 --- a/include/etl/private/chrono/duration.h +++ b/include/etl/private/chrono/duration.h @@ -482,7 +482,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 template [[nodiscard]] constexpr auto operator<=>(const etl::chrono::duration& lhs, const etl::chrono::duration& rhs) ETL_NOEXCEPT @@ -492,7 +492,37 @@ namespace etl common_t l = etl::chrono::duration_cast(lhs); common_t r = etl::chrono::duration_cast(rhs); + #if ETL_USING_STL return (l.count() <=> r.count()); + #else + // The STL returns the ordering category of the representation type. + // Mirror that: a floating point representation is only partially ordered. + if constexpr (etl::is_floating_point::value) + { + if (l.count() < r.count()) + { + return etl::partial_ordering::less; + } + else if (r.count() < l.count()) + { + return etl::partial_ordering::greater; + } + else if ((l.count() <= r.count()) && (r.count() <= l.count())) + { + // Equivalent. Expressed without '==' so that -Wfloat-equal is not triggered. + return etl::partial_ordering::equivalent; + } + else + { + // At least one of the values is NaN. + return etl::partial_ordering::unordered; + } + } + else + { + return etl::make_strong_ordering((l.count() < r.count()) ? -1 : (r.count() < l.count()) ? 1 : 0); + } + #endif } #endif diff --git a/include/etl/private/chrono/month.h b/include/etl/private/chrono/month.h index 40c2e594..b166f509 100644 --- a/include/etl/private/chrono/month.h +++ b/include/etl/private/chrono/month.h @@ -224,11 +224,11 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::month& d1, const etl::chrono::month& d2) ETL_NOEXCEPT { - return (static_cast(d1) <=> static_cast(d2)); + return etl::make_strong_ordering(d1.compare(d2)); } #endif diff --git a/include/etl/private/chrono/month_day.h b/include/etl/private/chrono/month_day.h index f4f77628..e53f8ac8 100644 --- a/include/etl/private/chrono/month_day.h +++ b/include/etl/private/chrono/month_day.h @@ -178,7 +178,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::month_day& lhs, const etl::chrono::month_day& rhs) ETL_NOEXCEPT { @@ -298,11 +298,11 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::month_day_last& mdl1, const etl::chrono::month_day_last& mdl2) ETL_NOEXCEPT { - return (static_cast(mdl1.month()) <=> static_cast(mdl2.month())); + return mdl1.month() <=> mdl2.month(); } #endif } // namespace chrono diff --git a/include/etl/private/chrono/time_point.h b/include/etl/private/chrono/time_point.h index a0e79bb1..f90f1c61 100644 --- a/include/etl/private/chrono/time_point.h +++ b/include/etl/private/chrono/time_point.h @@ -353,7 +353,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 template [[nodiscard]] constexpr auto operator<=>(const etl::chrono::time_point& lhs, const etl::chrono::time_point& rhs) diff --git a/include/etl/private/chrono/year.h b/include/etl/private/chrono/year.h index e6cb3416..35f67853 100644 --- a/include/etl/private/chrono/year.h +++ b/include/etl/private/chrono/year.h @@ -240,11 +240,11 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::year& y1, const etl::chrono::year& y2) ETL_NOEXCEPT { - return (static_cast(y1) <=> static_cast(y2)); + return etl::make_strong_ordering(y1.compare(y2)); } #endif diff --git a/include/etl/private/chrono/year_month.h b/include/etl/private/chrono/year_month.h index 44bb9fd1..16b3a431 100644 --- a/include/etl/private/chrono/year_month.h +++ b/include/etl/private/chrono/year_month.h @@ -234,7 +234,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::year_month& lhs, const etl::chrono::year_month& rhs) ETL_NOEXCEPT { diff --git a/include/etl/private/chrono/year_month_day.h b/include/etl/private/chrono/year_month_day.h index 91088c3b..a0f42474 100644 --- a/include/etl/private/chrono/year_month_day.h +++ b/include/etl/private/chrono/year_month_day.h @@ -426,7 +426,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::year_month_day& lhs, const etl::chrono::year_month_day& rhs) ETL_NOEXCEPT { @@ -722,7 +722,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::year_month_day_last& lhs, const etl::chrono::year_month_day_last& rhs) ETL_NOEXCEPT { diff --git a/test/test_chrono_day.cpp b/test/test_chrono_day.cpp index 47ab3fc8..706a3de9 100644 --- a/test/test_chrono_day.cpp +++ b/test/test_chrono_day.cpp @@ -278,7 +278,7 @@ namespace CHECK_TRUE(day10 >= day10); CHECK_TRUE(day20 >= day10); -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 CHECK_TRUE((day10 <=> day10) == 0); CHECK_TRUE((day10 <=> day20) < 0); CHECK_TRUE((day20 <=> day10) > 0); diff --git a/test/test_chrono_duration.cpp b/test/test_chrono_duration.cpp index 4122cdd5..1372d6f6 100644 --- a/test/test_chrono_duration.cpp +++ b/test/test_chrono_duration.cpp @@ -1045,16 +1045,41 @@ namespace CHECK_EQUAL(1, s1.count()); // 10 seconds % (-3) seconds = 1 second } -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 //************************************************************************* TEST(test_duration_spaceship_operator) { Chrono::seconds s1(2); // 2 seconds Chrono::milliseconds ms1(1500); // 1500 milliseconds - CHECK_TRUE(s1 <=> s1 == std::strong_ordering::equal); - CHECK_TRUE(ms1 <=> s1 == std::strong_ordering::less); - CHECK_TRUE(s1 <=> ms1 == std::strong_ordering::greater); + CHECK_TRUE(s1 <=> s1 == etl::strong_ordering::equal); + CHECK_TRUE(ms1 <=> s1 == etl::strong_ordering::less); + CHECK_TRUE(s1 <=> ms1 == etl::strong_ordering::greater); + + static_assert(etl::is_same ms1), etl::strong_ordering>::value, "Must return etl::strong_ordering"); + } + + //************************************************************************* + TEST(test_duration_spaceship_operator_floating_point_representation) + { + typedef Chrono::duration fp_seconds; + + fp_seconds fs1(1.5); + fp_seconds fs2(2.5); + + // A floating point representation is only partially ordered. + static_assert(etl::is_same fs2), etl::partial_ordering>::value, "Must return etl::partial_ordering"); + + CHECK_TRUE((fs1 <=> fs1) == etl::partial_ordering::equivalent); + CHECK_TRUE((fs1 <=> fs2) == etl::partial_ordering::less); + CHECK_TRUE((fs2 <=> fs1) == etl::partial_ordering::greater); + + fp_seconds nan_seconds(etl::numeric_limits::quiet_NaN()); + + CHECK_TRUE((nan_seconds <=> fs1) == etl::partial_ordering::unordered); + CHECK_FALSE((nan_seconds <=> fs1) < 0); + CHECK_FALSE((nan_seconds <=> fs1) == 0); + CHECK_FALSE((nan_seconds <=> fs1) > 0); } #endif diff --git a/test/test_chrono_month.cpp b/test/test_chrono_month.cpp index a4e61491..46522d4f 100644 --- a/test/test_chrono_month.cpp +++ b/test/test_chrono_month.cpp @@ -303,7 +303,7 @@ namespace CHECK_TRUE(month1 >= month1); CHECK_TRUE(month2 >= month1); -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 CHECK_TRUE((month1 <=> month1) == 0); CHECK_TRUE((month1 <=> month2) < 0); CHECK_TRUE((month2 <=> month1) > 0); diff --git a/test/test_chrono_month_day.cpp b/test/test_chrono_month_day.cpp index 7e7bf9b5..91a61482 100644 --- a/test/test_chrono_month_day.cpp +++ b/test/test_chrono_month_day.cpp @@ -89,7 +89,7 @@ namespace CHECK_FALSE(md.ok()); // Invalid month_day } -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 //************************************************************************* TEST(test_month_day_spaceship_operator) { @@ -97,9 +97,9 @@ namespace Chrono::month_day md2{Chrono::February, Chrono::day{10}}; Chrono::month_day md3{Chrono::January, Chrono::day{15}}; - CHECK_TRUE((md1 <=> md3) == std::strong_ordering::equal); // Same month and day - CHECK_TRUE((md1 <=> md2) == std::strong_ordering::less); // Different month and day - CHECK_TRUE((md2 <=> md1) == std::strong_ordering::greater); // Same month and day + CHECK_TRUE((md1 <=> md3) == etl::strong_ordering::equal); // Same month and day + CHECK_TRUE((md1 <=> md2) == etl::strong_ordering::less); // Different month and day + CHECK_TRUE((md2 <=> md1) == etl::strong_ordering::greater); // Same month and day } #endif diff --git a/test/test_chrono_month_day_last.cpp b/test/test_chrono_month_day_last.cpp index bd5bee7f..927a9369 100644 --- a/test/test_chrono_month_day_last.cpp +++ b/test/test_chrono_month_day_last.cpp @@ -100,6 +100,20 @@ namespace CHECK_TRUE(month_day_last1 != month_day_last3); } +#if ETL_USING_CPP20 + //************************************************************************* + TEST(test_month_day_last_spaceship_operator) + { + Chrono::month_day_last month_day_last1(Chrono::January); + Chrono::month_day_last month_day_last2(Chrono::February); + Chrono::month_day_last month_day_last3(Chrono::January); + + CHECK_TRUE((month_day_last1 <=> month_day_last3) == etl::strong_ordering::equal); + CHECK_TRUE((month_day_last1 <=> month_day_last2) == etl::strong_ordering::less); + CHECK_TRUE((month_day_last2 <=> month_day_last1) == etl::strong_ordering::greater); + } +#endif + #if ETL_USING_ETL_CHRONO //************************************************************************* TEST(test_month_day_last_hashes_are_unique) diff --git a/test/test_chrono_time_point.cpp b/test/test_chrono_time_point.cpp index a4866f0c..89a00227 100644 --- a/test/test_chrono_time_point.cpp +++ b/test/test_chrono_time_point.cpp @@ -365,7 +365,7 @@ namespace CHECK_FALSE(tp10d >= tp20h); CHECK_FALSE(tp10h >= tp20d); -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 CHECK_TRUE((tp10h <=> tp10h) == 0); CHECK_TRUE((tp10h <=> tp20h) < 0); CHECK_TRUE((tp20h <=> tp10h) > 0); @@ -377,6 +377,8 @@ namespace CHECK_TRUE((tp10d <=> tp10h) == 0); CHECK_TRUE((tp10h <=> tp20d) < 0); CHECK_TRUE((tp20d <=> tp10h) > 0); + + static_assert(etl::is_same tp20d), etl::strong_ordering>::value, "Must return etl::strong_ordering"); #endif } diff --git a/test/test_chrono_year.cpp b/test/test_chrono_year.cpp index 2b8dad29..8e3c9f1c 100644 --- a/test/test_chrono_year.cpp +++ b/test/test_chrono_year.cpp @@ -284,7 +284,7 @@ namespace CHECK_FALSE(year10 == year20); CHECK_TRUE(year10 != year20); -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 CHECK_TRUE((year10 <=> year10) == 0); CHECK_TRUE((year10 <=> year20) < 0); CHECK_TRUE((year20 <=> year10) > 0); diff --git a/test/test_chrono_year_month.cpp b/test/test_chrono_year_month.cpp index 73065121..ad051386 100644 --- a/test/test_chrono_year_month.cpp +++ b/test/test_chrono_year_month.cpp @@ -89,7 +89,7 @@ namespace CHECK_FALSE(ym.ok()); // Invalid year_month } -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 //************************************************************************* TEST(test_year_month_spaceship_operator) { @@ -97,11 +97,11 @@ namespace Chrono::year_month ym2{Chrono::year(2001), Chrono::January}; Chrono::year_month ym3{Chrono::year(2000), Chrono::February}; - CHECK_TRUE((ym1 <=> ym1) == std::strong_ordering::equal); - CHECK_TRUE((ym1 <=> ym2) == std::strong_ordering::less); - CHECK_TRUE((ym2 <=> ym1) == std::strong_ordering::greater); - CHECK_TRUE((ym1 <=> ym3) == std::strong_ordering::less); - CHECK_TRUE((ym3 <=> ym1) == std::strong_ordering::greater); + CHECK_TRUE((ym1 <=> ym1) == etl::strong_ordering::equal); + CHECK_TRUE((ym1 <=> ym2) == etl::strong_ordering::less); + CHECK_TRUE((ym2 <=> ym1) == etl::strong_ordering::greater); + CHECK_TRUE((ym1 <=> ym3) == etl::strong_ordering::less); + CHECK_TRUE((ym3 <=> ym1) == etl::strong_ordering::greater); } #endif diff --git a/test/test_chrono_year_month_day.cpp b/test/test_chrono_year_month_day.cpp index e44ee967..a83f7ebe 100644 --- a/test/test_chrono_year_month_day.cpp +++ b/test/test_chrono_year_month_day.cpp @@ -98,7 +98,7 @@ namespace CHECK_FALSE(ymd.ok()); // Invalid year_month_day } -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 //************************************************************************* TEST(test_year_month_day_spaceship_operator) { @@ -107,13 +107,13 @@ namespace Chrono::year_month_day ym3{Chrono::year(2000), Chrono::February, Chrono::day(1)}; Chrono::year_month_day ym4{Chrono::year(2000), Chrono::January, Chrono::day(2)}; - CHECK_TRUE((ym1 <=> ym1) == std::strong_ordering::equal); - CHECK_TRUE((ym1 <=> ym2) == std::strong_ordering::less); - CHECK_TRUE((ym2 <=> ym1) == std::strong_ordering::greater); - CHECK_TRUE((ym1 <=> ym3) == std::strong_ordering::less); - CHECK_TRUE((ym3 <=> ym1) == std::strong_ordering::greater); - CHECK_TRUE((ym1 <=> ym4) == std::strong_ordering::less); - CHECK_TRUE((ym4 <=> ym1) == std::strong_ordering::greater); + CHECK_TRUE((ym1 <=> ym1) == etl::strong_ordering::equal); + CHECK_TRUE((ym1 <=> ym2) == etl::strong_ordering::less); + CHECK_TRUE((ym2 <=> ym1) == etl::strong_ordering::greater); + CHECK_TRUE((ym1 <=> ym3) == etl::strong_ordering::less); + CHECK_TRUE((ym3 <=> ym1) == etl::strong_ordering::greater); + CHECK_TRUE((ym1 <=> ym4) == etl::strong_ordering::less); + CHECK_TRUE((ym4 <=> ym1) == etl::strong_ordering::greater); } #endif diff --git a/test/test_chrono_year_month_day_last.cpp b/test/test_chrono_year_month_day_last.cpp index 53a556af..8edaca2f 100644 --- a/test/test_chrono_year_month_day_last.cpp +++ b/test/test_chrono_year_month_day_last.cpp @@ -160,6 +160,23 @@ namespace CHECK_TRUE(year_month_day_last1 != year_month_day_last3); } +#if ETL_USING_CPP20 + //************************************************************************* + TEST(test_year_month_day_last_spaceship_operator) + { + Chrono::year_month_day_last year_month_day_last1(Chrono::year(2000), Chrono::month_day_last(Chrono::January)); + Chrono::year_month_day_last year_month_day_last2(Chrono::year(2001), Chrono::month_day_last(Chrono::January)); + Chrono::year_month_day_last year_month_day_last3(Chrono::year(2000), Chrono::month_day_last(Chrono::February)); + Chrono::year_month_day_last year_month_day_last4(Chrono::year(2000), Chrono::month_day_last(Chrono::January)); + + CHECK_TRUE((year_month_day_last1 <=> year_month_day_last4) == etl::strong_ordering::equal); + CHECK_TRUE((year_month_day_last1 <=> year_month_day_last2) == etl::strong_ordering::less); + CHECK_TRUE((year_month_day_last2 <=> year_month_day_last1) == etl::strong_ordering::greater); + CHECK_TRUE((year_month_day_last1 <=> year_month_day_last3) == etl::strong_ordering::less); + CHECK_TRUE((year_month_day_last3 <=> year_month_day_last1) == etl::strong_ordering::greater); + } +#endif + #if ETL_USING_ETL_CHRONO //************************************************************************* TEST(test_year_month_day_last_hashes_are_unique) diff --git a/test/test_compare.cpp b/test/test_compare.cpp index 032cbd41..d0ec3b7c 100644 --- a/test/test_compare.cpp +++ b/test/test_compare.cpp @@ -91,6 +91,20 @@ namespace } }; + struct ComparatorOnlyObject + { + int a; + int b; + }; + + struct LessBySecond + { + bool constexpr operator()(const ComparatorOnlyObject& lhs, const ComparatorOnlyObject& rhs) const + { + return lhs.b < rhs.b; + } + }; + typedef etl::compare CompareTest; SUITE(test_compare) @@ -191,5 +205,139 @@ namespace CHECK_EQUAL(CompareInt::Greater, cmp_test_greater); CHECK_EQUAL(CompareInt::Equal, cmp_test_equal); } + +#if ETL_USING_CPP20 + //************************************************************************* + TEST(test_strong_ordering) + { + constexpr etl::strong_ordering ordering_less(etl::strong_ordering::less); + constexpr etl::strong_ordering ordering_equal(etl::strong_ordering::equal); + constexpr etl::strong_ordering ordering_greater(etl::strong_ordering::greater); + + CHECK(ordering_less < 0); + CHECK(ordering_less <= 0); + CHECK(!(ordering_less == 0)); + CHECK(!(ordering_less > 0)); + CHECK(ordering_equal == 0); + CHECK(ordering_equal <= 0); + CHECK(ordering_equal >= 0); + CHECK(ordering_greater > 0); + CHECK(ordering_greater >= 0); + CHECK(!(ordering_greater == 0)); + + // Reversed operands. + CHECK(0 > ordering_less); + CHECK(0 < ordering_greater); + + static_assert(etl::strong_ordering::equivalent == etl::strong_ordering::equal, "Equivalent must alias equal"); + static_assert(etl::is_same::value, + "Must convert to weak_ordering"); + CHECK(etl::weak_ordering(etl::strong_ordering::less) < 0); + CHECK(etl::partial_ordering(etl::strong_ordering::greater) > 0); + } + + //************************************************************************* + TEST(test_make_strong_ordering) + { + constexpr etl::strong_ordering ordering_less = etl::make_strong_ordering(-1); + constexpr etl::strong_ordering ordering_equal = etl::make_strong_ordering(0); + constexpr etl::strong_ordering ordering_greater = etl::make_strong_ordering(1); + + CHECK(ordering_less < 0); + CHECK(ordering_equal == 0); + CHECK(ordering_greater > 0); + + static_assert(etl::is_same::value, "Must return etl::strong_ordering"); + } + + //************************************************************************* + TEST(test_weak_ordering) + { + constexpr etl::weak_ordering ordering_less(etl::weak_ordering::less); + constexpr etl::weak_ordering ordering_equivalent(etl::weak_ordering::equivalent); + constexpr etl::weak_ordering ordering_greater(etl::weak_ordering::greater); + + CHECK(ordering_less < 0); + CHECK(ordering_equivalent == 0); + CHECK(ordering_greater > 0); + + CHECK(0 > ordering_less); + CHECK(0 < ordering_greater); + + static_assert(etl::weak_ordering::equivalent == 0, "Equivalent must be zero"); + CHECK(etl::partial_ordering(etl::weak_ordering::less) < 0); + } + + //************************************************************************* + TEST(test_make_weak_ordering) + { + constexpr etl::weak_ordering ordering_less = etl::make_weak_ordering(-1); + constexpr etl::weak_ordering ordering_equivalent = etl::make_weak_ordering(0); + constexpr etl::weak_ordering ordering_greater = etl::make_weak_ordering(1); + + CHECK(ordering_less < 0); + CHECK(ordering_equivalent == 0); + CHECK(ordering_greater > 0); + + static_assert(etl::is_same::value, "Must return etl::weak_ordering"); + } + + //************************************************************************* + TEST(test_partial_ordering) + { + constexpr etl::partial_ordering ordering_less(etl::partial_ordering::less); + constexpr etl::partial_ordering ordering_equivalent(etl::partial_ordering::equivalent); + constexpr etl::partial_ordering ordering_greater(etl::partial_ordering::greater); + constexpr etl::partial_ordering ordering_unordered(etl::partial_ordering::unordered); + + CHECK(ordering_less < 0); + CHECK(ordering_equivalent == 0); + CHECK(ordering_greater > 0); + + // An unordered result compares false against zero in every direction. + CHECK(!(ordering_unordered < 0)); + CHECK(!(ordering_unordered <= 0)); + CHECK(!(ordering_unordered == 0)); + CHECK(!(ordering_unordered >= 0)); + CHECK(!(ordering_unordered > 0)); + CHECK(ordering_unordered != 0); + + static_assert(etl::partial_ordering::equivalent == 0, "Equivalent must be zero"); + } + + //************************************************************************* + TEST(test_make_partial_ordering) + { + constexpr etl::partial_ordering ordering_less = etl::make_partial_ordering(-1); + constexpr etl::partial_ordering ordering_equivalent = etl::make_partial_ordering(0); + constexpr etl::partial_ordering ordering_greater = etl::make_partial_ordering(1); + + CHECK(ordering_less < 0); + CHECK(ordering_equivalent == 0); + CHECK(ordering_greater > 0); + + static_assert(etl::is_same::value, "Must return etl::partial_ordering"); + } + + //************************************************************************* + TEST(test_three_way_compare_custom_less) + { + constexpr ComparatorOnlyObject lhs = {5, 1}; + constexpr ComparatorOnlyObject rhs = {1, 2}; + constexpr ComparatorOnlyObject eq = {9, 1}; + + constexpr int cmp_less = etl::three_way_compare(lhs, rhs); + constexpr int cmp_greater = etl::three_way_compare(rhs, lhs); + constexpr int cmp_equal = etl::three_way_compare(lhs, eq); + + CHECK_EQUAL(-1, cmp_less); + CHECK_EQUAL(1, cmp_greater); + CHECK_EQUAL(0, cmp_equal); + + CHECK(etl::make_strong_ordering(cmp_less) < 0); + CHECK(etl::make_strong_ordering(cmp_greater) > 0); + CHECK(etl::make_strong_ordering(cmp_equal) == 0); + } +#endif } } // namespace From 525fd0304aabc8c61098a011d24483bb227269de Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sun, 13 Sep 2026 10:50:31 +0100 Subject: [PATCH 04/16] Updated ignore for hard link for CLion --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b5cc3865..035a6dfa 100644 --- a/.gitignore +++ b/.gitignore @@ -423,3 +423,4 @@ include/etl/iterator.h~RFfd5eda.TMP test/vs2022/Build docs/Messaging test/vs2026/Build +test/include From 22bdef2ee41279a75d92c91a7c831b73084e4c1f Mon Sep 17 00:00:00 2001 From: Mike Bloom <91038685+mike919192@users.noreply.github.com> Date: Thu, 17 Sep 2026 07:57:10 -0400 Subject: [PATCH 05/16] Simplify noexcept expressions using ETL_NOT_USING_EXCEPTIONS (#1606) * Simplify noexcept containing ETL_NOT_USING_EXCEPTIONS * Revert some format --------- Co-authored-by: John Wellbelove Co-authored-by: Roland Reichwein --- include/etl/array.h | 4 ++-- include/etl/array_view.h | 4 ++-- include/etl/array_wrapper.h | 4 ++-- include/etl/span.h | 26 +++++++++++--------------- include/etl/string_view.h | 2 +- include/etl/vector.h | 8 +++----- 6 files changed, 21 insertions(+), 27 deletions(-) diff --git a/include/etl/array.h b/include/etl/array.h index c1d7d6e4..a5f48e27 100644 --- a/include/etl/array.h +++ b/include/etl/array.h @@ -137,7 +137,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 operator[](size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) + ETL_NODISCARD ETL_CONSTEXPR14 reference operator[](size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR) { ETL_ASSERT_CHECK_INDEX_OPERATOR(i < SIZE, ETL_ERROR(array_out_of_range)); @@ -149,7 +149,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_CONSTEXPR const_reference operator[](size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) + ETL_NODISCARD ETL_CONSTEXPR const_reference operator[](size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR) { // Throwing from c++11 constexpr requires special syntax #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_INDEX_OPERATOR diff --git a/include/etl/array_view.h b/include/etl/array_view.h index bb25dac0..7d20c5a2 100644 --- a/include/etl/array_view.h +++ b/include/etl/array_view.h @@ -518,7 +518,7 @@ namespace etl /// If asserts or exceptions are enabled, throws an etl::array_view_bounds /// if the index is out of bounds. //************************************************************************* - reference operator[](const size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) + reference operator[](const size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR) { ETL_ASSERT_CHECK_INDEX_OPERATOR(i < size(), ETL_ERROR(array_view_bounds)); @@ -531,7 +531,7 @@ namespace etl /// If asserts or exceptions are enabled, throws an etl::array_view_bounds /// if the index is out of bounds. //************************************************************************* - const_reference operator[](const size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) + const_reference operator[](const size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR) { ETL_ASSERT_CHECK_INDEX_OPERATOR(i < size(), ETL_ERROR(array_view_bounds)); diff --git a/include/etl/array_wrapper.h b/include/etl/array_wrapper.h index 2c434a05..97e198ec 100644 --- a/include/etl/array_wrapper.h +++ b/include/etl/array_wrapper.h @@ -271,7 +271,7 @@ namespace etl /// If asserts or exceptions are enabled, throws an /// etl::array_wrapper_bounds if the index is out of bounds. //************************************************************************* - reference operator[](size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) + reference operator[](size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR) { ETL_ASSERT_CHECK_INDEX_OPERATOR(i < SIZE, ETL_ERROR(etl::array_wrapper_bounds)); return ARRAY_[i]; @@ -282,7 +282,7 @@ namespace etl /// If asserts or exceptions are enabled, throws an /// etl::array_wrapper_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_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR) { // Throwing from c++11 constexpr requires special syntax #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_INDEX_OPERATOR diff --git a/include/etl/span.h b/include/etl/span.h index d948c355..71c7e907 100644 --- a/include/etl/span.h +++ b/include/etl/span.h @@ -644,8 +644,7 @@ namespace etl /// Obtains a span that is a view over the first count elements of this /// span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span first(size_t count) const - ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span first(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return count <= size() ? etl::span(pbegin, pbegin + count) : throw(ETL_ERROR(span_out_of_range)); @@ -672,8 +671,7 @@ namespace etl //************************************************************************* /// Obtains a span that is a view over the last count elements of this span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span last(size_t count) const - ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span last(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return count <= size() ? etl::span((pbegin + Extent) - count, (pbegin + Extent)) @@ -735,7 +733,7 @@ namespace etl /// elements of this span. //************************************************************************* ETL_NODISCARD ETL_CONSTEXPR etl::span subspan(size_t offset, size_t count = etl::dynamic_extent) const - ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return (offset <= size()) && (count != etl::dynamic_extent ? count <= (size() - offset) : true) @@ -1003,7 +1001,7 @@ namespace etl //************************************************************************* /// Returns a reference to the first element. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR reference front() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR reference front() const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return size() > 0 ? *pbegin : throw(ETL_ERROR(span_out_of_range)); @@ -1017,7 +1015,7 @@ namespace etl //************************************************************************* /// Returns a reference to the last element. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR reference back() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR reference back() const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return size() > 0 ? *(pend - 1) : throw(ETL_ERROR(span_out_of_range)); @@ -1197,7 +1195,7 @@ namespace etl /// span. //************************************************************************* template - ETL_NODISCARD ETL_CONSTEXPR etl::span first() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span first() const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return COUNT <= size() ? etl::span(pbegin, pbegin + COUNT) : throw(ETL_ERROR(span_out_of_range)); @@ -1212,8 +1210,7 @@ namespace etl /// Obtains a span that is a view over the first count elements of this /// span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span first(size_t count) const - ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span first(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return count <= size() ? etl::span(pbegin, pbegin + count) : throw(ETL_ERROR(span_out_of_range)); @@ -1228,7 +1225,7 @@ namespace etl /// Obtains a span that is a view over the last COUNT elements of this span. //************************************************************************* template - ETL_NODISCARD ETL_CONSTEXPR etl::span last() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span last() const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return COUNT <= size() ? etl::span(pend - COUNT, pend) : throw(ETL_ERROR(span_out_of_range)); @@ -1242,8 +1239,7 @@ namespace etl //************************************************************************* /// Obtains a span that is a view over the last count elements of this span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span last(size_t count) const - ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span last(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return count <= size() ? etl::span(pend - count, pend) : throw(ETL_ERROR(span_out_of_range)); @@ -1261,7 +1257,7 @@ namespace etl //************************************************************************* template ETL_NODISCARD ETL_CONSTEXPR etl::span< element_type, COUNT != etl::dynamic_extent ? COUNT : etl::dynamic_extent> subspan() const - ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return (OFFSET <= size()) && (COUNT != etl::dynamic_extent ? COUNT <= (size() - OFFSET) : true) @@ -1305,7 +1301,7 @@ namespace etl /// elements of this span. //************************************************************************* ETL_NODISCARD ETL_CONSTEXPR14 etl::span subspan(size_t offset, size_t count = etl::dynamic_extent) const - ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { ETL_ASSERT_CHECK_EXTRA(offset <= size(), ETL_ERROR(span_out_of_range)); ETL_ASSERT_CHECK_EXTRA(count != etl::dynamic_extent ? count <= (size() - offset) : true, ETL_ERROR(span_out_of_range)); diff --git a/include/etl/string_view.h b/include/etl/string_view.h index 2cb20e37..0827636c 100644 --- a/include/etl/string_view.h +++ b/include/etl/string_view.h @@ -355,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_type 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_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)); diff --git a/include/etl/vector.h b/include/etl/vector.h index beac6cdc..61ee6c26 100644 --- a/include/etl/vector.h +++ b/include/etl/vector.h @@ -393,7 +393,7 @@ namespace etl typename etl::enable_if::value && etl::is_convertible::value_type, T>::value, typename etl::enable_if::value, void>::type>::type - assign(TIterator first, TIterator last) ETL_NOEXCEPT_IF((etl::is_nothrow_copy_constructible::value && ETL_NOT_USING_EXCEPTIONS)) + assign(TIterator first, TIterator last) { #if ETL_USING_CPP11 ETL_STATIC_ASSERT((etl::is_same::type, @@ -2082,7 +2082,6 @@ namespace etl /// Copy constructor. //************************************************************************* vector_ext(const vector_ext& other, void* buffer, size_t max_size) - ETL_NOEXCEPT_IF((etl::is_nothrow_copy_constructible::value && ETL_NOT_USING_EXCEPTIONS)) : etl::ivector(reinterpret_cast(buffer), max_size) { if (&other != this) @@ -2094,7 +2093,7 @@ namespace etl //************************************************************************* /// Assignment operator. //************************************************************************* - vector_ext& operator=(const vector_ext& rhs) ETL_NOEXCEPT_IF((etl::is_nothrow_copy_constructible::value && ETL_NOT_USING_EXCEPTIONS)) + vector_ext& operator=(const vector_ext& rhs) { if (&rhs != this) { @@ -2109,7 +2108,6 @@ namespace etl /// Move constructor. //************************************************************************* vector_ext(vector_ext&& other, void* buffer, size_t max_size) - ETL_NOEXCEPT_IF((etl::is_nothrow_move_constructible::value && ETL_NOT_USING_EXCEPTIONS)) : etl::ivector(reinterpret_cast(buffer), max_size) { if (&other != this) @@ -2123,7 +2121,7 @@ namespace etl //************************************************************************* /// Move assignment operator. //************************************************************************* - vector_ext& operator=(vector_ext&& rhs) ETL_NOEXCEPT_IF((etl::is_nothrow_move_constructible::value && ETL_NOT_USING_EXCEPTIONS)) + vector_ext& operator=(vector_ext&& rhs) { if (&rhs != this) { From d9a9488582305cbb1fb19ec314655570798d26df Mon Sep 17 00:00:00 2001 From: Drew Rife Date: Sat, 26 Sep 2026 05:45:48 -0400 Subject: [PATCH 06/16] Add referenced clamped values with shared ranges (#1597) * Support runtime floating-point clamped_value before C++20 * Add referenced clamped values with shared ranges --- docs/utilities/clamped-value.md | 176 ++++++++++- include/etl/clamped_value.h | 395 ++++++++++++++++++++++++ test/syntax_check/clamped_value.h.t.cpp | 10 + test/test_clamped_value.cpp | 152 +++++++++ 4 files changed, 732 insertions(+), 1 deletion(-) diff --git a/docs/utilities/clamped-value.md b/docs/utilities/clamped-value.md index 5bae0543..2396a96a 100644 --- a/docs/utilities/clamped-value.md +++ b/docs/utilities/clamped-value.md @@ -26,7 +26,8 @@ including the header to override automatic detection. 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. +runtime bounds. `clamped_value_range` and `referenced_clamped_value` are +available in C++11 and later. ```cpp // C++11 and later @@ -48,6 +49,20 @@ etl::clamped_value float_ct(0.5f); #endif ``` +In C++11 and later, multiple values can share one immutable runtime range: + +```cpp +const etl::clamped_value_range range(-1.5f, 2.5f); + +etl::referenced_clamped_value value1(range, 0.5f); +etl::referenced_clamped_value value2(range, 1.5f); +``` + +`referenced_clamped_value` stores an +`etl::reference_wrapper>`. The range must outlive +every value that refers to it. Construction from a temporary range is deleted. +Copying or swapping a referenced value also copies or swaps its range reference. + ## Types `difference_type` is the signed counterpart of `T` for integral values. For @@ -55,6 +70,9 @@ floating-point values, `difference_type` is `T`, allowing fractional steps. `limits_type` and `difference_limits_type` provide the corresponding `etl::numeric_limits` specializations. +`referenced_clamped_value` also defines `range_type` as +`clamped_value_range`. + ## Constructors ```cpp @@ -76,6 +94,23 @@ 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. +### Referenced ranges + +```cpp +clamped_value_range range(min, max); +referenced_clamped_value value(range); +referenced_clamped_value value(range, initial); +``` + +The one-argument value constructor initializes to `range.min()`. The +two-argument form clamps `initial` to the shared range. A range is immutable +after construction, so every referring value observes stable bounds. A +default-constructed range uses the full representable range of `T`. + +`referenced_clamped_value` is non-owning. The referenced range must outlive +the value and any copies of it. The constructors taking `range_type&&` are +deleted to prevent direct construction from a temporary range. + ## Modifiers ```cpp @@ -93,6 +128,10 @@ 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. +`referenced_clamped_value` supports `set(T)`, `to_min()`, `to_max()`, and +`advance(difference_type)`. It does not provide `set(min, max)` because its +range is immutable and shared. + Positive steps move toward `max`; negative steps move toward `min`. Infinite steps saturate at a finite bound. Infinite values and bounds remain valid. @@ -122,6 +161,14 @@ 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. +`referenced_clamped_value` additionally provides: + +```cpp +ETL_NODISCARD const range_type& range() const noexcept; +``` + +This returns the exact `clamped_value_range` object referenced by the value. + ## Operators ```cpp @@ -143,6 +190,8 @@ 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. +`referenced_clamped_value` provides the same conversion, assignment, +increment, decrement, and compound-assignment operators. ## Operations @@ -153,6 +202,10 @@ ETL_CONSTEXPR14 void swap(clamped_value& lhs, clamped_value& rhs); Swaps clamped values. Runtime values also swap their bounds. +Swapping two `referenced_clamped_value` objects swaps both their current +values and range references. Copy construction and copy assignment preserve +the source's range identity; they do not copy the range itself. + 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 @@ -169,3 +222,124 @@ 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. + +The same validation applies when constructing `clamped_value_range` and +when assigning or advancing a `referenced_clamped_value`. + +## Shared Referenced Ranges + +`clamped_value_range` and `referenced_clamped_value` are available in +C++11 and later. They let independent values share immutable runtime bounds. + +### `clamped_value_range` + +```cpp +template +class clamped_value_range; +``` + +#### Constructors + +```cpp +clamped_value_range(); +clamped_value_range(T min, T max); +``` + +The default constructor uses the full representable range of `T`. The +two-argument constructor stores immutable bounds and rejects reversed or NaN +bounds. + +#### Accessors + +```cpp +ETL_NODISCARD T min() const noexcept; +ETL_NODISCARD T max() const noexcept; +``` + +### `referenced_clamped_value` + +```cpp +template +class referenced_clamped_value; +``` + +The class stores a current value and an +`etl::reference_wrapper>`. It does not own the +range, which must outlive the value and all copies of it. + +Member types are `range_type`, `difference_type`, `limits_type`, and +`difference_limits_type`, with the same numeric meanings as `clamped_value`. + +#### Constructors + +```cpp +explicit referenced_clamped_value(const range_type& range); +referenced_clamped_value(const range_type& range, T initial); +referenced_clamped_value(const referenced_clamped_value& other); + +referenced_clamped_value(range_type&&) = delete; +referenced_clamped_value(range_type&&, T) = delete; +``` + +The one-argument constructor initializes to `range.min()`. The two-argument +constructor clamps `initial`. Temporary ranges are rejected to prevent an +immediately dangling reference. Copy construction preserves the source value +and range identity. + +#### Modifiers + +```cpp +void set(T value); +void to_min() noexcept; +void to_max() noexcept; +void advance(difference_type n); +``` + +These operations mirror `clamped_value`. There is no `set(min, max)` because +the referenced range is immutable and shared. + +#### Accessors + +```cpp +ETL_NODISCARD T get() const noexcept; +ETL_NODISCARD T min() const noexcept; +ETL_NODISCARD T max() const noexcept; +ETL_NODISCARD const range_type& range() const noexcept; +ETL_NODISCARD operator T() const noexcept; +``` + +`range()` returns the exact range object held by reference. + +#### Operators + +```cpp +referenced_clamped_value& operator=(const referenced_clamped_value& other) & noexcept; +referenced_clamped_value& operator=(T value) &; +referenced_clamped_value& operator++() & noexcept; +referenced_clamped_value operator++(int) noexcept; +referenced_clamped_value& operator--() & noexcept; +referenced_clamped_value operator--(int) noexcept; +referenced_clamped_value& operator+=(difference_type n) &; +referenced_clamped_value& operator-=(difference_type n) &; +``` + +Copy assignment copies both the current value and range reference. Arithmetic +and value assignment clamp to the referenced bounds. Comparisons compare only +current values, not range identity or bounds. + +#### Swap + +```cpp +void swap(referenced_clamped_value& other) noexcept; +void swap(referenced_clamped_value& lhs, + referenced_clamped_value& rhs) noexcept; +``` + +Swap exchanges both current values and range references, keeping each value +associated with its original bounds. + +#### Errors + +Invalid or NaN range bounds trigger an ETL assertion. NaN initial values, +assignments, and steps are also rejected. Infinite values follow the same +behavior as `clamped_value`. diff --git a/include/etl/clamped_value.h b/include/etl/clamped_value.h index d7f3dcf9..37385139 100644 --- a/include/etl/clamped_value.h +++ b/include/etl/clamped_value.h @@ -35,6 +35,7 @@ SOFTWARE. #include "absolute.h" #include "algorithm.h" #include "error_handler.h" +#include "functional.h" #include "limits.h" #include "math.h" #include "static_assert.h" @@ -978,6 +979,400 @@ namespace etl T min_value; T max_value; }; + +#if ETL_USING_CPP11 + //*************************************************************************** + /// An immutable pair of runtime limits that may be shared by multiple + /// referenced_clamped_value objects. + ///@tparam T An integral type, or a floating-point type when + /// ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`. + ///\ingroup clamped_value + //*************************************************************************** + template + class clamped_value_range + { + public: + + typedef typename private_clamped_value::traits::limits_type limits_type; + + //************************************************************************* + /// Constructs the full representable range of T. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value_range() + : min_value(limits_type::lowest()) + , max_value(limits_type::max()) + { + } + + //************************************************************************* + /// Constructs a range with the supplied limits. + /// Reversed bounds and NaN bounds are rejected. + ///\param min_ The minimum value. + ///\param max_ The maximum value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value_range(T min_, T max_) + : 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_range: invalid range")); + } + + //************************************************************************* + /// Gets the minimum value. + //************************************************************************* + ETL_NODISCARD ETL_CONSTEXPR T min() const ETL_NOEXCEPT + { + return min_value; + } + + //************************************************************************* + /// Gets the maximum value. + //************************************************************************* + ETL_NODISCARD ETL_CONSTEXPR T max() const ETL_NOEXCEPT + { + return max_value; + } + + private: + + const T min_value; + const T max_value; + }; + + //*************************************************************************** + /// Provides a value clamped to a shared immutable runtime range. + /// The referenced range must outlive this object. + ///@tparam T An integral type, or a floating-point type when + /// ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`. + ///\ingroup clamped_value + //*************************************************************************** + template + class referenced_clamped_value + { + public: + + /// The referenced range type. + typedef clamped_value_range range_type; + + /// 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; + + //************************************************************************* + /// Constructs a value initialized to the minimum of the referenced range. + ///\param range_ The range to reference. It must outlive this object. + //************************************************************************* + explicit referenced_clamped_value(const range_type& range_) + : value(range_.min()) + , value_range(etl::cref(range_)) + { + } + + //************************************************************************* + /// Constructs a value clamped to the referenced range. + ///\param range_ The range to reference. It must outlive this object. + ///\param initial The initial value. + //************************************************************************* + referenced_clamped_value(const range_type& range_, T initial) + : value(private_clamped_value::validated_clamp(initial, range_.min(), range_.max())) + , value_range(etl::cref(range_)) + { + } + + //************************************************************************* + /// Prevents construction with a temporary range. + //************************************************************************* + referenced_clamped_value(range_type&&) = delete; + referenced_clamped_value(range_type&&, T) = delete; + + //************************************************************************* + /// Copies the value and its range reference. + ///\param other The value to copy. + //************************************************************************* + referenced_clamped_value(const referenced_clamped_value& other) ETL_NOEXCEPT + : value(other.value) + , value_range(other.value_range) + { + } + + //************************************************************************* + /// Copy assignment. Copies the value and its range reference. + ///\param other The value and range reference to copy. + ///\return A reference to this value. + //************************************************************************* + referenced_clamped_value& operator=(const referenced_clamped_value& other) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + if (this != &other) + { + value = other.value; + value_range = other.value_range; + } + + return *this; + } + + //************************************************************************* + /// Sets the value and clamps it to the referenced range. + ///\param value_ The value to assign. + //************************************************************************* + void set(T value_) + { + value = private_clamped_value::validated_clamp(value_, min(), max()); + } + + //************************************************************************* + /// Resets the value to the minimum of the referenced range. + //************************************************************************* + void to_min() ETL_NOEXCEPT + { + value = min(); + } + + //************************************************************************* + /// Resets the value to the maximum of the referenced range. + //************************************************************************* + void to_max() ETL_NOEXCEPT + { + value = max(); + } + + //************************************************************************* + /// Advances the value and saturates it at the referenced bounds. + ///\param n The difference to add. + //************************************************************************* + void advance(difference_type n) + { + private_clamped_value::validate(n); + value = private_clamped_value::advance(value, min(), max(), n); + } + + //************************************************************************* + /// Converts to the underlying value type. + ///\return The current value. + //************************************************************************* + ETL_NODISCARD operator T() const ETL_NOEXCEPT + { + return value; + } + + //************************************************************************* + /// Advances by one and saturates at the maximum. + ///\return A reference to this value. + //************************************************************************* + referenced_clamped_value& operator++() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + value = private_clamped_value::advance(value, min(), max(), difference_type(1)); + return *this; + } + + //************************************************************************* + /// Advances by one and returns the previous value. + ///\return The value before incrementing. + //************************************************************************* + referenced_clamped_value operator++(int) ETL_NOEXCEPT + { + referenced_clamped_value temp(*this); + ++(*this); + return temp; + } + + //************************************************************************* + /// Subtracts one and saturates at the minimum. + ///\return A reference to this value. + //************************************************************************* + referenced_clamped_value& operator--() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + value = private_clamped_value::advance(value, min(), max(), difference_type(-1)); + return *this; + } + + //************************************************************************* + /// Subtracts one and returns the previous value. + ///\return The value before decrementing. + //************************************************************************* + referenced_clamped_value operator--(int) ETL_NOEXCEPT + { + referenced_clamped_value temp(*this); + --(*this); + return temp; + } + + //************************************************************************* + /// Assigns a value and clamps it to the referenced range. + ///\param value_ The value to assign. + ///\return A reference to this value. + //************************************************************************* + referenced_clamped_value& operator=(T value_) ETL_LVALUE_REF_QUALIFIER + { + set(value_); + return *this; + } + + //************************************************************************* + /// Adds a difference and clamps to the referenced range. + ///\param n The difference to add. + ///\return A reference to this value. + //************************************************************************* + referenced_clamped_value& operator+=(difference_type n) ETL_LVALUE_REF_QUALIFIER + { + advance(n); + return *this; + } + + //************************************************************************* + /// Subtracts a difference and clamps to the referenced range. + ///\param n The difference to subtract. + ///\return A reference to this value. + //************************************************************************* + referenced_clamped_value& operator-=(difference_type n) ETL_LVALUE_REF_QUALIFIER + { + private_clamped_value::validate(n); + value = private_clamped_value::subtract(value, min(), max(), n); + return *this; + } + + //************************************************************************* + /// Gets the current value. + ///\return The current value. + //************************************************************************* + ETL_NODISCARD + T get() const ETL_NOEXCEPT + { + return value; + } + + //************************************************************************* + /// Gets the minimum of the referenced range. + ///\return The minimum value. + //************************************************************************* + ETL_NODISCARD + T min() const ETL_NOEXCEPT + { + return value_range.get().min(); + } + + //************************************************************************* + /// Gets the maximum of the referenced range. + ///\return The maximum value. + //************************************************************************* + ETL_NODISCARD + T max() const ETL_NOEXCEPT + { + return value_range.get().max(); + } + + //************************************************************************* + /// Gets the referenced range. + ///\return The referenced range. + //************************************************************************* + ETL_NODISCARD + const range_type& range() const ETL_NOEXCEPT + { + return value_range.get(); + } + + //************************************************************************* + /// Swaps the values and range references. + ///\param other The value to swap with. + //************************************************************************* + void swap(referenced_clamped_value& other) ETL_NOEXCEPT + { + using ETL_OR_STD::swap; + swap(value, other.value); + swap(value_range, other.value_range); + } + + //************************************************************************* + /// Swaps the values and range references. + ///\param lhs The first value. + ///\param rhs The second value. + //************************************************************************* + friend void swap(referenced_clamped_value& lhs, referenced_clamped_value& rhs) ETL_NOEXCEPT + { + lhs.swap(rhs); + } + + //************************************************************************* + /// Compares current values for equality. + ///\param lhs The left-hand value. + ///\param rhs The right-hand value. + ///\return `true` if the current values are equal. + //************************************************************************* + friend bool operator==(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT + { + #include "private/diagnostic_float_equal_push.h" + return lhs.value == rhs.value; + #include "private/diagnostic_pop.h" + } + + //************************************************************************* + /// Compares current values for inequality. + ///\param lhs The left-hand value. + ///\param rhs The right-hand value. + ///\return `true` if the current values differ. + //************************************************************************* + friend bool operator!=(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT + { + return !(lhs == rhs); + } + + //************************************************************************* + /// Compares current values using less-than. + ///\param lhs The left-hand value. + ///\param rhs The right-hand value. + ///\return `true` if lhs is less than rhs. + //************************************************************************* + friend bool operator<(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT + { + return lhs.value < rhs.value; + } + + //************************************************************************* + /// Compares current values using less-than-or-equal. + ///\param lhs The left-hand value. + ///\param rhs The right-hand value. + ///\return `true` if lhs is less than or equal to rhs. + //************************************************************************* + friend bool operator<=(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT + { + return !(rhs < lhs); + } + + //************************************************************************* + /// Compares current values using greater-than. + ///\param lhs The left-hand value. + ///\param rhs The right-hand value. + ///\return `true` if lhs is greater than rhs. + //************************************************************************* + friend bool operator>(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT + { + return rhs < lhs; + } + + //************************************************************************* + /// Compares current values using greater-than-or-equal. + ///\param lhs The left-hand value. + ///\param rhs The right-hand value. + ///\return `true` if lhs is greater than or equal to rhs. + //************************************************************************* + friend bool operator>=(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT + { + return !(lhs < rhs); + } + + private: + + T value; + etl::reference_wrapper value_range; + }; +#endif } // namespace etl #endif diff --git a/test/syntax_check/clamped_value.h.t.cpp b/test/syntax_check/clamped_value.h.t.cpp index 7651ff8c..31f6d549 100644 --- a/test/syntax_check/clamped_value.h.t.cpp +++ b/test/syntax_check/clamped_value.h.t.cpp @@ -37,3 +37,13 @@ etl::clamped_value clamped_double_run_time; #if ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE etl::clamped_value clamped_float_compile_time; #endif + +#if ETL_USING_CPP11 +const etl::clamped_value_range referenced_clamped_range(0, 10); +etl::referenced_clamped_value referenced_clamped_int(referenced_clamped_range); + + #if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE +const etl::clamped_value_range referenced_clamped_float_range(-1.0f, 1.0f); +etl::referenced_clamped_value referenced_clamped_float(referenced_clamped_float_range); + #endif +#endif diff --git a/test/test_clamped_value.cpp b/test/test_clamped_value.cpp index cdf81420..09a64781 100644 --- a/test/test_clamped_value.cpp +++ b/test/test_clamped_value.cpp @@ -531,5 +531,157 @@ namespace } #endif #endif + +#if ETL_USING_CPP11 + //************************************************************************* + TEST(test_referenced_clamped_value_uses_shared_range) + { + const etl::clamped_value_range range(2, 7); + etl::referenced_clamped_value value1(range, 1); + etl::referenced_clamped_value value2(range, 8); + + CHECK_EQUAL(2, value1.get()); + CHECK_EQUAL(7, value2.get()); + CHECK_EQUAL(2, value1.min()); + CHECK_EQUAL(7, value1.max()); + CHECK_EQUAL(&range, &value1.range()); + CHECK_EQUAL(&range, &value2.range()); + } + + //************************************************************************* + TEST(test_referenced_clamped_value_arithmetic_saturates) + { + const etl::clamped_value_range range(-2, 3); + etl::referenced_clamped_value value(range, 0); + + value += 2; + CHECK_EQUAL(2, value.get()); + ++value; + ++value; + CHECK_EQUAL(3, value.get()); + value -= 4; + CHECK_EQUAL(-1, value.get()); + --value; + --value; + CHECK_EQUAL(-2, value.get()); + } + + //************************************************************************* + TEST(test_referenced_clamped_value_modifiers_and_postfix_operators) + { + const etl::clamped_value_range range(2, 7); + etl::referenced_clamped_value value(range); + + CHECK_EQUAL(2, value.get()); + value.set(4); + CHECK_EQUAL(4, value.get()); + + etl::referenced_clamped_value previous = value++; + CHECK_EQUAL(4, previous.get()); + CHECK_EQUAL(5, value.get()); + + previous = value--; + CHECK_EQUAL(5, previous.get()); + CHECK_EQUAL(4, value.get()); + + value.to_max(); + CHECK_EQUAL(7, value.get()); + value.to_min(); + CHECK_EQUAL(2, value.get()); + + value = 100; + CHECK_EQUAL(7, value.get()); + } + + //************************************************************************* + TEST(test_referenced_clamped_value_default_range_and_comparisons) + { + const etl::clamped_value_range default_range; + const etl::clamped_value_range range(0, 10); + etl::referenced_clamped_value value1(range, 3); + etl::referenced_clamped_value value2(range, 4); + const int converted = value1; + + CHECK_EQUAL(etl::numeric_limits::lowest(), default_range.min()); + CHECK_EQUAL(etl::numeric_limits::max(), default_range.max()); + CHECK_EQUAL(3, converted); + CHECK(value1 == value1); + CHECK(value1 != value2); + CHECK(value1 < value2); + CHECK(value1 <= value2); + CHECK(value2 > value1); + CHECK(value2 >= value1); + CHECK(value1 < 4); + CHECK(3 == value1); + } + + //************************************************************************* + TEST(test_referenced_clamped_value_copy_assignment_and_swap_ranges) + { + const etl::clamped_value_range range1(0, 10); + const etl::clamped_value_range range2(20, 30); + etl::referenced_clamped_value value1(range1, 4); + etl::referenced_clamped_value value2(range2, 24); + + etl::referenced_clamped_value copy(value1); + CHECK_EQUAL(&range1, ©.range()); + CHECK_EQUAL(4, copy.get()); + + copy = value2; + CHECK_EQUAL(&range2, ©.range()); + CHECK_EQUAL(24, copy.get()); + + swap(value1, value2); + CHECK_EQUAL(&range2, &value1.range()); + CHECK_EQUAL(24, value1.get()); + CHECK_EQUAL(&range1, &value2.range()); + CHECK_EQUAL(4, value2.get()); + } + + //************************************************************************* + TEST(test_referenced_clamped_value_rejects_invalid_range) + { + CHECK_THROW((etl::clamped_value_range(7, 2)), etl::exception); + } + + //************************************************************************* + TEST(test_referenced_clamped_value_rejects_temporary_range) + { + typedef etl::clamped_value_range range_type; + typedef etl::referenced_clamped_value value_type; + + CHECK_FALSE((etl::is_constructible::value)); + CHECK_FALSE((etl::is_constructible::value)); + } + + #if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE + //************************************************************************* + TEST(test_referenced_floating_clamped_value) + { + const etl::clamped_value_range range(-1.5F, 2.5F); + etl::referenced_clamped_value value(range, 0.5F); + + value += 1.25F; + CHECK_CLOSE(1.75F, value.get(), 0.0001F); + value.advance(10.0F); + CHECK_CLOSE(2.5F, value.get(), 0.0001F); + } + + //************************************************************************* + TEST(test_referenced_floating_clamped_value_rejects_nan) + { + const float nan = etl::numeric_limits::quiet_NaN(); + + CHECK_THROW((etl::clamped_value_range(nan, 1.0F)), etl::exception); + + const etl::clamped_value_range range(-1.0F, 1.0F); + CHECK_THROW((etl::referenced_clamped_value(range, nan)), etl::exception); + + etl::referenced_clamped_value value(range, 0.0F); + CHECK_THROW(value = nan, etl::exception); + CHECK_THROW(value.advance(nan), etl::exception); + } + #endif +#endif } } // namespace From c313e6b98ca2d75b68970cb0b078cc2a5ebebc7c Mon Sep 17 00:00:00 2001 From: Sergei Date: Sun, 27 Sep 2026 11:42:34 +0300 Subject: [PATCH 07/16] Add docs and tutorial for etl::intrusive_avl_tree (#1608) * Add docs and tutorial for etl::intrusive_avl_tree Documents the intrusive AVL tree added in PR #1425: a full API reference page under Containers/Trees, and a tutorial demonstrating ordered access to intrusively-linked objects with unpredictable lifetimes, including move support and destructor-driven automatic removal from the tree. Co-Authored-By: Claude Sonnet 5 * Add complexity shortcode and document its usage Introduces a {{< complexity >}} Hugo shortcode (with light/dark styling for constant/logarithmic/linear/linearithmic/polynomial/exponential classes) for rendering Big-O complexity inline, applies it throughout the intrusive_avl_tree API reference in place of raw "O(...)" text, and documents the Description/Complexity structure and supported classes in DOCUMENTATION.md. Co-Authored-By: Claude Sonnet 5 * Fix issues found by @CodeRabbit * Put complexity notes on their own lines in intrusive_avl_tree docs Move the complexity shortcode from the inline "**Complexity:**" label to its own line under a "**Complexity**" label. Notes that give several alternatives (e.g. amortized vs. worst-case) now show each one on a separate line. Update the guideline in DOCUMENTATION.md to match. * Update complexity examples in DOCUMENTATION.md Add the hard line break after the "**Complexity**" label in the example, and rewrite the list of supported complexity classes in the new two-line format, including an example with several alternatives. * Add logarithmic complexity example to DOCUMENTATION.md --------- Co-authored-by: Claude Sonnet 5 Co-authored-by: John Wellbelove --- DOCUMENTATION.md | 32 + docs/containers/trees/_index.md | 6 + docs/containers/trees/intrusive-avl-tree.md | 560 ++++++++++++++++++ docs/tutorials/_index.md | 5 + docs/tutorials/intrusive-avl-tree-tutorial.md | 210 +++++++ docs/utilities/intrusive-links.md | 2 + hugo/assets/css/custom.css | 76 +++ hugo/layouts/shortcodes/complexity.html | 3 + 8 files changed, 894 insertions(+) create mode 100644 docs/containers/trees/_index.md create mode 100644 docs/containers/trees/intrusive-avl-tree.md create mode 100644 docs/tutorials/intrusive-avl-tree-tutorial.md create mode 100644 hugo/layouts/shortcodes/complexity.html diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 2da43c6b..3b2c2ca6 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -120,6 +120,38 @@ The documentation is written in [Goldmark](https://github.com/teekennedy/goldmar Hugo uses [Goldmark](https://github.com/teekennedy/goldmark-markdown) as its default Markdown processor for versions 0.60.0 and newer. It is built into Hugo, providing high-performance and fully [CommonMark](https://commonmark.org/)-compliant rendering, along with support for [GitHub](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax) Markdown. +### Documenting Algorithmic Complexity + +When documenting the complexity of an operation, use the `complexity` shortcode instead of writing raw Big-O text. +Place it on its own `**Complexity**` lines, right after the `**Description**` line/paragraph, for example: + +```markdown +**Description** +Returns the number of linked items. + +**Complexity** +{{< complexity "constant" "1" >}} +``` + +The shortcode takes two arguments: a complexity class (used to color and label it) and the expression to show +inside `O(...)`. The supported classes are: + +```markdown +**Complexity** +{{< complexity "constant" "1" >}} +**Complexity** +{{< complexity "linear" "N" >}}, where N is number of items in the tree, and assuming comparator is {{< complexity "constant" "1" >}}. +**Complexity** +{{< complexity "logarithmic" "log N" >}}, where N is number of items in the tree. +**Complexity** +{{< complexity "linearithmic" "N log N" >}}, where N is number of items in the tree. +**Complexity** +{{< complexity "polynomial" "N²" >}}, where N is number of items in the tree. +**Complexity** +{{< complexity "constant" "1" >}} when something, or +{{< complexity "exponential" "2ᴺ" >}} some alternative, where N is number of items in the tree. +``` + ### Starting a New Page A template file is provided at `docs/page-template.md` to use as a starting point. Copy it to the diff --git a/docs/containers/trees/_index.md b/docs/containers/trees/_index.md new file mode 100644 index 00000000..c7bd6cc9 --- /dev/null +++ b/docs/containers/trees/_index.md @@ -0,0 +1,6 @@ +--- +title: "Trees" +weight: 100 +--- + +Tree like containers. diff --git a/docs/containers/trees/intrusive-avl-tree.md b/docs/containers/trees/intrusive-avl-tree.md new file mode 100644 index 00000000..ce52af92 --- /dev/null +++ b/docs/containers/trees/intrusive-avl-tree.md @@ -0,0 +1,560 @@ +--- +title: "intrusive_avl_tree" +weight: 1 +--- + +{{< callout type="info">}} + Header: `intrusive_avl_tree.h` + Similar to: `std::set` / `std::map` (ordering only, no ownership) +{{< /callout >}} + +An intrusive, self-balancing AVL tree. + +```cpp +template +etl::intrusive_avl_tree +``` + +`TValue` is the type that contains the actual values. It must derive from +`etl::intrusive_avl_tree_base::link_type`, exposed as the tree's own +`link_type`. Unlike `etl::intrusive_list`/`etl::intrusive_forward_list`, +which are templated directly on a link type and expect the node to derive +from it publicly, `intrusive_avl_tree` is templated only on `TValue` and an +`ID_`. Its `link_type` is a deliberately opaque wrapper around +`etl::tree_link` that exposes almost nothing as public (or even +protected) API, by design, so that inheriting from it imposes as little +"intrusive"-ness as possible, avoids name conflicts, and hides implementation +details. + +As with the other intrusive containers, an object may inherit multiple +`link_type`s with different `ID_` values to be a member of several trees (or +lists) at once: + +```cpp +typedef etl::intrusive_avl_tree_base<0>::link_type FirstLink; +typedef etl::intrusive_avl_tree_base<1>::link_type SecondLink; + +struct Item : public FirstLink, public SecondLink +{ + int value; +}; + +etl::intrusive_avl_tree tree0; +etl::intrusive_avl_tree tree1; +``` + +See also: +[Intrusive Links]({{% relref "../../utilities/intrusive-links.md" %}}) +[intrusive_list]({{% relref "../lists/intrusive-list.md" %}}) +[intrusive_forward_list]({{% relref "../lists/intrusive-forward-list.md" %}}) + +There is no class-level comparator template parameter. Every ordering +operation instead takes its own comparator functor at the call site. +Lookup functions (`find`, `lower_bound`, `upper_bound`, `find_or_insert`) +take a unary comparator accepting a single `const value_type&` and returning +an `int`: `>0` if the search target is "greater" than the argument, `0` if +found, `<0` if "smaller". The range constructor takes a binary comparator +`int(const value_type& lhs, const value_type& rhs)`; `etl::compare::cmp` +can be used directly if `value_type` defines `<`. + +## Member types + +```cpp +link_type (nested, inherited from etl::intrusive_avl_tree_base) +value_type TValue +pointer value_type* +const_pointer const value_type* +reference value_type& +const_reference const value_type& +size_type size_t +rvalue_reference value_type&& // C++11 +``` + +## The link type + +`TValue` must derive from `link_type`. It has its own set of special member +functions that a node author relies on. + +```cpp +link_type() +``` +**Description** +Default constructor. Leaves the item unlinked. + +--- + +```cpp +link_type(link_type&& other) +``` +**Description** +C++11. Constructs a new item by moving `other` into `this`. After +construction, `this` replaces `other` in the same tree position that +`other` occupied, so no tree balancing is needed. `other` becomes unlinked. + +**Complexity** +{{< complexity "constant" "1" >}} + +--- + +```cpp +link_type& operator=(link_type&& other) +``` +**Description** +C++11. Assigns `other` by moving it into `this`. Does nothing on +self-assignment. After assignment, `this` replaces `other` in the same tree +position that `other` occupied; `other` becomes unlinked. `this` might end +up in a different tree than it started in. + +**Complexity** +{{< complexity "logarithmic" "log N" >}} if `this` is already linked to a tree (it has to be erased first, with rebalancing), or +{{< complexity "constant" "1" >}} if `this` is not linked, where `N` is number of items in the tree. + +--- + +```cpp +link_type(const link_type&) = delete; +link_type& operator=(const link_type&) = delete; +``` +**Description** +C++11. Copy construction and assignment are disabled. + +--- + +```cpp +~link_type() +``` +**Description** +Might rotate the tree as necessary to keep it balanced. The tree is not the +real owner of its nodes' memory - the node's actual owner may destroy it at +any time, with or without a prior explicit `erase`. If the link is still +linked to a tree when it's destroyed, the destructor unlinks it +automatically; otherwise the node's former parent and children would keep +dangling pointers to it, breaking the tree and leading to undefined +behaviour. This is what makes it safe to embed a tree node in an object +whose lifetime is managed elsewhere. + +**Complexity** +{{< complexity "logarithmic" "log N" >}}, where `N` is number of items in the tree. + +## Constructors + +```cpp +etl::intrusive_avl_tree() +``` +**Description** +Default constructor. Constructs an empty tree. + +--- + +```cpp +template +etl::intrusive_avl_tree(TIterator first, TIterator last, TBinaryCompare binary_comp) +``` +**Description** +Constructs the tree from the range [`first`, `last`) of items, using +`binary_comp` to order them. All items in the range must be unlinked +initially; if any item is already linked to some other tree, throws +`etl::intrusive_avl_tree_value_is_already_linked`, and any items already +inserted are unlinked again - it's all or nothing. +If duplicates should be kept, `binary_comp` should return a non-zero result +even for "equal" items - `<0` prepends a duplicate, `>0` appends one. +Returning `0` for equal items keeps only the first of each run of duplicates +in the range; the rest are left unlinked. `etl::compare::cmp` +can be used directly as `binary_comp`. + +**Complexity** +{{< complexity "linearithmic" "N log N" >}}, where `N` is number of items in the range. + +--- + +```cpp +intrusive_avl_tree(intrusive_avl_tree&&) = default; +``` +**Description** +C++11. Move constructor. + +**Complexity** +{{< complexity "constant" "1" >}} + +--- + +```cpp +intrusive_avl_tree(const intrusive_avl_tree&) = delete; +``` +**Description** +C++11. Copy construction is disabled. + +--- + +```cpp +~intrusive_avl_tree() +``` +**Description** +Every remaining node is unlinked; none of the value objects are destroyed, +since the tree never owns their memory. + +**Complexity** +{{< complexity "linear" "N" >}}, where `N` is number of items in the tree. + +## Assignment + +```cpp +intrusive_avl_tree& operator=(intrusive_avl_tree&&) = default; +``` +**Description** +C++11. Move assignment. Does nothing on self-assignment. All of `this` +tree's former items have to be unlinked. + +**Complexity** +{{< complexity "linear" "N" >}}, where `N` is the size of `this` tree before assignment. + +--- + +```cpp +intrusive_avl_tree& operator=(const intrusive_avl_tree&) = delete; +``` +**Description** +C++11. Copy assignment is disabled. + +## Capacity + +```cpp +bool empty() const +``` +**Description** +Returns `true` if the tree has no linked items. + +**Complexity** +{{< complexity "constant" "1" >}} + +--- + +```cpp +size_t size() const +``` +**Description** +Returns the number of linked items. + +**Complexity** +{{< complexity "constant" "1" >}} + +## Iterators + +Only bidirectional `iterator`/`const_iterator` are provided; there is no +`reverse_iterator` (use `visit_in_order` with `is_reverse = true` instead). +An iterator may be in one of three logical states: it references a real +node (`has_value() == true`), it is the terminal sentinel (`it == end()`), +or it is "valueless" (`!has_value() && it != end()` - e.g. a +default-constructed iterator, or the result of `get_parent()`/`get_child()` +when there is no such node). + +```cpp +iterator begin() +const_iterator begin() const +const_iterator cbegin() const +``` +**Description** +Returns an iterator to the smallest item. Returns `end()` if the tree is +empty. + +**Complexity** +{{< complexity "logarithmic" "log N" >}}, where `N` is number of items in the tree. + +--- + +```cpp +iterator end() +const_iterator end() const +const_iterator cend() const +``` +**Description** +Returns an iterator to the terminal sentinel. + +**Complexity** +{{< complexity "constant" "1" >}} + +--- + +```cpp +iterator min() +const_iterator min() const +``` +**Description** +Equivalent to `begin()`. + +**Complexity** +{{< complexity "logarithmic" "log N" >}}, where `N` is number of items in the tree. + +--- + +```cpp +iterator max() +const_iterator max() const +``` +**Description** +Equivalent to `--end()`. + +**Complexity** +{{< complexity "logarithmic" "log N" >}}, where `N` is number of items in the tree. + +--- + +```cpp +iterator& operator++() +iterator operator++(int) +``` +**Description** +Advances the iterator to the next ("greater") item. + +**Complexity** +{{< complexity "constant" "1" >}} amortized, +{{< complexity "logarithmic" "log N" >}} worst-case, where `N` is number of items in the tree. + +--- + +```cpp +iterator& operator--() +iterator operator--(int) +``` +**Description** +Moves the iterator to the previous ("smaller") item. + +**Complexity** +{{< complexity "constant" "1" >}} amortized, +{{< complexity "logarithmic" "log N" >}} worst-case, where `N` is number of items in the tree. + +--- + +```cpp +reference operator*() const +pointer operator->() const +pointer get() const +``` +**Description** +Dereferences an iterator that has a value. `operator*`/`operator->` throw +`etl::intrusive_avl_tree_iterator_exception` if `has_value()` is `false`. +`get()` returns `nullptr` instead of throwing. + +--- + +```cpp +bool has_value() const +explicit operator bool() const +``` +**Description** +Returns `true` if the iterator references a real node. + +## Advanced traversal + +These are normally only needed for advanced traversal. + +```cpp +iterator get_root() +const_iterator get_root() const +``` +**Description** +Returns an iterator to the root node. Valueless (`has_value() == false`) if +the tree is empty. + +**Complexity** +{{< complexity "constant" "1" >}} + +--- + +```cpp +int_fast8_t get_balance_factor() const +``` +**Description** +Returns the node's AVL balance factor: `-1`, `0` or `+1`. + +**Complexity** +{{< complexity "constant" "1" >}} + +--- + +```cpp +iterator get_parent() const +``` +**Description** +Returns an iterator to the node's parent. Valueless if there is no parent. + +**Complexity** +{{< complexity "constant" "1" >}} + +--- + +```cpp +iterator get_child(bool is_right) const +``` +**Description** +Returns an iterator to the node's left (`is_right == false`) or right +(`is_right == true`) child. Valueless if there is no such child. + +**Complexity** +{{< complexity "constant" "1" >}} + +## Lookup + +```cpp +template +iterator find(TCompare comp) +template +const_iterator find(TCompare comp) const +``` +**Description** +Finds an item using the unary comparator `comp`. Returns `end()` if there +is no matching item. + +**Complexity** +{{< complexity "logarithmic" "log N" >}}, where `N` is number of items in the tree, +and assuming `comp` is {{< complexity "constant" "1" >}}. + +--- + +```cpp +template +iterator lower_bound(TCompare comp) +template +const_iterator lower_bound(TCompare comp) const +``` +**Description** +Returns an iterator to the first item that compares as "not less" than the +target of `comp`. Returns `end()` if no such item exists. + +**Complexity** +{{< complexity "logarithmic" "log N" >}}, where `N` is number of items in the tree, +and assuming `comp` is {{< complexity "constant" "1" >}}. + +--- + +```cpp +template +iterator upper_bound(TCompare comp) +template +const_iterator upper_bound(TCompare comp) const +``` +**Description** +Returns an iterator to the first item that compares as "greater" than the +target of `comp`. Returns `end()` if no such item exists. + +**Complexity** +{{< complexity "logarithmic" "log N" >}}, where `N` is number of items in the tree, +and assuming `comp` is {{< complexity "constant" "1" >}}. + +## Modifiers + +```cpp +template +etl::pair find_or_insert(TCompare comp, TFactory factory) +``` +**Description** +Finds an existing item using the unary comparator `comp`; if not found, +calls `factory()` (no arguments) and inserts the item it returns at the +position where the search stopped. This operation never invalidates +existing iterators, though depending on where the new item was linked, +existing iterators may skip over it. +`factory` must return the address of the new value (castable to +`link_type*`). If it returns `nullptr`, the tree is left unmodified and the +result iterator is valueless. If the returned value is already linked to +any tree, throws `etl::intrusive_avl_tree_value_is_already_linked`. +Returns a pair of: an iterator to the found or inserted item (never `end()`, +but may be valueless), and a `bool` that is `true` if the tree was modified. +If duplicates are wanted, `comp` should return a non-zero result even for +"equal" items - `+1` appends after existing duplicates, `-1` prepends +before them. + +**Complexity** +{{< complexity "logarithmic" "log N" >}}, where `N` is number of items in the tree, +and assuming `comp` and `factory` are {{< complexity "constant" "1" >}}. + +--- + +```cpp +iterator erase(iterator position) +iterator erase(const_iterator position) +``` +**Description** +Erases the item at `position`, including tree rebalancing. `position` must +reference a real item (asserts/throws +`etl::intrusive_avl_tree_iterator_exception` otherwise) and must originate +from this tree instance. Invalidates any existing iterator to the erased +item, but no others. Returns an iterator to the next node. +Use `clear()` instead if every item needs to be erased - no rebalancing is +involved. + +**Complexity** +{{< complexity "logarithmic" "log N" >}}, where `N` is number of items in the tree. + +--- + +```cpp +void clear() +``` +**Description** +Unlinks every item, leaving the tree empty. Invalidates all existing +iterators. This is cheaper than calling `erase` on every item, which would +cost {{< complexity "linearithmic" "N log N" >}} once intermediate +rebalancing is taken into account. + +**Complexity** +{{< complexity "linear" "N" >}}, where `N` is number of items in the tree. + +--- + +```cpp +void swap(intrusive_avl_tree& other) +friend void swap(intrusive_avl_tree& lhs, intrusive_avl_tree& rhs) +``` +**Description** +Swaps the contents of two trees. Does nothing on self-swap. + +**Complexity** +{{< complexity "constant" "1" >}} + +## Traversal + +Each of these visits every item exactly once, without recursion, and has a +`const` overload. The visitor must not modify the tree during visitation. + +```cpp +template +void visit_in_order(bool is_reverse, Visitor visitor) +``` +**Description** +Visits every item in sorted order. `is_reverse` selects ascending (`false`) +or descending (`true`) order. + +**Complexity** +{{< complexity "linear" "N" >}}, where `N` is number of items in the tree. + +--- + +```cpp +template +void visit_pre_order(bool is_reverse, Visitor visitor) +``` +**Description** +Visits every item in pre-order (parent before children). + +**Complexity** +{{< complexity "linear" "N" >}}, where `N` is number of items in the tree. + +--- + +```cpp +template +void visit_post_order(bool is_reverse, Visitor visitor) +``` +**Description** +Visits every item in post-order (children before parent). + +**Complexity** +{{< complexity "linear" "N" >}}, where `N` is number of items in the tree. + +## Exceptions + +```cpp +intrusive_avl_tree_exception // base exception +intrusive_avl_tree_iterator_exception // invalid iterator use +intrusive_avl_tree_value_is_already_linked // item already linked to a tree +``` diff --git a/docs/tutorials/_index.md b/docs/tutorials/_index.md index 43e6530f..d41ac916 100644 --- a/docs/tutorials/_index.md +++ b/docs/tutorials/_index.md @@ -13,6 +13,11 @@ A quick introduction to containers. --- +[Intrusive AVL Tree](./intrusive-avl-tree-tutorial) +Ordering objects that already exist, without a second container. + +--- + [observer](./observer-tutorial) A templated set of classes to allow easier implementation of the observer pattern. diff --git a/docs/tutorials/intrusive-avl-tree-tutorial.md b/docs/tutorials/intrusive-avl-tree-tutorial.md new file mode 100644 index 00000000..033ff380 --- /dev/null +++ b/docs/tutorials/intrusive-avl-tree-tutorial.md @@ -0,0 +1,210 @@ +--- +title: "Intrusive AVL Tree" +--- + +A tutorial on how `etl::intrusive_avl_tree` can give ordered, +{{< complexity "logarithmic" "log N" >}} access to objects that already +exist elsewhere, without a second container to track them. + +## Overview + +Imagine a set of driver/sensor tasks, each owned by its own subsystem. They +are created and destroyed at unpredictable times as modules attach and +detach, so there is no compile-time upper bound on how many exist at once. +A scheduler needs to visit the tasks that are currently due, in ascending +deadline order, on every tick. + +A normal fixed-capacity ordered container (`etl::set`/`etl::map`) would need +a second, separately-sized place to store pointers to these tasks. Instead, +embedding an `etl::intrusive_avl_tree` link directly into each task object +gives the scheduler ordered, {{< complexity "logarithmic" "log N" >}} +access to exactly the tasks that exist right now - nothing more is +allocated, and nothing needs to be kept in sync by hand. That's the payoff of "intrusive": the tree links the +objects that already exist, rather than storing copies of them. + +The example below also demonstrates two things that fall out of the design +for free: +- **Move support.** When the `std::vector` that owns the tasks reallocates, + every task is moved into new storage - and each move takes over its + task's exact tree position, with no rebalancing and no code of its own. +- **Safe removal without an explicit `erase()` call.** Removing a task's + owning object removes it from the schedule too, with no dangling entry + left behind - whether that's because the object is destroyed while still + linked (`link_type`'s destructor unlinks it automatically), or, as in + this example, because `std::vector::erase` move-assigns another task over + it (`link_type`'s move assignment unlinks `this` before taking over the + moved-from tree position). + +## Example + +```cpp +#include "etl/intrusive_avl_tree.h" + +#include +#include +#include +#include + +// A driver/sensor task that already exists as a long-lived object owned by +// its own subsystem. Inheriting from the tree's link_type is all it takes +// to make it schedulable - no separate fixed-capacity container is needed +// to track which tasks exist. +class SensorTask : public etl::intrusive_avl_tree_base<0>::link_type +{ +public: + SensorTask(uint32_t id_, uint32_t deadline_, uint32_t period_) + : id(id_), deadline(deadline_), period(period_) + { + } + + // Orders primarily by deadline; ties are broken by id so two tasks due + // on the same tick still occupy distinct tree positions. + struct Locate + { + uint32_t target_deadline; + uint32_t target_id; + + int operator()(const SensorTask& node) const + { + if (target_deadline != node.deadline) + { + return (target_deadline < node.deadline) ? -1 : 1; + } + if (target_id != node.id) + { + return (target_id < node.id) ? -1 : 1; + } + return 0; + } + }; + + uint32_t id; + uint32_t deadline; // next tick at which this task must be serviced + uint32_t period; +}; + +typedef etl::intrusive_avl_tree Schedule; + +// The only place a task gets linked - called whenever a module attaches, +// or whenever a task is rescheduled after being serviced. +void schedule_task(Schedule& schedule, SensorTask& task) +{ + schedule.find_or_insert(SensorTask::Locate{ task.deadline, task.id }, + [&task] { return &task; }); +} + +// Visits every currently-due task in ascending deadline order, services it, +// then reschedules it at its next deadline. +void service_due_tasks(Schedule& schedule, uint32_t now) +{ + for (auto it = schedule.begin(); (it != schedule.end()) && (it->deadline <= now); ) + { + SensorTask& task = *it; + it = schedule.erase(it); // O(log N), rebalances + + std::cout << "servicing task " << task.id << " (was due at " << task.deadline << ")\n"; + + task.deadline = now + task.period; + schedule_task(schedule, task); // reschedule at the new deadline + } +} + +void print_schedule(const Schedule& schedule) +{ + for (auto it = schedule.begin(); it != schedule.end(); ++it) + { + std::cout << " task " << it->id << " due at " << it->deadline << "\n"; + } +} + +int main() +{ + Schedule schedule; + + // Driver registry: a plain std::vector that a subsystem uses to own its + // tasks for whatever reason it already needs to - the tree does not + // require this vector to exist, it's just a stand-in for "wherever the + // real owner happens to keep the object". + std::vector driver_registry; + driver_registry.reserve(2); + + driver_registry.emplace_back(1, 100, 1000); + schedule_task(schedule, driver_registry.back()); + + driver_registry.emplace_back(2, 300, 1000); + schedule_task(schedule, driver_registry.back()); + + std::cout << "Initial schedule (ascending deadline):\n"; + print_schedule(schedule); + + std::cout << "\nschedule.size() before growth: " << schedule.size() << "\n"; + + // driver_registry.reserve()'d capacity was 2; this emplace_back forces a + // reallocation. std::vector move-constructs every existing SensorTask + // into the new storage. Each move steals its task's exact tree position + // (O(1), no rebalancing) - the tree needs no code of its own to survive + // the underlying storage moving. + driver_registry.emplace_back(3, 200, 1100); + schedule_task(schedule, driver_registry.back()); + + std::cout << "schedule.size() after growth: " << schedule.size() << "\n\n"; + + std::cout << "Schedule after growth (still correctly ordered):\n"; + print_schedule(schedule); + + std::cout << "\nServicing due tasks at tick 250:\n"; + service_due_tasks(schedule, 250); + + std::cout << "\nSchedule after servicing:\n"; + print_schedule(schedule); + + // Unplugging a module removes its SensorTask from driver_registry. Task 2 + // isn't the last element, so std::vector::erase() doesn't just destroy it + // in place: it move-assigns the following element (task 3) into task 2's + // slot, then destroys the now-duplicate last slot. That move-assignment + // is SensorTask's (implicitly-generated) one, which calls + // link_type::operator=(link_type&&): it first unlinks *this* - task 2's + // slot, still linked at deadline 300 - from the tree, then takes over + // task 3's tree position. The destructor that runs afterwards, on the + // vacated last slot, fires on an already-unlinked, moved-from object, so + // it has nothing left to do - no explicit schedule.erase() call is made + // here, and no dangling entry is left behind. + std::cout << "\nschedule.size() before unplugging task 2: " << schedule.size() << "\n"; + + auto pos = std::find_if(driver_registry.begin(), driver_registry.end(), + [](const SensorTask& t) { return t.id == 2; }); + driver_registry.erase(pos); // move-assigns task 3 over task 2, unlinking task 2 + + std::cout << "schedule.size() after unplugging task 2: " << schedule.size() << "\n"; + + return 0; +} +``` + +## Program output + +``` +Initial schedule (ascending deadline): + task 1 due at 100 + task 2 due at 300 + +schedule.size() before growth: 2 +schedule.size() after growth: 3 + +Schedule after growth (still correctly ordered): + task 1 due at 100 + task 3 due at 200 + task 2 due at 300 + +Servicing due tasks at tick 250: +servicing task 1 (was due at 100) +servicing task 3 (was due at 200) + +Schedule after servicing: + task 2 due at 300 + task 1 due at 1250 + task 3 due at 1350 + +schedule.size() before unplugging task 2: 3 +schedule.size() after unplugging task 2: 2 +``` diff --git a/docs/utilities/intrusive-links.md b/docs/utilities/intrusive-links.md index 6ee73e3f..ac16e511 100644 --- a/docs/utilities/intrusive-links.md +++ b/docs/utilities/intrusive-links.md @@ -297,6 +297,8 @@ template struct tree_link ``` +`etl::intrusive_avl_tree` is built on top of `tree_link` - see [intrusive_avl_tree]({{% relref "../containers/trees/intrusive-avl-tree.md" %}}). + ### Template parameters ```cpp const size_t ID_ diff --git a/hugo/assets/css/custom.css b/hugo/assets/css/custom.css index e23c0ebf..ee5b7e09 100644 --- a/hugo/assets/css/custom.css +++ b/hugo/assets/css/custom.css @@ -420,4 +420,80 @@ tr td code{ font-size: 0.8rem !important; } +.complexity { + padding: 3px 6px; + border-radius: 999px; + font-weight: 600; + letter-spacing: 0.04em; + border: 1px solid transparent; +} +html.dark .complexity { + &.constant { + color: #7CFFB2; + background: rgba(0,255,120,0.08); + border-color: rgba(0,255,120,0.25); + } + &.logarithmic { + color: #7CE7FF; + background: rgba(0,200,255,0.08); + border-color: rgba(0,200,255,0.18); + } + &.linear { + color: #6AB7FF; + background: rgba(80,140,255,0.08); + border-color: rgba(80,140,255,0.18); + } + &.linearithmic { + color: #B78CFF; + background: rgba(140,80,255,0.08); + border-color: rgba(140,80,255,0.18); + } + &.polynomial { + color: #FFD36A; + background: rgba(255,190,0,0.08); + border-color: rgba(255,190,0,0.18); + } + &.exponential { + color: #ff7d9c; + background: rgba(255,0,80,0.08); + border-color: rgba(255,0,80,0.2); + } +} +html:not(.dark) .complexity { + &.constant { + color: #15803D; + background: rgba(34,197,94,0.10); + border-color: rgba(34,197,94,0.22); + } + + &.logarithmic { + color: #0E7490; + background: rgba(6,182,212,0.10); + border-color: rgba(6,182,212,0.22); + } + + &.linear { + color: #2563EB; + background: rgba(59,130,246,0.10); + border-color: rgba(59,130,246,0.22); + } + + &.linearithmic { + color: #7C3AED; + background: rgba(139,92,246,0.10); + border-color: rgba(139,92,246,0.22); + } + + &.polynomial { + color: #CA8A04; + background: rgba(234,179,8,0.12); + border-color: rgba(234,179,8,0.24); + } + + &.exponential { + color: #DC2626; + background: rgba(239,68,68,0.10); + border-color: rgba(239,68,68,0.22); + } +} diff --git a/hugo/layouts/shortcodes/complexity.html b/hugo/layouts/shortcodes/complexity.html new file mode 100644 index 00000000..644440ab --- /dev/null +++ b/hugo/layouts/shortcodes/complexity.html @@ -0,0 +1,3 @@ + + 𝐎({{ .Get 1 }}) + From f0d9d681a87dceb7161407d20d9fddb33e3081dd Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Mon, 28 Sep 2026 10:38:08 +0100 Subject: [PATCH 08/16] Updated sponsors pages --- docs/business-and-financial-case-for-commercial-sponsorship.md | 2 +- docs/sponsors.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/business-and-financial-case-for-commercial-sponsorship.md b/docs/business-and-financial-case-for-commercial-sponsorship.md index aa7683f4..956bbea3 100644 --- a/docs/business-and-financial-case-for-commercial-sponsorship.md +++ b/docs/business-and-financial-case-for-commercial-sponsorship.md @@ -2,7 +2,7 @@ title: "Why CFOs should care about Open Source" description: "The business and financial case for commercially sponsoring the Embedded Template Library (ETL)." date: 2026-09-12 -weight: 3003 +weight: 2 --- If your engineering team is using the **Embedded Template Library (ETL)** in commercial products, your company is benefiting from a critical piece of software infrastructure. diff --git a/docs/sponsors.md b/docs/sponsors.md index 25d7fb00..77be5bbc 100644 --- a/docs/sponsors.md +++ b/docs/sponsors.md @@ -1,6 +1,6 @@ --- title: "Sponsors of the ETL" -weight: 3002 +weight: 3 --- A special thank you to all of our past and current sponsors. Your generosity directly funds the continuous development and maintenance of the ETL. From 202f997e666f2815fd21f9be26571423a8700d9f Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Mon, 28 Sep 2026 09:57:31 +0100 Subject: [PATCH 09/16] Refactor the documentation css to group styles for light and dark themes --- hugo/assets/css/custom.css | 679 ++++++++++++++++++++----------------- 1 file changed, 373 insertions(+), 306 deletions(-) diff --git a/hugo/assets/css/custom.css b/hugo/assets/css/custom.css index ee5b7e09..bbddd9ab 100644 --- a/hugo/assets/css/custom.css +++ b/hugo/assets/css/custom.css @@ -1,222 +1,6 @@ -/* Definitions for light mode */ -html:not(.dark) .highlight .chroma .c1 -{ - color: forestgreen !important; -} - -html:not(.dark) .highlight .chroma .cp, -html:not(.dark) .highlight .chroma .cpf { - color: mediumslateblue !important; -} - -html:not(.dark) h1 { - color: white !important; - width: 100%; - background-color: coral !important; - border: width 0.15rem solid rgb(163, 81, 51) !important; - padding: 0.5rem 0.5rem !important; -} - -html:not(.dark) h2 { - color: royalblue !important; - border-bottom: 3px solid black !important; -} - -html:not(.dark) h3, -html:not(.dark) h4 -{ - color: darkgoldenrod !important; - border-bottom: 2px solid darkgrey !important; -} - -html:not(.dark) p { - color: black !important; -} - -html:not(.dark) dl, -html:not(.dark) dl dt code, -html:not(.dark) dl dd { - color: black !important; - background-color: #eeeeee !important; -} - -html:not(.dark) code, -html:not(.dark) pre, -html:not(.dark) pre code { - color: black !important; - background-color: #eeeeee !important; -} - -html:not(.dark) hr { - background-color: #eeeeee !important; -} - -html:not(.dark) .hx\:font-extrabold { - font-family: Roboto, sans-serif !important; - font-size: 1.6rem !important; - color: royalblue !important; -} - -/* inactive */ -html:not(.dark) .hx\:text-gray-500 { - color: black !important; -} - -/* active */ -html:not(.dark) .hextra-sidebar-active-item { - color: black !important; -} - -html:not(.dark) .content p.label-text { - color: darkgoldenrod !important; -} - -/* Regular item hover */ -html:not(.dark) .hextra-sidebar-item a:hover, -html:not(.dark) .hextra-sidebar-item a:hover span { - background-color: rgb(250, 195, 175) !important; - color: black !important; -} - -/* Active item default + hover - must come AFTER regular hover */ -html:not(.dark) .hextra-sidebar-item[data-active="true"] a, -html:not(.dark) .hextra-sidebar-item[data-active="true"] a span, -html:not(.dark) .hextra-sidebar-item[data-active="true"] a:hover, -html:not(.dark) .hextra-sidebar-item[data-active="true"] a:hover span { - background-color: coral !important; - color: white !important; -} - - -/* Definitions for dark mode */ -html.dark .highlight .chroma .c1 -{ - color: lightgreen !important; -} - -html.dark .highlight .chroma .cp, -html.dark.highlight .chroma .cpf { - color: lightsteelblue !important; -} - -html.dark body { - background-color: #1e1e1e !important; -} - -html.dark main { - background-color: #252525 !important; -} - -html.dark .hx\:dark\:shadow-\[0_-12px_16px_\#111\] { - box-shadow: 0 -12px 16px #1e1e1e !important; -} - -html.dark .hx\:dark\:bg-dark { - background-color: #1e1e1e !important; - border-color: grey !important; -} - -html.dark h1 -{ - color: white !important; - width: 100%; - background-color: rgb(177, 87, 54) !important; - padding: 0.5rem 0.5rem !important; -} - -html.dark h2 { - color: #7099c7 !important; - border-bottom: 2px solid #c0c0c0 !important; -} - -html.dark h3, -html.dark h4 { - color: lightgreen !important; - border-bottom: 2px solid #505050 !important; -} - -html.dark p { - color: #c0c0c0 !important; -} - -html.dark p strong { - color: #c4c3a6; -} - -html.dark dl, -html.dark dl dt code, -html.dark dl dd { - color: #c0c0c0 !important; - background-color: #444444 !important; -} - -html.dark code, -html.dark pre, -html.dark pre code { - color: #c0c0c0 !important; - background-color: #444444 !important; -} - -html.dark hr { - background-color: #505050 !important; -} - -html.dark .hx\:font-extrabold { - font-family: Roboto, sans-serif !important; - font-size: 1.6rem !important; - color: #7099c7 !important; -} - -/* inactive */ -html.dark .hx\:text-gray-500 { - color: #c0c0c0 !important; -} - -/* active */ -html.dark .hextra-sidebar-active-item { - color: #c0c0c0 !important; -} - -html.dark .hextra-code-block .highlight .chroma span.p { - color: #c0c0c0; -} - -html.dark .content p.label-text { - color: salmon !important; -} - -/* Regular item hover */ -html.dark .hextra-sidebar-item a:hover, -html.dark .hextra-sidebar-item a:hover span { - background-color: rgb(250, 195, 175) !important; - color: black !important; -} - -/* Active item default + hover - must come AFTER regular hover */ -html.dark .hextra-sidebar-item[data-active="true"] a, -html.dark .hextra-sidebar-item[data-active="true"] a span, -html.dark .hextra-sidebar-item[data-active="true"] a:hover, -html.dark .hextra-sidebar-item[data-active="true"] a:hover span { - background-color: rgb(177, 87, 54) !important; - color: #e0e0e0 !important; -} - -/* Common definitions */ - -@font-face { - font-family: Roboto; - src: url('/fonts/roboto-v51-latin-regular.woff2') format('woff2'); - font-weight:500; - font-style: normal; -} - -@font-face { - font-family: 'Roboto Mono'; - src: url('/fonts/roboto-mono-v31-latin-500.woff2') format('woff2'); - font-weight: nornal; - font-style: normal; -} - +/**********************/ +/* .highlight .chroma */ +/**********************/ .highlight .chroma .k, .highlight .chroma .kc, .highlight .chroma .kd, @@ -233,64 +17,21 @@ html.dark .hextra-sidebar-item[data-active="true"] a:hover span { font-style: normal !important; } -.hx\:mb-0 { - display: block; - width: 100%; +html.dark .highlight .chroma .cp, +html.dark.highlight .chroma .cpf { + color: lightgreen !important; + color: lightsteelblue !important; } -.hextra-code-block { - margin-top: 0.5rem !important; - -} - -/* .highlight .chroma .c1 -{ +html:not(.dark) .highlight .chroma .cp, +html:not(.dark) .highlight .chroma .cpf { color: forestgreen !important; -} - -.highlight .chroma .cp, -.highlight .chroma .cpf { color: mediumslateblue !important; -} */ - -html.dark .hextra-code-block .highlight .chroma .cs, -html.dark .hextra-code-block .highlight .chroma .cp, -html.dark .hextra-code-block .highlight .chroma .cpf { - font-weight: normal; -} - -.badges { - display: flex; - flex-wrap: wrap; - gap: 4px; - justify-content: flex-start; - margin-top: 1rem; -} - -.badges p { - display: contents; - margin: 0; -} - -.badges div { - width: 100%; - display: flex; - flex-wrap: wrap; - gap: 4px; -} - -.content ul li { - font-family: Roboto, sans-serif !important; - font-style: normal !important; - font-weight: normal !important; - font-size: 1.1rem !important; -} - -.err { - background-color: transparent !important; - color: inherit !important; } +/***********************/ +/* h1 */ +/***********************/ h1 { font-family: Roboto, sans-serif !important; font-style: i !important; @@ -301,6 +42,25 @@ h1 { border-radius: 0.5rem !important; } +html.dark h1 +{ + color: white !important; + width: 100%; + background-color: rgb(177, 87, 54) !important; + padding: 0.5rem 0.5rem !important; +} + +html:not(.dark) h1 { + color: white !important; + width: 100%; + background-color: coral !important; + border: width 0.15rem solid rgb(163, 81, 51) !important; + padding: 0.5rem 0.5rem !important; +} + +/***********************/ +/* h2 */ +/***********************/ h2 { font-family: Roboto, sans-serif !important; font-style: normal !important; @@ -310,6 +70,19 @@ h2 { margin-bottom: 0.5em !important; } +html.dark h2 { + color: #7099c7 !important; + border-bottom: 2px solid #c0c0c0 !important; +} + +html:not(.dark) h2 { + color: royalblue !important; + border-bottom: 3px solid black !important; +} + +/***********************/ +/* h3 */ +/***********************/ h3 { font-family: Roboto, sans-serif !important; font-style: normal !important; @@ -321,6 +94,20 @@ h3 { #display: inline-block; } +html.dark h3 { + color: lightgreen !important; + border-bottom: 2px solid #505050 !important; +} + +html:not(.dark) h3 +{ + color: darkgoldenrod !important; + border-bottom: 2px solid darkgrey !important; +} + +/***********************/ +/* h4 */ +/***********************/ h4 { font-family: Roboto, sans-serif !important; font-style: normal !important; @@ -331,6 +118,43 @@ h4 { display: inline-block; } +html.dark h4 { + color: lightgreen !important; + border-bottom: 2px solid #505050 !important; +} + +html:not(.dark) h4 +{ + color: darkgoldenrod !important; + border-bottom: 2px solid darkgrey !important; +} + +/***********************/ +/* p */ +/***********************/ +p { + font-family: Roboto, sans-serif !important; + font-style: normal !important; + font-weight: normal !important; + font-size: 1.1rem !important; + margin-top: 0.5em !important; +} + +html.dark p { + color: #c0c0c0 !important; +} + +html.dark p strong { + color: #c4c3a6; +} + +html:not(.dark) p { + color: black !important; +} + +/***********************/ +/* dl */ +/***********************/ dl { font-family: 'Roboto Mono', monospace !important; font-style: normal !important; @@ -352,27 +176,23 @@ dl dd { font-size: 1rem !important; } -ol { - font-family: 'Roboto Mono', monospace !important; - font-style: normal !important; - font-weight: normal !important; - font-size: 1rem !important; +html.dark dl, +html.dark dl dt code, +html.dark dl dd { + color: #c0c0c0 !important; + background-color: #444444 !important; } -ol li { - font-family: Roboto, sans-serif !important; - font-style: normal !important; - font-weight: normal !important; - font-size: 1rem !important; -} - -ol li code { - font-family: 'Roboto Mono', monospace !important; - font-style: normal !important; - font-weight: normal !important; - font-size: 1rem !important; +html:not(.dark) dl, +html:not(.dark) dl dt code, +html:not(.dark) dl dd { + color: black !important; + background-color: #eeeeee !important; } +/***********************/ +/* pre & code */ +/***********************/ code { font-family: 'Roboto Mono', monospace !important; font-weight: normal !important; @@ -381,15 +201,6 @@ code { border: none !important; } -pre code { - font-family: 'Roboto Mono', monospace !important; - font-style: normal !important; - font-weight: normal !important; - font-size: 0.9rem !important; - margin-left: 0 !important; - border: none !important; -} - pre { font-family: 'Roboto Mono', monospace !important; font-size: 0.9rem !important; @@ -400,6 +211,32 @@ pre { border: none !important; } +pre code { + font-family: 'Roboto Mono', monospace !important; + font-style: normal !important; + font-weight: normal !important; + font-size: 0.9rem !important; + margin-left: 0 !important; + border: none !important; +} + +html.dark code, +html.dark pre, +html.dark pre code { + color: #c0c0c0 !important; + background-color: #444444 !important; +} + +html:not(.dark) code, +html:not(.dark) pre, +html:not(.dark) pre code { + color: black !important; + background-color: #eeeeee !important; +} + +/***********************/ +/* hr */ +/***********************/ hr { height: 2px !important; border: none !important; @@ -407,23 +244,154 @@ hr { margin-bottom: 1em !important; } -p { +html.dark hr { + background-color: #505050 !important; +} + +html:not(.dark) hr { + background-color: #eeeeee !important; +} + +/***********************/ +/* body */ +/***********************/ +html.dark body { + background-color: #1e1e1e !important; +} + +/***********************/ +/* .hx\:font-extrabold */ +/***********************/ +html.dark .hx\:font-extrabold { font-family: Roboto, sans-serif !important; - font-style: normal !important; - font-weight: normal !important; - font-size: 1.1rem !important; - margin-top: 0.5em !important; + font-size: 1.6rem !important; + color: #7099c7 !important; } -tr td code{ - font-family: 'Roboto Mono', monospace !important; - font-size: 0.8rem !important; +html:not(.dark) .hx\:font-extrabold { + font-family: Roboto, sans-serif !important; + font-size: 1.6rem !important; + color: royalblue !important; } +/***********************/ +/* .hx\:text-gray-500 */ +/* inactive */ +/***********************/ +html.dark .hx\:text-gray-500 { + color: #c0c0c0 !important; +} + +html:not(.dark) .hx\:text-gray-500 { + color: black !important; +} + +/*******************************/ +/* .hextra-sidebar-active-item */ +/* active */ +/*******************************/ +html.dark .hextra-sidebar-active-item { + color: #c0c0c0 !important; +} + +html:not(.dark) .hextra-sidebar-active-item { + color: black !important; +} + +/*************************/ +/* .content p.label-text */ +/*************************/ +html.dark .content p.label-text { + color: salmon !important; +} + +html:not(.dark) .content p.label-text { + color: darkgoldenrod !important; +} + +/********************************/ +/* .hextra-sidebar-item a:hover */ +/********************************/ +html.dark .hextra-sidebar-item a:hover, +html.dark .hextra-sidebar-item a:hover span { + background-color: rgb(250, 195, 175) !important; + color: black !important; +} + +html:not(.dark) .hextra-sidebar-item a:hover, +html:not(.dark) .hextra-sidebar-item a:hover span { + background-color: rgb(250, 195, 175) !important; + color: black !important; +} + +/*********************************/ +/* .hextra-sidebar-item */ +/* Active item default + hover */ +/* Must come AFTER regular hover */ +/*********************************/ +html.dark .hextra-sidebar-item[data-active="true"] a, +html.dark .hextra-sidebar-item[data-active="true"] a span, +html.dark .hextra-sidebar-item[data-active="true"] a:hover, +html.dark .hextra-sidebar-item[data-active="true"] a:hover span { + background-color: rgb(177, 87, 54) !important; + color: #e0e0e0 !important; +} + +html:not(.dark) .hextra-sidebar-item[data-active="true"] a, +html:not(.dark) .hextra-sidebar-item[data-active="true"] a span, +html:not(.dark) .hextra-sidebar-item[data-active="true"] a:hover, +html:not(.dark) .hextra-sidebar-item[data-active="true"] a:hover span { + background-color: coral !important; + color: white !important; +} + +/***********************/ +/* main */ +/***********************/ +html.dark main { + background-color: #252525 !important; +} + +/***********************/ +/* .hx\:dark\:shadow */ +/***********************/ +html.dark .hx\:dark\:shadow-\[0_-12px_16px_\#111\] { + box-shadow: 0 -12px 16px #1e1e1e !important; +} + +/***********************/ +/* .hx\:dark\:bg-dark */ +/***********************/ +html.dark .hx\:dark\:bg-dark { + background-color: #1e1e1e !important; + border-color: grey !important; +} + +/***********************/ +/* hextra-code-block */ +/***********************/ +.hextra-code-block { + margin-top: 0.5rem !important; +} + +html.dark .hextra-code-block .highlight .chroma span.p { + color: #c0c0c0; +} + +html.dark .hextra-code-block .highlight .chroma .cs, +html.dark .hextra-code-block .highlight .chroma .cp, +html.dark .hextra-code-block .highlight .chroma .cpf { + font-weight: normal; +} + +/***********************/ +/* complexity */ +/***********************/ .complexity { padding: 3px 6px; - border-radius: 999px; + border-radius: 0.5rem !important; font-weight: 600; + font-size: 0.9rem !important; letter-spacing: 0.04em; border: 1px solid transparent; } @@ -460,6 +428,7 @@ html.dark .complexity { border-color: rgba(255,0,80,0.2); } } + html:not(.dark) .complexity { &.constant { color: #15803D; @@ -497,3 +466,101 @@ html:not(.dark) .complexity { border-color: rgba(239,68,68,0.22); } } + +/***********************/ +/* font-face */ +/***********************/ +@font-face { + font-family: Roboto; + src: url('/fonts/roboto-v51-latin-regular.woff2') format('woff2'); + font-weight:500; + font-style: normal; +} + +@font-face { + font-family: 'Roboto Mono'; + src: url('/fonts/roboto-mono-v31-latin-500.woff2') format('woff2'); + font-weight: nornal; + font-style: normal; +} + +/***********************/ +/* .hx\:mb */ +/***********************/ +.hx\:mb-0 { + display: block; + width: 100%; +} + +/***********************/ +/* .badges */ +/***********************/ +.badges { + display: flex; + flex-wrap: wrap; + gap: 4px; + justify-content: flex-start; + margin-top: 1rem; +} + +.badges p { + display: contents; + margin: 0; +} + +.badges div { + width: 100%; + display: flex; + flex-wrap: wrap; + gap: 4px; +} + +/***********************/ +/* .content */ +/***********************/ +.content ul li { + font-family: Roboto, sans-serif !important; + font-style: normal !important; + font-weight: normal !important; + font-size: 1.1rem !important; +} + +/***********************/ +/* .err */ +/***********************/ +.err { + background-color: transparent !important; + color: inherit !important; +} + +/***********************/ +/* ol */ +/***********************/ +ol { + font-family: 'Roboto Mono', monospace !important; + font-style: normal !important; + font-weight: normal !important; + font-size: 1rem !important; +} + +ol li { + font-family: Roboto, sans-serif !important; + font-style: normal !important; + font-weight: normal !important; + font-size: 1rem !important; +} + +ol li code { + font-family: 'Roboto Mono', monospace !important; + font-style: normal !important; + font-weight: normal !important; + font-size: 1rem !important; +} + +/***********************/ +/* tr */ +/***********************/ +tr td code{ + font-family: 'Roboto Mono', monospace !important; + font-size: 0.8rem !important; +} From 3e6b486786752b406b23c5d60fa0fb366c5fa80d Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Tue, 29 Sep 2026 12:37:27 +0100 Subject: [PATCH 10/16] Fix Codacy badge display --- docs/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_index.md b/docs/_index.md index 472fb0ac..dc6fde75 100644 --- a/docs/_index.md +++ b/docs/_index.md @@ -40,7 +40,7 @@ type: hextra-home
coverage - Codacy +Codacy Badge
From 7e328341ae83c301318d2f7c0ba785d2d63593ef Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Wed, 30 Sep 2026 09:26:46 +0200 Subject: [PATCH 11/16] Add be_uint24_t etc. to unaligned_type.h (#1592) * Add be_uint24_t etc. to unaligned_type.h A typical use case is to endian convert 24, 40, 48 bits etc. This change generalizes the pre-existing types in unaligned_type.h to those odd sizes. * Add documentation for odd sized unaligned_type specializations --- docs/types/unaligned-type.md | 314 +++++++++ include/etl/unaligned_type.h | 1061 +++++++++++++++++++++++++++++- test/test_unaligned_type.cpp | 202 ++++++ test/test_unaligned_type_ext.cpp | 244 +++++++ 4 files changed, 1817 insertions(+), 4 deletions(-) diff --git a/docs/types/unaligned-type.md b/docs/types/unaligned-type.md index 28c9f85f..8ae1378f 100644 --- a/docs/types/unaligned-type.md +++ b/docs/types/unaligned-type.md @@ -459,3 +459,317 @@ ETL_CONSTEXPR14 bool operator !=(T lhs, const unaligned_type& rhs) **Description** Inequality operator. Removed: `20.39.3` + +--- + +# Arbitrary width integers + +{{< callout type="info">}} + From: `20.50.0` +{{< /callout >}} + +`etl::unaligned_type` is templated on a fundamental type, so it can only store +integers whose width is that of a standard integer type. For protocols that use +odd widths, such as 24 or 48 bit integers, the following types are templated on +the **number of bytes of storage** instead. + +```cpp +template +class unaligned_uint_type + +template +class unaligned_int_type +``` + +Where `Size_` is the number of bytes of storage and `Endian_` is +`etl::endian::big` or `etl::endian::little`. + +`unaligned_uint_type` stores an unsigned integer. +`unaligned_int_type` stores a signed, two's complement integer, which is sign +extended when read. + +The value is interfaced to via `value_type`: the smallest standard integer type +that can hold `Size_` bytes. For example, `unaligned_uint_type<3U, ...>` has a +`value_type` of `uint32_t`. + +Values that do not fit in `Size_` bytes are truncated, in the same way as a +narrowing `static_cast`. + +Marked as `packed` for MSVC, GCC and Clang. + +## Constants +`int Endian` + The endianness of the type. +`size_t Size` + The size, in bytes, of the type. +`value_type Min_Value` + The lowest value that may be stored. +`value_type Max_Value` + The highest value that may be stored. + +## Predefined types + +The 40, 48 and 56 bit types are only defined if `ETL_USING_64BIT_TYPES`. +The host order types are only defined if `ETL_HAS_CONSTEXPR_ENDIANNESS`. + +### Little Endian +```cpp +le_uint24_t +le_int24_t +le_uint40_t ETL_USING_64BIT_TYPES +le_int40_t ETL_USING_64BIT_TYPES +le_uint48_t ETL_USING_64BIT_TYPES +le_int48_t ETL_USING_64BIT_TYPES +le_uint56_t ETL_USING_64BIT_TYPES +le_int56_t ETL_USING_64BIT_TYPES +``` + +### Big Endian +```cpp +be_uint24_t +be_int24_t +be_uint40_t ETL_USING_64BIT_TYPES +be_int40_t ETL_USING_64BIT_TYPES +be_uint48_t ETL_USING_64BIT_TYPES +be_int48_t ETL_USING_64BIT_TYPES +be_uint56_t ETL_USING_64BIT_TYPES +be_int56_t ETL_USING_64BIT_TYPES +``` + +### Network Order +Synonyms for Big Endian. +```cpp +using net_uint24_t = be_uint24_t +using net_int24_t = be_int24_t +using net_uint40_t = be_uint40_t +using net_int40_t = be_int40_t +using net_uint48_t = be_uint48_t +using net_int48_t = be_int48_t +using net_uint56_t = be_uint56_t +using net_int56_t = be_int56_t +``` + +### Host order +```cpp +host_uint24_t +host_int24_t +host_uint40_t +host_int40_t +host_uint48_t +host_int48_t +host_uint56_t +host_int56_t +``` + +## Constructors + +```cpp +unaligned_uint_type() +unaligned_int_type() +``` +**Description** +Constructs an uninitialised object. + +--- + +```cpp +ETL_CONSTEXPR14 unaligned_uint_type(value_type value) +ETL_CONSTEXPR14 unaligned_int_type(value_type value) +``` +**Description** +Constructs with the supplied value. +Values that do not fit in `Size` bytes are truncated. + +--- + +```cpp +unaligned_uint_type(const void* address) +unaligned_int_type(const void* address) +``` +**Description** +Constructs from the `Size` bytes at the address. + +--- + +```cpp +unaligned_uint_type(const void* address, size_t buffer_size) +unaligned_int_type(const void* address, size_t buffer_size) +``` +**Description** +Constructs from the `Size` bytes at the address. +`buffer_size` must be greater than or equal to `Size`. This is a precondition, +checked by `ETL_ASSERT`. If checks are disabled then a smaller buffer size +results in a read beyond the end of the buffer. + +--- + +```cpp +ETL_CONSTEXPR14 unaligned_uint_type(const unsigned char* address) +ETL_CONSTEXPR14 unaligned_int_type(const unsigned char* address) +``` +**Description** +Constructs from the `Size` bytes at the address. +Unlike the `const void*` overload, this requires no `reinterpret_cast` and so +may be used in a constexpr context. + +--- + +```cpp +ETL_CONSTEXPR14 unaligned_uint_type(const unsigned char* address, size_t buffer_size) +ETL_CONSTEXPR14 unaligned_int_type(const unsigned char* address, size_t buffer_size) +``` +**Description** +Constructs from the `Size` bytes at the address. +`buffer_size` must be greater than or equal to `Size`. This is a precondition, +checked by `ETL_ASSERT`. + +--- + +```cpp +template +unaligned_uint_type(const unaligned_uint_type& other) +``` +**Description** +Constructs from another type of the same size. +The endianness is converted, if necessary. + +## Member functions + +The storage interface (`data()`, `size()`, the iterators and `operator[]`) is +the same as that of `etl::unaligned_type`. + +```cpp +ETL_CONSTEXPR14 unaligned_uint_type& operator =(value_type value) +``` +**Description** +Assignment operator. + +--- + +```cpp +ETL_CONSTEXPR14 operator value_type() const +``` +**Description** +Conversion operator. +For `unaligned_int_type` the stored value is sign extended. + +--- + +```cpp +ETL_CONSTEXPR14 value_type value() const +``` +**Description** +Gets the value. +For `unaligned_int_type` the stored value is sign extended. + +--- + +```cpp +static ETL_CONSTEXPR14 value_type value_from(const_pointer address) +``` +**Description** +Gets the value of the `Size` bytes at the address. +Unlike construction from an address, the bytes are not copied to storage first. + +--- + +```cpp +template +ETL_CONSTEXPR14 unaligned_uint_type& operator =(const unaligned_uint_type& other) +``` +**Description** +Assignment operator from other endianness. + +--- + +# Arbitrary width integers with an external buffer + +{{< callout type="info">}} + From: `20.50.0` +{{< /callout >}} + +```cpp +template +class unaligned_uint_type_ext + +template +class unaligned_int_type_ext +``` + +These are the equivalents of `unaligned_uint_type` and `unaligned_int_type` +that do not own their storage, but reference an externally supplied buffer. +They have the same `value_type`, constants and value interface. + +The default constructor is deleted: the storage must always be supplied. + +### Predefined types +As for the internal storage versions, but with an `_ext_t` suffix. +```cpp +le_uint24_ext_t be_uint24_ext_t net_uint24_ext_t host_uint24_ext_t +le_int24_ext_t be_int24_ext_t net_int24_ext_t host_int24_ext_t +le_uint40_ext_t be_uint40_ext_t net_uint40_ext_t host_uint40_ext_t +le_int40_ext_t be_int40_ext_t net_int40_ext_t host_int40_ext_t +le_uint48_ext_t be_uint48_ext_t net_uint48_ext_t host_uint48_ext_t +le_int48_ext_t be_int48_ext_t net_int48_ext_t host_int48_ext_t +le_uint56_ext_t be_uint56_ext_t net_uint56_ext_t host_uint56_ext_t +le_int56_ext_t be_int56_ext_t net_int56_ext_t host_int56_ext_t +``` + +## Constructors + +```cpp +unaligned_uint_type_ext() ETL_DELETE +``` +**Description** +Deleted. The storage must always be supplied. + +--- + +```cpp +unaligned_uint_type_ext(pointer storage) +``` +**Description** +Constructs, referencing the supplied storage. +The storage is not modified. + +--- + +```cpp +unaligned_uint_type_ext(value_type value, pointer storage) +``` +**Description** +Constructs, referencing the supplied storage, and writes the value to it. + +--- + +```cpp +template +unaligned_uint_type_ext(const unaligned_uint_type_ext& other, pointer storage) +``` +**Description** +Constructs, referencing the supplied storage, and copies the value of `other` +to it, converting the endianness if necessary. +**Note** +The storage of `other` must either be the same storage as that supplied, or +completely separate. Partially overlapping storage is undefined behaviour. + +## Member functions + +```cpp +void set_storage(pointer storage) +``` +**Description** +Sets the storage for the type. +The storage is not modified. + +--- + +```cpp +template +unaligned_uint_type_ext& operator =(const unaligned_uint_type_ext& other) +``` +**Description** +Assignment operator from other endianness. +**Note** +The storage of `other` must either be the same storage as this object's, or +completely separate. Partially overlapping storage is undefined behaviour. diff --git a/include/etl/unaligned_type.h b/include/etl/unaligned_type.h index 6d8ef74c..34c945b0 100644 --- a/include/etl/unaligned_type.h +++ b/include/etl/unaligned_type.h @@ -43,6 +43,7 @@ SOFTWARE. #include "endianness.h" #include "exception.h" #include "file_error_numbers.h" +#include "integral_limits.h" #include "iterator.h" #include "type_traits.h" @@ -463,12 +464,40 @@ namespace etl value = value_from(store); } + //******************************* + /// \note 'src' and 'dst' must either be equal or refer to completely + /// separate storage. Partially overlapping storage is undefined + /// behaviour. //******************************* static ETL_CONSTEXPR14 void copy_store_to_store(const_pointer src, int endian_src, pointer dst) { - for (size_t i = 0UL; i < Size_; ++i) + if (Endian_ == endian_src) { - dst[i] = (Endian_ == endian_src) ? src[i] : src[Size_ - 1U - i]; + if (src != dst) + { + for (size_t i = 0UL; i < Size_; ++i) + { + dst[i] = src[i]; + } + } + } + else + { + // Swap mirrored bytes in pairs, so that the reversal is correct even + // when 'src' and 'dst' are the same storage. + for (size_t i = 0UL; i < (Size_ / 2UL); ++i) + { + const storage_type low = src[i]; + const storage_type high = src[Size_ - 1UL - i]; + + dst[i] = high; + dst[Size_ - 1UL - i] = low; + } + + if ((Size_ % 2UL) != 0UL) + { + dst[Size_ / 2UL] = src[Size_ / 2UL]; + } } } }; @@ -602,12 +631,39 @@ namespace etl // 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. + /// \note 'src' and 'dst' must either be equal or refer to completely + /// separate storage. Partially overlapping storage is undefined + /// behaviour. //******************************* static ETL_CONSTEXPR14 void copy_store_to_store(const_pointer src, int endian_src, pointer dst) { - for (size_t i = 0UL; i < Size_; ++i) + if (Endian_ == endian_src) { - dst[i] = (Endian_ == endian_src) ? src[i] : src[Size_ - 1U - i]; + if (src != dst) + { + for (size_t i = 0UL; i < Size_; ++i) + { + dst[i] = src[i]; + } + } + } + else + { + // Swap mirrored bytes in pairs, so that the reversal is correct even + // when 'src' and 'dst' are the same storage. + for (size_t i = 0UL; i < (Size_ / 2UL); ++i) + { + const storage_type low = src[i]; + const storage_type high = src[Size_ - 1UL - i]; + + dst[i] = high; + dst[Size_ - 1UL - i] = low; + } + + if ((Size_ % 2UL) != 0UL) + { + dst[Size_ / 2UL] = src[Size_ / 2UL]; + } } } }; @@ -787,6 +843,475 @@ namespace etl template ETL_CONSTANT size_t unaligned_type::Size; + namespace private_unaligned_type + { + //************************************************************************* + /// The unsigned integer type used to interface to an unaligned unsigned + /// integer of 'Size' bytes. + /// The smallest standard unsigned integer type that can hold all of the + /// bytes is selected. + //************************************************************************* + template + struct interface_uint; + + template <> + struct interface_uint<1U> + { + typedef uint8_t type; + }; + + template <> + struct interface_uint<2U> + { + typedef uint16_t type; + }; + + template <> + struct interface_uint<3U> + { + typedef uint32_t type; + }; + + template <> + struct interface_uint<4U> + { + typedef uint32_t type; + }; + +#if ETL_USING_64BIT_TYPES + template <> + struct interface_uint<5U> + { + typedef uint64_t type; + }; + + template <> + struct interface_uint<6U> + { + typedef uint64_t type; + }; + + template <> + struct interface_uint<7U> + { + typedef uint64_t type; + }; + + template <> + struct interface_uint<8U> + { + typedef uint64_t type; + }; +#endif + + //************************************************************************* + /// The signed integer type used to interface to an unaligned signed + /// integer of 'Size' bytes. + /// The smallest standard signed integer type that can hold all of the + /// bytes is selected. + //************************************************************************* + template + struct interface_int; + + template <> + struct interface_int<1U> + { + typedef int8_t type; + }; + + template <> + struct interface_int<2U> + { + typedef int16_t type; + }; + + template <> + struct interface_int<3U> + { + typedef int32_t type; + }; + + template <> + struct interface_int<4U> + { + typedef int32_t type; + }; + +#if ETL_USING_64BIT_TYPES + template <> + struct interface_int<5U> + { + typedef int64_t type; + }; + + template <> + struct interface_int<6U> + { + typedef int64_t type; + }; + + template <> + struct interface_int<7U> + { + typedef int64_t type; + }; + + template <> + struct interface_int<8U> + { + typedef int64_t type; + }; +#endif + + //************************************************************************* + /// Sign extends the low 'Size_ * 8' bits of an unsigned value into the + /// full width of the signed interface type. + /// Uses only well defined unsigned arithmetic, so it is usable in a + /// constexpr context. + //************************************************************************* + template + ETL_CONSTEXPR TSigned sign_extend(TUnsigned uvalue) + { + return static_cast(static_cast(static_cast(uvalue ^ (static_cast(1) << ((Size_ * 8U) - 1U))) + - (static_cast(1) << ((Size_ * 8U) - 1U)))); + } + } // namespace private_unaligned_type + + //************************************************************************* + /// unaligned_uint_type + ///\brief Allows an unsigned integer of an arbitrary number of bytes to be + /// stored at an unaligned address. + /// Useful for types that have no native equivalent, such as a 24 bit + /// unsigned integer. + /// The value is interfaced to via the smallest standard unsigned + /// integer type that can hold it. + ///\tparam Size_ The number of bytes of storage. + ///\tparam Endian_ The endianness of the stored integer. + ///\note Values that do not fit in 'Size_' bytes are truncated, in the same + /// way as a narrowing static_cast. + //************************************************************************* + template + ETL_PACKED_CLASS(unaligned_uint_type) + : public private_unaligned_type::unaligned_type_storage + { + public: + + typedef typename private_unaligned_type::interface_uint::type value_type; + + typedef private_unaligned_type::unaligned_copy unaligned_copy; + + typedef typename private_unaligned_type::unaligned_type_storage::storage_type storage_type; + typedef typename private_unaligned_type::unaligned_type_storage::pointer pointer; + typedef typename private_unaligned_type::unaligned_type_storage::const_pointer const_pointer; + typedef typename private_unaligned_type::unaligned_type_storage::iterator iterator; + typedef typename private_unaligned_type::unaligned_type_storage::const_iterator const_iterator; + typedef typename private_unaligned_type::unaligned_type_storage::reverse_iterator reverse_iterator; + typedef typename private_unaligned_type::unaligned_type_storage::const_reverse_iterator const_reverse_iterator; + + static ETL_CONSTANT int Endian = Endian_; + static ETL_CONSTANT size_t Size = Size_; + static ETL_CONSTANT value_type Min_Value = static_cast(0); + static ETL_CONSTANT value_type Max_Value = static_cast(etl::integral_limits::max >> ((sizeof(value_type) - Size_) * 8U)); + + //************************************************************************* + /// Default constructor + //************************************************************************* + unaligned_uint_type() {} + + //************************************************************************* + /// Construct from a value. + //************************************************************************* + ETL_CONSTEXPR14 unaligned_uint_type(value_type value) + { + unaligned_copy::copy_value_to_store(value, this->storage); + } + + //************************************************************************* + /// Construct from an address. + //************************************************************************* + unaligned_uint_type(const void* address) + { + etl::copy_n(reinterpret_cast(address), Size_, this->storage); + } + + //************************************************************************* + /// Construct from an address and buffer size. + /// \note 'buffer_size' must be greater than or equal to 'Size_'. + //************************************************************************* + unaligned_uint_type(const void* address, size_t buffer_size) + { + ETL_ASSERT(Size_ <= buffer_size, ETL_ERROR(etl::unaligned_type_buffer_size)); + + etl::copy_n(reinterpret_cast(address), Size_, this->storage); + } + + //************************************************************************* + /// Construct from a byte buffer. + /// Usable in a constexpr context (C++14 and later). + //************************************************************************* + ETL_CONSTEXPR14 unaligned_uint_type(const unsigned char* address) + { + etl::copy_n(address, Size_, this->storage); + } + + //************************************************************************* + /// Construct from a byte buffer and buffer size. + /// \note 'buffer_size' must be greater than or equal to 'Size_'. + //************************************************************************* + ETL_CONSTEXPR14 unaligned_uint_type(const unsigned char* address, size_t buffer_size) + { + ETL_ASSERT(Size_ <= buffer_size, ETL_ERROR(etl::unaligned_type_buffer_size)); + + etl::copy_n(address, Size_, this->storage); + } + + //************************************************************************* + /// Copy constructor + //************************************************************************* + ETL_CONSTEXPR14 unaligned_uint_type(const unaligned_uint_type& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian, this->storage); + } + + //************************************************************************* + /// Copy constructor from other endianness. + //************************************************************************* + template + ETL_CONSTEXPR14 unaligned_uint_type(const unaligned_uint_type& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); + } + + //************************************************************************* + /// Assignment operator + //************************************************************************* + ETL_CONSTEXPR14 unaligned_uint_type& operator=(value_type value) + { + unaligned_copy::copy_value_to_store(value, this->storage); + + return *this; + } + + //************************************************************************* + /// Assignment operator. + //************************************************************************* + ETL_CONSTEXPR14 unaligned_uint_type& operator=(const unaligned_uint_type& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian_, this->storage); + + return *this; + } + + //************************************************************************* + /// Assignment operator from other endianness. + //************************************************************************* + template + ETL_CONSTEXPR14 unaligned_uint_type& operator=(const unaligned_uint_type& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); + + return *this; + } + + //************************************************************************* + /// Conversion operator + //************************************************************************* + ETL_CONSTEXPR14 operator value_type() const + { + value_type value = value_type(); + + unaligned_copy::copy_store_to_value(this->storage, value); + + return value; + } + + //************************************************************************* + /// Get the value. + //************************************************************************* + ETL_CONSTEXPR14 value_type value() const + { + value_type value = value_type(); + + unaligned_copy::copy_store_to_value(this->storage, value); + + return value; + } + }; + ETL_END_PACKED + + template + ETL_CONSTANT int unaligned_uint_type::Endian; + + template + ETL_CONSTANT size_t unaligned_uint_type::Size; + + template + ETL_CONSTANT typename unaligned_uint_type::value_type unaligned_uint_type::Min_Value; + + template + ETL_CONSTANT typename unaligned_uint_type::value_type unaligned_uint_type::Max_Value; + + //************************************************************************* + /// unaligned_int_type + ///\brief Allows a signed, two's complement, integer of an arbitrary number + /// of bytes to be stored at an unaligned address. + /// Useful for types that have no native equivalent, such as a 24 bit + /// signed integer. + /// The value is interfaced to via the smallest standard signed + /// integer type that can hold it, and is sign extended on reading. + ///\tparam Size_ The number of bytes of storage. + ///\tparam Endian_ The endianness of the stored integer. + ///\note Values that do not fit in 'Size_' bytes are truncated, in the same + /// way as a narrowing static_cast. + //************************************************************************* + template + ETL_PACKED_CLASS(unaligned_int_type) + : public private_unaligned_type::unaligned_type_storage + { + public: + + typedef typename private_unaligned_type::interface_int::type value_type; + + typedef private_unaligned_type::unaligned_copy unaligned_copy; + + typedef typename private_unaligned_type::unaligned_type_storage::storage_type storage_type; + typedef typename private_unaligned_type::unaligned_type_storage::pointer pointer; + typedef typename private_unaligned_type::unaligned_type_storage::const_pointer const_pointer; + typedef typename private_unaligned_type::unaligned_type_storage::iterator iterator; + typedef typename private_unaligned_type::unaligned_type_storage::const_iterator const_iterator; + typedef typename private_unaligned_type::unaligned_type_storage::reverse_iterator reverse_iterator; + typedef typename private_unaligned_type::unaligned_type_storage::const_reverse_iterator const_reverse_iterator; + + static ETL_CONSTANT int Endian = Endian_; + static ETL_CONSTANT size_t Size = Size_; + static ETL_CONSTANT value_type Max_Value = + static_cast(etl::integral_limits::type>::max >> (((sizeof(value_type) - Size_) * 8U) + 1U)); + static ETL_CONSTANT value_type Min_Value = static_cast(-Max_Value - 1); + + //************************************************************************* + /// Default constructor + //************************************************************************* + unaligned_int_type() {} + + //************************************************************************* + /// Construct from a value. + //************************************************************************* + ETL_CONSTEXPR14 unaligned_int_type(value_type value) + { + unaligned_copy::copy_value_to_store(value, this->storage); + } + + //************************************************************************* + /// Construct from an address. + //************************************************************************* + unaligned_int_type(const void* address) + { + etl::copy_n(reinterpret_cast(address), Size_, this->storage); + } + + //************************************************************************* + /// Construct from an address and buffer size. + /// \note 'buffer_size' must be greater than or equal to 'Size_'. + //************************************************************************* + unaligned_int_type(const void* address, size_t buffer_size) + { + ETL_ASSERT(Size_ <= buffer_size, ETL_ERROR(etl::unaligned_type_buffer_size)); + + etl::copy_n(reinterpret_cast(address), Size_, this->storage); + } + + //************************************************************************* + /// Construct from a byte buffer. + /// Usable in a constexpr context (C++14 and later). + //************************************************************************* + ETL_CONSTEXPR14 unaligned_int_type(const unsigned char* address) + { + etl::copy_n(address, Size_, this->storage); + } + + //************************************************************************* + /// Construct from a byte buffer and buffer size. + /// \note 'buffer_size' must be greater than or equal to 'Size_'. + //************************************************************************* + ETL_CONSTEXPR14 unaligned_int_type(const unsigned char* address, size_t buffer_size) + { + ETL_ASSERT(Size_ <= buffer_size, ETL_ERROR(etl::unaligned_type_buffer_size)); + + etl::copy_n(address, Size_, this->storage); + } + + //************************************************************************* + /// Copy constructor + //************************************************************************* + ETL_CONSTEXPR14 unaligned_int_type(const unaligned_int_type& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian, this->storage); + } + + //************************************************************************* + /// Copy constructor from other endianness. + //************************************************************************* + template + ETL_CONSTEXPR14 unaligned_int_type(const unaligned_int_type& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); + } + + //************************************************************************* + /// Assignment operator + //************************************************************************* + ETL_CONSTEXPR14 unaligned_int_type& operator=(value_type value) + { + unaligned_copy::copy_value_to_store(value, this->storage); + + return *this; + } + + //************************************************************************* + /// Assignment operator. + //************************************************************************* + ETL_CONSTEXPR14 unaligned_int_type& operator=(const unaligned_int_type& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian_, this->storage); + + return *this; + } + + //************************************************************************* + /// Assignment operator from other endianness. + //************************************************************************* + template + ETL_CONSTEXPR14 unaligned_int_type& operator=(const unaligned_int_type& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); + + return *this; + } + + //************************************************************************* + /// Conversion operator + //************************************************************************* + ETL_CONSTEXPR14 operator value_type() const + { + return value(); + } + + //************************************************************************* + /// Get the value. + //************************************************************************* + ETL_CONSTEXPR14 value_type value() const + { + typedef typename etl::unsigned_type::type unsigned_t; + + unsigned_t uvalue = unsigned_t(0); + + unaligned_copy::copy_store_to_value(this->storage, uvalue); + + return private_unaligned_type::sign_extend(uvalue); + } + }; + ETL_END_PACKED + //************************************************************************* /// unaligned_type_ext ///\brief Allows an arithmetic type to be stored at an unaligned address. @@ -839,6 +1364,9 @@ namespace etl //************************************************************************* /// Copy constructor with storage pointer + /// \note 'storage_' must either be the same storage as 'other's, or refer + /// to completely separate storage. Partially overlapping storage is + /// undefined behaviour. //************************************************************************* template unaligned_type_ext(const unaligned_type_ext& other, pointer storage_) @@ -897,6 +1425,9 @@ namespace etl //************************************************************************* /// Copy assignment operator from other endianness. + /// \note The storage of 'other' must either be the same storage as this + /// object's, or completely separate. Partially overlapping storage is + /// undefined behaviour. //************************************************************************* template unaligned_type_ext& operator=(const unaligned_type_ext& other) @@ -984,6 +1515,528 @@ namespace etl template ETL_CONSTANT size_t unaligned_type_ext::Size; + //************************************************************************* + /// unaligned_uint_type_ext + ///\brief Allows an unsigned integer of an arbitrary number of bytes to be + /// stored at an unaligned address. Uses an external buffer. + /// The value is interfaced to via the smallest standard unsigned + /// integer type that can hold it. + ///\tparam Size_ The number of bytes of storage. + ///\tparam Endian_ The endianness of the stored integer. + ///\note Values that do not fit in 'Size_' bytes are truncated, in the same + /// way as a narrowing static_cast. + //************************************************************************* + template + ETL_PACKED_CLASS(unaligned_uint_type_ext) + : public private_unaligned_type::unaligned_type_storage_ext + { + public: + + template + friend class unaligned_uint_type_ext; + + typedef typename private_unaligned_type::interface_uint::type value_type; + + typedef private_unaligned_type::unaligned_copy unaligned_copy; + + typedef typename private_unaligned_type::unaligned_type_storage_ext::storage_type storage_type; + typedef typename private_unaligned_type::unaligned_type_storage_ext::pointer pointer; + typedef typename private_unaligned_type::unaligned_type_storage_ext::const_pointer const_pointer; + typedef typename private_unaligned_type::unaligned_type_storage_ext::iterator iterator; + typedef typename private_unaligned_type::unaligned_type_storage_ext::const_iterator const_iterator; + typedef typename private_unaligned_type::unaligned_type_storage_ext::reverse_iterator reverse_iterator; + typedef typename private_unaligned_type::unaligned_type_storage_ext::const_reverse_iterator const_reverse_iterator; + + static ETL_CONSTANT int Endian = Endian_; + static ETL_CONSTANT size_t Size = Size_; + static ETL_CONSTANT value_type Min_Value = static_cast(0); + static ETL_CONSTANT value_type Max_Value = static_cast(etl::integral_limits::max >> ((sizeof(value_type) - Size_) * 8U)); + + //************************************************************************* + /// Construct from a storage pointer + //************************************************************************* + unaligned_uint_type_ext(pointer storage_) + : private_unaligned_type::unaligned_type_storage_ext(storage_) + { + } + + //************************************************************************* + /// Construct from a value and storage pointer + //************************************************************************* + unaligned_uint_type_ext(value_type value, pointer storage_) + : private_unaligned_type::unaligned_type_storage_ext(storage_) + { + unaligned_copy::copy_value_to_store(value, this->storage); + } + + //************************************************************************* + /// Copy constructor with storage pointer + /// \note 'storage_' must either be the same storage as 'other's, or refer + /// to completely separate storage. Partially overlapping storage is + /// undefined behaviour. + //************************************************************************* + template + unaligned_uint_type_ext(const unaligned_uint_type_ext& other, pointer storage_) + : private_unaligned_type::unaligned_type_storage_ext(storage_) + { + unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); + } + +#if ETL_USING_CPP11 + //************************************************************************* + /// Move constructor + //************************************************************************* + unaligned_uint_type_ext(unaligned_uint_type_ext && other) + : private_unaligned_type::unaligned_type_storage_ext(other.storage) + { + other.storage = ETL_NULLPTR; + } + + //************************************************************************* + /// Move constructor from other endianness + //************************************************************************* + template + unaligned_uint_type_ext(unaligned_uint_type_ext && other) + : private_unaligned_type::unaligned_type_storage_ext(other.storage) + { + // If we're constructing from a different endianness then we need to + // reverse the data order. + if (Endian != Endian_Other) + { + etl::reverse(this->begin(), this->end()); + } + + other.storage = ETL_NULLPTR; + } +#endif + + //************************************************************************* + /// Assignment operator + //************************************************************************* + unaligned_uint_type_ext& operator=(value_type value) + { + unaligned_copy::copy_value_to_store(value, this->storage); + + return *this; + } + + //************************************************************************* + /// Copy assignment operator. + //************************************************************************* + unaligned_uint_type_ext& operator=(const unaligned_uint_type_ext& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian, this->storage); + + return *this; + } + + //************************************************************************* + /// Copy assignment operator from other endianness. + /// \note The storage of 'other' must either be the same storage as this + /// object's, or completely separate. Partially overlapping storage is + /// undefined behaviour. + //************************************************************************* + template + unaligned_uint_type_ext& operator=(const unaligned_uint_type_ext& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); + + return *this; + } + +#if ETL_USING_CPP11 + //************************************************************************* + /// Move assignment operator. + //************************************************************************* + unaligned_uint_type_ext& operator=(unaligned_uint_type_ext&& other) + { + this->storage = other.storage; + other.storage = ETL_NULLPTR; + + return *this; + } + + //************************************************************************* + /// Move assignment operator from other endianness. + //************************************************************************* + template + unaligned_uint_type_ext& operator=(unaligned_uint_type_ext&& other) + { + this->storage = other.storage; + + // If we're assigning from a different endianness then we need to reverse + // the data order. + if (Endian != Endian_Other) + { + etl::reverse(this->begin(), this->end()); + } + + other.storage = ETL_NULLPTR; + + return *this; + } +#endif + + //************************************************************************* + /// Conversion operator + //************************************************************************* + operator value_type() const + { + return value(); + } + + //************************************************************************* + /// Get the value. + //************************************************************************* + value_type value() const + { + value_type v = value_type(); + + unaligned_copy::copy_store_to_value(this->storage, v); + + return v; + } + + //************************************************************************* + /// Sets the storage for the type. + //************************************************************************* + void set_storage(pointer storage_) + { + this->storage = storage_; + } + + private: + + unaligned_uint_type_ext() ETL_DELETE; + }; + ETL_END_PACKED + + template + ETL_CONSTANT int unaligned_uint_type_ext::Endian; + + template + ETL_CONSTANT size_t unaligned_uint_type_ext::Size; + + template + ETL_CONSTANT typename unaligned_uint_type_ext::value_type unaligned_uint_type_ext::Min_Value; + + template + ETL_CONSTANT typename unaligned_uint_type_ext::value_type unaligned_uint_type_ext::Max_Value; + + //************************************************************************* + /// unaligned_int_type_ext + ///\brief Allows a signed, two's complement, integer of an arbitrary number + /// of bytes to be stored at an unaligned address. Uses an external + /// buffer. + /// The value is interfaced to via the smallest standard signed + /// integer type that can hold it, and is sign extended on reading. + ///\tparam Size_ The number of bytes of storage. + ///\tparam Endian_ The endianness of the stored integer. + ///\note Values that do not fit in 'Size_' bytes are truncated, in the same + /// way as a narrowing static_cast. + //************************************************************************* + template + ETL_PACKED_CLASS(unaligned_int_type_ext) + : public private_unaligned_type::unaligned_type_storage_ext + { + public: + + template + friend class unaligned_int_type_ext; + + typedef typename private_unaligned_type::interface_int::type value_type; + + typedef private_unaligned_type::unaligned_copy unaligned_copy; + + typedef typename private_unaligned_type::unaligned_type_storage_ext::storage_type storage_type; + typedef typename private_unaligned_type::unaligned_type_storage_ext::pointer pointer; + typedef typename private_unaligned_type::unaligned_type_storage_ext::const_pointer const_pointer; + typedef typename private_unaligned_type::unaligned_type_storage_ext::iterator iterator; + typedef typename private_unaligned_type::unaligned_type_storage_ext::const_iterator const_iterator; + typedef typename private_unaligned_type::unaligned_type_storage_ext::reverse_iterator reverse_iterator; + typedef typename private_unaligned_type::unaligned_type_storage_ext::const_reverse_iterator const_reverse_iterator; + + static ETL_CONSTANT int Endian = Endian_; + static ETL_CONSTANT size_t Size = Size_; + static ETL_CONSTANT value_type Max_Value = + static_cast(etl::integral_limits::type>::max >> (((sizeof(value_type) - Size_) * 8U) + 1U)); + static ETL_CONSTANT value_type Min_Value = static_cast(-Max_Value - 1); + + //************************************************************************* + /// Construct from a storage pointer + //************************************************************************* + unaligned_int_type_ext(pointer storage_) + : private_unaligned_type::unaligned_type_storage_ext(storage_) + { + } + + //************************************************************************* + /// Construct from a value and storage pointer + //************************************************************************* + unaligned_int_type_ext(value_type value, pointer storage_) + : private_unaligned_type::unaligned_type_storage_ext(storage_) + { + unaligned_copy::copy_value_to_store(value, this->storage); + } + + //************************************************************************* + /// Copy constructor with storage pointer + /// \note 'storage_' must either be the same storage as 'other's, or refer + /// to completely separate storage. Partially overlapping storage is + /// undefined behaviour. + //************************************************************************* + template + unaligned_int_type_ext(const unaligned_int_type_ext& other, pointer storage_) + : private_unaligned_type::unaligned_type_storage_ext(storage_) + { + unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); + } + +#if ETL_USING_CPP11 + //************************************************************************* + /// Move constructor + //************************************************************************* + unaligned_int_type_ext(unaligned_int_type_ext && other) + : private_unaligned_type::unaligned_type_storage_ext(other.storage) + { + other.storage = ETL_NULLPTR; + } + + //************************************************************************* + /// Move constructor from other endianness + //************************************************************************* + template + unaligned_int_type_ext(unaligned_int_type_ext && other) + : private_unaligned_type::unaligned_type_storage_ext(other.storage) + { + // If we're constructing from a different endianness then we need to + // reverse the data order. + if (Endian != Endian_Other) + { + etl::reverse(this->begin(), this->end()); + } + + other.storage = ETL_NULLPTR; + } +#endif + + //************************************************************************* + /// Assignment operator + //************************************************************************* + unaligned_int_type_ext& operator=(value_type value) + { + unaligned_copy::copy_value_to_store(value, this->storage); + + return *this; + } + + //************************************************************************* + /// Copy assignment operator. + //************************************************************************* + unaligned_int_type_ext& operator=(const unaligned_int_type_ext& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian, this->storage); + + return *this; + } + + //************************************************************************* + /// Copy assignment operator from other endianness. + /// \note The storage of 'other' must either be the same storage as this + /// object's, or completely separate. Partially overlapping storage is + /// undefined behaviour. + //************************************************************************* + template + unaligned_int_type_ext& operator=(const unaligned_int_type_ext& other) + { + unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); + + return *this; + } + +#if ETL_USING_CPP11 + //************************************************************************* + /// Move assignment operator. + //************************************************************************* + unaligned_int_type_ext& operator=(unaligned_int_type_ext&& other) + { + this->storage = other.storage; + other.storage = ETL_NULLPTR; + + return *this; + } + + //************************************************************************* + /// Move assignment operator from other endianness. + //************************************************************************* + template + unaligned_int_type_ext& operator=(unaligned_int_type_ext&& other) + { + this->storage = other.storage; + + // If we're assigning from a different endianness then we need to reverse + // the data order. + if (Endian != Endian_Other) + { + etl::reverse(this->begin(), this->end()); + } + + other.storage = ETL_NULLPTR; + + return *this; + } +#endif + + //************************************************************************* + /// Conversion operator + //************************************************************************* + operator value_type() const + { + return value(); + } + + //************************************************************************* + /// Get the value. + //************************************************************************* + value_type value() const + { + typedef typename etl::unsigned_type::type unsigned_t; + + unsigned_t uvalue = unsigned_t(0); + + unaligned_copy::copy_store_to_value(this->storage, uvalue); + + return private_unaligned_type::sign_extend(uvalue); + } + + //************************************************************************* + /// Sets the storage for the type. + //************************************************************************* + void set_storage(pointer storage_) + { + this->storage = storage_; + } + + private: + + unaligned_int_type_ext() ETL_DELETE; + }; + ETL_END_PACKED + + template + ETL_CONSTANT int unaligned_int_type::Endian; + + template + ETL_CONSTANT size_t unaligned_int_type::Size; + + template + ETL_CONSTANT typename unaligned_int_type::value_type unaligned_int_type::Min_Value; + + template + ETL_CONSTANT typename unaligned_int_type::value_type unaligned_int_type::Max_Value; + + // 24 bit integers. + typedef unaligned_uint_type<3U, etl::endian::little> le_uint24_t; + typedef unaligned_uint_type<3U, etl::endian::big> be_uint24_t; + typedef be_uint24_t net_uint24_t; + typedef unaligned_int_type<3U, etl::endian::little> le_int24_t; + typedef unaligned_int_type<3U, etl::endian::big> be_int24_t; + typedef be_int24_t net_int24_t; +#if ETL_HAS_CONSTEXPR_ENDIANNESS + typedef unaligned_uint_type<3U, etl::endianness::value()> host_uint24_t; + typedef unaligned_int_type<3U, etl::endianness::value()> host_int24_t; +#endif + +#if ETL_USING_64BIT_TYPES + // 40, 48 and 56 bit integers. + typedef unaligned_uint_type<5U, etl::endian::little> le_uint40_t; + typedef unaligned_uint_type<5U, etl::endian::big> be_uint40_t; + typedef be_uint40_t net_uint40_t; + typedef unaligned_int_type<5U, etl::endian::little> le_int40_t; + typedef unaligned_int_type<5U, etl::endian::big> be_int40_t; + typedef be_int40_t net_int40_t; + + typedef unaligned_uint_type<6U, etl::endian::little> le_uint48_t; + typedef unaligned_uint_type<6U, etl::endian::big> be_uint48_t; + typedef be_uint48_t net_uint48_t; + typedef unaligned_int_type<6U, etl::endian::little> le_int48_t; + typedef unaligned_int_type<6U, etl::endian::big> be_int48_t; + typedef be_int48_t net_int48_t; + + typedef unaligned_uint_type<7U, etl::endian::little> le_uint56_t; + typedef unaligned_uint_type<7U, etl::endian::big> be_uint56_t; + typedef be_uint56_t net_uint56_t; + typedef unaligned_int_type<7U, etl::endian::little> le_int56_t; + typedef unaligned_int_type<7U, etl::endian::big> be_int56_t; + typedef be_int56_t net_int56_t; + + #if ETL_HAS_CONSTEXPR_ENDIANNESS + typedef unaligned_uint_type<5U, etl::endianness::value()> host_uint40_t; + typedef unaligned_uint_type<6U, etl::endianness::value()> host_uint48_t; + typedef unaligned_uint_type<7U, etl::endianness::value()> host_uint56_t; + typedef unaligned_int_type<5U, etl::endianness::value()> host_int40_t; + typedef unaligned_int_type<6U, etl::endianness::value()> host_int48_t; + typedef unaligned_int_type<7U, etl::endianness::value()> host_int56_t; + #endif +#endif + + template + ETL_CONSTANT int unaligned_int_type_ext::Endian; + + template + ETL_CONSTANT size_t unaligned_int_type_ext::Size; + + template + ETL_CONSTANT typename unaligned_int_type_ext::value_type unaligned_int_type_ext::Min_Value; + + template + ETL_CONSTANT typename unaligned_int_type_ext::value_type unaligned_int_type_ext::Max_Value; + + // 24 bit integers, external buffer. + typedef unaligned_uint_type_ext<3U, etl::endian::little> le_uint24_ext_t; + typedef unaligned_uint_type_ext<3U, etl::endian::big> be_uint24_ext_t; + typedef be_uint24_ext_t net_uint24_ext_t; + typedef unaligned_int_type_ext<3U, etl::endian::little> le_int24_ext_t; + typedef unaligned_int_type_ext<3U, etl::endian::big> be_int24_ext_t; + typedef be_int24_ext_t net_int24_ext_t; +#if ETL_HAS_CONSTEXPR_ENDIANNESS + typedef unaligned_uint_type_ext<3U, etl::endianness::value()> host_uint24_ext_t; + typedef unaligned_int_type_ext<3U, etl::endianness::value()> host_int24_ext_t; +#endif + +#if ETL_USING_64BIT_TYPES + // 40, 48 and 56 bit integers, external buffer. + typedef unaligned_uint_type_ext<5U, etl::endian::little> le_uint40_ext_t; + typedef unaligned_uint_type_ext<5U, etl::endian::big> be_uint40_ext_t; + typedef be_uint40_ext_t net_uint40_ext_t; + typedef unaligned_int_type_ext<5U, etl::endian::little> le_int40_ext_t; + typedef unaligned_int_type_ext<5U, etl::endian::big> be_int40_ext_t; + typedef be_int40_ext_t net_int40_ext_t; + + typedef unaligned_uint_type_ext<6U, etl::endian::little> le_uint48_ext_t; + typedef unaligned_uint_type_ext<6U, etl::endian::big> be_uint48_ext_t; + typedef be_uint48_ext_t net_uint48_ext_t; + typedef unaligned_int_type_ext<6U, etl::endian::little> le_int48_ext_t; + typedef unaligned_int_type_ext<6U, etl::endian::big> be_int48_ext_t; + typedef be_int48_ext_t net_int48_ext_t; + + typedef unaligned_uint_type_ext<7U, etl::endian::little> le_uint56_ext_t; + typedef unaligned_uint_type_ext<7U, etl::endian::big> be_uint56_ext_t; + typedef be_uint56_ext_t net_uint56_ext_t; + typedef unaligned_int_type_ext<7U, etl::endian::little> le_int56_ext_t; + typedef unaligned_int_type_ext<7U, etl::endian::big> be_int56_ext_t; + typedef be_int56_ext_t net_int56_ext_t; + + #if ETL_HAS_CONSTEXPR_ENDIANNESS + typedef unaligned_uint_type_ext<5U, etl::endianness::value()> host_uint40_ext_t; + typedef unaligned_uint_type_ext<6U, etl::endianness::value()> host_uint48_ext_t; + typedef unaligned_uint_type_ext<7U, etl::endianness::value()> host_uint56_ext_t; + typedef unaligned_int_type_ext<5U, etl::endianness::value()> host_int40_ext_t; + typedef unaligned_int_type_ext<6U, etl::endianness::value()> host_int48_ext_t; + typedef unaligned_int_type_ext<7U, etl::endianness::value()> host_int56_ext_t; + #endif +#endif + #if ETL_HAS_CONSTEXPR_ENDIANNESS // Host order typedef unaligned_type host_char_t; diff --git a/test/test_unaligned_type.cpp b/test/test_unaligned_type.cpp index 578d0cfb..21b8502f 100644 --- a/test/test_unaligned_type.cpp +++ b/test/test_unaligned_type.cpp @@ -367,6 +367,208 @@ namespace CHECK_EQUAL(f, etl::be_float_t::value_from(be_f.data())); CHECK_EQUAL(d, etl::le_double_t::value_from(le_d.data())); } + /// Unsigned integers that have no native equivalent, such as 24 bit. + //************************************************************************* + TEST(test_uint24) + { + CHECK_EQUAL(3U, sizeof(etl::le_uint24_t)); + CHECK_EQUAL(3U, sizeof(etl::be_uint24_t)); + CHECK_EQUAL(3U, size_t(etl::le_uint24_t::Size)); + + CHECK((etl::is_same::value)); + CHECK_EQUAL(uint32_t(0x00FFFFFF), etl::le_uint24_t::Max_Value); + + etl::le_uint24_t le_v(0x123456U); + etl::be_uint24_t be_v(0x123456U); + + CHECK_EQUAL(uint32_t(0x123456), le_v.value()); + CHECK_EQUAL(uint32_t(0x123456), be_v.value()); + + // Check the storage byte order. + CHECK_EQUAL(0x56, int(le_v.data()[0])); + CHECK_EQUAL(0x34, int(le_v.data()[1])); + CHECK_EQUAL(0x12, int(le_v.data()[2])); + + CHECK_EQUAL(0x12, int(be_v.data()[0])); + CHECK_EQUAL(0x34, int(be_v.data()[1])); + CHECK_EQUAL(0x56, int(be_v.data()[2])); + + // Copy construction, including from the other endianness. + etl::le_uint24_t le_v2(le_v); + etl::be_uint24_t be_v2(be_v); + etl::le_uint24_t le_v3(be_v); + etl::be_uint24_t be_v3(le_v); + + CHECK_EQUAL(uint32_t(0x123456), le_v2.value()); + CHECK_EQUAL(uint32_t(0x123456), be_v2.value()); + CHECK_EQUAL(uint32_t(0x123456), le_v3.value()); + CHECK_EQUAL(uint32_t(0x123456), be_v3.value()); + + // Assignment. + le_v2 = 0xFFFFFFU; + be_v2 = 0xFFFFFFU; + CHECK_EQUAL(uint32_t(0xFFFFFF), le_v2.value()); + CHECK_EQUAL(uint32_t(0xFFFFFF), be_v2.value()); + + // Boundary values. + etl::le_uint24_t le_zero(0U); + etl::be_uint24_t be_zero(0U); + CHECK_EQUAL(uint32_t(0), le_zero.value()); + CHECK_EQUAL(uint32_t(0), be_zero.value()); + + // Values that do not fit are truncated, like a narrowing cast. + etl::le_uint24_t le_truncated(0xAB123456U); + etl::be_uint24_t be_truncated(0xAB123456U); + CHECK_EQUAL(uint32_t(0x123456), le_truncated.value()); + CHECK_EQUAL(uint32_t(0x123456), be_truncated.value()); + } + + //************************************************************************* + TEST(test_uint24_decode_buffer) + { + const std::array buffer = {0xAA, 0xBB, 0xCC, 0xDD}; + + etl::le_uint24_t le_v(buffer.data(), buffer.size()); + etl::be_uint24_t be_v(buffer.data(), buffer.size()); + + CHECK_EQUAL(uint32_t(0xCCBBAA), le_v.value()); + CHECK_EQUAL(uint32_t(0xAABBCC), be_v.value()); + + // One byte short. + CHECK_THROW(etl::le_uint24_t le_short(buffer.data(), 2U), etl::unaligned_type_buffer_size); + CHECK_THROW(etl::be_uint24_t be_short(buffer.data(), 2U), etl::unaligned_type_buffer_size); + } + +#if ETL_USING_64BIT_TYPES + //************************************************************************* + TEST(test_uint40_uint48_uint56) + { + CHECK_EQUAL(5U, sizeof(etl::be_uint40_t)); + CHECK_EQUAL(6U, sizeof(etl::be_uint48_t)); + CHECK_EQUAL(7U, sizeof(etl::be_uint56_t)); + + etl::be_uint48_t be_v(0x0123456789ABULL); + etl::le_uint48_t le_v(0x0123456789ABULL); + + CHECK_EQUAL(uint64_t(0x0123456789AB), be_v.value()); + CHECK_EQUAL(uint64_t(0x0123456789AB), le_v.value()); + + CHECK_EQUAL(0x01, int(be_v.data()[0])); + CHECK_EQUAL(0xAB, int(le_v.data()[0])); + + CHECK_EQUAL(uint64_t(0x000000FFFFFFFFFF), etl::be_uint40_t::Max_Value); + CHECK_EQUAL(uint64_t(0x00FFFFFFFFFFFFFF), etl::be_uint56_t::Max_Value); + } +#endif + + //************************************************************************* + /// Signed integers that have no native equivalent, such as 24 bit. + //************************************************************************* + TEST(test_int24) + { + CHECK_EQUAL(3U, sizeof(etl::le_int24_t)); + CHECK_EQUAL(3U, sizeof(etl::be_int24_t)); + CHECK_EQUAL(3U, size_t(etl::le_int24_t::Size)); + + CHECK((etl::is_same::value)); + CHECK_EQUAL(int32_t(8388607), etl::le_int24_t::Max_Value); + CHECK_EQUAL(int32_t(-8388608), etl::le_int24_t::Min_Value); + + etl::le_int24_t le_v(0x123456); + etl::be_int24_t be_v(0x123456); + + CHECK_EQUAL(int32_t(0x123456), le_v.value()); + CHECK_EQUAL(int32_t(0x123456), be_v.value()); + + // Check the storage byte order. + CHECK_EQUAL(0x56, int(le_v.data()[0])); + CHECK_EQUAL(0x12, int(le_v.data()[2])); + CHECK_EQUAL(0x12, int(be_v.data()[0])); + CHECK_EQUAL(0x56, int(be_v.data()[2])); + + // Negative values are sign extended on reading. + etl::le_int24_t le_n(-2); + etl::be_int24_t be_n(-2); + + CHECK_EQUAL(int32_t(-2), le_n.value()); + CHECK_EQUAL(int32_t(-2), be_n.value()); + + CHECK_EQUAL(0xFE, int(le_n.data()[0])); + CHECK_EQUAL(0xFF, int(le_n.data()[1])); + CHECK_EQUAL(0xFF, int(le_n.data()[2])); + + CHECK_EQUAL(0xFF, int(be_n.data()[0])); + CHECK_EQUAL(0xFF, int(be_n.data()[1])); + CHECK_EQUAL(0xFE, int(be_n.data()[2])); + + // Copy construction, including from the other endianness. + etl::le_int24_t le_n2(be_n); + etl::be_int24_t be_n2(le_n); + + CHECK_EQUAL(int32_t(-2), le_n2.value()); + CHECK_EQUAL(int32_t(-2), be_n2.value()); + + // Assignment. + le_n2 = -1; + be_n2 = -1; + CHECK_EQUAL(int32_t(-1), le_n2.value()); + CHECK_EQUAL(int32_t(-1), be_n2.value()); + + // Boundary values. + etl::le_int24_t le_min(etl::le_int24_t::Min_Value); + etl::be_int24_t be_min(etl::be_int24_t::Min_Value); + etl::le_int24_t le_max(etl::le_int24_t::Max_Value); + etl::be_int24_t be_max(etl::be_int24_t::Max_Value); + + CHECK_EQUAL(int32_t(-8388608), le_min.value()); + CHECK_EQUAL(int32_t(-8388608), be_min.value()); + CHECK_EQUAL(int32_t(8388607), le_max.value()); + CHECK_EQUAL(int32_t(8388607), be_max.value()); + + etl::le_int24_t le_zero(0); + CHECK_EQUAL(int32_t(0), le_zero.value()); + + // Values that do not fit are truncated, like a narrowing cast. + etl::be_int24_t be_truncated(0x7F123456); + CHECK_EQUAL(int32_t(0x123456), be_truncated.value()); + } + + //************************************************************************* + TEST(test_int24_decode_buffer) + { + const std::array buffer = {0x80, 0x00, 0x00, 0xDD}; + + etl::le_int24_t le_v(buffer.data(), buffer.size()); + etl::be_int24_t be_v(buffer.data(), buffer.size()); + + CHECK_EQUAL(int32_t(0x000080), le_v.value()); + CHECK_EQUAL(int32_t(-8388608), be_v.value()); + + // One byte short. + CHECK_THROW(etl::le_int24_t le_short(buffer.data(), 2U), etl::unaligned_type_buffer_size); + CHECK_THROW(etl::be_int24_t be_short(buffer.data(), 2U), etl::unaligned_type_buffer_size); + } + +#if ETL_USING_64BIT_TYPES + //************************************************************************* + TEST(test_int40_int48_int56) + { + CHECK_EQUAL(5U, sizeof(etl::be_int40_t)); + CHECK_EQUAL(6U, sizeof(etl::be_int48_t)); + CHECK_EQUAL(7U, sizeof(etl::be_int56_t)); + + etl::be_int48_t be_v(-1099511627775LL); + etl::le_int48_t le_v(-1099511627775LL); + + CHECK_EQUAL(int64_t(-1099511627775LL), be_v.value()); + CHECK_EQUAL(int64_t(-1099511627775LL), le_v.value()); + + CHECK_EQUAL(int64_t(549755813887LL), etl::be_int40_t::Max_Value); + CHECK_EQUAL(int64_t(-549755813888LL), etl::be_int40_t::Min_Value); + CHECK_EQUAL(int64_t(36028797018963967LL), etl::be_int56_t::Max_Value); + CHECK_EQUAL(int64_t(-36028797018963968LL), etl::be_int56_t::Min_Value); + } +#endif //************************************************************************* // The following tests demonstrate the 'decode' direction: given a raw byte diff --git a/test/test_unaligned_type_ext.cpp b/test/test_unaligned_type_ext.cpp index 1ffa050a..f858b5bb 100644 --- a/test/test_unaligned_type_ext.cpp +++ b/test/test_unaligned_type_ext.cpp @@ -49,6 +49,8 @@ namespace using storage_i16_t = std::array; using storage_i32_t = std::array; using storage_i64_t = std::array; + using storage_24_t = std::array; + using storage_48_t = std::array; SUITE(test_unaligned_type_ext) { @@ -506,6 +508,148 @@ namespace CHECK_EQUAL(sizeof(uint64_t), etl::net_uint64_ext_t::Size); } + //************************************************************************* + /// Integers that have no native equivalent, such as 24 bit. + //************************************************************************* + TEST(test_uint24) + { + storage_24_t store_le1; + storage_24_t store_be1; + storage_24_t store_le2; + storage_24_t store_be2; + + CHECK_EQUAL(3U, size_t(etl::le_uint24_ext_t::Size)); + CHECK((etl::is_same::value)); + CHECK_EQUAL(uint32_t(0x00FFFFFF), etl::le_uint24_ext_t::Max_Value); + CHECK_EQUAL(uint32_t(0), etl::le_uint24_ext_t::Min_Value); + + etl::le_uint24_ext_t le_v1(store_le1.data()); + le_v1 = 0x123456U; + etl::be_uint24_ext_t be_v1(0x123456U, store_be1.data()); + + CHECK_EQUAL(uint32_t(0x123456), le_v1.value()); + CHECK_EQUAL(uint32_t(0x123456), be_v1.value()); + + // The external buffers hold the requested byte order. + CHECK_EQUAL(0x56, int(store_le1[0])); + CHECK_EQUAL(0x12, int(store_le1[2])); + CHECK_EQUAL(0x12, int(store_be1[0])); + CHECK_EQUAL(0x56, int(store_be1[2])); + + // Copy construct from the other endianness. + etl::le_uint24_ext_t le_v2(be_v1, store_le2.data()); + etl::be_uint24_ext_t be_v2(le_v1, store_be2.data()); + + CHECK_EQUAL(uint32_t(0x123456), le_v2.value()); + CHECK_EQUAL(uint32_t(0x123456), be_v2.value()); + + // Assignment. + le_v2 = le_v1; + be_v2 = le_v1; + CHECK_EQUAL(uint32_t(0x123456), le_v2.value()); + CHECK_EQUAL(uint32_t(0x123456), be_v2.value()); + + // Values that do not fit are truncated. + be_v2 = 0xABCDEF12U; + CHECK_EQUAL(uint32_t(0xCDEF12), be_v2.value()); + } + + //************************************************************************* + TEST(test_move_construction_uint24) + { + storage_24_t store_le1; + storage_24_t store_be1; + + etl::le_uint24_ext_t le_v1(0x123456U, store_le1.data()); + etl::be_uint24_ext_t be_v1(0x123456U, store_be1.data()); + + etl::le_uint24_ext_t le_v2(etl::move(le_v1)); + etl::be_uint24_ext_t be_v2(etl::move(be_v1)); + + CHECK_TRUE(nullptr == le_v1.data()); + CHECK_TRUE(nullptr == be_v1.data()); + CHECK_EQUAL(uint32_t(0x123456), le_v2.value()); + CHECK_EQUAL(uint32_t(0x123456), be_v2.value()); + } + + //************************************************************************* + TEST(test_int24) + { + storage_24_t store_le1; + storage_24_t store_be1; + storage_24_t store_le2; + storage_24_t store_be2; + + CHECK_EQUAL(3U, size_t(etl::le_int24_ext_t::Size)); + CHECK((etl::is_same::value)); + CHECK_EQUAL(int32_t(8388607), etl::le_int24_ext_t::Max_Value); + CHECK_EQUAL(int32_t(-8388608), etl::le_int24_ext_t::Min_Value); + + etl::le_int24_ext_t le_v1(store_le1.data()); + le_v1 = -2; + etl::be_int24_ext_t be_v1(-2, store_be1.data()); + + // Negative values are sign extended on reading. + CHECK_EQUAL(int32_t(-2), le_v1.value()); + CHECK_EQUAL(int32_t(-2), be_v1.value()); + + CHECK_EQUAL(0xFE, int(store_le1[0])); + CHECK_EQUAL(0xFF, int(store_le1[2])); + CHECK_EQUAL(0xFF, int(store_be1[0])); + CHECK_EQUAL(0xFE, int(store_be1[2])); + + // Copy construct from the other endianness. + etl::le_int24_ext_t le_v2(be_v1, store_le2.data()); + etl::be_int24_ext_t be_v2(le_v1, store_be2.data()); + + CHECK_EQUAL(int32_t(-2), le_v2.value()); + CHECK_EQUAL(int32_t(-2), be_v2.value()); + + // Boundary values. + le_v2 = etl::le_int24_ext_t::Min_Value; + be_v2 = etl::be_int24_ext_t::Max_Value; + + CHECK_EQUAL(int32_t(-8388608), le_v2.value()); + CHECK_EQUAL(int32_t(8388607), be_v2.value()); + + le_v2 = 0; + CHECK_EQUAL(int32_t(0), le_v2.value()); + } + + //************************************************************************* + TEST(test_move_construction_int24) + { + storage_24_t store_le1; + storage_24_t store_be1; + + etl::le_int24_ext_t le_v1(-1234567, store_le1.data()); + etl::be_int24_ext_t be_v1(-1234567, store_be1.data()); + + etl::le_int24_ext_t le_v2(etl::move(le_v1)); + etl::be_int24_ext_t be_v2(etl::move(be_v1)); + + CHECK_TRUE(nullptr == le_v1.data()); + CHECK_TRUE(nullptr == be_v1.data()); + CHECK_EQUAL(int32_t(-1234567), le_v2.value()); + CHECK_EQUAL(int32_t(-1234567), be_v2.value()); + } + +#if ETL_USING_64BIT_TYPES + //************************************************************************* + TEST(test_uint48_int48) + { + storage_48_t store_u; + storage_48_t store_i; + + etl::be_uint48_ext_t be_u(0x0123456789ABULL, store_u.data()); + etl::be_int48_ext_t be_i(-1099511627775LL, store_i.data()); + + CHECK_EQUAL(6U, size_t(etl::be_uint48_ext_t::Size)); + CHECK_EQUAL(uint64_t(0x0123456789AB), be_u.value()); + CHECK_EQUAL(int64_t(-1099511627775LL), be_i.value()); + } +#endif + #if ETL_HAS_CONSTEXPR_ENDIANNESS //************************************************************************* TEST(test_host_sizes) @@ -1528,6 +1672,106 @@ namespace CHECK_EQUAL(0x12, bev0); CHECK_EQUAL(0x34, bev1); } + + //************************************************************************* + TEST(test_shared_storage_copy_construction_uint24) + { + storage_24_t store; + + etl::be_uint24_ext_t be_v(0x123456, store.data()); + + // Reinterpret the same buffer with the opposite endianness. + etl::le_uint24_ext_t le_v(be_v, store.data()); + + CHECK_EQUAL(0x56, int(store[0])); + CHECK_EQUAL(0x34, int(store[1])); + CHECK_EQUAL(0x12, int(store[2])); + CHECK_EQUAL(0x123456, le_v.value()); + } + + //************************************************************************* + TEST(test_shared_storage_copy_construction_int24) + { + storage_24_t store; + + etl::be_int24_ext_t be_v(-0x123456, store.data()); + + etl::le_int24_ext_t le_v(be_v, store.data()); + + CHECK_EQUAL(-0x123456, le_v.value()); + } + + //************************************************************************* + TEST(test_shared_storage_copy_assignment_uint24) + { + storage_24_t store; + + etl::be_uint24_ext_t be_v(0x123456, store.data()); + etl::le_uint24_ext_t le_v(store.data()); + + le_v = be_v; + + CHECK_EQUAL(0x56, int(store[0])); + CHECK_EQUAL(0x34, int(store[1])); + CHECK_EQUAL(0x12, int(store[2])); + CHECK_EQUAL(0x123456, le_v.value()); + } + + //************************************************************************* + TEST(test_shared_storage_copy_assignment_int24) + { + storage_24_t store; + + etl::be_int24_ext_t be_v(-0x123456, store.data()); + etl::le_int24_ext_t le_v(store.data()); + + le_v = be_v; + + CHECK_EQUAL(-0x123456, le_v.value()); + } + + //************************************************************************* + TEST(test_shared_storage_copy_assignment_uint32) + { + storage_i32_t store; + + etl::be_uint32_ext_t be_v(0x12345678, store.data()); + etl::le_uint32_ext_t le_v(store.data()); + + le_v = be_v; + + CHECK_EQUAL(0x78, int(store[0])); + CHECK_EQUAL(0x56, int(store[1])); + CHECK_EQUAL(0x34, int(store[2])); + CHECK_EQUAL(0x12, int(store[3])); + CHECK_EQUAL(0x12345678, le_v.value()); + } + + //************************************************************************* + TEST(test_shared_storage_copy_assignment_same_endianness) + { + storage_i32_t store; + + etl::le_uint32_ext_t le_v1(0x12345678, store.data()); + etl::le_uint32_ext_t le_v2(store.data()); + + le_v2 = le_v1; + + CHECK_EQUAL(0x12345678, le_v2.value()); + } + + //************************************************************************* + TEST(test_shared_storage_copy_assignment_float) + { + storage_f_t store; + + etl::be_float_ext_t be_v(3.1415927f, store.data()); + etl::le_float_ext_t le_v(store.data()); + + le_v = be_v; + + CHECK_EQUAL(3.1415927f, le_v.value()); + } } } // namespace From b0548949df3eaed6b810a7433a5b15639832a2f3 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Wed, 30 Sep 2026 09:27:23 +0200 Subject: [PATCH 12/16] Fix circular reference in callback_timer_* (#1611) * Add docs and tutorial for etl::intrusive_avl_tree (#1608) * Add docs and tutorial for etl::intrusive_avl_tree Documents the intrusive AVL tree added in PR #1425: a full API reference page under Containers/Trees, and a tutorial demonstrating ordered access to intrusively-linked objects with unpredictable lifetimes, including move support and destructor-driven automatic removal from the tree. Co-Authored-By: Claude Sonnet 5 * Add complexity shortcode and document its usage Introduces a {{< complexity >}} Hugo shortcode (with light/dark styling for constant/logarithmic/linear/linearithmic/polynomial/exponential classes) for rendering Big-O complexity inline, applies it throughout the intrusive_avl_tree API reference in place of raw "O(...)" text, and documents the Description/Complexity structure and supported classes in DOCUMENTATION.md. Co-Authored-By: Claude Sonnet 5 * Fix issues found by @CodeRabbit * Put complexity notes on their own lines in intrusive_avl_tree docs Move the complexity shortcode from the inline "**Complexity:**" label to its own line under a "**Complexity**" label. Notes that give several alternatives (e.g. amortized vs. worst-case) now show each one on a separate line. Update the guideline in DOCUMENTATION.md to match. * Update complexity examples in DOCUMENTATION.md Add the hard line break after the "**Complexity**" label in the example, and rewrite the list of supported complexity classes in the new two-line format, including an example with several alternatives. * Add logarithmic complexity example to DOCUMENTATION.md --------- Co-authored-by: Claude Sonnet 5 Co-authored-by: John Wellbelove * Updated sponsors pages * Refactor the documentation css to group styles for light and dark themes * Fix circular reference in callback_timer_* Fixes #967 * Unify timer tick() ordering and detect duplicate inserts Address review feedback on #1611. Replace the is_active() guard with a consistent tick() ordering across all timer controllers: a repeating timer is now reinserted before its callback is invoked or its message is sent, matching etl::callback_timer. A callback that restarts the timer then replaces the existing entry instead of causing a second insert. Applied to message_timer_atomic, message_timer_interrupt and message_timer_locked as well, which are affected in the same way. Reject an insert of a timer that is already in the active list, rather than linking it to itself, and record that it happened. The regression tests now assert duplicate_insert_detected() instead of relying on the resulting circular reference hanging the test. --------- Co-authored-by: Sergei Co-authored-by: Claude Sonnet 5 Co-authored-by: John Wellbelove Co-authored-by: John Wellbelove --- include/etl/callback_timer.h | 34 +++++++++++ include/etl/callback_timer_atomic.h | 48 ++++++++++++++-- include/etl/callback_timer_interrupt.h | 47 +++++++++++++-- include/etl/callback_timer_locked.h | 47 +++++++++++++-- include/etl/message_timer.h | 34 +++++++++++ include/etl/message_timer_atomic.h | 47 +++++++++++++-- include/etl/message_timer_interrupt.h | 47 +++++++++++++-- include/etl/message_timer_locked.h | 47 +++++++++++++-- test/test_callback_timer_atomic.cpp | 77 +++++++++++++++++++++++++ test/test_callback_timer_interrupt.cpp | 77 +++++++++++++++++++++++++ test/test_callback_timer_locked.cpp | 80 ++++++++++++++++++++++++++ 11 files changed, 554 insertions(+), 31 deletions(-) diff --git a/include/etl/callback_timer.h b/include/etl/callback_timer.h index 800a34e2..591ace1f 100644 --- a/include/etl/callback_timer.h +++ b/include/etl/callback_timer.h @@ -411,6 +411,16 @@ namespace etl return false; } + //******************************************* + /// Returns true if an attempt was ever made to insert a timer into the + /// active list when it was already there. Such an insert is rejected, as + /// it would create a circular reference in the list. + //******************************************* + bool duplicate_insert_detected() const + { + return active_list.duplicate_insert_detected(); + } + //******************************************* /// Check if there is an active timer. //******************************************* @@ -630,6 +640,7 @@ namespace etl : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) , ptimers(ptimers_) + , duplicate_insert(false) { } @@ -639,6 +650,15 @@ namespace etl return head == etl::timer::id::NO_TIMER; } + //******************************* + /// Returns true if an attempt was ever made to insert a timer that was + /// already in the list. + //******************************* + bool duplicate_insert_detected() const + { + return duplicate_insert; + } + //******************************* // Inserts the timer at the correct delta position //******************************* @@ -646,6 +666,16 @@ namespace etl { timer_data& timer = ptimers[id_]; + // A timer is in this list exactly when it is the head or has a previous + // link, as remove() clears both links. Inserting one that is already + // here would link it to itself and create a circular reference, so + // record the attempt and abandon the insert to keep the list intact. + if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER)) + { + duplicate_insert = true; + return; + } + if (head == etl::timer::id::NO_TIMER) { // No entries yet. @@ -788,6 +818,10 @@ namespace etl etl::timer::id::type tail; timer_data* const ptimers; + + // Set if an attempt was ever made to insert a timer that was already + // in the list. + bool duplicate_insert; }; // The array of timer data structures. diff --git a/include/etl/callback_timer_atomic.h b/include/etl/callback_timer_atomic.h index 98d92651..462601da 100644 --- a/include/etl/callback_timer_atomic.h +++ b/include/etl/callback_timer_atomic.h @@ -174,12 +174,9 @@ namespace etl active_list.remove(timer.id, true); remove_callback.call_if(timer.id); - if (timer.callback.is_valid()) - { - // Call the delegate callback. - timer.callback(); - } - + // The timer is reinserted before the callback is called, so that + // a callback that restarts the timer replaces this entry instead + // of the timer being inserted into the active list twice. if (timer.repeating) { // Reinsert the timer. @@ -188,6 +185,11 @@ namespace etl insert_callback.call_if(timer.id); } + if (timer.callback.is_valid()) + { + timer.callback(); + } + has_active = !active_list.empty(); } @@ -301,6 +303,16 @@ namespace etl return false; } + //******************************************* + /// Returns true if an attempt was ever made to insert a timer into the + /// active list when it was already there. Such an insert is rejected, as + /// it would create a circular reference in the list. + //******************************************* + bool duplicate_insert_detected() const + { + return active_list.duplicate_insert_detected(); + } + //******************************************* /// Check if there is an active timer. //******************************************* @@ -486,6 +498,7 @@ namespace etl : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) , ptimers(ptimers_) + , duplicate_insert(false) { } @@ -495,6 +508,15 @@ namespace etl return head == etl::timer::id::NO_TIMER; } + //******************************* + /// Returns true if an attempt was ever made to insert a timer that was + /// already in the list. + //******************************* + bool duplicate_insert_detected() const + { + return duplicate_insert; + } + //******************************* // Inserts the timer at the correct delta position //******************************* @@ -502,6 +524,16 @@ namespace etl { timer_data& timer = ptimers[id_]; + // A timer is in this list exactly when it is the head or has a previous + // link, as remove() clears both links. Inserting one that is already + // here would link it to itself and create a circular reference, so + // record the attempt and abandon the insert to keep the list intact. + if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER)) + { + duplicate_insert = true; + return; + } + if (head == etl::timer::id::NO_TIMER) { // No entries yet. @@ -644,6 +676,10 @@ namespace etl etl::timer::id::type tail; timer_data* const ptimers; + + // Set if an attempt was ever made to insert a timer that was already + // in the list. + bool duplicate_insert; }; // The array of timer data structures. diff --git a/include/etl/callback_timer_interrupt.h b/include/etl/callback_timer_interrupt.h index a67754cb..3c6aeb9b 100644 --- a/include/etl/callback_timer_interrupt.h +++ b/include/etl/callback_timer_interrupt.h @@ -178,11 +178,9 @@ namespace etl active_list.remove(timer.id, true); remove_callback.call_if(timer.id); - if (timer.callback.is_valid()) - { - timer.callback(); - } - + // The timer is reinserted before the callback is called, so that + // a callback that restarts the timer replaces this entry instead + // of the timer being inserted into the active list twice. if (timer.repeating) { // Reinsert the timer. @@ -191,6 +189,11 @@ namespace etl insert_callback.call_if(timer.id); } + if (timer.callback.is_valid()) + { + timer.callback(); + } + has_active = !active_list.empty(); } @@ -305,6 +308,16 @@ namespace etl return false; } + //******************************************* + /// Returns true if an attempt was ever made to insert a timer into the + /// active list when it was already there. Such an insert is rejected, as + /// it would create a circular reference in the list. + //******************************************* + bool duplicate_insert_detected() const + { + return active_list.duplicate_insert_detected(); + } + //******************************************* /// Check if there is an active timer. //******************************************* @@ -487,6 +500,7 @@ namespace etl : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) , ptimers(ptimers_) + , duplicate_insert(false) { } @@ -496,6 +510,15 @@ namespace etl return head == etl::timer::id::NO_TIMER; } + //******************************* + /// Returns true if an attempt was ever made to insert a timer that was + /// already in the list. + //******************************* + bool duplicate_insert_detected() const + { + return duplicate_insert; + } + //******************************* // Inserts the timer at the correct delta position //******************************* @@ -503,6 +526,16 @@ namespace etl { timer_data& timer = ptimers[id_]; + // A timer is in this list exactly when it is the head or has a previous + // link, as remove() clears both links. Inserting one that is already + // here would link it to itself and create a circular reference, so + // record the attempt and abandon the insert to keep the list intact. + if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER)) + { + duplicate_insert = true; + return; + } + if (head == etl::timer::id::NO_TIMER) { // No entries yet. @@ -645,6 +678,10 @@ namespace etl etl::timer::id::type tail; timer_data* const ptimers; + + // Set if an attempt was ever made to insert a timer that was already + // in the list. + bool duplicate_insert; }; // The array of timer data structures. diff --git a/include/etl/callback_timer_locked.h b/include/etl/callback_timer_locked.h index 5579ec0b..fde93249 100644 --- a/include/etl/callback_timer_locked.h +++ b/include/etl/callback_timer_locked.h @@ -264,6 +264,16 @@ namespace etl unlock = unlock_; } + //******************************************* + /// Returns true if an attempt was ever made to insert a timer into the + /// active list when it was already there. Such an insert is rejected, as + /// it would create a circular reference in the list. + //******************************************* + bool duplicate_insert_detected() const + { + return active_list.duplicate_insert_detected(); + } + //******************************************* /// Check if there is an active timer. //******************************************* @@ -460,6 +470,7 @@ namespace etl : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) , ptimers(ptimers_) + , duplicate_insert(false) { } @@ -469,6 +480,15 @@ namespace etl return head == etl::timer::id::NO_TIMER; } + //******************************* + /// Returns true if an attempt was ever made to insert a timer that was + /// already in the list. + //******************************* + bool duplicate_insert_detected() const + { + return duplicate_insert; + } + //******************************* // Inserts the timer at the correct delta position //******************************* @@ -476,6 +496,16 @@ namespace etl { timer_data& timer = ptimers[id_]; + // A timer is in this list exactly when it is the head or has a previous + // link, as remove() clears both links. Inserting one that is already + // here would link it to itself and create a circular reference, so + // record the attempt and abandon the insert to keep the list intact. + if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER)) + { + duplicate_insert = true; + return; + } + if (head == etl::timer::id::NO_TIMER) { // No entries yet. @@ -618,6 +648,10 @@ namespace etl etl::timer::id::type tail; timer_data* const ptimers; + + // Set if an attempt was ever made to insert a timer that was already + // in the list. + bool duplicate_insert; }; //******************************************* @@ -716,11 +750,9 @@ namespace etl active_list.remove(timer.id, true); remove_callback.call_if(timer.id); - if (timer.callback.is_valid()) - { - timer.callback(); - } - + // The timer is reinserted before the callback is called, so that + // a callback that restarts the timer replaces this entry instead + // of the timer being inserted into the active list twice. if (timer.repeating) { // Reinsert the timer. @@ -729,6 +761,11 @@ namespace etl insert_callback.call_if(timer.id); } + if (timer.callback.is_valid()) + { + timer.callback(); + } + has_active = !active_list.empty(); } diff --git a/include/etl/message_timer.h b/include/etl/message_timer.h index a9c0ce31..cb2151ec 100644 --- a/include/etl/message_timer.h +++ b/include/etl/message_timer.h @@ -158,6 +158,7 @@ namespace etl : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) , ptimers(ptimers_) + , duplicate_insert(false) { } @@ -167,6 +168,15 @@ namespace etl return head == etl::timer::id::NO_TIMER; } + //******************************* + /// Returns true if an attempt was ever made to insert a timer that was + /// already in the list. + //******************************* + bool duplicate_insert_detected() const + { + return duplicate_insert; + } + //******************************* // Inserts the timer at the correct delta position //******************************* @@ -174,6 +184,16 @@ namespace etl { etl::message_timer_data& timer = ptimers[id_]; + // A timer is in this list exactly when it is the head or has a previous + // link, as remove() clears both links. Inserting one that is already + // here would link it to itself and create a circular reference, so + // record the attempt and abandon the insert to keep the list intact. + if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER)) + { + duplicate_insert = true; + return; + } + if (head == etl::timer::id::NO_TIMER) { // No entries yet. @@ -316,6 +336,10 @@ namespace etl etl::timer::id::type tail; etl::message_timer_data* const ptimers; + + // Set if an attempt was ever made to insert a timer that was already + // in the list. + bool duplicate_insert; }; } // namespace private_message_timer @@ -579,6 +603,16 @@ namespace etl return false; } + //******************************************* + /// Returns true if an attempt was ever made to insert a timer into the + /// active list when it was already there. Such an insert is rejected, as + /// it would create a circular reference in the list. + //******************************************* + bool duplicate_insert_detected() const + { + return active_list.duplicate_insert_detected(); + } + //******************************************* /// Check if there is an active timer. //******************************************* diff --git a/include/etl/message_timer_atomic.h b/include/etl/message_timer_atomic.h index 8d716d8b..02dc4ab2 100644 --- a/include/etl/message_timer_atomic.h +++ b/include/etl/message_timer_atomic.h @@ -183,11 +183,9 @@ namespace etl active_list.remove(timer.id, true); remove_callback.call_if(timer.id); - if (timer.p_router != ETL_NULLPTR) - { - timer.p_router->receive(timer.destination_router_id, *(timer.p_message)); - } - + // The timer is reinserted before the message is sent, so that + // a receiver that restarts the timer replaces this entry instead + // of the timer being inserted into the active list twice. if (timer.repeating) { timer.delta = timer.period; @@ -195,6 +193,11 @@ namespace etl insert_callback.call_if(timer.id); } + if (timer.p_router != ETL_NULLPTR) + { + timer.p_router->receive(timer.destination_router_id, *(timer.p_message)); + } + has_active = !active_list.empty(); } @@ -308,6 +311,16 @@ namespace etl return false; } + //******************************************* + /// Returns true if an attempt was ever made to insert a timer into the + /// active list when it was already there. Such an insert is rejected, as + /// it would create a circular reference in the list. + //******************************************* + bool duplicate_insert_detected() const + { + return active_list.duplicate_insert_detected(); + } + //******************************************* /// Check if there is an active timer. //******************************************* @@ -467,6 +480,7 @@ namespace etl : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) , ptimers(ptimers_) + , duplicate_insert(false) { } @@ -476,6 +490,15 @@ namespace etl return head == etl::timer::id::NO_TIMER; } + //******************************* + /// Returns true if an attempt was ever made to insert a timer that was + /// already in the list. + //******************************* + bool duplicate_insert_detected() const + { + return duplicate_insert; + } + //******************************* // Inserts the timer at the correct delta position //******************************* @@ -483,6 +506,16 @@ namespace etl { timer_data& timer = ptimers[id_]; + // A timer is in this list exactly when it is the head or has a previous + // link, as remove() clears both links. Inserting one that is already + // here would link it to itself and create a circular reference, so + // record the attempt and abandon the insert to keep the list intact. + if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER)) + { + duplicate_insert = true; + return; + } + if (head == etl::timer::id::NO_TIMER) { // No entries yet. @@ -625,6 +658,10 @@ namespace etl etl::timer::id::type tail; timer_data* const ptimers; + + // Set if an attempt was ever made to insert a timer that was already + // in the list. + bool duplicate_insert; }; // The array of timer data structures. diff --git a/include/etl/message_timer_interrupt.h b/include/etl/message_timer_interrupt.h index 33ac248c..18aa4d24 100644 --- a/include/etl/message_timer_interrupt.h +++ b/include/etl/message_timer_interrupt.h @@ -189,11 +189,9 @@ namespace etl active_list.remove(timer.id, true); remove_callback.call_if(timer.id); - if (timer.p_router != ETL_NULLPTR) - { - timer.p_router->receive(timer.destination_router_id, *(timer.p_message)); - } - + // The timer is reinserted before the message is sent, so that + // a receiver that restarts the timer replaces this entry instead + // of the timer being inserted into the active list twice. if (timer.repeating) { // Reinsert the timer. @@ -202,6 +200,11 @@ namespace etl insert_callback.call_if(timer.id); } + if (timer.p_router != ETL_NULLPTR) + { + timer.p_router->receive(timer.destination_router_id, *(timer.p_message)); + } + has_active = !active_list.empty(); } @@ -316,6 +319,16 @@ namespace etl return false; } + //******************************************* + /// Returns true if an attempt was ever made to insert a timer into the + /// active list when it was already there. Such an insert is rejected, as + /// it would create a circular reference in the list. + //******************************************* + bool duplicate_insert_detected() const + { + return active_list.duplicate_insert_detected(); + } + //******************************************* /// Check if there is an active timer. //******************************************* @@ -473,6 +486,7 @@ namespace etl : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) , ptimers(ptimers_) + , duplicate_insert(false) { } @@ -482,6 +496,15 @@ namespace etl return head == etl::timer::id::NO_TIMER; } + //******************************* + /// Returns true if an attempt was ever made to insert a timer that was + /// already in the list. + //******************************* + bool duplicate_insert_detected() const + { + return duplicate_insert; + } + //******************************* // Inserts the timer at the correct delta position //******************************* @@ -489,6 +512,16 @@ namespace etl { timer_data& timer = ptimers[id_]; + // A timer is in this list exactly when it is the head or has a previous + // link, as remove() clears both links. Inserting one that is already + // here would link it to itself and create a circular reference, so + // record the attempt and abandon the insert to keep the list intact. + if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER)) + { + duplicate_insert = true; + return; + } + if (head == etl::timer::id::NO_TIMER) { // No entries yet. @@ -631,6 +664,10 @@ namespace etl etl::timer::id::type tail; timer_data* const ptimers; + + // Set if an attempt was ever made to insert a timer that was already + // in the list. + bool duplicate_insert; }; // The array of timer data structures. diff --git a/include/etl/message_timer_locked.h b/include/etl/message_timer_locked.h index 92b41488..aa95aa57 100644 --- a/include/etl/message_timer_locked.h +++ b/include/etl/message_timer_locked.h @@ -186,11 +186,9 @@ namespace etl active_list.remove(timer.id, true); remove_callback.call_if(timer.id); - if (timer.p_router != ETL_NULLPTR) - { - timer.p_router->receive(timer.destination_router_id, *(timer.p_message)); - } - + // The timer is reinserted before the message is sent, so that + // a receiver that restarts the timer replaces this entry instead + // of the timer being inserted into the active list twice. if (timer.repeating) { timer.delta = timer.period; @@ -198,6 +196,11 @@ namespace etl insert_callback.call_if(timer.id); } + if (timer.p_router != ETL_NULLPTR) + { + timer.p_router->receive(timer.destination_router_id, *(timer.p_message)); + } + has_active = !active_list.empty(); } @@ -323,6 +326,16 @@ namespace etl unlock = unlock_; } + //******************************************* + /// Returns true if an attempt was ever made to insert a timer into the + /// active list when it was already there. Such an insert is rejected, as + /// it would create a circular reference in the list. + //******************************************* + bool duplicate_insert_detected() const + { + return active_list.duplicate_insert_detected(); + } + //******************************************* /// Check if there is an active timer. //******************************************* @@ -481,6 +494,7 @@ namespace etl : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) , ptimers(ptimers_) + , duplicate_insert(false) { } @@ -490,6 +504,15 @@ namespace etl return head == etl::timer::id::NO_TIMER; } + //******************************* + /// Returns true if an attempt was ever made to insert a timer that was + /// already in the list. + //******************************* + bool duplicate_insert_detected() const + { + return duplicate_insert; + } + //******************************* // Inserts the timer at the correct delta position //******************************* @@ -497,6 +520,16 @@ namespace etl { timer_data& timer = ptimers[id_]; + // A timer is in this list exactly when it is the head or has a previous + // link, as remove() clears both links. Inserting one that is already + // here would link it to itself and create a circular reference, so + // record the attempt and abandon the insert to keep the list intact. + if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER)) + { + duplicate_insert = true; + return; + } + if (head == etl::timer::id::NO_TIMER) { // No entries yet. @@ -639,6 +672,10 @@ namespace etl etl::timer::id::type tail; timer_data* const ptimers; + + // Set if an attempt was ever made to insert a timer that was already + // in the list. + bool duplicate_insert; }; // The array of timer data structures. diff --git a/test/test_callback_timer_atomic.cpp b/test/test_callback_timer_atomic.cpp index 0dcea9ab..06e543f6 100644 --- a/test/test_callback_timer_atomic.cpp +++ b/test/test_callback_timer_atomic.cpp @@ -149,6 +149,35 @@ namespace callback_type free_function_callback2 = callback_type::create(); + //*************************************************************************** + // Fixture for a repeating timer that restarts itself from within its own + // callback (regression test for issue #967). + //*************************************************************************** + struct RestartInCallback + { + etl::callback_timer_atomic<1, std::atomic_uint32_t>* p_controller = nullptr; + etl::timer::id::type id = etl::timer::id::NO_TIMER; + uint32_t new_period = 0U; + std::vector call_ticks; + + void callback() + { + call_ticks.push_back(ticks); + + // Change the period (this stops/deactivates the timer) and restart it + // from inside the callback. Before the fix, tick() would then insert the + // timer a second time, creating a circular reference in the active list. + p_controller->set_period(id, new_period); + p_controller->start(id); + } + }; + + RestartInCallback restart_in_callback; + + using timer_callback_type = etl::icallback_timer_atomic::callback_type; + timer_callback_type restart_in_callback_member = + timer_callback_type::create(); + SUITE(test_callback_timer_atomic) { //************************************************************************* @@ -855,6 +884,54 @@ namespace CHECK_EQUAL(3, timerInsertRemoveTest.removed); } + //************************************************************************* + // Regression test for issue #967: + // Calling start() from within a repeating timer's callback (after the + // period has been changed, which stops the timer) must not insert the timer + // twice and create a circular reference in the active list. + //************************************************************************* + TEST(callback_timer_atomic_restart_in_callback_no_circular_reference) + { + etl::callback_timer_atomic<1, std::atomic_uint32_t> timer_controller; + + restart_in_callback.p_controller = &timer_controller; + restart_in_callback.new_period = 20U; + restart_in_callback.call_ticks.clear(); + + etl::timer::id::type id = timer_controller.register_timer(restart_in_callback_member, 10, etl::timer::mode::Repeating); + restart_in_callback.id = id; + + timer_controller.start(id); + timer_controller.enable(true); + + ticks = 0; + + const uint32_t step = 1U; + + // Run long enough for several firings at the original (10) then new (20) period. + while (ticks < 100U) + { + ticks += step; + timer_controller.tick(step); + } + + // Detect a duplicated entry in the active list directly, rather than + // relying on the resulting circular reference hanging the test. + CHECK_FALSE(timer_controller.duplicate_insert_detected()); + + // The timer must still be active (alive, not corrupted) and scheduled at + // the new period. A circular reference would corrupt the active list. + CHECK_TRUE(timer_controller.is_active(id)); + + // First firing at 10, then every 20 thereafter: 10, 30, 50, 70, 90. + std::vector compare = {10, 30, 50, 70, 90}; + + CHECK_EQUAL(compare.size(), restart_in_callback.call_ticks.size()); + CHECK_ARRAY_EQUAL(compare.data(), restart_in_callback.call_ticks.data(), compare.size()); + + timer_controller.clear(); + } + //************************************************************************* #if REALTIME_TEST diff --git a/test/test_callback_timer_interrupt.cpp b/test/test_callback_timer_interrupt.cpp index b7830cf4..e2d71377 100644 --- a/test/test_callback_timer_interrupt.cpp +++ b/test/test_callback_timer_interrupt.cpp @@ -162,6 +162,33 @@ namespace callback_type free_function_callback2 = callback_type::create(); + //*************************************************************************** + // Fixture for a repeating timer that restarts itself from within its own + // callback (regression test for issue #967). + //*************************************************************************** + struct RestartInCallback + { + etl::callback_timer_interrupt<1, ScopedGuard>* p_controller = nullptr; + etl::timer::id::type id = etl::timer::id::NO_TIMER; + uint32_t new_period = 0U; + std::vector call_ticks; + + void callback() + { + call_ticks.push_back(ticks); + + // Change the period (this stops/deactivates the timer) and restart it + // from inside the callback. Before the fix, tick() would then insert the + // timer a second time, creating a circular reference in the active list. + p_controller->set_period(id, new_period); + p_controller->start(id); + } + }; + + RestartInCallback restart_in_callback; + + callback_type restart_in_callback_member = callback_type::create(); + SUITE(test_callback_timer_interrupt) { //************************************************************************* @@ -1001,5 +1028,55 @@ namespace // Check the CHECK_EQUAL(0U, ScopedGuard::guard_count); } + + //************************************************************************* + // Regression test for issue #967: + // Calling start() from within a repeating timer's callback (after the + // period has been changed, which stops the timer) must not insert the timer + // twice and create a circular reference in the active list. + //************************************************************************* + TEST(callback_timer_interrupt_restart_in_callback_no_circular_reference) + { + etl::callback_timer_interrupt<1, ScopedGuard> timer_controller; + + restart_in_callback.p_controller = &timer_controller; + restart_in_callback.new_period = 20U; + restart_in_callback.call_ticks.clear(); + + etl::timer::id::type id = timer_controller.register_timer(restart_in_callback_member, 10, etl::timer::mode::Repeating); + restart_in_callback.id = id; + + timer_controller.start(id); + timer_controller.enable(true); + + ticks = 0; + + const uint32_t step = 1U; + + // Run long enough for several firings at the original (10) then new (20) period. + while (ticks < 100U) + { + ticks += step; + timer_controller.tick(step); + } + + // Detect a duplicated entry in the active list directly, rather than + // relying on the resulting circular reference hanging the test. + CHECK_FALSE(timer_controller.duplicate_insert_detected()); + + // The timer must still be active (alive, not corrupted) and scheduled at + // the new period. A circular reference would corrupt the active list. + CHECK_TRUE(timer_controller.is_active(id)); + + // First firing at 10, then every 20 thereafter: 10, 30, 50, 70, 90. + std::vector compare = {10, 30, 50, 70, 90}; + + CHECK_EQUAL(compare.size(), restart_in_callback.call_ticks.size()); + CHECK_ARRAY_EQUAL(compare.data(), restart_in_callback.call_ticks.data(), compare.size()); + + timer_controller.clear(); + + CHECK_EQUAL(0U, ScopedGuard::guard_count); + } } } // namespace diff --git a/test/test_callback_timer_locked.cpp b/test/test_callback_timer_locked.cpp index b87c4b8c..2ce71280 100644 --- a/test/test_callback_timer_locked.cpp +++ b/test/test_callback_timer_locked.cpp @@ -184,6 +184,33 @@ namespace callback_type free_function_callback2 = callback_type::create(); + //*************************************************************************** + // Fixture for a repeating timer that restarts itself from within its own + // callback (regression test for issue #967). + //*************************************************************************** + struct RestartInCallback + { + etl::callback_timer_locked<1>* p_controller = nullptr; + etl::timer::id::type id = etl::timer::id::NO_TIMER; + uint32_t new_period = 0U; + std::vector call_ticks; + + void callback() + { + call_ticks.push_back(ticks); + + // Change the period (this stops/deactivates the timer) and restart it + // from inside the callback. Before the fix, tick() would then insert the + // timer a second time, creating a circular reference in the active list. + p_controller->set_period(id, new_period); + p_controller->start(id); + } + }; + + RestartInCallback restart_in_callback; + + callback_type restart_in_callback_member = callback_type::create(); + SUITE(test_callback_timer_locked) { //************************************************************************* @@ -1004,6 +1031,59 @@ namespace CHECK_FALSE(timer_controller.has_active_timer()); } + //************************************************************************* + // Regression test for issue #967: + // Calling start() from within a repeating timer's callback (after the + // period has been changed, which stops the timer) must not insert the timer + // twice and create a circular reference in the active list. + //************************************************************************* + TEST(callback_timer_locked_restart_in_callback_no_circular_reference) + { + locks.clear(); + try_lock_type try_lock = try_lock_type::create(); + lock_type lock = lock_type::create(); + unlock_type unlock = unlock_type::create(); + + etl::callback_timer_locked<1> timer_controller(try_lock, lock, unlock); + + restart_in_callback.p_controller = &timer_controller; + restart_in_callback.new_period = 20U; + restart_in_callback.call_ticks.clear(); + + etl::timer::id::type id = timer_controller.register_timer(restart_in_callback_member, 10, etl::timer::mode::Repeating); + restart_in_callback.id = id; + + timer_controller.start(id); + timer_controller.enable(true); + + ticks = 0; + + const uint32_t step = 1U; + + // Run long enough for several firings at the original (10) then new (20) period. + while (ticks < 100U) + { + ticks += step; + timer_controller.tick(step); + } + + // Detect a duplicated entry in the active list directly, rather than + // relying on the resulting circular reference hanging the test. + CHECK_FALSE(timer_controller.duplicate_insert_detected()); + + // The timer must still be active (alive, not corrupted) and scheduled at + // the new period. A circular reference would corrupt the active list. + CHECK_TRUE(timer_controller.is_active(id)); + + // First firing at 10, then every 20 thereafter: 10, 30, 50, 70, 90. + std::vector compare = {10, 30, 50, 70, 90}; + + CHECK_EQUAL(compare.size(), restart_in_callback.call_ticks.size()); + CHECK_ARRAY_EQUAL(compare.data(), restart_in_callback.call_ticks.data(), compare.size()); + + timer_controller.clear(); + } + //************************************************************************* #if REALTIME_TEST From 360e18df189ccc9dfc037159ec0864b2b80de07f Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Wed, 30 Sep 2026 10:12:00 +0200 Subject: [PATCH 13/16] Fix is_swappable for MSVC in C++14 (#1600) * Fix is_swappable for MSVC in C++14 * MSVC 19.44, Debug build, C++20 specific format adjustment * Add docs and tutorial for etl::intrusive_avl_tree (#1608) * Add docs and tutorial for etl::intrusive_avl_tree Documents the intrusive AVL tree added in PR #1425: a full API reference page under Containers/Trees, and a tutorial demonstrating ordered access to intrusively-linked objects with unpredictable lifetimes, including move support and destructor-driven automatic removal from the tree. Co-Authored-By: Claude Sonnet 5 * Add complexity shortcode and document its usage Introduces a {{< complexity >}} Hugo shortcode (with light/dark styling for constant/logarithmic/linear/linearithmic/polynomial/exponential classes) for rendering Big-O complexity inline, applies it throughout the intrusive_avl_tree API reference in place of raw "O(...)" text, and documents the Description/Complexity structure and supported classes in DOCUMENTATION.md. Co-Authored-By: Claude Sonnet 5 * Fix issues found by @CodeRabbit * Put complexity notes on their own lines in intrusive_avl_tree docs Move the complexity shortcode from the inline "**Complexity:**" label to its own line under a "**Complexity**" label. Notes that give several alternatives (e.g. amortized vs. worst-case) now show each one on a separate line. Update the guideline in DOCUMENTATION.md to match. * Update complexity examples in DOCUMENTATION.md Add the hard line break after the "**Complexity**" label in the example, and rewrite the list of supported complexity classes in the new two-line format, including an example with several alternatives. * Add logarithmic complexity example to DOCUMENTATION.md --------- Co-authored-by: Claude Sonnet 5 Co-authored-by: John Wellbelove * Updated sponsors pages * Refactor the documentation css to group styles for light and dark themes --------- Co-authored-by: John Wellbelove Co-authored-by: Sergei Co-authored-by: Claude Sonnet 5 Co-authored-by: John Wellbelove --- .github/workflows/msvc.yml | 32 ++++-- include/etl/format.h | 17 +++- include/etl/type_traits.h | 194 ++++++++++++++++++++++++++++--------- 3 files changed, 187 insertions(+), 56 deletions(-) diff --git a/.github/workflows/msvc.yml b/.github/workflows/msvc.yml index 8f7d4c00..dd5d3e45 100644 --- a/.github/workflows/msvc.yml +++ b/.github/workflows/msvc.yml @@ -36,8 +36,12 @@ concurrency: jobs: build-windows-msvc-stl: - name: Windows - STL + name: Windows - STL - C++${{ matrix.cxx_standard }} runs-on: [windows-2022] + strategy: + fail-fast: false + matrix: + cxx_standard: [14, 17, 20] steps: - name: Checkout @@ -50,7 +54,7 @@ jobs: - name: Build run: | - cmake -G "Visual Studio 17 2022" -AWin32 -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 ./ + cmake -G "Visual Studio 17 2022" -AWin32 -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=${{ matrix.cxx_standard }} ./ MSBuild.exe -version MSBuild.exe .\etl.sln @@ -58,8 +62,12 @@ jobs: run: test/Debug/etl_tests.exe -v build-windows-msvc-no-stl: - name: Windows - No STL + name: Windows - No STL - C++${{ matrix.cxx_standard }} runs-on: [windows-2022] + strategy: + fail-fast: false + matrix: + cxx_standard: [14, 17, 20] steps: - name: Checkout uses: actions/checkout@v6.0.3 @@ -71,7 +79,7 @@ jobs: - name: Build run: | - cmake -G "Visual Studio 17 2022" -AWin32 -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 ./ + cmake -G "Visual Studio 17 2022" -AWin32 -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=${{ matrix.cxx_standard }} ./ MSBuild.exe -version MSBuild.exe .\etl.sln @@ -79,8 +87,12 @@ jobs: run: test/Debug/etl_tests.exe -v build-windows-msvc-stl-force-cpp03: - name: Windows - STL - Force C++03 + name: Windows - STL - Force C++03 - C++${{ matrix.cxx_standard }} runs-on: [windows-2022] + strategy: + fail-fast: false + matrix: + cxx_standard: [14, 17, 20] steps: - name: Checkout @@ -93,7 +105,7 @@ jobs: - name: Build run: | - cmake -G "Visual Studio 17 2022" -AWin32 -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=17 ./ + cmake -G "Visual Studio 17 2022" -AWin32 -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=${{ matrix.cxx_standard }} ./ MSBuild.exe -version MSBuild.exe .\etl.sln @@ -101,8 +113,12 @@ jobs: run: test/Debug/etl_tests.exe -v build-windows-msvc-no-stl-force-cpp03: - name: Windows - No STL - Force C++03 + name: Windows - No STL - Force C++03 - C++${{ matrix.cxx_standard }} runs-on: [windows-2022] + strategy: + fail-fast: false + matrix: + cxx_standard: [14, 17, 20] steps: - name: Checkout @@ -115,7 +131,7 @@ jobs: - name: Build run: | - cmake -G "Visual Studio 17 2022" -AWin32 -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=17 ./ + cmake -G "Visual Studio 17 2022" -AWin32 -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=${{ matrix.cxx_standard }} ./ MSBuild.exe -version MSBuild.exe .\etl.sln diff --git a/include/etl/format.h b/include/etl/format.h index 288454fd..f34121b9 100644 --- a/include/etl/format.h +++ b/include/etl/format.h @@ -509,7 +509,8 @@ namespace etl struct basic_format_string { inline ETL_CONSTEVAL basic_format_string(const char* fmt) - : _sv(fmt) + : _str(fmt) + , _length(etl::char_traits::length(fmt)) { #if ETL_USING_CPP20 // Compile-time validation: check_format runs at compile time via consteval. @@ -527,12 +528,22 @@ namespace etl ETL_CONSTEXPR string_view get() const { - return _sv; + return string_view(_str, _length); } private: - string_view _sv; + // The format string is deliberately stored as a pointer plus a length, and + // not as a string_view. + // + // A string_view holds a pair of pointers, so constructing one here would + // initialise a member with the pointer arithmetic 'fmt + length(fmt)'. + // Microsoft's compiler (19.44) miscompiles exactly that case in a consteval + // constructor in debug builds: the resulting end pointer, and therefore the + // size of the view, is wrong. Storing the length as an integer instead + // computes correctly, and the view is formed on demand in get(). + const char* _str; + size_t _length; }; template diff --git a/include/etl/type_traits.h b/include/etl/type_traits.h index b01b308f..a7df81ae 100644 --- a/include/etl/type_traits.h +++ b/include/etl/type_traits.h @@ -4310,13 +4310,53 @@ namespace etl #endif #if ETL_USING_CPP11 + //********************************************* + // Builtin-free noexcept-constructibility/assignability detection. Used when + // the __is_nothrow_constructible/__is_nothrow_assignable builtins are + // unavailable. Mirrors the void_t-based is_constructible/is_assignable + // fallbacks above so that (e.g.) a class with implicit noexcept move + // operations is correctly reported nothrow-(move-)constructible/assignable, + // and a const-qualified type is correctly reported NOT nothrow-assignable + // (the old is_arithmetic||is_pointer fallback ignored const-qualification). + #if !ETL_USING_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE + namespace private_type_traits + { + template + struct is_nothrow_constructible_ : etl::false_type + { + }; + + template + struct is_nothrow_constructible_()...))>, T, TArgs...> + : etl::bool_constant()...))> + { + }; + } // namespace private_type_traits + #endif + + #if !ETL_USING_BUILTIN_IS_NOTHROW_ASSIGNABLE + namespace private_type_traits + { + template + struct is_nothrow_assignable_ : etl::false_type + { + }; + + template + struct is_nothrow_assignable_() = etl::declval())>, T, U> + : etl::bool_constant() = etl::declval())> + { + }; + } // namespace private_type_traits + #endif + //********************************************* // 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> + struct is_nothrow_constructible : public private_type_traits::is_nothrow_constructible_, T, TArgs...> #endif { }; @@ -4324,33 +4364,21 @@ namespace etl //********************************************* // 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 { }; @@ -4360,7 +4388,7 @@ namespace etl #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> + struct is_nothrow_assignable : public private_type_traits::is_nothrow_assignable_, T, U> #endif { }; @@ -4368,24 +4396,16 @@ namespace etl //********************************************* // 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 @@ -5175,20 +5195,26 @@ namespace etl #elif ETL_USING_CPP11 namespace private_type_traits { + template + struct is_swappable_helper; + 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 + // A dummy declaration that can never be selected, as it takes no + // arguments, but which makes the name visible to ordinary lookup so that + // the unqualified calls below are well formed. + // Ordinary lookup stops here, so only a swap found by argument dependent + // lookup takes part in overload resolution. + // The generic swap that the standard library (or etl) provides is not + // detected this way, because not every implementation constrains it + // (Microsoft's C++14 std::swap does not), which would make every type + // appear to be swappable. Its viability is determined by the + // is_default_swappable traits below instead. + void swap(); // Selected only if 'swap(declval(), declval())' is a valid expression. template - struct is_swappable_test + struct is_adl_swappable_test { private: @@ -5206,7 +5232,7 @@ namespace etl // Selected only if both 'swap(declval(), declval())' and // 'swap(declval(), declval())' are valid expressions. template - struct is_swappable_with_test + struct is_adl_swappable_with_test { private: @@ -5221,15 +5247,61 @@ namespace etl static ETL_CONSTANT bool value = decltype(test(0))::value; }; + + // Determines whether an expression of type T can be bound to a non-const + // lvalue reference to its referenced type. + // This is not simply 'is_lvalue_reference', because some compilers offer + // an extension that binds a class rvalue to a non-const lvalue reference. + template + void binds_to_lvalue_reference_test(T&); + + template + struct binds_to_lvalue_reference : etl::false_type + { + }; + + template + struct binds_to_lvalue_reference< + T, etl::void_t::type>(etl::declval()))>> : etl::true_type + { + }; } // namespace swap_detect + // Determines whether the generic swap(T&, T&) is viable for T. template - struct is_swappable_helper : etl::bool_constant::value> + struct is_default_swappable : etl::bool_constant::value && etl::is_move_assignable::value> + { + }; + + // The generic swap for arrays requires the element type to be swappable. + template + struct is_default_swappable : is_swappable_helper + { + }; + + // An array of unknown bound cannot be swapped. + template + struct is_default_swappable : etl::false_type + { + }; + + // The generic swap only participates when both arguments are lvalues of + // the same type. + template + struct is_default_swappable_with + : etl::bool_constant::value && etl::is_same::value + && is_default_swappable::type>::value> + { + }; + + template + struct is_swappable_helper : etl::bool_constant::value || is_default_swappable::value> { }; template - struct is_swappable_with_helper : etl::bool_constant::value> + struct is_swappable_with_helper + : etl::bool_constant::value || is_default_swappable_with::value> { }; } // namespace private_type_traits @@ -5271,43 +5343,75 @@ namespace etl #elif ETL_USING_CPP11 namespace private_type_traits { + template + struct is_nothrow_swappable_helper; + namespace swap_detect { - // Only 'true' if the type is swappable and the swap expression is noexcept. - template ::value> - struct is_nothrow_swappable_test + // Only 'true' if the type has an ADL found swap and the swap expression is noexcept. + template ::value> + struct is_nothrow_adl_swappable_test { static ETL_CONSTANT bool value = false; }; template - struct is_nothrow_swappable_test + struct is_nothrow_adl_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 + // Only 'true' if the types have ADL found swaps for each other and both of + // the swap expressions are noexcept. + template ::value> + struct is_nothrow_adl_swappable_with_test { static ETL_CONSTANT bool value = false; }; template - struct is_nothrow_swappable_with_test + struct is_nothrow_adl_swappable_with_test { static ETL_CONSTANT bool value = noexcept(swap(etl::declval(), etl::declval())) && noexcept(swap(etl::declval(), etl::declval())); }; } // namespace swap_detect + // Determines whether the generic swap(T&, T&) is viable for T and cannot throw. template - struct is_nothrow_swappable_helper : etl::bool_constant::value> + struct is_nothrow_default_swappable + : etl::bool_constant::value && etl::is_nothrow_move_assignable::value> + { + }; + + template + struct is_nothrow_default_swappable : is_nothrow_swappable_helper + { + }; + + template + struct is_nothrow_default_swappable : etl::false_type { }; template - struct is_nothrow_swappable_with_helper : etl::bool_constant::value> + struct is_nothrow_default_swappable_with + : etl::bool_constant::value && etl::is_same::value + && is_nothrow_default_swappable::type>::value> + { + }; + + // An ADL found swap hides the generic one, so it decides the result. + template + struct is_nothrow_swappable_helper + : etl::bool_constant::value ? swap_detect::is_nothrow_adl_swappable_test::value + : is_nothrow_default_swappable::value> + { + }; + + template + struct is_nothrow_swappable_with_helper + : etl::bool_constant::value ? swap_detect::is_nothrow_adl_swappable_with_test::value + : is_nothrow_default_swappable_with::value> { }; } // namespace private_type_traits From a2da3ed9405b9a5e96ea26fe87b30f36bd26df1f Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Fri, 9 Oct 2026 18:39:04 +0200 Subject: [PATCH 14/16] Optimise cyclic value (#1607) * Updated ignore for hard link for CLion * Initial working version * Optimised advance template call * Further optimsed advance Updated docs and tests * Moved comparison operators out of the class. * Implement spaceship operator <=> for No-STL case (#1604) This was disabled explicitly in recent commit 232c948674cff1b4b72c2d6b3acfa7342d096dd1 to prevent using the header from STL. Now, re-enabling by implementing an ETL version of it in compare.h * Sponsorship message updates * Fix duplicated check runs * Refactored the advance function to fix issues with signed unsigned mod operations. * clang-format * Updated documentation for cyclic_value * Modified code comments * Fixed a logic issue in advance() where a less than maximum step from the minimum value for a cyclic value covering the full type's range would take the wrong logical path (Step would roll over), although calculating the correct result. This could be cofusing to someone debugging cyclc_value. * Added runtime ans static asserts for reverse ordered limts. * clang-format * Spelling fix 'fast' -> 'last' * Code review updates * clang-format * Code review updates * Code review updates --------- Co-authored-by: John Wellbelove Co-authored-by: Roland Reichwein --- docs/_index.md | 5 +- docs/utilities/cyclic-value.md | 201 +++++++--- include/etl/cyclic_value.h | 610 ++++++++++++++++++++++--------- include/etl/file_error_numbers.h | 1 + test/run-syntax-checks.sh | 33 +- test/test_cyclic_value.cpp | 519 +++++++++++++++++++++++++- 6 files changed, 1133 insertions(+), 236 deletions(-) diff --git a/docs/_index.md b/docs/_index.md index dc6fde75..c31c6a6d 100644 --- a/docs/_index.md +++ b/docs/_index.md @@ -118,9 +118,9 @@ CRC calculations (8, 16, 32 & 64-bit) Checksums and hash functions - Variants (type-safe unions) - Extensive template support utilities -- Robust Error Handling: Configurable error checking using asserts, exceptions, error handlers, or no checks; user’s choice. +- Robust Error Handling: Configurable error checking using asserts, exceptions, error handlers, or no checks; user's choice. - Thoroughly Tested: -Over 10,000 unit tests +Over **14,000** tests Tested with Visual Studio 2022, GCC 12, and Clang 14 Continuous integration via GitHub Actions. - Readable and Well-Documented: Clean, maintainable source code with clear documentation. @@ -128,6 +128,7 @@ Continuous integration via GitHub Actions. - Archived: A snapshot of the ETL is preserved in the Arctic Code Vault for long-term digital preservation. ## Support the ETL + Maintaining the ETL can take a lot of man-hours of work, but unfortunately it doesn't pay the bills. When I have to take on paying work, the ETL gets a lot less attention. So if you have found the library is an important component in your work and you would like to help out, then please consider by supporting the project. --- diff --git a/docs/utilities/cyclic-value.md b/docs/utilities/cyclic-value.md index 37a92b65..d29a7b42 100644 --- a/docs/utilities/cyclic-value.md +++ b/docs/utilities/cyclic-value.md @@ -15,6 +15,9 @@ Supports compile time and runtime variants. template class cyclic_value ``` +Static asserts if `Last` < `First`. + +**Example** ```cpp etl::cyclic_value value_ct; // Compile time. Fixed range of 2 to 7 inclusive. @@ -24,126 +27,183 @@ value_rt.set(3, 8); ``` ## Constructor + +### Run time limits + ```cpp -cyclic_value value; +cyclic_value() noexcept; ``` -Creates a runtime cyclic value of type int with default constructed initial defined limits. +Constructs a cyclic value of type `T` with an initial value of `first`. +`constexpr` from C++11. --- ```cpp -cyclic_value value; +cyclic_value(T first, T last); ``` -Creates a compile time cyclic value of type int with fixed limits of N and M. +Constructs a *run time* cyclic value of type `T` with limits of `first` and `last` and an initial value of `first`. +Asserts an `etl::cyclic_value_reversed_limits` if `last` < `first`. +`constexpr` from C++11. + +--- -**Example** ```cpp -cyclic_value value(N, M); +cyclic_value(T first, T last, T initial); ``` -Creates a runtime cyclic value of type int with initial defined limits of N and M. +Constructs a cyclic value of type `T` with limits of `first` and `last` and an initial value of `initial`. +`initial` will be clamped to be in the range `(first, last)`. +Asserts an `etl::cyclic_value_reversed_limits` if `last` < `first`. +`constexpr` from C++11. + +### Compile time limits + +```cpp +cyclic_value() noexcept; +``` +Constructs a cyclic value of type `T` default constructed with an initial value of `First`. +`constexpr` from C++11. + +--- + +```cpp +cyclic_value(T initial) noexcept; +``` +Constructs a cyclic value of type `T` constructed with an `initial`. +`initial` will be clamped to be in the range `(First, Last)`. +`constexpr` from C++11. ## Modifiers + ```cpp -cyclic_value& operator ++(); -cyclic_value& operator ++(int); +cyclic_value& operator ++() noexcept; +cyclic_value& operator ++(int) noexcept; ``` -Increments the value. If the value is at the last value then is set to the first. +Increments the value. If the value is at the last value then is set to the first. +`constexpr` from C++14. --- ```cpp -cyclic_value& operator --(); -cyclic_value& operator --(int); +cyclic_value& operator --() noexcept; +cyclic_value& operator --(int) noexcept; ``` -Decrements the value. If the value is at the first value then is set to the last. +Decrements the value. If the value is at the first value then is set to the last. +`constexpr` from C++14. --- ```cpp -void advance(int n); +template +void advance(TStep n) noexcept; ``` -Advances the value by the specified amount, wrapping if necessary. +Advances the value by the specified amount, wrapping if necessary. +`constexpr` from C++14. + +--- + +```cpp +template +cyclic_value& operator +=(TStep n) noexcept; +``` +Advances the value by the specified amount, wrapping if necessary. +`constexpr` from C++14. + +--- + +```cpp +template +cyclic_value& operator -=(TStep n) noexcept; +``` +Decrements the value by the specified amount, wrapping if necessary. +`constexpr` from C++14. ## Access + ```cpp T get() const; ``` -Gets the current value +Gets the current value. +`constexpr` from C++11. --- ```cpp -T first() const; +T first() const noexcept; ``` -Sets the current value to the first value +Gets the first value. +`constexpr` from C++11. --- ```cpp -T last() const; +T last() const noexcept; ``` -Sets the current value to the last value +Gets the last value. +`constexpr` from C++11. --- ```cpp -void set(T first, T last); +void set(T first, T last) noexcept; ``` -Sets the new first and last values. Sets the current value to first. +Sets the new *run time* `first` and `last` values. Sets the current value to `first`. +Asserts an `etl::cyclic_value_reversed_limits` if `last` < `first`. +`constexpr` from C++14. --- ```cpp -void set(T value); +void set(T value) noexcept; ``` -Sets the current value. +Sets the current value. +`constexpr` from C++14. --- ```cpp -void to_first(); +void to_first() noexcept; ``` -Sets the current value to the first value +Sets the current value to the first value. +`constexpr` from C++14. --- ```cpp -void to_last(); +void to_last() noexcept; ``` -Sets the current value to the last value +Sets the current value to the last value. +`constexpr` from C++14. ## Operations ```cpp -void swap(cyclic_value& other); +void swap(cyclic_value& other) noexcept; ``` -Swaps with another cyclic value. +Swaps with another cyclic value. +`constexpr` from C++14. ---- - -```cpp -void swap(cyclic_value& lhs, cyclic_value& rhs); -``` -Swaps with another cyclic value. ## Operators + ```cpp -operator T(); -operator const T() const; +operator T() noexcept; +operator const T() const noexcept; ``` -Conversion operators to `T`. +Conversion operators to `T`. +`constexpr` from C++11. --- ```cpp -cyclic_value& operator =(T t); +cyclic_value& operator =(T t) noexcept; ``` -Sets the current value to `t`. +Sets the current value to `t`. +`constexpr` from C++14. ## Non-member functions ```cpp template -void swap(cyclic_value& lhs, cyclic_value& rhs) +void swap(cyclic_value& lhs, cyclic_value& rhs) noexcept ``` Swaps two cyclic values. @@ -151,14 +211,63 @@ Swaps two cyclic values. ```cpp template -bool operator == (cyclic_value& lhs, cyclic_value& rhs) +bool operator == (cyclic_value& lhs, cyclic_value& rhs) noexcept ``` -Checks equality of two cyclic values. +Checks equality of two cyclic values. +`constexpr` from C++11. + +{{< callout type="warning">}} + After version `20.49.0` equality is judged only by the current value. + Previously the first and last limits were also considered. + + <= 20.49.0 + `etl::cyclic_value<2, 7>(3) != etl::cyclic_value<2, 6>(3)` + + > 20.49.0 + `etl::cyclic_value<2, 7>(3) == etl::cyclic_value<2, 6>(3)` +{{< /callout >}} --- ```cpp template -bool operator != (cyclic_value& lhs, cyclic_value& rhs) +bool operator != (cyclic_value& lhs, cyclic_value& rhs) noexcept ``` -Checks inequality of two cyclic values. +Checks inequality of two cyclic values. +`constexpr` from C++11. + +--- + +```cpp +template +bool operator < (cyclic_value& lhs, cyclic_value& rhs) noexcept +``` +Checks if the current value of `lhs` is less than the current value of `rhs`. +`constexpr` from C++11. + +--- + +```cpp +template +bool operator <= (cyclic_value& lhs, cyclic_value& rhs) noexcept +``` +Checks if the current value of `lhs` is less than or equal the current value of `rhs`. +`constexpr` from C++11. + +--- + +```cpp +template +bool operator > (cyclic_value& lhs, cyclic_value& rhs) noexcept +``` +Checks if the current value of `lhs` is greater than the current value of `rhs`. +`constexpr` from C++11. + +--- + +```cpp +template +bool operator >= (cyclic_value& lhs, cyclic_value& rhs) noexcept +``` +Checks if the current value of `lhs` is greater than or equal the current value of `rhs`. +`constexpr` from C++11. diff --git a/include/etl/cyclic_value.h b/include/etl/cyclic_value.h index 964ed664..6acdd1f1 100644 --- a/include/etl/cyclic_value.h +++ b/include/etl/cyclic_value.h @@ -38,10 +38,198 @@ SOFTWARE. #include "platform.h" #include "algorithm.h" #include "exception.h" +#include "integral_limits.h" +#include "largest.h" +#include "negative.h" +#include "static_assert.h" #include "type_traits.h" namespace etl { + namespace private_cyclic_value + { + template + struct traits + { + typedef typename etl::make_unsigned::type unsigned_type; + }; + + //************************************************************************* + /// Increments a value and wraps it at the supplied limits. + ///\param value The current value. + ///\param min_value The minimum value. + ///\param max_value The maximum value. + ///\return The new value. + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 T increment(T value, T min_value, T max_value) ETL_NOEXCEPT + { + return value == max_value ? min_value : static_cast(etl::to_unsigned(value) + 1U); + } + + //************************************************************************* + /// Decrements a value and wraps it at the supplied limits. + ///\param value The current value. + ///\param min_value The minimum value. + ///\param max_value The maximum value. + ///\return The new value. + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 T decrement(T value, T min_value, T max_value) ETL_NOEXCEPT + { + return value == min_value ? max_value : static_cast(etl::to_unsigned(value) - 1U); + } + + //************************************************************************* + /// Advances a value and wraps it at the supplied limits. + /// Called for the case where the range is not the full width of the type. + ///\param value The current value. + ///\param min_value The minimum value. + ///\param max_value The maximum value. + ///\param step The number of steps. + ///\param subtract True if the value is to be decremented, false if it is to be incremented. + ///\return The advanced value. + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 T advance(T value, T min_value, T max_value, typename traits::unsigned_type step, bool subtract) ETL_NOEXCEPT + { + typedef typename traits::unsigned_type unsigned_type; + + switch (step) + { + case 0: + { + return value; + } + + case 1: + { + return subtract ? decrement(value, min_value, max_value) : increment(value, min_value, max_value); + } + + default: + { + if (subtract) + { + unsigned_type distance_to_first = static_cast(etl::to_unsigned(value) - etl::to_unsigned(min_value)); + + if (step <= distance_to_first) + { + // Room to subtract the step. + value = static_cast(etl::to_unsigned(value) - step); + } + else + { + // Step would roll over. + step -= distance_to_first + 1U; + value = static_cast(etl::to_unsigned(max_value) - step); + } + } + else + { + unsigned_type distance_to_last = static_cast(etl::to_unsigned(max_value) - etl::to_unsigned(value)); + + if (step <= distance_to_last) + { + // Room to add the step. + value = static_cast(etl::to_unsigned(value) + step); + } + else + { + // Step would roll over. + step -= distance_to_last + 1U; + value = static_cast(etl::to_unsigned(min_value) + step); + } + } + + return value; + } + } + } + + //************************************************************************* + /// Advances a value and wraps it at the supplied limits. + /// Called for the case where the range is the full width of the type. + ///\param value The current value. + ///\param step The number of steps. + ///\param subtract True if the value is to be decremented, false if it is to be incremented. + ///\return The advanced value. + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 T advance(T value, typename traits::unsigned_type step, bool subtract) ETL_NOEXCEPT + { + if (subtract) + { + value = static_cast(etl::to_unsigned(value) - step); + } + else + { + value = static_cast(etl::to_unsigned(value) + step); + } + + return value; + } + + //************************************************************************* + // Provides information about a range. + // Ensures that Last - First + 1 is only calculated for when the range is not the full width of the type, to avoid overflow errors. + //************************************************************************* + template ::min) && (Last == etl::integral_limits::max))> + struct range_traits; + + //************************************************************************* + // Specialisation for when the range is the full width of the type. + template + struct range_traits + { + typedef typename traits::unsigned_type unsigned_type; + + static const bool is_full_range = true; + static const unsigned_type value = 0; // Unused, but required for template specialisation. + }; + + //************************************************************************* + // Specialisation for when the range is not the full width of the type. + template + struct range_traits + { + typedef typename traits::unsigned_type unsigned_type; + + static const bool is_full_range = false; + static const unsigned_type value = static_cast(static_cast(Last) - static_cast(First)) + 1U; + }; + + // Out-of-class definitions (required pre-C++17 for ODR-use of static const members). + template + ETL_CONSTANT bool range_traits::is_full_range; + + template + ETL_CONSTANT typename range_traits::unsigned_type range_traits::value; + + template + ETL_CONSTANT bool range_traits::is_full_range; + + template + ETL_CONSTANT typename range_traits::unsigned_type range_traits::value; + + } // namespace private_cyclic_value + + struct cyclic_value_exception : etl::exception + { + cyclic_value_exception(string_type reason_, string_type file_, numeric_type line_) + : etl::exception(reason_, file_, line_) + { + } + }; + + struct cyclic_value_reversed_limits : cyclic_value_exception + { + cyclic_value_reversed_limits(string_type file_, numeric_type line_) + : cyclic_value_exception(ETL_ERROR_TEXT("cyclic_value_exception:reversed limits", ETL_CYCLIC_VALUE_FILE_ID"A"), file_, line_) + { + } + }; + //*************************************************************************** /// Provides a value that cycles between two limits. //*************************************************************************** @@ -59,13 +247,20 @@ namespace etl template class cyclic_value { + private: + + typedef typename private_cyclic_value::traits::unsigned_type unsigned_type; + public: + ETL_STATIC_ASSERT(etl::is_integral::value, "T must be an integral type"); + ETL_STATIC_ASSERT(First <= Last, "First is not <= Last"); + //************************************************************************* /// Default constructor. /// The initial value is set to the first value. //************************************************************************* - ETL_CONSTEXPR cyclic_value() + ETL_CONSTEXPR cyclic_value() ETL_NOEXCEPT : value(First) { } @@ -75,15 +270,15 @@ namespace etl /// Set to an initial value. /// Clamped to the range. //************************************************************************* - ETL_CONSTEXPR14 explicit cyclic_value(T initial) + ETL_CONSTEXPR explicit cyclic_value(T initial) ETL_NOEXCEPT + : value(etl::clamp(initial, First, Last)) { - set(initial); } //************************************************************************* /// Copy constructor. //************************************************************************* - ETL_CONSTEXPR cyclic_value(const cyclic_value& other) + ETL_CONSTEXPR cyclic_value(const cyclic_value& other) ETL_NOEXCEPT : value(other.value) { } @@ -91,19 +286,48 @@ namespace etl //************************************************************************* /// Assignment operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) + ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { value = other.value; return *this; } + //************************************************************************* + /// Adds a number of steps and clamps to the range. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + template + ETL_CONSTEXPR14 cyclic_value& operator+=(TStep n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + advance(n); + return *this; + } + + //************************************************************************* + /// Subtracts a number of steps and clamps to the range. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + template + ETL_CONSTEXPR14 cyclic_value& operator-=(TStep n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + do_advance(n, !etl::is_negative(n)); + + return *this; + } + //************************************************************************* /// Sets the value. /// Truncates to the First/Last range. ///\param value The value. //************************************************************************* - ETL_CONSTEXPR14 void set(T value_) + ETL_CONSTEXPR14 void set(T value_) ETL_NOEXCEPT { value = etl::clamp(value_, First, Last); } @@ -111,7 +335,7 @@ namespace etl //************************************************************************* /// Resets the value to the first in the range. //************************************************************************* - ETL_CONSTEXPR14 void to_first() + ETL_CONSTEXPR14 void to_first() ETL_NOEXCEPT { value = First; } @@ -119,7 +343,7 @@ namespace etl //************************************************************************* /// Resets the value to the last in the range. //************************************************************************* - ETL_CONSTEXPR14 void to_last() + ETL_CONSTEXPR14 void to_last() ETL_NOEXCEPT { value = Last; } @@ -128,30 +352,13 @@ namespace etl /// Advances to value by a number of steps. ///\param n The number of steps to advance. //************************************************************************* - ETL_CONSTEXPR14 void advance(int n) + template + ETL_CONSTEXPR14 T advance(TStep n) ETL_NOEXCEPT { - if (n > 0) - { - for (int i = 0; i < n; ++i) - { - operator++(); - } - } - else - { - for (int i = 0; i < -n; ++i) - { - operator--(); - } - } - } + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + do_advance(n, etl::is_negative(n)); - //************************************************************************* - /// Conversion operator. - /// \return The value of the underlying type. - //************************************************************************* - ETL_CONSTEXPR14 operator T() - { return value; } @@ -159,7 +366,7 @@ namespace etl /// Const conversion operator. /// \return The value of the underlying type. //************************************************************************* - ETL_CONSTEXPR operator const T() const + ETL_CONSTEXPR operator T() const ETL_NOEXCEPT { return value; } @@ -167,16 +374,9 @@ namespace etl //************************************************************************* /// ++ operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator++() + ETL_CONSTEXPR14 cyclic_value& operator++() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { - if (value >= Last) ETL_UNLIKELY - { - value = First; - } - else - { - ++value; - } + value = private_cyclic_value::increment(value, First, Last); return *this; } @@ -184,7 +384,7 @@ namespace etl //************************************************************************* /// ++ operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value operator++(int) + ETL_CONSTEXPR14 cyclic_value operator++(int) ETL_NOEXCEPT { cyclic_value temp(*this); @@ -196,16 +396,9 @@ namespace etl //************************************************************************* /// -- operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator--() + ETL_CONSTEXPR14 cyclic_value& operator--() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { - if (value <= First) ETL_UNLIKELY - { - value = Last; - } - else - { - --value; - } + value = private_cyclic_value::decrement(value, First, Last); return *this; } @@ -213,7 +406,7 @@ namespace etl //************************************************************************* /// -- operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value operator--(int) + ETL_CONSTEXPR14 cyclic_value operator--(int) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { cyclic_value temp(*this); @@ -225,7 +418,7 @@ namespace etl //************************************************************************* /// = operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator=(T t) + ETL_CONSTEXPR14 cyclic_value& operator=(T t) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { set(t); return *this; @@ -234,8 +427,8 @@ namespace etl //************************************************************************* /// = operator. //************************************************************************* - template - ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) + template + ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { set(other.get()); return *this; @@ -244,7 +437,7 @@ namespace etl //************************************************************************* /// Gets the value. //************************************************************************* - ETL_CONSTEXPR T get() const + ETL_CONSTEXPR T get() const ETL_NOEXCEPT { return value; } @@ -252,7 +445,7 @@ namespace etl //************************************************************************* /// Gets the first value. //************************************************************************* - static ETL_CONSTEXPR T first() + static ETL_CONSTEXPR T first() ETL_NOEXCEPT { return First; } @@ -260,7 +453,7 @@ namespace etl //************************************************************************* /// Gets the last value. //************************************************************************* - static ETL_CONSTEXPR T last() + static ETL_CONSTEXPR T last() ETL_NOEXCEPT { return Last; } @@ -268,39 +461,39 @@ namespace etl //************************************************************************* /// Swaps the values. //************************************************************************* - void swap(cyclic_value& other) + ETL_CONSTEXPR14 void swap(cyclic_value& other) ETL_NOEXCEPT { using ETL_OR_STD::swap; // Allow ADL swap(value, other.value); } - //************************************************************************* - /// Swaps the values. - //************************************************************************* - friend void swap(cyclic_value& lhs, cyclic_value& rhs) - { - lhs.swap(rhs); - } - - //************************************************************************* - /// Operator ==. - //************************************************************************* - friend ETL_CONSTEXPR bool operator==(const cyclic_value& lhs, const cyclic_value& rhs) - { - return lhs.value == rhs.value; - } - - //************************************************************************* - /// Operator !=. - //************************************************************************* - friend ETL_CONSTEXPR bool operator!=(const cyclic_value& lhs, const cyclic_value& rhs) - { - return !(lhs == rhs); - } - private: + //************************************************************************* + template + ETL_CONSTEXPR14 void do_advance(TStep n, bool subtract) ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + typedef private_cyclic_value::range_traits range_info; + + // Are the limits the full width of the type? If so, we can do a simpler calculation. + if ETL_IF_CONSTEXPR (range_info::is_full_range) + { + unsigned_type step = static_cast(etl::absolute_unsigned(n)); + + value = private_cyclic_value::advance(value, step, subtract); + } + else + { + unsigned_type range = range_info::value; + unsigned_type step = static_cast(etl::absolute_unsigned(n) % range); + + value = private_cyclic_value::advance(value, First, Last, step, subtract); + } + } + T value; ///< The current value. }; @@ -315,14 +508,20 @@ namespace etl template class cyclic_value { + private: + + typedef typename private_cyclic_value::traits::unsigned_type unsigned_type; + public: + ETL_STATIC_ASSERT(etl::is_integral::value, "T must be an integral type"); + //************************************************************************* /// Constructor. - /// Sets 'first' and 'last' to the template parameter values. + /// Sets 'first' and 'last' to the template parameter values which will be zero. /// The initial value is set to the first value. //************************************************************************* - ETL_CONSTEXPR cyclic_value() + ETL_CONSTEXPR cyclic_value() ETL_NOEXCEPT : value(First) , first_value(First) , last_value(Last) @@ -335,11 +534,12 @@ namespace etl ///\param first The first value in the range. ///\param last The last value in the range. //************************************************************************* - ETL_CONSTEXPR cyclic_value(T first_, T last_) + ETL_CONSTEXPR14 cyclic_value(T first_, T last_) : value(first_) , first_value(first_) , last_value(last_) { + ETL_ASSERT(first_ <= last_, ETL_ERROR(cyclic_value_reversed_limits)); } //************************************************************************* @@ -350,16 +550,17 @@ namespace etl ///\param last The last value in the range. //************************************************************************* ETL_CONSTEXPR14 cyclic_value(T first_, T last_, T initial) - : first_value(first_) + : value(etl::clamp(initial, first_, last_)) + , first_value(first_) , last_value(last_) { - set(initial); + ETL_ASSERT(first_ <= last_, ETL_ERROR(cyclic_value_reversed_limits)); } //************************************************************************* /// Copy constructor. //************************************************************************* - ETL_CONSTEXPR cyclic_value(const cyclic_value& other) + ETL_CONSTEXPR cyclic_value(const cyclic_value& other) ETL_NOEXCEPT : value(other.value) , first_value(other.first_value) , last_value(other.last_value) @@ -374,6 +575,8 @@ namespace etl //************************************************************************* ETL_CONSTEXPR14 void set(T first_, T last_) { + ETL_ASSERT(first_ <= last_, ETL_ERROR(cyclic_value_reversed_limits)); + first_value = first_; last_value = last_; value = first_; @@ -383,7 +586,7 @@ namespace etl /// Sets the value. ///\param value The value. //************************************************************************* - ETL_CONSTEXPR14 void set(T value_) + ETL_CONSTEXPR14 void set(T value_) ETL_NOEXCEPT { value = etl::clamp(value_, first_value, last_value); } @@ -391,7 +594,7 @@ namespace etl //************************************************************************* /// Resets the value to the first in the range. //************************************************************************* - ETL_CONSTEXPR14 void to_first() + ETL_CONSTEXPR14 void to_first() ETL_NOEXCEPT { value = first_value; } @@ -399,7 +602,7 @@ namespace etl //************************************************************************* /// Resets the value to the last in the range. //************************************************************************* - ETL_CONSTEXPR14 void to_last() + ETL_CONSTEXPR14 void to_last() ETL_NOEXCEPT { value = last_value; } @@ -408,30 +611,13 @@ namespace etl /// Advances to value by a number of steps. ///\param n The number of steps to advance. //************************************************************************* - ETL_CONSTEXPR14 void advance(int n) + template + ETL_CONSTEXPR14 T advance(TStep n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { - if (n > 0) - { - for (int i = 0; i < n; ++i) - { - operator++(); - } - } - else - { - for (int i = 0; i < -n; ++i) - { - operator--(); - } - } - } + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + do_advance(n, etl::is_negative(n)); - //************************************************************************* - /// Conversion operator. - /// \return The value of the underlying type. - //************************************************************************* - ETL_CONSTEXPR14 operator T() - { return value; } @@ -439,7 +625,7 @@ namespace etl /// Const conversion operator. /// \return The value of the underlying type. //************************************************************************* - ETL_CONSTEXPR operator const T() const + ETL_CONSTEXPR operator T() const ETL_NOEXCEPT { return value; } @@ -447,16 +633,9 @@ namespace etl //************************************************************************* /// ++ operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator++() + ETL_CONSTEXPR14 cyclic_value& operator++() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { - if (value >= last_value) - { - value = first_value; - } - else - { - ++value; - } + value = private_cyclic_value::increment(value, first_value, last_value); return *this; } @@ -464,7 +643,7 @@ namespace etl //************************************************************************* /// ++ operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value operator++(int) + ETL_CONSTEXPR14 cyclic_value operator++(int) ETL_NOEXCEPT { cyclic_value temp(*this); @@ -476,16 +655,9 @@ namespace etl //************************************************************************* /// -- operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator--() + ETL_CONSTEXPR14 cyclic_value& operator--() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { - if (value <= first_value) - { - value = last_value; - } - else - { - --value; - } + value = private_cyclic_value::decrement(value, first_value, last_value); return *this; } @@ -493,7 +665,7 @@ namespace etl //************************************************************************* /// -- operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value operator--(int) + ETL_CONSTEXPR14 cyclic_value operator--(int) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { cyclic_value temp(*this); @@ -505,7 +677,7 @@ namespace etl //************************************************************************* /// = operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator=(T t) + ETL_CONSTEXPR14 cyclic_value& operator=(T t) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { set(t); return *this; @@ -514,7 +686,7 @@ namespace etl //************************************************************************* /// = operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) + ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { value = other.value; first_value = other.first_value; @@ -522,10 +694,39 @@ namespace etl return *this; } + //************************************************************************* + /// Adds a number of steps and clamps to the range. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + template + ETL_CONSTEXPR14 cyclic_value& operator+=(TStep n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + advance(n); + return *this; + } + + //************************************************************************* + /// Subtracts a number of steps and clamps to the range. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + template + ETL_CONSTEXPR14 cyclic_value& operator-=(TStep n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + do_advance(n, !etl::is_negative(n)); + + return *this; + } + //************************************************************************* /// Gets the value. //************************************************************************* - ETL_CONSTEXPR T get() const + ETL_CONSTEXPR T get() const ETL_NOEXCEPT { return value; } @@ -533,7 +734,7 @@ namespace etl //************************************************************************* /// Gets the first value. //************************************************************************* - ETL_CONSTEXPR T first() const + ETL_CONSTEXPR T first() const ETL_NOEXCEPT { return first_value; } @@ -541,7 +742,7 @@ namespace etl //************************************************************************* /// Gets the last value. //************************************************************************* - ETL_CONSTEXPR T last() const + ETL_CONSTEXPR T last() const ETL_NOEXCEPT { return last_value; } @@ -549,7 +750,7 @@ namespace etl //************************************************************************* /// Swaps the values. //************************************************************************* - void swap(cyclic_value& other) + ETL_CONSTEXPR14 void swap(cyclic_value& other) ETL_NOEXCEPT { using ETL_OR_STD::swap; // Allow ADL @@ -558,36 +759,117 @@ namespace etl swap(value, other.value); } - //************************************************************************* - /// Swaps the values. - //************************************************************************* - friend void swap(cyclic_value& lhs, cyclic_value& rhs) - { - lhs.swap(rhs); - } - - //************************************************************************* - /// Operator ==. - //************************************************************************* - friend ETL_CONSTEXPR bool operator==(const cyclic_value& lhs, const cyclic_value& rhs) - { - return (lhs.value == rhs.value) && (lhs.first_value == rhs.first_value) && (lhs.last_value == rhs.last_value); - } - - //************************************************************************* - /// Operator !=. - //************************************************************************* - friend ETL_CONSTEXPR bool operator!=(const cyclic_value& lhs, const cyclic_value& rhs) - { - return !(lhs == rhs); - } - private: + //************************************************************************* + template + ETL_CONSTEXPR14 void do_advance(TStep n, bool subtract) ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + const bool Is_Full_Range = (first_value == etl::integral_limits::min) && (last_value == etl::integral_limits::max); + + // Are the limits the full width of the type? If so, we can do a simpler calculation. + if (Is_Full_Range) + { + unsigned_type step = static_cast(etl::absolute_unsigned(n)); + + value = private_cyclic_value::advance(value, step, subtract); + } + else + { + unsigned_type range = static_cast(etl::to_unsigned(last_value) - etl::to_unsigned(first_value)) + 1U; + unsigned_type step = static_cast(etl::absolute_unsigned(n) % range); + + value = private_cyclic_value::advance(value, first_value, last_value, step, subtract); + } + } + T value; ///< The current value. T first_value; ///< The first value in the range. T last_value; ///< The last value in the range. }; + + //************************************************************************* + /// Swaps the values. + //************************************************************************* + template + ETL_CONSTEXPR14 void swap(etl::cyclic_value& lhs, etl::cyclic_value& rhs) ETL_NOEXCEPT + { + lhs.swap(rhs); + } } // namespace etl +//************************************************************************* +/// Operator == +///\param lhs The left-hand operand. +///\param rhs The right-hand operand. +///\return `true` if lhs == rhs. +//************************************************************************* +template +ETL_CONSTEXPR bool operator==(const etl::cyclic_value& lhs, const etl::cyclic_value& rhs) ETL_NOEXCEPT +{ + return (lhs.get() == rhs.get()); +} + +//************************************************************************* +/// Operator != +///\param lhs The left-hand operand. +///\param rhs The right-hand operand. +///\return `true` if lhs != rhs. +//************************************************************************* +template +ETL_CONSTEXPR bool operator!=(const etl::cyclic_value& lhs, const etl::cyclic_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. +//************************************************************************* +template +ETL_CONSTEXPR bool operator<(const etl::cyclic_value& lhs, const etl::cyclic_value& rhs) ETL_NOEXCEPT +{ + return lhs.get() < rhs.get(); +} + +//************************************************************************* +/// Operator <= +///\param lhs The left-hand operand. +///\param rhs The right-hand operand. +///\return `true` if lhs is less than or equal to rhs. +//************************************************************************* +template +ETL_CONSTEXPR bool operator<=(const etl::cyclic_value& lhs, const etl::cyclic_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. +//************************************************************************* +template +ETL_CONSTEXPR bool operator>(const etl::cyclic_value& lhs, const etl::cyclic_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. +//************************************************************************* +template +ETL_CONSTEXPR bool operator>=(const etl::cyclic_value& lhs, const etl::cyclic_value& rhs) ETL_NOEXCEPT +{ + return !(lhs < rhs); +} + #endif diff --git a/include/etl/file_error_numbers.h b/include/etl/file_error_numbers.h index 945f1d87..1bcddc48 100644 --- a/include/etl/file_error_numbers.h +++ b/include/etl/file_error_numbers.h @@ -112,4 +112,5 @@ SOFTWARE. #define ETL_FORMAT_FILE_ID "79" #define ETL_INPLACE_FUNCTION_FILE_ID "80" #define ETL_INTRUSIVE_AVL_TREE_FILE_ID "81" +#define ETL_CYCLIC_VALUE_FILE_ID "82" #endif diff --git a/test/run-syntax-checks.sh b/test/run-syntax-checks.sh index fcac0287..2fd0c804 100755 --- a/test/run-syntax-checks.sh +++ b/test/run-syntax-checks.sh @@ -11,14 +11,14 @@ TitleColour=$'\033[38;2;107;210;255m' HelpColour=$'\033[38;2;250;180;250m' NoColour=$'\033[0m' -ParseGitBranch() +ParseGitBranch() { git rev-parse --abbrev-ref HEAD } SetConfigurationName() { - configuration_name=$1 + configuration_name=$1 } PrintHeader() @@ -147,10 +147,9 @@ RunCheck() local cc_name=$1 local msg=$2 local no_stl=$3 - local builtins=$4 - local user_defined=$5 - local force_03=$6 - local build_dir=$7 + local user_defined=$4 + local force_03=$5 + local build_dir=$6 if [ "$compiler_enabled" != "$cc_name" ] && [ "$compiler_enabled" != "All compilers" ]; then return @@ -171,7 +170,7 @@ RunCheck() rm -rdf bgcc rm -rdf bclang cmake -E make_directory bgcc bclang - CC=$cc CXX=$cxx cmake -E chdir $build_dir cmake -DNO_STL=$no_stl -DETL_USE_TYPE_TRAITS_BUILTINS=$builtins -DETL_USER_DEFINED_TYPE_TRAITS=$user_defined -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=$force_03 -DETL_CXX_STANDARD=$cxx_standard .. + CC=$cc CXX=$cxx cmake -E chdir $build_dir cmake -DNO_STL=$no_stl -DETL_USER_DEFINED_TYPE_TRAITS=$user_defined -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=$force_03 -DETL_CXX_STANDARD=$cxx_standard .. cmake --build $build_dir if [ $? -eq 0 ]; then PassedCompilation @@ -193,25 +192,21 @@ RunStandard() cxx_standard=$std # GCC configurations - RunCheck gcc "STL" OFF OFF OFF OFF bgcc - RunCheck gcc "No STL" ON OFF OFF OFF bgcc - RunCheck gcc "STL - Built-in traits" OFF ON OFF OFF bgcc - RunCheck gcc "No STL - Built-in traits" ON ON OFF OFF bgcc + RunCheck gcc "STL" OFF OFF OFF bgcc + RunCheck gcc "No STL" ON OFF OFF bgcc if [ "$has_force_03" = "1" ]; then - RunCheck gcc "STL - Force C++03" OFF OFF OFF ON bgcc - RunCheck gcc "No STL - Force C++03" ON OFF OFF ON bgcc + RunCheck gcc "STL - Force C++03" OFF OFF ON bgcc + RunCheck gcc "No STL - Force C++03" ON OFF ON bgcc fi # Clang configurations - RunCheck clang "STL" OFF OFF OFF OFF bclang - RunCheck clang "No STL" ON OFF OFF OFF bclang - RunCheck clang "STL - Built-in traits" OFF ON OFF OFF bgcc - RunCheck clang "No STL - Built-in traits" ON ON OFF OFF bgcc + RunCheck clang "STL" OFF OFF OFF bclang + RunCheck clang "No STL" ON OFF OFF bclang if [ "$has_force_03" = "1" ]; then - RunCheck clang "STL - Force C++03" OFF OFF OFF ON bclang - RunCheck clang "No STL - Force C++03" ON OFF OFF ON bclang + RunCheck clang "STL - Force C++03" OFF OFF ON bclang + RunCheck clang "No STL - Force C++03" ON OFF ON bclang fi } diff --git a/test/test_cyclic_value.cpp b/test/test_cyclic_value.cpp index 7077628a..414bba44 100644 --- a/test/test_cyclic_value.cpp +++ b/test/test_cyclic_value.cpp @@ -56,6 +56,15 @@ namespace CHECK_EQUAL(7, value.last()); } + //************************************************************************* + TEST(test_reversed_run_time_initialisation) + { + etl::cyclic_value value(1, 2); + + CHECK_THROW(etl::cyclic_value(7, 2), etl::cyclic_value_reversed_limits); + CHECK_THROW(value.set(7, 2), etl::cyclic_value_reversed_limits); + } + //************************************************************************* TEST(test_copy_constructor_compile_time) { @@ -336,7 +345,7 @@ namespace { etl::cyclic_value value; - value.advance(14); + value.advance(20); CHECK_EQUAL(4, value); } @@ -347,7 +356,7 @@ namespace etl::cyclic_value value; value.set(2, 7); - value.advance(14); + value.advance(20); CHECK_EQUAL(4, value); } @@ -358,7 +367,7 @@ namespace etl::cyclic_value value; value.to_last(); - value.advance(-14); + value.advance(-20); CHECK_EQUAL(5, value); } @@ -370,7 +379,7 @@ namespace value.set(2, 7); value.to_last(); - value.advance(-14); + value.advance(-20); CHECK_EQUAL(5, value); } @@ -408,6 +417,168 @@ namespace CHECK_EQUAL(1U, value); } + //************************************************************************* + TEST(test_add_positive_n_compile_time) + { + etl::cyclic_value value; + + value += 2; + + CHECK_EQUAL(4, value); + } + + //************************************************************************* + TEST(test_add_positive_n_run_time) + { + etl::cyclic_value value; + + value.set(2, 7); + value += 2; + + CHECK_EQUAL(4, value); + } + + //************************************************************************* + TEST(test_add_positive_n_large_compile_time) + { + etl::cyclic_value value; + + value += 20; + + CHECK_EQUAL(4, value); + } + + //************************************************************************* + TEST(test_add_positive_n_large_run_time) + { + etl::cyclic_value value; + + value.set(2, 7); + value += 20; + + CHECK_EQUAL(4, value); + } + + //************************************************************************* + TEST(test_add_negative_n_large_compile_time) + { + etl::cyclic_value value; + + value.to_last(); + value += -20; + + CHECK_EQUAL(5, value); + } + + //************************************************************************* + TEST(test_add_negative_n_large_run_time) + { + etl::cyclic_value value; + + value.set(2, 7); + value.to_last(); + value += -20; + + CHECK_EQUAL(5, value); + } + + //************************************************************************* + TEST(test_add_negative_n_unsigned_compile_time) + { + etl::cyclic_value value; + + value += -2; + + CHECK_EQUAL(1U, value); + } + + //************************************************************************* + TEST(test_add_negative_n_run_time) + { + etl::cyclic_value value; + + value.set(2, 7); + value.to_last(); + value += -20; + + CHECK_EQUAL(5, value); + } + + //************************************************************************* + TEST(test_decrease_positive_compile_time) + { + etl::cyclic_value value; + + value -= 2; + + CHECK_EQUAL(6, value); + } + + //************************************************************************* + TEST(test_subtract_positive_n_run_time) + { + etl::cyclic_value value; + + value.set(2, 7); + value -= 2; + + CHECK_EQUAL(6, value); + } + + //************************************************************************* + TEST(test_subtract_positive_n_large_compile_time) + { + etl::cyclic_value value; + + value -= 20; + + CHECK_EQUAL(6, value); + } + + //************************************************************************* + TEST(test_subtract_positive_n_large_run_time) + { + etl::cyclic_value value; + + value.set(2, 7); + value -= 20; + + CHECK_EQUAL(6, value); + } + + //************************************************************************* + TEST(test_subtract_negative_n_large_compile_time) + { + etl::cyclic_value value; + + value.to_last(); + value -= -20; + + CHECK_EQUAL(3, value); + } + + //************************************************************************* + TEST(test_subtract_negative_n_large_run_time) + { + etl::cyclic_value value; + + value.set(2, 7); + value.to_last(); + value -= -20; + + CHECK_EQUAL(3, value); + } + + //************************************************************************* + TEST(test_subtract_negative_n_unsigned_compile_time) + { + etl::cyclic_value value; + + value -= -2; + + CHECK_EQUAL(2U, value); + } + //************************************************************************* TEST(test_assignment_compile_time) { @@ -493,7 +664,50 @@ namespace CHECK(data2 == compare1); } -#if ETL_USING_CPP14 + //************************************************************************* + TEST(test_comparison_operators) + { + etl::cyclic_value cv1(3); + etl::cyclic_value cv2(3); + etl::cyclic_value cv3(5); + etl::cyclic_value cv4(3); + + CHECK_TRUE(cv1 == cv2); + CHECK_FALSE(cv1 == cv3); + + CHECK_FALSE(cv1 != cv2); + CHECK_TRUE(cv1 != cv3); + + CHECK_FALSE(cv1 < cv2); + CHECK_FALSE(cv2 < cv1); + CHECK_TRUE(cv1 < cv3); + CHECK_FALSE(cv3 < cv1); + CHECK_FALSE(cv1 < cv4); + CHECK_FALSE(cv4 < cv1); + + CHECK_FALSE(cv1 > cv2); + CHECK_FALSE(cv2 > cv1); + CHECK_FALSE(cv1 > cv3); + CHECK_TRUE(cv3 > cv1); + CHECK_FALSE(cv1 > cv4); + CHECK_FALSE(cv4 > cv1); + + CHECK_TRUE(cv1 <= cv2); + CHECK_TRUE(cv2 <= cv1); + CHECK_TRUE(cv1 <= cv3); + CHECK_FALSE(cv3 <= cv1); + CHECK_TRUE(cv1 <= cv4); + CHECK_TRUE(cv4 <= cv1); + + CHECK_TRUE(cv1 >= cv2); + CHECK_TRUE(cv2 >= cv1); + CHECK_FALSE(cv1 >= cv3); + CHECK_TRUE(cv3 >= cv1); + CHECK_TRUE(cv1 >= cv4); + CHECK_TRUE(cv4 >= cv1); + } + +#if ETL_USING_CPP11 //************************************************************************* TEST(test_cyclic_value_constexpr_ctor) { @@ -502,6 +716,7 @@ namespace CHECK(true); } + #if ETL_USING_CPP14 //************************************************************************* TEST(test_cyclic_value_constexpr_ctor_with_range) { @@ -510,6 +725,7 @@ namespace static_assert(cv.last() == 9, "constexpr range ctor last"); CHECK(true); } + #endif //************************************************************************* TEST(test_cyclic_value_constexpr_copy_ctor) @@ -519,6 +735,299 @@ namespace static_assert(cv2.get() == 0, "constexpr copy ctor"); CHECK(true); } + + //************************************************************************* + TEST(test_constexpr_comparison_operators) + { + constexpr etl::cyclic_value cv1(3); + constexpr etl::cyclic_value cv2(3); + constexpr etl::cyclic_value cv3(5); + constexpr etl::cyclic_value cv4(3); + + static_assert((cv1 == cv2), "(cv1 == cv2) failed"); + static_assert(!(cv1 == cv3), "!(cv1 == cv3) failed"); + + static_assert(!(cv1 != cv2), "!(cv1 != cv2) failed"); + static_assert((cv1 != cv3), "(cv1 != cv3) failed"); + + static_assert(!(cv1 < cv2), "!(cv1 < cv2) failed"); + static_assert(!(cv2 < cv1), "!(cv2 < cv1) failed"); + static_assert((cv1 < cv3), "(cv1 < cv3) failed"); + static_assert(!(cv3 < cv1), "!(cv3 < cv1) failed"); + static_assert(!(cv1 < cv4), "!(cv1 < cv4) failed"); + static_assert(!(cv4 < cv1), "!(cv4 < cv1) failed"); + + static_assert(!(cv1 > cv2), "!(cv1 > cv2) failed"); + static_assert(!(cv2 > cv1), "!(cv2 > cv1) failed"); + static_assert(!(cv1 > cv3), "!(cv1 > cv3) failed"); + static_assert((cv3 > cv1), "(cv3 > cv1) failed"); + static_assert(!(cv1 > cv4), "!(cv1 > cv4) failed"); + static_assert(!(cv4 > cv1), "!(cv4 > cv1) failed"); + + static_assert((cv1 <= cv2), "(cv1 <= cv2) failed"); + static_assert((cv2 <= cv1), "(cv2 <= cv1) failed"); + static_assert((cv1 <= cv3), "(cv1 <= cv3) failed"); + static_assert(!(cv3 <= cv1), "!(cv3 <= cv1) failed"); + static_assert((cv1 <= cv4), "(cv1 <= cv4) failed"); + static_assert((cv4 <= cv1), "(cv4 <= cv1) failed"); + + static_assert((cv1 >= cv2), "(cv1 >= cv2) failed"); + static_assert((cv2 >= cv1), "(cv2 >= cv1) failed"); + static_assert(!(cv1 >= cv3), "!(cv1 >= cv3) failed"); + static_assert((cv3 >= cv1), "(cv3 >= cv1) failed"); + static_assert((cv1 >= cv4), "(cv1 >= cv4) failed"); + static_assert((cv4 >= cv1), "(cv4 >= cv1) failed"); + } #endif + + //************************************************************************* + TEST(test_maximum_unsigned_range_wrap_with_large_step_type) + { + etl::cyclic_value value(0); + + value.advance(static_cast(255)); + CHECK_EQUAL(255, value); + + value.advance(static_cast(1)); + CHECK_EQUAL(0, value); + + value.advance(static_cast(256)); + CHECK_EQUAL(0, value); + + value.advance(static_cast(-256)); + CHECK_EQUAL(0, value); + + value.advance(static_cast(-1)); + CHECK_EQUAL(255, value); + } + + //************************************************************************* + TEST(test_maximum_signed_range_wrap_with_large_step_type) + { + etl::cyclic_value value(0); + + value.advance(static_cast(127)); + CHECK_EQUAL(127, int(value)); + + value.advance(static_cast(1)); + CHECK_EQUAL(-128, int(value)); + + value.advance(static_cast(256)); + CHECK_EQUAL(-128, int(value)); + + value.advance(static_cast(-256)); + CHECK_EQUAL(-128, int(value)); + + value.advance(static_cast(-1)); + CHECK_EQUAL(127, int(value)); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_unsigned_limits_where_step_is_same_size_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_signed_limits_where_step_is_same_size_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_unsigned_limits_where_step_is_larger_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(0); + + value.advance(int16_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_signed_limits_where_step_is_larger_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(uint16_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_unsigned_limits_where_step_is_smaller_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(0); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_signed_limits_where_step_is_smaller_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_unsigned_limits_where_the_types_are_64bit) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(0); + + value.advance(int64_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_signed_limits_where_the_types_are_64bit) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int64_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_unsigned_limits_where_step_is_same_size_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_signed_limits_where_step_is_same_size_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_unsigned_limits_where_step_is_larger_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(0); + + value.advance(int16_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_signed_limits_where_step_is_larger_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(uint16_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_unsigned_limits_where_step_is_smaller_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(0); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_signed_limits_where_step_is_smaller_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_unsigned_limits_where_the_types_are_64bit) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(0); + + value.advance(int64_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_signed_limits_where_the_types_are_64bit) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int64_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } } } // namespace From a8ab0bbeea9b2049afe38031247fa551dd996ec3 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Sun, 11 Oct 2026 09:51:55 +0200 Subject: [PATCH 15/16] error_handler.h: Remove return statements after throw (#1613) --- README.md | 2 +- include/etl/error_handler.h | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index d66d2037..5b3a50cc 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ ![CI](https://github.com/ETLCPP/etl/actions/workflows/clang-c++26.yml/badge.svg?branch=master) ![CI](https://github.com/ETLCPP/etl/actions/workflows/clang-syntax-checks.yml/badge.svg?branch=master) -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/3c14cd918ccf40008d0bcd7b083d5946)](https://www.codacy.com/manual/jwellbelove/etl?utm_source=github.com&utm_medium=referral&utm_content=ETLCPP/etl&utm_campaign=Badge_Grade) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/c0c29f7b84554166b577e8a98fc18d0b)](https://app.codacy.com/gh/ETLCPP/etl/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [![](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86)](https://github.com/sponsors/ETLCPP) diff --git a/include/etl/error_handler.h b/include/etl/error_handler.h index 9f7dfba6..e234d9bb 100644 --- a/include/etl/error_handler.h +++ b/include/etl/error_handler.h @@ -367,7 +367,6 @@ namespace etl { \ etl::error_handler::error((e)); \ throw((e)); \ - return; \ } \ } while (false) // If the condition fails, calls the error handler then // throws an exception. @@ -377,7 +376,6 @@ namespace etl { \ etl::error_handler::error((e)); \ throw((e)); \ - return (v); \ } \ } while (false) // If the condition fails, calls the error handler then // throws an exception. @@ -391,13 +389,11 @@ namespace etl do { \ etl::error_handler::error((e)); \ throw((e)); \ - return; \ } while (false) // Calls the error handler then throws an exception. #define ETL_ASSERT_FAIL_AND_RETURN_VALUE(e, v) \ do { \ etl::error_handler::error((e)); \ throw((e)); \ - return (v); \ } while (false) // Calls the error handler then throws an exception. #else #define ETL_ASSERT(b, e) \ From 88c1b3441139d4218ce30745541e68cc006a3ec0 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Mon, 12 Oct 2026 10:36:21 +0200 Subject: [PATCH 16/16] Feature/to string float uses double math (#1610) * Added overloaded etl::floor() and etl::ceil() Added tests for float, double, long double * Documented functions from math.h * Added more math wrappers. Move wrappers to clib namespace * Cleanup of duplicated code. * clang-format * Code review updates --------- Co-authored-by: John Wellbelove --- docs/maths/math.md | 306 +++++++++++++++++ include/etl/format.h | 87 +---- include/etl/math.h | 203 +++++++++++ include/etl/platform.h | 15 +- include/etl/private/to_string_helper.h | 8 +- test/test_math.cpp | 115 +++++++ test/test_string_stream.cpp | 11 + test/test_string_stream_u16.cpp | 8 + test/test_string_stream_u32.cpp | 8 + test/test_string_stream_u8.cpp | 8 + test/test_string_stream_wchar_t.cpp | 8 + test/test_to_string.cpp | 453 +++++++++++++------------ test/test_to_u16string.cpp | 430 ++++++++++++----------- test/test_to_u32string.cpp | 432 ++++++++++++----------- test/test_to_u8string.cpp | 430 ++++++++++++----------- test/test_to_wstring.cpp | 441 ++++++++++++------------ test/vs2022/etl.vcxproj | 23 +- test/vs2022/etl.vcxproj.filters | 2 +- 18 files changed, 1856 insertions(+), 1132 deletions(-) create mode 100644 docs/maths/math.md diff --git a/docs/maths/math.md b/docs/maths/math.md new file mode 100644 index 00000000..3ec1cc88 --- /dev/null +++ b/docs/maths/math.md @@ -0,0 +1,306 @@ +--- +title: "math" +--- + +{{< callout type="info">}} + Header: `math.h` + From: `20.50.0` +{{< /callout >}} + +Various mathematical utilities + +## is_nan + +```cpp +template +ETL_CONSTEXPR bool is_nan(T value) +``` +**Returns** +`true` if `value` is `NAN` (Not A Number). +Always returns `false` if `T` is an integral. + +## is_infinity + +```cpp +template +ETL_CONSTEXPR bool is_infinity(T value) +``` +**Returns** +`true` if `value` is infinity. +Always returns `false` if `T` is an integral. + +## is_zero + +```cpp +template +ETL_CONSTEXPR bool is_zero(T value) +``` +**Returns** +`true` if `value` is Zero. + +## is_exactly_zero + +```cpp +template +ETL_CONSTEXPR bool is_exactly_equal(T value1, T value2) +``` +**Returns** +`true` if `value1` is equal to `value2`. + +## floor + +Defined in the `etl::math` namespace. + +--- + +```cpp +ETL_NODISCARD +float floor(float value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::floorf` in ``. + +**Returns** +`::floorf(value)` + +--- + +```cpp +ETL_NODISCARD +double floor(double value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::floor` in ``. + +**Returns** +`::floor(value)` + +--- + +```cpp +ETL_NODISCARD +long double floor(long double value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::floorl` in ``. + +**Returns** +If `ETL_NO_LONG_DOUBLE_MATH` is defined then returns +`::floor` with casts to `double`. +otherwise +`::floorl(value)` + +## ceil + +Defined in the `etl::math` namespace. + +--- + +```cpp +ETL_NODISCARD +float ceil(float value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::ceilf` in ``. + +**Returns** +`::ceilf(value)` + +--- + +```cpp +ETL_NODISCARD +double ceil(double value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::ceil` in ``. + +**Returns** +`::ceil(value)` + +--- + +```cpp +ETL_NODISCARD +long double ceil(long double value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::ceill` in ``. + +**Returns** +If `ETL_NO_LONG_DOUBLE_MATH` is defined then returns +`::ceil` with casts to `double`. +otherwise +`::ceill(value)` + +## log10 + +Defined in the `etl::math` namespace. + +--- + +```cpp +ETL_NODISCARD +float log10(float value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::log10f` in ``. + +**Returns** +`::log10f(value)` + +--- + +```cpp +ETL_NODISCARD +double log10(double value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::log10` in ``. + +**Returns** +`::log10(value)` + +--- + +```cpp +ETL_NODISCARD +long double log10(long double value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::log10l` in ``. + +**Returns** +If `ETL_NO_LONG_DOUBLE_MATH` is defined then returns +`::log10` with casts to `double`. +otherwise +`::log10l(value)` + +## round + +Defined in the `etl::math` namespace. + +--- + +```cpp +ETL_NODISCARD +float round(float value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::roundf` in ``. + +**Returns** +`::roundf(value)` + +--- + +```cpp +ETL_NODISCARD +double round(double value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::round` in ``. + +**Returns** +`::round(value)` + +--- + +```cpp +ETL_NODISCARD +long double round(long double value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::roundl` in ``. + +**Returns** +If `ETL_NO_LONG_DOUBLE_MATH` is defined then returns +`::round` with casts to `double`. +otherwise +`::roundl(value)` + +## pow + +Defined in the `etl::math` namespace. + +--- + +```cpp +ETL_NODISCARD +float pow(float base, float exponent) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::powf` in ``. + +**Returns** +`::powf(base, exponent)` + +--- + +```cpp +ETL_NODISCARD +double pow(double base, double exponent) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::pow` in ``. + +**Returns** +`::pow(base, exponent)` + +--- + +```cpp +ETL_NODISCARD +long double pow(long double base, long double exponent) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::powl` in ``. + +**Returns** +If `ETL_NO_LONG_DOUBLE_MATH` is defined then returns +`::pow` with casts to `double`. +otherwise +`::powl(base, intpart)` + +## modf + +Defined in the `etl::math` namespace. + +--- + +```cpp +ETL_NODISCARD +float modf(float value, float* intpart) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::modff` in ``. + +**Returns** +`::modff(value, intpart)` + +--- + +```cpp +ETL_NODISCARD +double modf(double value, double* intpart) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::modf` in ``. + +**Returns** +`::modf(value, intpart)` + +--- + +```cpp +ETL_NODISCARD +long double modf(long double value, long double* intpart) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::modfl` in ``. + +**Returns** +If `ETL_NO_LONG_DOUBLE_MATH` is defined then returns +`::modf` with casts to `double`. +otherwise +`::modfl(value, intpart)` diff --git a/include/etl/format.h b/include/etl/format.h index f34121b9..f736d025 100644 --- a/include/etl/format.h +++ b/include/etl/format.h @@ -47,10 +47,6 @@ SOFTWARE. #include "variant.h" #include "visitor.h" -#if ETL_USING_FORMAT_FLOATING_POINT - #include -#endif - #if ETL_USING_CPP11 namespace etl @@ -1751,65 +1747,6 @@ namespace etl 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 - // long double math functions (log10l, floorl, powl, modfl, roundl). - // When ETL_FORMAT_NO_LONG_DOUBLE_MATH is defined, long double overloads - // cast through double. For float and double, the standard functions are - // called directly via the template versions. - //*********************************** - inline long double format_log10(long double value) - { - return static_cast(::log10(static_cast(value))); - } - inline long double format_floor(long double value) - { - return static_cast(::floor(static_cast(value))); - } - inline long double format_pow(long double base, long double exp) - { - return static_cast(::pow(static_cast(base), static_cast(exp))); - } - inline long double format_round(long double value) - { - return static_cast(::round(static_cast(value))); - } - inline long double format_modf(long double value, long double* iptr) - { - double d_iptr; - double result = ::modf(static_cast(value), &d_iptr); - *iptr = static_cast(d_iptr); - return static_cast(result); - } - #endif - - template - T format_log10(T value) - { - return ::log10(value); - } - template - T format_floor(T value) - { - return ::floor(value); - } - template - T format_pow(T base, T exp) - { - return ::pow(base, exp); - } - template - T format_round(T value) - { - return ::round(value); - } - template - T format_modf(T value, T* iptr) - { - return ::modf(value, iptr); - } - template void format_floating_default(OutputIt& it, T value, const format_spec_t& spec) { @@ -1830,7 +1767,7 @@ namespace etl } T integral; - T fractional = format_modf(value, &integral); + T fractional = etl::math::modf(value, &integral); // Take absolute values to avoid casting negative values to unsigned if (sign) @@ -1840,7 +1777,7 @@ namespace etl } unsigned long long int scale = int_pow(10, fractional_decimals); - unsigned long long int fractional_int = static_cast(format_round(fractional * scale)); + unsigned long long int fractional_int = static_cast(etl::math::round(fractional * scale)); unsigned long long int integral_int = static_cast(integral); if (fractional_int == scale) @@ -1867,20 +1804,20 @@ namespace etl bool sign = signbit(value); T integral; - T fractional = format_modf(value, &integral); + T fractional = etl::math::modf(value, &integral); while (value >= 0x10 || value <= -0x10) { ++exponent_int; value /= 0x10; - fractional = format_modf(value, &integral); + fractional = etl::math::modf(value, &integral); } while ((value > 0.0000000000001 && value < 1) || (value < -0.0000000000001 && value > -1)) { --exponent_int; value *= 0x10; - fractional = format_modf(value, &integral); + fractional = etl::math::modf(value, &integral); } // Take absolute values to avoid casting negative values to unsigned @@ -1891,7 +1828,7 @@ namespace etl } unsigned long long int scale = int_pow(0x10, fractional_decimals); - unsigned long long int fractional_int = static_cast(format_round(fractional * scale)); + unsigned long long int fractional_int = static_cast(etl::math::round(fractional * scale)); unsigned long long int integral_int = static_cast(integral); if (fractional_int == scale) @@ -1935,8 +1872,8 @@ namespace etl if (abs_value > static_cast(0)) { - exponent_int = static_cast(format_floor(format_log10(abs_value))); - value = abs_value / format_pow(static_cast(10), static_cast(exponent_int)); + exponent_int = static_cast(etl::math::floor(etl::math::log10(abs_value))); + value = abs_value / etl::math::pow(static_cast(10), static_cast(exponent_int)); // Correct for floating-point rounding in log10/pow if (value >= static_cast(10)) { @@ -1955,10 +1892,10 @@ namespace etl } T integral; - T fractional = format_modf(value, &integral); + T fractional = etl::math::modf(value, &integral); unsigned long long int scale = int_pow(10, fractional_decimals); - unsigned long long int fractional_int = static_cast(format_round(fractional * scale)); + unsigned long long int fractional_int = static_cast(etl::math::round(fractional * scale)); unsigned long long int integral_int = static_cast(integral); if (fractional_int == scale) @@ -1996,7 +1933,7 @@ namespace etl bool sign = signbit(value); T integral; - T fractional = format_modf(value, &integral); + T fractional = etl::math::modf(value, &integral); // Take absolute values to avoid casting negative values to unsigned if (sign) @@ -2006,7 +1943,7 @@ namespace etl } unsigned long long int scale = int_pow(10, fractional_decimals); - unsigned long long int fractional_int = static_cast(format_round(fractional * scale)); + unsigned long long int fractional_int = static_cast(etl::math::round(fractional * scale)); unsigned long long int integral_int = static_cast(integral); if (fractional_int == scale) diff --git a/include/etl/math.h b/include/etl/math.h index 485ddef4..079b51fe 100644 --- a/include/etl/math.h +++ b/include/etl/math.h @@ -131,6 +131,209 @@ namespace etl return value1 == value2; } #include "private/diagnostic_pop.h" + + //*************************************************************************** + // These functions wrap the C math.h functions. + // The functions are overloaded for the different floating point types. + // They provide a consistent interface for float, double and long double. + // They are in the `math` namespace to avoid conflicts with the C library and ETL functions. + // These take the place of std::floor, std::ceil etc which may not be available `No STL` environments. + // Avoids unpredictable behaviour for global calls of ::floor, ::ceil etc when both and are included. + //*************************************************************************** + namespace math + { + //*************************************************************************** + // floor + //*************************************************************************** + ETL_NODISCARD + inline float floor(float value) ETL_NOEXCEPT + { + return ::floorf(value); + } + + //********************************* + ETL_NODISCARD + inline double floor(double value) ETL_NOEXCEPT + { + return ::floor(value); + } + +#if ETL_USING_LONG_DOUBLE_MATH + //********************************* + ETL_NODISCARD + inline long double floor(long double value) ETL_NOEXCEPT + { + return ::floorl(value); + } +#else + //********************************* + ETL_NODISCARD + inline long double floor(long double value) ETL_NOEXCEPT + { + return static_cast(::floor(static_cast(value))); + } +#endif + + //*************************************************************************** + // ceil + //*************************************************************************** + ETL_NODISCARD + inline float ceil(float value) ETL_NOEXCEPT + { + return ::ceilf(value); + } + + //********************************* + ETL_NODISCARD + inline double ceil(double value) ETL_NOEXCEPT + { + return ::ceil(value); + } + +#if ETL_USING_LONG_DOUBLE_MATH + //********************************* + ETL_NODISCARD + inline long double ceil(long double value) ETL_NOEXCEPT + { + return ::ceill(value); + } +#else + //********************************* + ETL_NODISCARD + inline long double ceil(long double value) ETL_NOEXCEPT + { + return static_cast(::ceil(static_cast(value))); + } +#endif + + //*************************************************************************** + // log10 + //*************************************************************************** + ETL_NODISCARD + inline float log10(float value) ETL_NOEXCEPT + { + return ::log10f(value); + } + + //********************************* + ETL_NODISCARD + inline double log10(double value) ETL_NOEXCEPT + { + return ::log10(value); + } + +#if ETL_USING_LONG_DOUBLE_MATH + //********************************* + ETL_NODISCARD + inline long double log10(long double value) ETL_NOEXCEPT + { + return ::log10l(value); + } +#else + //********************************* + ETL_NODISCARD + inline long double log10(long double value) ETL_NOEXCEPT + { + return static_cast(::log10(static_cast(value))); + } +#endif + + //*************************************************************************** + // round + //*************************************************************************** + ETL_NODISCARD + inline float round(float value) ETL_NOEXCEPT + { + return ::roundf(value); + } + + //********************************* + ETL_NODISCARD + inline double round(double value) ETL_NOEXCEPT + { + return ::round(value); + } + +#if ETL_USING_LONG_DOUBLE_MATH + //********************************* + ETL_NODISCARD + inline long double round(long double value) ETL_NOEXCEPT + { + return ::roundl(value); + } +#else + //********************************* + ETL_NODISCARD + inline long double round(long double value) ETL_NOEXCEPT + { + return static_cast(::round(static_cast(value))); + } +#endif + + //*************************************************************************** + // power + //*************************************************************************** + ETL_NODISCARD + inline float pow(float base, float exponent) ETL_NOEXCEPT + { + return ::powf(base, exponent); + } + + //********************************* + ETL_NODISCARD + inline double pow(double base, double exponent) ETL_NOEXCEPT + { + return ::pow(base, exponent); + } + +#if ETL_USING_LONG_DOUBLE_MATH + //********************************* + ETL_NODISCARD + inline long double pow(long double base, long double exponent) ETL_NOEXCEPT + { + return ::powl(base, exponent); + } +#else + //********************************* + ETL_NODISCARD + inline long double pow(long double base, long double exponent) ETL_NOEXCEPT + { + return static_cast(::pow(static_cast(base), static_cast(exponent))); + } +#endif + + //*************************************************************************** + // modf + //*************************************************************************** + inline float modf(float value, float* intpart) ETL_NOEXCEPT + { + return ::modff(value, intpart); + } + + //********************************* + inline double modf(double value, double* intpart) ETL_NOEXCEPT + { + return ::modf(value, intpart); + } + +#if ETL_USING_LONG_DOUBLE_MATH + //********************************* + inline long double modf(long double value, long double* intpart) ETL_NOEXCEPT + { + return ::modfl(value, intpart); + } +#else + //********************************* + inline long double modf(long double value, long double* intpart) ETL_NOEXCEPT + { + double d_iptr; + double result = ::modf(static_cast(value), &d_iptr); + *intpart = static_cast(d_iptr); + + return static_cast(result); + } +#endif + } // namespace math } // namespace etl #endif diff --git a/include/etl/platform.h b/include/etl/platform.h index 494b4981..0eb78ae5 100644 --- a/include/etl/platform.h +++ b/include/etl/platform.h @@ -175,16 +175,17 @@ SOFTWARE. #endif //************************************* -// Helper macro for ETL_FORMAT_NO_LONG_DOUBLE_MATH. -// Define ETL_FORMAT_NO_LONG_DOUBLE_MATH if the toolchain does not provide +// Helper macro for ETL_NO_LONG_DOUBLE_MATH. +// Define ETL_NO_LONG_DOUBLE_MATH if the toolchain does not provide // long double math functions (log10l, floorl, powl, modfl, roundl). // When defined, long double arguments are cast to double for math operations. -#if defined(ETL_FORMAT_NO_LONG_DOUBLE_MATH) - #define ETL_USING_FORMAT_LONG_DOUBLE_MATH 0 - #define ETL_NOT_USING_FORMAT_LONG_DOUBLE_MATH 1 +// ETL_FORMAT_NO_LONG_DOUBLE_MATH is deprecated, but still supported for backwards compatibility. +#if defined(ETL_NO_LONG_DOUBLE_MATH) || defined(ETL_FORMAT_NO_LONG_DOUBLE_MATH) + #define ETL_USING_LONG_DOUBLE_MATH 0 + #define ETL_NOT_USING_LONG_DOUBLE_MATH 1 #else - #define ETL_USING_FORMAT_LONG_DOUBLE_MATH 1 - #define ETL_NOT_USING_FORMAT_LONG_DOUBLE_MATH 0 + #define ETL_USING_LONG_DOUBLE_MATH 1 + #define ETL_NOT_USING_LONG_DOUBLE_MATH 0 #endif //************************************* diff --git a/include/etl/private/to_string_helper.h b/include/etl/private/to_string_helper.h index a8969eae..4baaaf76 100644 --- a/include/etl/private/to_string_helper.h +++ b/include/etl/private/to_string_helper.h @@ -44,8 +44,6 @@ SOFTWARE. #include "../negative.h" #include "../type_traits.h" -#include - #if ETL_USING_STL && ETL_USING_CPP11 #include // For std::begin, std::end and std::size #endif @@ -347,11 +345,11 @@ namespace etl } // Find the integral part of the floating point - T f_integral = ::floor(scaled); + T f_integral = etl::math::floor(scaled); uworkspace_t integral = static_cast(f_integral); // Find the fractional part of the floating point. - uworkspace_t fractional = static_cast(::round((scaled - f_integral) * multiplier)); + uworkspace_t fractional = static_cast(etl::math::round((scaled - f_integral) * multiplier)); // Check for a rounding carry to the integral. if (fractional == multiplier) @@ -404,7 +402,7 @@ namespace etl } // Find the integral part of the floating point - T f_integral = ::floor(etl::absolute(value)); + T f_integral = etl::math::floor(etl::absolute(value)); uworkspace_t integral = static_cast(f_integral); // Find the fractional part of the floating point. diff --git a/test/test_math.cpp b/test/test_math.cpp index b28029e5..2592d7db 100644 --- a/test/test_math.cpp +++ b/test/test_math.cpp @@ -130,5 +130,120 @@ namespace CHECK_TRUE(etl::is_exactly_equal(i1, i1)); CHECK_FALSE(etl::is_exactly_equal(i1, i2)); } + + //************************************************************************* + TEST(test_floor) + { + float f1 = 1.7f; + float f2 = -1.7f; + double d1 = 1.7; + double d2 = -1.7; + long double ld1 = 1.7L; + long double ld2 = -1.7L; + + CHECK_CLOSE(1.0f, etl::math::floor(f1), 0.0001f); + CHECK_CLOSE(-2.0f, etl::math::floor(f2), 0.0001f); + CHECK_CLOSE(1.0, etl::math::floor(d1), 0.0001); + CHECK_CLOSE(-2.0, etl::math::floor(d2), 0.0001); + CHECK_CLOSE(1.0L, etl::math::floor(ld1), 0.0001L); + CHECK_CLOSE(-2.0L, etl::math::floor(ld2), 0.0001L); + } + + //************************************************************************* + TEST(test_ceil) + { + float f1 = 1.2f; + float f2 = -1.2f; + double d1 = 1.2; + double d2 = -1.2; + long double ld1 = 1.2L; + long double ld2 = -1.2L; + + CHECK_CLOSE(2.0f, etl::math::ceil(f1), 0.0001f); + CHECK_CLOSE(-1.0f, etl::math::ceil(f2), 0.0001f); + CHECK_CLOSE(2.0, etl::math::ceil(d1), 0.0001); + CHECK_CLOSE(-1.0, etl::math::ceil(d2), 0.0001); + CHECK_CLOSE(2.0L, etl::math::ceil(ld1), 0.0001L); + CHECK_CLOSE(-1.0L, etl::math::ceil(ld2), 0.0001L); + } + + //************************************************************************* + TEST(test_log10) + { + float f1 = 100.0f; + double d1 = 1000.0; + long double ld1 = 10000.0L; + + CHECK_CLOSE(2.0f, etl::math::log10(f1), 0.0001f); + CHECK_CLOSE(3.0, etl::math::log10(d1), 0.0001); + CHECK_CLOSE(4.0L, etl::math::log10(ld1), 0.0001L); + } + + //************************************************************************* + TEST(test_round) + { + float f1 = 1.5f; + float f2 = -1.5f; + float f3 = 1.4f; + float f4 = -1.4f; + double d1 = 2.5; + double d2 = -2.5; + double d3 = 2.4; + double d4 = -2.4; + long double ld1 = 3.5L; + long double ld2 = -3.5L; + long double ld3 = 3.4L; + long double ld4 = -3.4L; + + CHECK_CLOSE(2.0f, etl::math::round(f1), 0.0001f); + CHECK_CLOSE(-2.0f, etl::math::round(f2), 0.0001f); + CHECK_CLOSE(1.0f, etl::math::round(f3), 0.0001f); + CHECK_CLOSE(-1.0f, etl::math::round(f4), 0.0001f); + CHECK_CLOSE(3.0, etl::math::round(d1), 0.0001); + CHECK_CLOSE(-3.0, etl::math::round(d2), 0.0001); + CHECK_CLOSE(2.0, etl::math::round(d3), 0.0001); + CHECK_CLOSE(-2.0, etl::math::round(d4), 0.0001); + CHECK_CLOSE(4.0L, etl::math::round(ld1), 0.0001L); + CHECK_CLOSE(-4.0L, etl::math::round(ld2), 0.0001L); + CHECK_CLOSE(3.0L, etl::math::round(ld3), 0.0001L); + CHECK_CLOSE(-3.0L, etl::math::round(ld4), 0.0001L); + } + + //************************************************************************* + TEST(test_pow) + { + float fb = 2.0f; + float fe = 3.0f; + double db = 2.0; + double de = 10.0; + long double ldb = 2.0L; + long double lde = 4.0L; + + CHECK_CLOSE(8.0f, etl::math::pow(fb, fe), 0.0001f); + CHECK_CLOSE(1024.0, etl::math::pow(db, de), 0.0001); + CHECK_CLOSE(16.0L, etl::math::pow(ldb, lde), 0.0001L); + } + + //************************************************************************* + TEST(test_modf) + { + float f1 = 3.75f; + float f_intpart = 0.0f; + double d1 = 3.75; + double d_intpart = 0.0; + long double ld1 = 3.75L; + long double ld_intpart = 0.0L; + + float f_frac = etl::math::modf(f1, &f_intpart); + double d_frac = etl::math::modf(d1, &d_intpart); + long double ld_frac = etl::math::modf(ld1, &ld_intpart); + + CHECK_CLOSE(3.0f, f_intpart, 0.0001f); + CHECK_CLOSE(0.75f, f_frac, 0.0001f); + CHECK_CLOSE(3.0, d_intpart, 0.0001); + CHECK_CLOSE(0.75, d_frac, 0.0001); + CHECK_CLOSE((double)3.0L, (double)ld_intpart, 0.0001); + CHECK_CLOSE((double)0.75L, (double)ld_frac, 0.0001); + } } } // namespace diff --git a/test/test_string_stream.cpp b/test/test_string_stream.cpp index 27ecd72d..e3099c09 100644 --- a/test/test_string_stream.cpp +++ b/test/test_string_stream.cpp @@ -181,10 +181,21 @@ namespace ss << etl::noboolalpha << false << STR(" ") << true << STR(" ") << etl::boolalpha << false << STR(" ") << true; CHECK_EQUAL(String(STR("0 1 false true")), ss.str()); + // Float + ss.str().clear(); + ss << etl::setprecision(4) << 3.1415927f; + CHECK_EQUAL(String(STR("3.1416")), ss.str()); + + // Double ss.str().clear(); ss << etl::setprecision(4) << 3.1415927; CHECK_EQUAL(String(STR("3.1416")), ss.str()); + // Long double + ss.str().clear(); + ss << etl::setprecision(4) << 3.1415927l; + CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); ss << STR("abcdef") << STR(" ") << etl::uppercase << STR("abcdef"); CHECK_EQUAL(String(STR("abcdef abcdef")), ss.str()); diff --git a/test/test_string_stream_u16.cpp b/test/test_string_stream_u16.cpp index 6489e572..fb2fe545 100644 --- a/test/test_string_stream_u16.cpp +++ b/test/test_string_stream_u16.cpp @@ -181,10 +181,18 @@ namespace ss << etl::noboolalpha << false << STR(" ") << true << STR(" ") << etl::boolalpha << false << STR(" ") << true; CHECK_EQUAL(String(STR("0 1 false true")), ss.str()); + ss.str().clear(); + ss << etl::setprecision(4) << 3.1415927f; + CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); ss << etl::setprecision(4) << 3.1415927; CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); + ss << etl::setprecision(4) << 3.1415927l; + CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); ss << STR("abcdef") << STR(" ") << etl::uppercase << STR("abcdef"); CHECK_EQUAL(String(STR("abcdef abcdef")), ss.str()); diff --git a/test/test_string_stream_u32.cpp b/test/test_string_stream_u32.cpp index bde30bf2..7c6a0e4c 100644 --- a/test/test_string_stream_u32.cpp +++ b/test/test_string_stream_u32.cpp @@ -181,10 +181,18 @@ namespace ss << etl::noboolalpha << false << STR(" ") << true << STR(" ") << etl::boolalpha << false << STR(" ") << true; CHECK_EQUAL(String(STR("0 1 false true")), ss.str()); + ss.str().clear(); + ss << etl::setprecision(4) << 3.1415927f; + CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); ss << etl::setprecision(4) << 3.1415927; CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); + ss << etl::setprecision(4) << 3.1415927l; + CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); ss << STR("abcdef") << STR(" ") << etl::uppercase << STR("abcdef"); CHECK_EQUAL(String(STR("abcdef abcdef")), ss.str()); diff --git a/test/test_string_stream_u8.cpp b/test/test_string_stream_u8.cpp index 7046a107..53b36205 100644 --- a/test/test_string_stream_u8.cpp +++ b/test/test_string_stream_u8.cpp @@ -184,10 +184,18 @@ namespace ss << etl::noboolalpha << false << STR(" ") << true << STR(" ") << etl::boolalpha << false << STR(" ") << true; CHECK_EQUAL(String(STR("0 1 false true")), ss.str()); + ss.str().clear(); + ss << etl::setprecision(4) << 3.1415927f; + CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); ss << etl::setprecision(4) << 3.1415927; CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); + ss << etl::setprecision(4) << 3.1415927l; + CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); ss << STR("abcdef") << STR(" ") << etl::uppercase << STR("abcdef"); CHECK_EQUAL(String(STR("abcdef abcdef")), ss.str()); diff --git a/test/test_string_stream_wchar_t.cpp b/test/test_string_stream_wchar_t.cpp index bcd8d364..931c9135 100644 --- a/test/test_string_stream_wchar_t.cpp +++ b/test/test_string_stream_wchar_t.cpp @@ -182,10 +182,18 @@ namespace ss << etl::noboolalpha << false << STR(" ") << true << STR(" ") << etl::boolalpha << false << STR(" ") << true; CHECK_EQUAL(String(STR("0 1 false true")), ss.str()); + ss.str().clear(); + ss << etl::setprecision(4) << 3.1415927f; + CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); ss << etl::setprecision(4) << 3.1415927; CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); + ss << etl::setprecision(4) << 3.1415927l; + CHECK_EQUAL(String(STR("3.1416")), ss.str()); + ss.str().clear(); ss << STR("abcdef") << STR(" ") << etl::uppercase << STR("abcdef"); CHECK_EQUAL(String(STR("abcdef abcdef")), ss.str()); diff --git a/test/test_to_string.cpp b/test/test_to_string.cpp index 1870697e..016726e0 100644 --- a/test/test_to_string.cpp +++ b/test/test_to_string.cpp @@ -44,32 +44,35 @@ namespace { typedef etl::format_spec Format; + using String20 = etl::string<20>; + using String64 = etl::string<64>; + SUITE(test_to_string) { //************************************************************************* TEST(test_default_format_no_append) { - etl::string<20> str; + String20 str; - CHECK(etl::string<20>(STR("0")) == etl::to_string(uint8_t(0), str)); - CHECK(etl::string<20>(STR("0")) == etl::to_string(uint16_t(0), str)); - CHECK(etl::string<20>(STR("0")) == etl::to_string(uint32_t(0), str)); - CHECK(etl::string<20>(STR("0")) == etl::to_string(uint64_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint8_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint16_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint32_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint64_t(0), str)); - CHECK(etl::string<20>(STR("128")) == etl::to_string(uint8_t(128), str)); - CHECK(etl::string<20>(STR("32768")) == etl::to_string(uint16_t(32768), str)); - CHECK(etl::string<20>(STR("2147483648")) == etl::to_string(uint32_t(2147483648ul), str)); - CHECK(etl::string<20>(STR("9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str)); + CHECK(String20(STR("128")) == etl::to_string(uint8_t(128), str)); + CHECK(String20(STR("32768")) == etl::to_string(uint16_t(32768), str)); + CHECK(String20(STR("2147483648")) == etl::to_string(uint32_t(2147483648ul), str)); + CHECK(String20(STR("9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str)); - CHECK(etl::string<20>(STR("127")) == etl::to_string(INT8_MAX, str)); - CHECK(etl::string<20>(STR("32767")) == etl::to_string(INT16_MAX, str)); - CHECK(etl::string<20>(STR("2147483647")) == etl::to_string(INT32_MAX, str)); - CHECK(etl::string<20>(STR("9223372036854775807")) == etl::to_string(INT64_MAX, str)); + CHECK(String20(STR("127")) == etl::to_string(INT8_MAX, str)); + CHECK(String20(STR("32767")) == etl::to_string(INT16_MAX, str)); + CHECK(String20(STR("2147483647")) == etl::to_string(INT32_MAX, str)); + CHECK(String20(STR("9223372036854775807")) == etl::to_string(INT64_MAX, str)); - CHECK(etl::string<20>(STR("-128")) == etl::to_string(INT8_MIN, str)); - CHECK(etl::string<20>(STR("-32768")) == etl::to_string(INT16_MIN, str)); - CHECK(etl::string<20>(STR("-2147483648")) == etl::to_string(INT32_MIN, str)); - CHECK(etl::string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str)); + CHECK(String20(STR("-128")) == etl::to_string(INT8_MIN, str)); + CHECK(String20(STR("-32768")) == etl::to_string(INT16_MIN, str)); + CHECK(String20(STR("-2147483648")) == etl::to_string(INT32_MIN, str)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str)); } //************************************************************************* @@ -107,89 +110,89 @@ namespace //************************************************************************* TEST(test_format_right_justified_no_append) { - etl::string<20> str; + String20 str; Format format = Format().base(10).width(20).fill(STR('#')); etl::to_string(uint8_t(0), str, format); - CHECK(etl::string<20>(STR("###################0")) == etl::to_string(uint8_t(0), str, format)); - CHECK(etl::string<20>(STR("###################0")) == etl::to_string(uint16_t(0), str, format)); - CHECK(etl::string<20>(STR("###################0")) == etl::to_string(uint32_t(0), str, format)); - CHECK(etl::string<20>(STR("###################0")) == etl::to_string(uint64_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint8_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint16_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint32_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint64_t(0), str, format)); - CHECK(etl::string<20>(STR("#################128")) == etl::to_string(uint8_t(128), str, format)); - CHECK(etl::string<20>(STR("###############32768")) == etl::to_string(uint16_t(32768), str, format)); - CHECK(etl::string<20>(STR("##########2147483648")) == etl::to_string(uint32_t(2147483648ul), str, format)); - CHECK(etl::string<20>(STR("#9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); + CHECK(String20(STR("#################128")) == etl::to_string(uint8_t(128), str, format)); + CHECK(String20(STR("###############32768")) == etl::to_string(uint16_t(32768), str, format)); + CHECK(String20(STR("##########2147483648")) == etl::to_string(uint32_t(2147483648ul), str, format)); + CHECK(String20(STR("#9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); - CHECK(etl::string<20>(STR("#################127")) == etl::to_string(INT8_MAX, str, format)); - CHECK(etl::string<20>(STR("###############32767")) == etl::to_string(INT16_MAX, str, format)); - CHECK(etl::string<20>(STR("##########2147483647")) == etl::to_string(INT32_MAX, str, format)); - CHECK(etl::string<20>(STR("#9223372036854775807")) == etl::to_string(INT64_MAX, str, format)); + CHECK(String20(STR("#################127")) == etl::to_string(INT8_MAX, str, format)); + CHECK(String20(STR("###############32767")) == etl::to_string(INT16_MAX, str, format)); + CHECK(String20(STR("##########2147483647")) == etl::to_string(INT32_MAX, str, format)); + CHECK(String20(STR("#9223372036854775807")) == etl::to_string(INT64_MAX, str, format)); - CHECK(etl::string<20>(STR("################-128")) == etl::to_string(INT8_MIN, str, format)); - CHECK(etl::string<20>(STR("##############-32768")) == etl::to_string(INT16_MIN, str, format)); - CHECK(etl::string<20>(STR("#########-2147483648")) == etl::to_string(INT32_MIN, str, format)); - CHECK(etl::string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); + CHECK(String20(STR("################-128")) == etl::to_string(INT8_MIN, str, format)); + CHECK(String20(STR("##############-32768")) == etl::to_string(INT16_MIN, str, format)); + CHECK(String20(STR("#########-2147483648")) == etl::to_string(INT32_MIN, str, format)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); } //************************************************************************* TEST(test_format_left_justified_no_append) { - etl::string<20> str; + String20 str; Format format = Format().base(10).width(20).fill(STR('#')).left(); - CHECK(etl::string<20>(STR("0###################")) == etl::to_string(uint8_t(0), str, format)); - CHECK(etl::string<20>(STR("0###################")) == etl::to_string(uint16_t(0), str, format)); - CHECK(etl::string<20>(STR("0###################")) == etl::to_string(uint32_t(0), str, format)); - CHECK(etl::string<20>(STR("0###################")) == etl::to_string(uint64_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint8_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint16_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint32_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint64_t(0), str, format)); - CHECK(etl::string<20>(STR("128#################")) == etl::to_string(uint8_t(128), str, format)); - CHECK(etl::string<20>(STR("32768###############")) == etl::to_string(uint16_t(32768), str, format)); - CHECK(etl::string<20>(STR("2147483648##########")) == etl::to_string(uint32_t(2147483648ul), str, format)); - CHECK(etl::string<20>(STR("9223372036854775808#")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); + CHECK(String20(STR("128#################")) == etl::to_string(uint8_t(128), str, format)); + CHECK(String20(STR("32768###############")) == etl::to_string(uint16_t(32768), str, format)); + CHECK(String20(STR("2147483648##########")) == etl::to_string(uint32_t(2147483648ul), str, format)); + CHECK(String20(STR("9223372036854775808#")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); - CHECK(etl::string<20>(STR("127#################")) == etl::to_string(INT8_MAX, str, format)); - CHECK(etl::string<20>(STR("32767###############")) == etl::to_string(INT16_MAX, str, format)); - CHECK(etl::string<20>(STR("2147483647##########")) == etl::to_string(INT32_MAX, str, format)); - CHECK(etl::string<20>(STR("9223372036854775807#")) == etl::to_string(INT64_MAX, str, format)); + CHECK(String20(STR("127#################")) == etl::to_string(INT8_MAX, str, format)); + CHECK(String20(STR("32767###############")) == etl::to_string(INT16_MAX, str, format)); + CHECK(String20(STR("2147483647##########")) == etl::to_string(INT32_MAX, str, format)); + CHECK(String20(STR("9223372036854775807#")) == etl::to_string(INT64_MAX, str, format)); - CHECK(etl::string<20>(STR("-128################")) == etl::to_string(INT8_MIN, str, format)); - CHECK(etl::string<20>(STR("-32768##############")) == etl::to_string(INT16_MIN, str, format)); - CHECK(etl::string<20>(STR("-2147483648#########")) == etl::to_string(INT32_MIN, str, format)); - CHECK(etl::string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); + CHECK(String20(STR("-128################")) == etl::to_string(INT8_MIN, str, format)); + CHECK(String20(STR("-32768##############")) == etl::to_string(INT16_MIN, str, format)); + CHECK(String20(STR("-2147483648#########")) == etl::to_string(INT32_MIN, str, format)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); } //************************************************************************* TEST(test_binary_format_no_append) { - etl::string<64> str; + String64 str; - CHECK(etl::string<64>(STR("00000000")) == etl::to_string(uint8_t(0), str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::string<64>(STR("0000000000000000")) == etl::to_string(uint16_t(0), str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::string<64>(STR("00000000000000000000000000000000")) == etl::to_string(uint32_t(0), str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::string<64>(STR("0000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("00000000")) == etl::to_string(uint8_t(0), str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("0000000000000000")) == etl::to_string(uint16_t(0), str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("00000000000000000000000000000000")) == etl::to_string(uint32_t(0), str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("0000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(uint64_t(0), str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::string<64>(STR("10000000")) == etl::to_string(uint8_t(128), str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::string<64>(STR("1000000000000000")) == etl::to_string(uint16_t(32768), str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::string<64>(STR("10000000000000000000000000000000")) + CHECK(String64(STR("10000000")) == etl::to_string(uint8_t(128), str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("1000000000000000")) == etl::to_string(uint16_t(32768), str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("10000000000000000000000000000000")) == etl::to_string(uint32_t(2147483648ul), str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::string<64>(STR("1000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("1000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(uint64_t(9223372036854775808ull), str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::string<64>(STR("01111111")) == etl::to_string(INT8_MAX, str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::string<64>(STR("0111111111111111")) == etl::to_string(INT16_MAX, str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::string<64>(STR("01111111111111111111111111111111")) == etl::to_string(INT32_MAX, str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::string<64>(STR("0111111111111111111111111111111111111111111111111111111111111111")) + CHECK(String64(STR("01111111")) == etl::to_string(INT8_MAX, str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("0111111111111111")) == etl::to_string(INT16_MAX, str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("01111111111111111111111111111111")) == etl::to_string(INT32_MAX, str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("0111111111111111111111111111111111111111111111111111111111111111")) == etl::to_string(INT64_MAX, str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::string<64>(STR("10000000")) == etl::to_string(INT8_MIN, str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::string<64>(STR("1000000000000000")) == etl::to_string(INT16_MIN, str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::string<64>(STR("10000000000000000000000000000000")) == etl::to_string(INT32_MIN, str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::string<64>(STR("1000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("10000000")) == etl::to_string(INT8_MIN, str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("1000000000000000")) == etl::to_string(INT16_MIN, str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("10000000000000000000000000000000")) == etl::to_string(INT32_MIN, str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("1000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(INT64_MIN, str, Format().base(2).width(64).fill(STR('0')))); } @@ -261,126 +264,126 @@ namespace //************************************************************************* TEST(test_floating_point_no_append) { - etl::string<20> str; + String20 str; - CHECK(etl::string<20>(STR(" 0.000000")) == etl::to_string(0.0, str, Format().precision(6).width(10).right())); - CHECK(etl::string<20>(STR("0.000000 ")) == etl::to_string(0.0, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 0.000000")) == etl::to_string(0.0, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("0.000000 ")) == etl::to_string(0.0, str, Format().precision(6).width(10).left())); - CHECK(etl::string<20>(STR(" 0.000001")) == etl::to_string(0.000001, str, Format().precision(6).width(10).right())); - CHECK(etl::string<20>(STR("0.000001 ")) == etl::to_string(0.000001, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 0.000001")) == etl::to_string(0.000001, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("0.000001 ")) == etl::to_string(0.000001, str, Format().precision(6).width(10).left())); - CHECK(etl::string<20>(STR(" 1.000000")) == etl::to_string(1.0, str, Format().precision(6).width(10).right())); - CHECK(etl::string<20>(STR("1.000000 ")) == etl::to_string(1.0, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 1.000000")) == etl::to_string(1.0, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("1.000000 ")) == etl::to_string(1.0, str, Format().precision(6).width(10).left())); - CHECK(etl::string<20>(STR(" 1.000001")) == etl::to_string(1.000001, str, Format().precision(6).width(10).right())); - CHECK(etl::string<20>(STR("1.000001 ")) == etl::to_string(1.000001, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 1.000001")) == etl::to_string(1.000001, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("1.000001 ")) == etl::to_string(1.000001, str, Format().precision(6).width(10).left())); - CHECK(etl::string<20>(STR(" 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right())); - CHECK(etl::string<20>(STR("12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left())); - CHECK(etl::string<20>(STR(" -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right())); - CHECK(etl::string<20>(STR("-12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left())); + CHECK(String20(STR(" -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right())); + CHECK(String20(STR("-12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left())); - CHECK(etl::string<20>(STR(" -0.123456")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).right())); - CHECK(etl::string<20>(STR("-0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" -0.123456")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("-0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left())); // Positive rollover - CHECK(etl::string<20>(STR(" 2.000000")) == etl::to_string(1.9999996, str, Format().precision(6).width(10).right())); - CHECK(etl::string<20>(STR("2.000000 ")) == etl::to_string(1.9999996, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 2.000000")) == etl::to_string(1.9999996, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("2.000000 ")) == etl::to_string(1.9999996, str, Format().precision(6).width(10).left())); // Negative rollover - CHECK(etl::string<20>(STR(" -2.000000")) == etl::to_string(-1.9999996, str, Format().precision(6).width(10).right())); - CHECK(etl::string<20>(STR("-2.000000 ")) == etl::to_string(-1.9999996, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" -2.000000")) == etl::to_string(-1.9999996, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("-2.000000 ")) == etl::to_string(-1.9999996, str, Format().precision(6).width(10).left())); } //************************************************************************* TEST(test_floating_point_append) { - etl::string<20> str; + String20 str; str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right(), true)); + CHECK(String20(STR("Result 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right(), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result 12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left(), true)); + CHECK(String20(STR("Result 12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left(), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right(), true)); + CHECK(String20(STR("Result -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right(), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result -12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left(), true)); + CHECK(String20(STR("Result -12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left(), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result -0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left(), true)); + CHECK(String20(STR("Result -0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left(), true)); } //************************************************************************* TEST(test_floating_point_rounding) { - etl::string<20> str; + String20 str; - CHECK(etl::string<20>(STR("0.00001")) == etl::to_string(0.000009, str, Format().precision(5).width(7).right())); - CHECK(etl::string<20>(STR("0.0001")) == etl::to_string(0.000099, str, Format().precision(4).width(6).right())); - CHECK(etl::string<20>(STR("0.001")) == etl::to_string(0.000999, str, Format().precision(3).width(5).right())); - CHECK(etl::string<20>(STR("0.01")) == etl::to_string(0.009999, str, Format().precision(2).width(4).right())); - CHECK(etl::string<20>(STR("0.1")) == etl::to_string(0.099999, str, Format().precision(1).width(3).right())); - CHECK(etl::string<20>(STR("1.0")) == etl::to_string(0.999999, str, Format().precision(1).width(3).right())); - CHECK(etl::string<20>(STR("1")) == etl::to_string(0.999999, str, Format().precision(0).width(1).right())); - CHECK(etl::string<20>(STR("2")) == etl::to_string(1.999999, str, Format().precision(0).width(1).right())); - CHECK(etl::string<20>(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); - CHECK(etl::string<20>(STR("20.0")) == etl::to_string(19.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("0.00001")) == etl::to_string(0.000009, str, Format().precision(5).width(7).right())); + CHECK(String20(STR("0.0001")) == etl::to_string(0.000099, str, Format().precision(4).width(6).right())); + CHECK(String20(STR("0.001")) == etl::to_string(0.000999, str, Format().precision(3).width(5).right())); + CHECK(String20(STR("0.01")) == etl::to_string(0.009999, str, Format().precision(2).width(4).right())); + CHECK(String20(STR("0.1")) == etl::to_string(0.099999, str, Format().precision(1).width(3).right())); + CHECK(String20(STR("1.0")) == etl::to_string(0.999999, str, Format().precision(1).width(3).right())); + CHECK(String20(STR("1")) == etl::to_string(0.999999, str, Format().precision(0).width(1).right())); + CHECK(String20(STR("2")) == etl::to_string(1.999999, str, Format().precision(0).width(1).right())); + CHECK(String20(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("20.0")) == etl::to_string(19.999999, str, Format().precision(1).width(4).right())); } //************************************************************************* TEST(test_bool_no_append) { - etl::string<20> str; + String20 str; - CHECK(etl::string<20>(STR(" 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false))); - CHECK(etl::string<20>(STR(" 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false))); - CHECK(etl::string<20>(STR("0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false))); - CHECK(etl::string<20>(STR("1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false))); + CHECK(String20(STR(" 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false))); + CHECK(String20(STR(" 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false))); + CHECK(String20(STR("0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false))); + CHECK(String20(STR("1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false))); - CHECK(etl::string<20>(STR(" false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true))); - CHECK(etl::string<20>(STR(" true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true))); - CHECK(etl::string<20>(STR("false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true))); - CHECK(etl::string<20>(STR("true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true))); + CHECK(String20(STR(" false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true))); + CHECK(String20(STR(" true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true))); + CHECK(String20(STR("false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true))); + CHECK(String20(STR("true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true))); } //************************************************************************* TEST(test_bool_append) { - etl::string<20> str; + String20 str; str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false), true)); + CHECK(String20(STR("Result 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false), true)); + CHECK(String20(STR("Result 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result 0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false), true)); + CHECK(String20(STR("Result 0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result 1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false), true)); + CHECK(String20(STR("Result 1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true), true)); + CHECK(String20(STR("Result false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true), true)); + CHECK(String20(STR("Result true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true), true)); + CHECK(String20(STR("Result false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::string<20>(STR("Result true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true), true)); + CHECK(String20(STR("Result true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true), true)); } //************************************************************************* TEST(test_pointer_no_append) { - etl::string<20> str; + String20 str; static const volatile int cvi = 0; @@ -388,8 +391,8 @@ namespace oss.width(10); oss.fill(STR('0')); oss << std::hex << std::right << uintptr_t(&cvi); - std::string temp(oss.str()); - etl::string<20> compare(temp.begin(), temp.end()); + std::string temp(oss.str()); + String20 compare(temp.begin(), temp.end()); to_string(&cvi, str, Format().hex().width(10).right().fill(STR('0'))); CHECK(compare == str); @@ -409,7 +412,7 @@ namespace //************************************************************************* TEST(test_pointer_append) { - etl::string<20> str; + String20 str; static const volatile int cvi = 0; @@ -419,7 +422,7 @@ namespace oss << std::hex << std::right << uintptr_t(&cvi); std::string temp(STR("Result ")); temp.append(oss.str()); - etl::string<20> compare(temp.begin(), temp.end()); + String20 compare(temp.begin(), temp.end()); str.assign(STR("Result ")); to_string(&cvi, str, Format().hex().width(10).right().fill(STR('0')), true); @@ -442,229 +445,229 @@ namespace //************************************************************************* TEST(test_integer_denominator_default_format) { - etl::string<20> result; - int value = -1234567; + String20 result; + int value = -1234567; etl::to_string(value, 6U, result); - CHECK_EQUAL(etl::string<20>(STR("-1")).c_str(), result.c_str()); + CHECK_EQUAL(String20(STR("-1")).c_str(), result.c_str()); } //************************************************************************* TEST(test_integer_denominator_huge_precision) { - etl::string<20> result; - int value = -1234560; + String20 result; + int value = -1234560; Format format = Format().precision(100); etl::to_string(value, 6U, result, format); - CHECK_EQUAL(etl::string<20>(STR("-1.234560")).c_str(), result.c_str()); + CHECK_EQUAL(String20(STR("-1.234560")).c_str(), result.c_str()); } //************************************************************************* TEST(test_integer_denominator_huge_precision_64bit) { - etl::string<20> result; - int64_t value = INT64_MIN; + String20 result; + int64_t value = INT64_MIN; Format format = Format().precision(100); etl::to_string(value, 12U, result, format); - CHECK_EQUAL(etl::string<20>(STR("-9223372.036854775808")).c_str(), result.c_str()); + CHECK_EQUAL(String20(STR("-9223372.036854775808")).c_str(), result.c_str()); } //************************************************************************* TEST(test_integer_denominator_zero_fractional) { - etl::string<20> result_i; - int value_i = -1000000; + String20 result_i; + int value_i = -1000000; - etl::string<20> result_d; - double value_d = -1.000000; + String20 result_d; + double value_d = -1.000000; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR("-1.0000")).c_str(), result_i.c_str()); + CHECK_EQUAL(String20(STR("-1.0000")).c_str(), result_i.c_str()); CHECK_EQUAL(result_d.c_str(), result_i.c_str()); } //************************************************************************* TEST(test_integer_denominator_zero_value) { - etl::string<20> result_i; - int value_i = 0; + String20 result_i; + int value_i = 0; - etl::string<20> result_d; - double value_d = -0.000000; + String20 result_d; + double value_d = -0.000000; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR("0.0000")).c_str(), result_i.c_str()); + CHECK_EQUAL(String20(STR("0.0000")).c_str(), result_i.c_str()); CHECK_EQUAL(result_d.c_str(), result_i.c_str()); } //************************************************************************* TEST(test_integer_denominator_zero_integral_small_fractional) { - etl::string<20> result_i; - int value_i = -400; + String20 result_i; + int value_i = -400; - etl::string<20> result_d; - double value_d = -0.000400; + String20 result_d; + double value_d = -0.000400; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR("-0.0004")).c_str(), result_i.c_str()); + CHECK_EQUAL(String20(STR("-0.0004")).c_str(), result_i.c_str()); CHECK_EQUAL(result_d.c_str(), result_i.c_str()); } //************************************************************************* TEST(test_integer_denominator_small_fractional) { - etl::string<20> result_i; - int value_i = -123000400; + String20 result_i; + int value_i = -123000400; - etl::string<20> result_d; - double value_d = -123.000400; + String20 result_d; + double value_d = -123.000400; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR("-123.0004")).c_str(), result_i.c_str()); + CHECK_EQUAL(String20(STR("-123.0004")).c_str(), result_i.c_str()); CHECK_EQUAL(result_d.c_str(), result_i.c_str()); } //************************************************************************* TEST(test_integer_denominator_very_small_fractional) { - etl::string<20> result_i; - int value_i = -123000004; + String20 result_i; + int value_i = -123000004; - etl::string<20> result_d; - double value_d = -123.000004; + String20 result_d; + double value_d = -123.000004; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR("-123.0000")).c_str(), result_i.c_str()); + CHECK_EQUAL(String20(STR("-123.0000")).c_str(), result_i.c_str()); CHECK_EQUAL(result_d.c_str(), result_i.c_str()); } //************************************************************************* TEST(test_integer_denominator_very_small_fractional_rounded_up) { - etl::string<20> result_i; - int value_i = -123000050; + String20 result_i; + int value_i = -123000050; - etl::string<20> result_d; - double value_d = -123.000050; + String20 result_d; + double value_d = -123.000050; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR("-123.0001")).c_str(), result_i.c_str()); + CHECK_EQUAL(String20(STR("-123.0001")).c_str(), result_i.c_str()); CHECK_EQUAL(result_d.c_str(), result_i.c_str()); } //************************************************************************* TEST(test_integer_denominator_shorter_width) { - etl::string<20> result_i; - int value_i = -123456780; + String20 result_i; + int value_i = -123456780; - etl::string<20> result_d; - double value_d = -123.456780; + String20 result_d; + double value_d = -123.456780; Format format = Format().precision(4).width(6).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR("-123.4568")).c_str(), result_i.c_str()); + CHECK_EQUAL(String20(STR("-123.4568")).c_str(), result_i.c_str()); CHECK_EQUAL(result_d.c_str(), result_i.c_str()); } //************************************************************************* TEST(test_double_formatting_10_decimal_point) { - etl::string<20> result_d; - double value_d = 1.2345; + String20 result_d; + double value_d = 1.2345; Format format = Format().precision(10); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR("1.2345000000")).c_str(), result_d.c_str()); + CHECK_EQUAL(String20(STR("1.2345000000")).c_str(), result_d.c_str()); } //************************************************************************* TEST(test_integer_denominator_larger_width) { - etl::string<20> result_i; - int value_i = -123456780; + String20 result_i; + int value_i = -123456780; - etl::string<20> result_d; - double value_d = -123.456780; + String20 result_d; + double value_d = -123.456780; Format format = Format().precision(4).width(15).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR(" -123.4568")).c_str(), result_i.c_str()); + CHECK_EQUAL(String20(STR(" -123.4568")).c_str(), result_i.c_str()); CHECK_EQUAL(result_d.c_str(), result_i.c_str()); } //************************************************************************* TEST(test_integer_denominator_positive_rollover) { - etl::string<20> result_i; - int value_i = 123999990; + String20 result_i; + int value_i = 123999990; - etl::string<20> result_d; - double value_d = 123.999990; + String20 result_d; + double value_d = 123.999990; Format format = Format().precision(4).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR("124.0000")).c_str(), result_i.c_str()); + CHECK_EQUAL(String20(STR("124.0000")).c_str(), result_i.c_str()); CHECK_EQUAL(result_d.c_str(), result_i.c_str()); } //************************************************************************* TEST(test_integer_denominator_negative_rollover) { - etl::string<20> result_i; - int value_i = -123999990; + String20 result_i; + int value_i = -123999990; - etl::string<20> result_d; - double value_d = -123.999990; + String20 result_d; + double value_d = -123.999990; Format format = Format().precision(4).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK_EQUAL(etl::string<20>(STR("-124.0000")).c_str(), result_i.c_str()); + CHECK_EQUAL(String20(STR("-124.0000")).c_str(), result_i.c_str()); CHECK_EQUAL(result_d.c_str(), result_i.c_str()); } @@ -674,48 +677,48 @@ namespace if (std::numeric_limits::is_iec559) { // Check forced scientific formatting for smaller numbers. - etl::string<64> s0; + String64 s0; etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true)); - CHECK(etl::string<64>(STR(" 1.00000e+3")) == s0); + CHECK(String64(STR(" 1.00000e+3")) == s0); etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::string<64>(STR(" 1.00000E+3")) == s0); + CHECK(String64(STR(" 1.00000E+3")) == s0); // Maximum double value is 1.7976931348623157e+308, which rounds to 1.79769e+308 with 5 digits of precision. - etl::string<64> s1; + String64 s1; etl::to_string(std::numeric_limits::max(), s1, Format().precision(5).width(15).right()); - CHECK(etl::string<64>(STR(" 1.79769e+308")) == s1); + CHECK(String64(STR(" 1.79769e+308")) == s1); // Negative maximum double value is -1.7976931348623157e+308, which rounds to -1.79769e+308 with 5 digits of precision. - etl::string<64> s2; + String64 s2; etl::to_string(-std::numeric_limits::max(), s2, Format().precision(5).width(15).right()); - CHECK(etl::string<64>(STR(" -1.79769e+308")) == s2); + CHECK(String64(STR(" -1.79769e+308")) == s2); } #if ETL_USING_64BIT_TYPES using workspace_t = etl::private_to_string::workspace_t; // Maximum workspace_t value is 9223372036854775807. - etl::string<64> s3; + String64 s3; etl::to_string(std::numeric_limits::max(), s3, Format().precision(5).width(21).right()); - CHECK(etl::string<64>(STR(" 9223372036854775807")) == s3); + CHECK(String64(STR(" 9223372036854775807")) == s3); // Minimum workspace_t value is -9223372036854775808. - etl::string<64> s4; + String64 s4; etl::to_string(std::numeric_limits::min(), s4, Format().precision(5).width(21).right()); - CHECK(etl::string<64>(STR(" -9223372036854775808")) == s4); + CHECK(String64(STR(" -9223372036854775808")) == s4); using uworkspace_t = etl::private_to_string::uworkspace_t; // Maximum uworkspace_t value is 18446744073709551615. - etl::string<64> s5; + String64 s5; etl::to_string(std::numeric_limits::max(), s5, Format().precision(5).width(21).right()); - CHECK(etl::string<64>(STR(" 18446744073709551615")) == s5); + CHECK(String64(STR(" 18446744073709551615")) == s5); // Minimum uworkspace_t value is 0. - etl::string<64> s6; + String64 s6; etl::to_string(std::numeric_limits::min(), s6, Format().precision(5).width(21).right()); - CHECK(etl::string<64>(STR(" 0")) == s6); + CHECK(String64(STR(" 0")) == s6); #endif } @@ -724,30 +727,48 @@ namespace { if (std::numeric_limits::is_iec559) { - etl::string<64> s1; + String64 s1; etl::to_string(std::numeric_limits::quiet_NaN(), s1, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::string<64>(STR(" nan")) == s1); + CHECK(String64(STR(" nan")) == s1); - etl::string<64> s2; + String64 s2; etl::to_string(std::numeric_limits::infinity(), s2, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::string<64>(STR(" inf")) == s2); + CHECK(String64(STR(" inf")) == s2); - etl::string<64> s3; + String64 s3; etl::to_string(-std::numeric_limits::infinity(), s3, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::string<64>(STR(" -inf")) == s3); + CHECK(String64(STR(" -inf")) == s3); - etl::string<64> s4; + String64 s4; etl::to_string(std::numeric_limits::quiet_NaN(), s4, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::string<64>(STR(" NAN")) == s4); + CHECK(String64(STR(" NAN")) == s4); - etl::string<64> s5; + String64 s5; etl::to_string(std::numeric_limits::infinity(), s5, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::string<64>(STR(" INF")) == s5); + CHECK(String64(STR(" INF")) == s5); - etl::string<64> s6; + String64 s6; etl::to_string(-std::numeric_limits::infinity(), s6, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::string<64>(STR(" -INF")) == s6); + CHECK(String64(STR(" -INF")) == s6); } } + + //************************************************************************* + TEST(test_issue_1593_to_string_float_uses_double_math) + { + String64 s1; + + // Float + etl::to_string(3.1415927f, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + + // Double + etl::to_string(3.1415927, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + + // Long double + etl::to_string(3.1415927l, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + } } } // namespace diff --git a/test/test_to_u16string.cpp b/test/test_to_u16string.cpp index 22a21c39..cfb109fd 100644 --- a/test/test_to_u16string.cpp +++ b/test/test_to_u16string.cpp @@ -41,32 +41,35 @@ namespace { typedef etl::u16format_spec Format; + using String20 = etl::u16string<20>; + using String64 = etl::u16string<64>; + SUITE(test_to_u16string) { //************************************************************************* TEST(test_default_format_no_append) { - etl::u16string<20> str; + String20 str; - CHECK(etl::u16string<20>(STR("0")) == etl::to_string(uint8_t(0), str)); - CHECK(etl::u16string<20>(STR("0")) == etl::to_string(uint16_t(0), str)); - CHECK(etl::u16string<20>(STR("0")) == etl::to_string(uint32_t(0), str)); - CHECK(etl::u16string<20>(STR("0")) == etl::to_string(uint64_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint8_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint16_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint32_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint64_t(0), str)); - CHECK(etl::u16string<20>(STR("128")) == etl::to_string(uint8_t(128), str)); - CHECK(etl::u16string<20>(STR("32768")) == etl::to_string(uint16_t(32768), str)); - CHECK(etl::u16string<20>(STR("2147483648")) == etl::to_string(uint32_t(2147483648ul), str)); - CHECK(etl::u16string<20>(STR("9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str)); + CHECK(String20(STR("128")) == etl::to_string(uint8_t(128), str)); + CHECK(String20(STR("32768")) == etl::to_string(uint16_t(32768), str)); + CHECK(String20(STR("2147483648")) == etl::to_string(uint32_t(2147483648ul), str)); + CHECK(String20(STR("9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str)); - CHECK(etl::u16string<20>(STR("127")) == etl::to_string(INT8_MAX, str)); - CHECK(etl::u16string<20>(STR("32767")) == etl::to_string(INT16_MAX, str)); - CHECK(etl::u16string<20>(STR("2147483647")) == etl::to_string(INT32_MAX, str)); - CHECK(etl::u16string<20>(STR("9223372036854775807")) == etl::to_string(INT64_MAX, str)); + CHECK(String20(STR("127")) == etl::to_string(INT8_MAX, str)); + CHECK(String20(STR("32767")) == etl::to_string(INT16_MAX, str)); + CHECK(String20(STR("2147483647")) == etl::to_string(INT32_MAX, str)); + CHECK(String20(STR("9223372036854775807")) == etl::to_string(INT64_MAX, str)); - CHECK(etl::u16string<20>(STR("-128")) == etl::to_string(INT8_MIN, str)); - CHECK(etl::u16string<20>(STR("-32768")) == etl::to_string(INT16_MIN, str)); - CHECK(etl::u16string<20>(STR("-2147483648")) == etl::to_string(INT32_MIN, str)); - CHECK(etl::u16string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str)); + CHECK(String20(STR("-128")) == etl::to_string(INT8_MIN, str)); + CHECK(String20(STR("-32768")) == etl::to_string(INT16_MIN, str)); + CHECK(String20(STR("-2147483648")) == etl::to_string(INT32_MIN, str)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str)); } //************************************************************************* @@ -104,88 +107,87 @@ namespace //************************************************************************* TEST(test_format_right_justified_no_append) { - etl::u16string<20> str; + String20 str; Format format = Format().base(10).width(20).fill(STR('#')); - CHECK(etl::u16string<20>(STR("###################0")) == etl::to_string(uint8_t(0), str, format)); - CHECK(etl::u16string<20>(STR("###################0")) == etl::to_string(uint16_t(0), str, format)); - CHECK(etl::u16string<20>(STR("###################0")) == etl::to_string(uint32_t(0), str, format)); - CHECK(etl::u16string<20>(STR("###################0")) == etl::to_string(uint64_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint8_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint16_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint32_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint64_t(0), str, format)); - CHECK(etl::u16string<20>(STR("#################128")) == etl::to_string(uint8_t(128), str, format)); - CHECK(etl::u16string<20>(STR("###############32768")) == etl::to_string(uint16_t(32768), str, format)); - CHECK(etl::u16string<20>(STR("##########2147483648")) == etl::to_string(uint32_t(2147483648ul), str, format)); - CHECK(etl::u16string<20>(STR("#9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); + CHECK(String20(STR("#################128")) == etl::to_string(uint8_t(128), str, format)); + CHECK(String20(STR("###############32768")) == etl::to_string(uint16_t(32768), str, format)); + CHECK(String20(STR("##########2147483648")) == etl::to_string(uint32_t(2147483648ul), str, format)); + CHECK(String20(STR("#9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); - CHECK(etl::u16string<20>(STR("#################127")) == etl::to_string(INT8_MAX, str, format)); - CHECK(etl::u16string<20>(STR("###############32767")) == etl::to_string(INT16_MAX, str, format)); - CHECK(etl::u16string<20>(STR("##########2147483647")) == etl::to_string(INT32_MAX, str, format)); - CHECK(etl::u16string<20>(STR("#9223372036854775807")) == etl::to_string(INT64_MAX, str, format)); + CHECK(String20(STR("#################127")) == etl::to_string(INT8_MAX, str, format)); + CHECK(String20(STR("###############32767")) == etl::to_string(INT16_MAX, str, format)); + CHECK(String20(STR("##########2147483647")) == etl::to_string(INT32_MAX, str, format)); + CHECK(String20(STR("#9223372036854775807")) == etl::to_string(INT64_MAX, str, format)); - CHECK(etl::u16string<20>(STR("################-128")) == etl::to_string(INT8_MIN, str, format)); - CHECK(etl::u16string<20>(STR("##############-32768")) == etl::to_string(INT16_MIN, str, format)); - CHECK(etl::u16string<20>(STR("#########-2147483648")) == etl::to_string(INT32_MIN, str, format)); - CHECK(etl::u16string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); + CHECK(String20(STR("################-128")) == etl::to_string(INT8_MIN, str, format)); + CHECK(String20(STR("##############-32768")) == etl::to_string(INT16_MIN, str, format)); + CHECK(String20(STR("#########-2147483648")) == etl::to_string(INT32_MIN, str, format)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); } //************************************************************************* TEST(test_format_left_justified_no_append) { - etl::u16string<20> str; + String20 str; Format format = Format().base(10).width(20).fill(STR('#')).left(); - CHECK(etl::u16string<20>(STR("0###################")) == etl::to_string(uint8_t(0), str, format)); - CHECK(etl::u16string<20>(STR("0###################")) == etl::to_string(uint16_t(0), str, format)); - CHECK(etl::u16string<20>(STR("0###################")) == etl::to_string(uint32_t(0), str, format)); - CHECK(etl::u16string<20>(STR("0###################")) == etl::to_string(uint64_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint8_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint16_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint32_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint64_t(0), str, format)); - CHECK(etl::u16string<20>(STR("128#################")) == etl::to_string(uint8_t(128), str, format)); - CHECK(etl::u16string<20>(STR("32768###############")) == etl::to_string(uint16_t(32768), str, format)); - CHECK(etl::u16string<20>(STR("2147483648##########")) == etl::to_string(uint32_t(2147483648ul), str, format)); - CHECK(etl::u16string<20>(STR("9223372036854775808#")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); + CHECK(String20(STR("128#################")) == etl::to_string(uint8_t(128), str, format)); + CHECK(String20(STR("32768###############")) == etl::to_string(uint16_t(32768), str, format)); + CHECK(String20(STR("2147483648##########")) == etl::to_string(uint32_t(2147483648ul), str, format)); + CHECK(String20(STR("9223372036854775808#")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); - CHECK(etl::u16string<20>(STR("127#################")) == etl::to_string(INT8_MAX, str, format)); - CHECK(etl::u16string<20>(STR("32767###############")) == etl::to_string(INT16_MAX, str, format)); - CHECK(etl::u16string<20>(STR("2147483647##########")) == etl::to_string(INT32_MAX, str, format)); - CHECK(etl::u16string<20>(STR("9223372036854775807#")) == etl::to_string(INT64_MAX, str, format)); + CHECK(String20(STR("127#################")) == etl::to_string(INT8_MAX, str, format)); + CHECK(String20(STR("32767###############")) == etl::to_string(INT16_MAX, str, format)); + CHECK(String20(STR("2147483647##########")) == etl::to_string(INT32_MAX, str, format)); + CHECK(String20(STR("9223372036854775807#")) == etl::to_string(INT64_MAX, str, format)); - CHECK(etl::u16string<20>(STR("-128################")) == etl::to_string(INT8_MIN, str, format)); - CHECK(etl::u16string<20>(STR("-32768##############")) == etl::to_string(INT16_MIN, str, format)); - CHECK(etl::u16string<20>(STR("-2147483648#########")) == etl::to_string(INT32_MIN, str, format)); - CHECK(etl::u16string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); + CHECK(String20(STR("-128################")) == etl::to_string(INT8_MIN, str, format)); + CHECK(String20(STR("-32768##############")) == etl::to_string(INT16_MIN, str, format)); + CHECK(String20(STR("-2147483648#########")) == etl::to_string(INT32_MIN, str, format)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); } //************************************************************************* TEST(test_binary_format_no_append) { - etl::u16string<64> str; + String64 str; - CHECK(etl::u16string<64>(STR("00000000")) == etl::to_string(uint8_t(0), str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("0000000000000000")) == etl::to_string(uint16_t(0), str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("00000000000000000000000000000000")) - == etl::to_string(uint32_t(0), str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("0000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("00000000")) == etl::to_string(uint8_t(0), str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("0000000000000000")) == etl::to_string(uint16_t(0), str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("00000000000000000000000000000000")) == etl::to_string(uint32_t(0), str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("0000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(uint64_t(0), str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("10000000")) == etl::to_string(uint8_t(128), str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("1000000000000000")) == etl::to_string(uint16_t(32768), str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("10000000000000000000000000000000")) + CHECK(String64(STR("10000000")) == etl::to_string(uint8_t(128), str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("1000000000000000")) == etl::to_string(uint16_t(32768), str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("10000000000000000000000000000000")) == etl::to_string(uint32_t(2147483648ul), str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("1000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("1000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(uint64_t(9223372036854775808ull), str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("01111111")) == etl::to_string(INT8_MAX, str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("0111111111111111")) == etl::to_string(INT16_MAX, str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("01111111111111111111111111111111")) == etl::to_string(INT32_MAX, str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("0111111111111111111111111111111111111111111111111111111111111111")) + CHECK(String64(STR("01111111")) == etl::to_string(INT8_MAX, str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("0111111111111111")) == etl::to_string(INT16_MAX, str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("01111111111111111111111111111111")) == etl::to_string(INT32_MAX, str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("0111111111111111111111111111111111111111111111111111111111111111")) == etl::to_string(INT64_MAX, str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("10000000")) == etl::to_string(INT8_MIN, str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("1000000000000000")) == etl::to_string(INT16_MIN, str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("10000000000000000000000000000000")) == etl::to_string(INT32_MIN, str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u16string<64>(STR("1000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("10000000")) == etl::to_string(INT8_MIN, str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("1000000000000000")) == etl::to_string(INT16_MIN, str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("10000000000000000000000000000000")) == etl::to_string(INT32_MIN, str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("1000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(INT64_MIN, str, Format().base(2).width(64).fill(STR('0')))); } @@ -257,328 +259,328 @@ namespace //************************************************************************* TEST(test_floating_point_no_append) { - etl::u16string<20> str; + String20 str; - CHECK(etl::u16string<20>(STR(" 0.000000")) == etl::to_string(0.0, str, Format().precision(6).width(10).right())); - CHECK(etl::u16string<20>(STR("0.000000 ")) == etl::to_string(0.0, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 0.000000")) == etl::to_string(0.0, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("0.000000 ")) == etl::to_string(0.0, str, Format().precision(6).width(10).left())); - CHECK(etl::u16string<20>(STR(" 0.000001")) == etl::to_string(0.000001, str, Format().precision(6).width(10).right())); - CHECK(etl::u16string<20>(STR("0.000001 ")) == etl::to_string(0.000001, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 0.000001")) == etl::to_string(0.000001, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("0.000001 ")) == etl::to_string(0.000001, str, Format().precision(6).width(10).left())); - CHECK(etl::u16string<20>(STR(" 1.000000")) == etl::to_string(1.0, str, Format().precision(6).width(10).right())); - CHECK(etl::u16string<20>(STR("1.000000 ")) == etl::to_string(1.0, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 1.000000")) == etl::to_string(1.0, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("1.000000 ")) == etl::to_string(1.0, str, Format().precision(6).width(10).left())); - CHECK(etl::u16string<20>(STR(" 1.000001")) == etl::to_string(1.000001, str, Format().precision(6).width(10).right())); - CHECK(etl::u16string<20>(STR("1.000001 ")) == etl::to_string(1.000001, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 1.000001")) == etl::to_string(1.000001, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("1.000001 ")) == etl::to_string(1.000001, str, Format().precision(6).width(10).left())); - CHECK(etl::u16string<20>(STR(" 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right())); - CHECK(etl::u16string<20>(STR("12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left())); - CHECK(etl::u16string<20>(STR(" -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right())); - CHECK(etl::u16string<20>(STR("-12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left())); + CHECK(String20(STR(" -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right())); + CHECK(String20(STR("-12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left())); - CHECK(etl::u16string<20>(STR(" -0.123456")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).right())); - CHECK(etl::u16string<20>(STR("-0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" -0.123456")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("-0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left())); } //************************************************************************* TEST(test_floating_point_append) { - etl::u16string<20> str; + String20 str; str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right(), true)); + CHECK(String20(STR("Result 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right(), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result 12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left(), true)); + CHECK(String20(STR("Result 12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left(), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right(), true)); + CHECK(String20(STR("Result -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right(), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result -12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left(), true)); + CHECK(String20(STR("Result -12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left(), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result -0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left(), true)); + CHECK(String20(STR("Result -0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left(), true)); } //************************************************************************* TEST(test_floating_point_rounding) { - etl::u16string<20> str; + String20 str; - CHECK(etl::u16string<20>(STR("0.00001")) == etl::to_string(0.000009, str, Format().precision(5).width(7).right())); - CHECK(etl::u16string<20>(STR("0.0001")) == etl::to_string(0.000099, str, Format().precision(4).width(6).right())); - CHECK(etl::u16string<20>(STR("0.001")) == etl::to_string(0.000999, str, Format().precision(3).width(5).right())); - CHECK(etl::u16string<20>(STR("0.01")) == etl::to_string(0.009999, str, Format().precision(2).width(4).right())); - CHECK(etl::u16string<20>(STR("0.1")) == etl::to_string(0.099999, str, Format().precision(1).width(3).right())); - CHECK(etl::u16string<20>(STR("1.0")) == etl::to_string(0.999999, str, Format().precision(1).width(3).right())); - CHECK(etl::u16string<20>(STR("1")) == etl::to_string(0.999999, str, Format().precision(0).width(1).right())); - CHECK(etl::u16string<20>(STR("2")) == etl::to_string(1.999999, str, Format().precision(0).width(1).right())); - CHECK(etl::u16string<20>(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); - CHECK(etl::u16string<20>(STR("20.0")) == etl::to_string(19.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("0.00001")) == etl::to_string(0.000009, str, Format().precision(5).width(7).right())); + CHECK(String20(STR("0.0001")) == etl::to_string(0.000099, str, Format().precision(4).width(6).right())); + CHECK(String20(STR("0.001")) == etl::to_string(0.000999, str, Format().precision(3).width(5).right())); + CHECK(String20(STR("0.01")) == etl::to_string(0.009999, str, Format().precision(2).width(4).right())); + CHECK(String20(STR("0.1")) == etl::to_string(0.099999, str, Format().precision(1).width(3).right())); + CHECK(String20(STR("1.0")) == etl::to_string(0.999999, str, Format().precision(1).width(3).right())); + CHECK(String20(STR("1")) == etl::to_string(0.999999, str, Format().precision(0).width(1).right())); + CHECK(String20(STR("2")) == etl::to_string(1.999999, str, Format().precision(0).width(1).right())); + CHECK(String20(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("20.0")) == etl::to_string(19.999999, str, Format().precision(1).width(4).right())); } //************************************************************************* TEST(test_bool_no_append) { - etl::u16string<20> str; + String20 str; - CHECK(etl::u16string<20>(STR(" 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false))); - CHECK(etl::u16string<20>(STR(" 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false))); - CHECK(etl::u16string<20>(STR("0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false))); - CHECK(etl::u16string<20>(STR("1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false))); + CHECK(String20(STR(" 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false))); + CHECK(String20(STR(" 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false))); + CHECK(String20(STR("0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false))); + CHECK(String20(STR("1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false))); - CHECK(etl::u16string<20>(STR(" false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true))); - CHECK(etl::u16string<20>(STR(" true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true))); - CHECK(etl::u16string<20>(STR("false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true))); - CHECK(etl::u16string<20>(STR("true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true))); + CHECK(String20(STR(" false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true))); + CHECK(String20(STR(" true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true))); + CHECK(String20(STR("false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true))); + CHECK(String20(STR("true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true))); } //************************************************************************* TEST(test_bool_append) { - etl::u16string<20> str; + String20 str; str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false), true)); + CHECK(String20(STR("Result 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false), true)); + CHECK(String20(STR("Result 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result 0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false), true)); + CHECK(String20(STR("Result 0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result 1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false), true)); + CHECK(String20(STR("Result 1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true), true)); + CHECK(String20(STR("Result false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true), true)); + CHECK(String20(STR("Result true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true), true)); + CHECK(String20(STR("Result false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::u16string<20>(STR("Result true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true), true)); + CHECK(String20(STR("Result true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true), true)); } //************************************************************************* TEST(test_integer_denominator_default_format) { - etl::u16string<20> result; - int value = -1234567; + String20 result; + int value = -1234567; etl::to_string(value, 6U, result); - CHECK(etl::u16string<20>(STR("-1")) == result); + CHECK(String20(STR("-1")) == result); } //************************************************************************* TEST(test_integer_denominator_huge_precision) { - etl::u16string<20> result; - int value = -1234560; + String20 result; + int value = -1234560; Format format = Format().precision(100); etl::to_string(value, 6U, result, format); - CHECK(etl::u16string<20>(STR("-1.234560")) == result); + CHECK(String20(STR("-1.234560")) == result); } //************************************************************************* TEST(test_integer_denominator_huge_precision_64bit) { - etl::u16string<20> result; - int64_t value = INT64_MIN; + String20 result; + int64_t value = INT64_MIN; Format format = Format().precision(100); etl::to_string(value, 12U, result, format); - CHECK(etl::u16string<20>(STR("-9223372.036854775808")) == result); + CHECK(String20(STR("-9223372.036854775808")) == result); } //************************************************************************* TEST(test_integer_denominator_zero_fractional) { - etl::u16string<20> result_i; - int value_i = -1000000; + String20 result_i; + int value_i = -1000000; - etl::u16string<20> result_d; - double value_d = -1.000000; + String20 result_d; + double value_d = -1.000000; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u16string<20>(STR("-1.0000")) == result_i); + CHECK(String20(STR("-1.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_zero_value) { - etl::u16string<20> result_i; - int value_i = 0; + String20 result_i; + int value_i = 0; - etl::u16string<20> result_d; - double value_d = -0.000000; + String20 result_d; + double value_d = -0.000000; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u16string<20>(STR("0.0000")) == result_i); + CHECK(String20(STR("0.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_zero_integral_small_fractional) { - etl::u16string<20> result_i; - int value_i = -400; + String20 result_i; + int value_i = -400; - etl::u16string<20> result_d; - double value_d = -0.000400; + String20 result_d; + double value_d = -0.000400; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u16string<20>(STR("-0.0004")) == result_i); + CHECK(String20(STR("-0.0004")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_small_fractional) { - etl::u16string<20> result_i; - int value_i = -123000400; + String20 result_i; + int value_i = -123000400; - etl::u16string<20> result_d; - double value_d = -123.000400; + String20 result_d; + double value_d = -123.000400; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u16string<20>(STR("-123.0004")) == result_i); + CHECK(String20(STR("-123.0004")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_very_small_fractional) { - etl::u16string<20> result_i; - int value_i = -123000004; + String20 result_i; + int value_i = -123000004; - etl::u16string<20> result_d; - double value_d = -123.000004; + String20 result_d; + double value_d = -123.000004; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u16string<20>(STR("-123.0000")) == result_i); + CHECK(String20(STR("-123.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_very_small_fractional_rounded_up) { - etl::u16string<20> result_i; - int value_i = -123000050; + String20 result_i; + int value_i = -123000050; - etl::u16string<20> result_d; - double value_d = -123.000050; + String20 result_d; + double value_d = -123.000050; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u16string<20>(STR("-123.0001")) == result_i); + CHECK(String20(STR("-123.0001")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_shorter_width) { - etl::u16string<20> result_i; - int value_i = -123456780; + String20 result_i; + int value_i = -123456780; - etl::u16string<20> result_d; - double value_d = -123.456780; + String20 result_d; + double value_d = -123.456780; Format format = Format().precision(4).width(6).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u16string<20>(STR("-123.4568")) == result_i); + CHECK(String20(STR("-123.4568")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_larger_width) { - etl::u16string<20> result_i; - int value_i = -123456780; + String20 result_i; + int value_i = -123456780; - etl::u16string<20> result_d; - double value_d = -123.456780; + String20 result_d; + double value_d = -123.456780; Format format = Format().precision(4).width(15).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u16string<20>(STR(" -123.4568")) == result_i); + CHECK(String20(STR(" -123.4568")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_positive_rollover) { - etl::u16string<20> result_i; - int value_i = 123999990; + String20 result_i; + int value_i = 123999990; - etl::u16string<20> result_d; - double value_d = 123.999990; + String20 result_d; + double value_d = 123.999990; Format format = Format().precision(4).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u16string<20>(STR("124.0000")) == result_i); + CHECK(String20(STR("124.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_negative_rollover) { - etl::u16string<20> result_i; - int value_i = -123999990; + String20 result_i; + int value_i = -123999990; - etl::u16string<20> result_d; - double value_d = -123.999990; + String20 result_d; + double value_d = -123.999990; Format format = Format().precision(4).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u16string<20>(STR("-124.0000")) == result_i); + CHECK(String20(STR("-124.0000")) == result_i); CHECK(result_d == result_i); } @@ -588,48 +590,48 @@ namespace if (std::numeric_limits::is_iec559) { // Check forced scientific formatting for smaller numbers. - etl::u16string<64> s0; + String64 s0; etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true)); - CHECK(etl::u16string<64>(STR(" 1.00000e+3")) == s0); + CHECK(String64(STR(" 1.00000e+3")) == s0); etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u16string<64>(STR(" 1.00000E+3")) == s0); + CHECK(String64(STR(" 1.00000E+3")) == s0); // Maximum double value is 1.7976931348623157e+308, which rounds to 1.79769e+308 with 5 digits of precision. - etl::u16string<64> s1; + String64 s1; etl::to_string(std::numeric_limits::max(), s1, Format().precision(5).width(15).right()); - CHECK(etl::u16string<64>(STR(" 1.79769e+308")) == s1); + CHECK(String64(STR(" 1.79769e+308")) == s1); // Negative maximum double value is -1.7976931348623157e+308, which rounds to -1.79769e+308 with 5 digits of precision. - etl::u16string<64> s2; + String64 s2; etl::to_string(-std::numeric_limits::max(), s2, Format().precision(5).width(15).right()); - CHECK(etl::u16string<64>(STR(" -1.79769e+308")) == s2); + CHECK(String64(STR(" -1.79769e+308")) == s2); } #if ETL_USING_64BIT_TYPES using workspace_t = etl::private_to_string::workspace_t; // Maximum workspace_t value is 9223372036854775807. - etl::u16string<64> s3; + String64 s3; etl::to_string(std::numeric_limits::max(), s3, Format().precision(5).width(21).right()); - CHECK(etl::u16string<64>(STR(" 9223372036854775807")) == s3); + CHECK(String64(STR(" 9223372036854775807")) == s3); // Minimum workspace_t value is -9223372036854775808. - etl::u16string<64> s4; + String64 s4; etl::to_string(std::numeric_limits::min(), s4, Format().precision(5).width(21).right()); - CHECK(etl::u16string<64>(STR(" -9223372036854775808")) == s4); + CHECK(String64(STR(" -9223372036854775808")) == s4); using uworkspace_t = etl::private_to_string::uworkspace_t; // Maximum uworkspace_t value is 18446744073709551615. - etl::u16string<64> s5; + String64 s5; etl::to_string(std::numeric_limits::max(), s5, Format().precision(5).width(21).right()); - CHECK(etl::u16string<64>(STR(" 18446744073709551615")) == s5); + CHECK(String64(STR(" 18446744073709551615")) == s5); // Minimum uworkspace_t value is 0. - etl::u16string<64> s6; + String64 s6; etl::to_string(std::numeric_limits::min(), s6, Format().precision(5).width(21).right()); - CHECK(etl::u16string<64>(STR(" 0")) == s6); + CHECK(String64(STR(" 0")) == s6); #endif } @@ -638,30 +640,48 @@ namespace { if (std::numeric_limits::is_iec559) { - etl::u16string<64> s1; + String64 s1; etl::to_string(std::numeric_limits::quiet_NaN(), s1, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::u16string<64>(STR(" nan")) == s1); + CHECK(String64(STR(" nan")) == s1); - etl::u16string<64> s2; + String64 s2; etl::to_string(std::numeric_limits::infinity(), s2, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::u16string<64>(STR(" inf")) == s2); + CHECK(String64(STR(" inf")) == s2); - etl::u16string<64> s3; + String64 s3; etl::to_string(-std::numeric_limits::infinity(), s3, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::u16string<64>(STR(" -inf")) == s3); + CHECK(String64(STR(" -inf")) == s3); - etl::u16string<64> s4; + String64 s4; etl::to_string(std::numeric_limits::quiet_NaN(), s4, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u16string<64>(STR(" NAN")) == s4); + CHECK(String64(STR(" NAN")) == s4); - etl::u16string<64> s5; + String64 s5; etl::to_string(std::numeric_limits::infinity(), s5, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u16string<64>(STR(" INF")) == s5); + CHECK(String64(STR(" INF")) == s5); - etl::u16string<64> s6; + String64 s6; etl::to_string(-std::numeric_limits::infinity(), s6, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u16string<64>(STR(" -INF")) == s6); + CHECK(String64(STR(" -INF")) == s6); } } + + //************************************************************************* + TEST(test_issue_1593_to_string_float_uses_double_math) + { + String64 s1; + + // Float + etl::to_string(3.1415927f, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + + // Double + etl::to_string(3.1415927, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + + // Long double + etl::to_string(3.1415927l, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + } } } // namespace diff --git a/test/test_to_u32string.cpp b/test/test_to_u32string.cpp index 523aa556..9c81ad0d 100644 --- a/test/test_to_u32string.cpp +++ b/test/test_to_u32string.cpp @@ -42,32 +42,35 @@ namespace { typedef etl::u32format_spec Format; + using String20 = etl::u32string<20>; + using String64 = etl::u32string<64>; + SUITE(test_to_u32string) { //************************************************************************* TEST(test_default_format_no_append) { - etl::u32string<20> str; + String20 str; - CHECK(etl::u32string<20>(STR("0")) == etl::to_string(uint8_t(0), str)); - CHECK(etl::u32string<20>(STR("0")) == etl::to_string(uint16_t(0), str)); - CHECK(etl::u32string<20>(STR("0")) == etl::to_string(uint32_t(0), str)); - CHECK(etl::u32string<20>(STR("0")) == etl::to_string(uint64_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint8_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint16_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint32_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint64_t(0), str)); - CHECK(etl::u32string<20>(STR("128")) == etl::to_string(uint8_t(128), str)); - CHECK(etl::u32string<20>(STR("32768")) == etl::to_string(uint16_t(32768), str)); - CHECK(etl::u32string<20>(STR("2147483648")) == etl::to_string(uint32_t(2147483648ul), str)); - CHECK(etl::u32string<20>(STR("9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str)); + CHECK(String20(STR("128")) == etl::to_string(uint8_t(128), str)); + CHECK(String20(STR("32768")) == etl::to_string(uint16_t(32768), str)); + CHECK(String20(STR("2147483648")) == etl::to_string(uint32_t(2147483648ul), str)); + CHECK(String20(STR("9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str)); - CHECK(etl::u32string<20>(STR("127")) == etl::to_string(INT8_MAX, str)); - CHECK(etl::u32string<20>(STR("32767")) == etl::to_string(INT16_MAX, str)); - CHECK(etl::u32string<20>(STR("2147483647")) == etl::to_string(INT32_MAX, str)); - CHECK(etl::u32string<20>(STR("9223372036854775807")) == etl::to_string(INT64_MAX, str)); + CHECK(String20(STR("127")) == etl::to_string(INT8_MAX, str)); + CHECK(String20(STR("32767")) == etl::to_string(INT16_MAX, str)); + CHECK(String20(STR("2147483647")) == etl::to_string(INT32_MAX, str)); + CHECK(String20(STR("9223372036854775807")) == etl::to_string(INT64_MAX, str)); - CHECK(etl::u32string<20>(STR("-128")) == etl::to_string(INT8_MIN, str)); - CHECK(etl::u32string<20>(STR("-32768")) == etl::to_string(INT16_MIN, str)); - CHECK(etl::u32string<20>(STR("-2147483648")) == etl::to_string(INT32_MIN, str)); - CHECK(etl::u32string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str)); + CHECK(String20(STR("-128")) == etl::to_string(INT8_MIN, str)); + CHECK(String20(STR("-32768")) == etl::to_string(INT16_MIN, str)); + CHECK(String20(STR("-2147483648")) == etl::to_string(INT32_MIN, str)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str)); } //************************************************************************* @@ -105,88 +108,87 @@ namespace //************************************************************************* TEST(test_format_right_justified_no_append) { - etl::u32string<20> str; + String20 str; Format format = Format().base(10).width(20).fill(STR('#')); - CHECK(etl::u32string<20>(STR("###################0")) == etl::to_string(uint8_t(0), str, format)); - CHECK(etl::u32string<20>(STR("###################0")) == etl::to_string(uint16_t(0), str, format)); - CHECK(etl::u32string<20>(STR("###################0")) == etl::to_string(uint32_t(0), str, format)); - CHECK(etl::u32string<20>(STR("###################0")) == etl::to_string(uint64_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint8_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint16_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint32_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint64_t(0), str, format)); - CHECK(etl::u32string<20>(STR("#################128")) == etl::to_string(uint8_t(128), str, format)); - CHECK(etl::u32string<20>(STR("###############32768")) == etl::to_string(uint16_t(32768), str, format)); - CHECK(etl::u32string<20>(STR("##########2147483648")) == etl::to_string(uint32_t(2147483648ul), str, format)); - CHECK(etl::u32string<20>(STR("#9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); + CHECK(String20(STR("#################128")) == etl::to_string(uint8_t(128), str, format)); + CHECK(String20(STR("###############32768")) == etl::to_string(uint16_t(32768), str, format)); + CHECK(String20(STR("##########2147483648")) == etl::to_string(uint32_t(2147483648ul), str, format)); + CHECK(String20(STR("#9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); - CHECK(etl::u32string<20>(STR("#################127")) == etl::to_string(INT8_MAX, str, format)); - CHECK(etl::u32string<20>(STR("###############32767")) == etl::to_string(INT16_MAX, str, format)); - CHECK(etl::u32string<20>(STR("##########2147483647")) == etl::to_string(INT32_MAX, str, format)); - CHECK(etl::u32string<20>(STR("#9223372036854775807")) == etl::to_string(INT64_MAX, str, format)); + CHECK(String20(STR("#################127")) == etl::to_string(INT8_MAX, str, format)); + CHECK(String20(STR("###############32767")) == etl::to_string(INT16_MAX, str, format)); + CHECK(String20(STR("##########2147483647")) == etl::to_string(INT32_MAX, str, format)); + CHECK(String20(STR("#9223372036854775807")) == etl::to_string(INT64_MAX, str, format)); - CHECK(etl::u32string<20>(STR("################-128")) == etl::to_string(INT8_MIN, str, format)); - CHECK(etl::u32string<20>(STR("##############-32768")) == etl::to_string(INT16_MIN, str, format)); - CHECK(etl::u32string<20>(STR("#########-2147483648")) == etl::to_string(INT32_MIN, str, format)); - CHECK(etl::u32string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); + CHECK(String20(STR("################-128")) == etl::to_string(INT8_MIN, str, format)); + CHECK(String20(STR("##############-32768")) == etl::to_string(INT16_MIN, str, format)); + CHECK(String20(STR("#########-2147483648")) == etl::to_string(INT32_MIN, str, format)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); } //************************************************************************* TEST(test_format_left_justified_no_append) { - etl::u32string<20> str; + String20 str; Format format = Format().base(10).width(20).fill(STR('#')).left(); - CHECK(etl::u32string<20>(STR("0###################")) == etl::to_string(uint8_t(0), str, format)); - CHECK(etl::u32string<20>(STR("0###################")) == etl::to_string(uint16_t(0), str, format)); - CHECK(etl::u32string<20>(STR("0###################")) == etl::to_string(uint32_t(0), str, format)); - CHECK(etl::u32string<20>(STR("0###################")) == etl::to_string(uint64_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint8_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint16_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint32_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint64_t(0), str, format)); - CHECK(etl::u32string<20>(STR("128#################")) == etl::to_string(uint8_t(128), str, format)); - CHECK(etl::u32string<20>(STR("32768###############")) == etl::to_string(uint16_t(32768), str, format)); - CHECK(etl::u32string<20>(STR("2147483648##########")) == etl::to_string(uint32_t(2147483648ul), str, format)); - CHECK(etl::u32string<20>(STR("9223372036854775808#")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); + CHECK(String20(STR("128#################")) == etl::to_string(uint8_t(128), str, format)); + CHECK(String20(STR("32768###############")) == etl::to_string(uint16_t(32768), str, format)); + CHECK(String20(STR("2147483648##########")) == etl::to_string(uint32_t(2147483648ul), str, format)); + CHECK(String20(STR("9223372036854775808#")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); - CHECK(etl::u32string<20>(STR("127#################")) == etl::to_string(INT8_MAX, str, format)); - CHECK(etl::u32string<20>(STR("32767###############")) == etl::to_string(INT16_MAX, str, format)); - CHECK(etl::u32string<20>(STR("2147483647##########")) == etl::to_string(INT32_MAX, str, format)); - CHECK(etl::u32string<20>(STR("9223372036854775807#")) == etl::to_string(INT64_MAX, str, format)); + CHECK(String20(STR("127#################")) == etl::to_string(INT8_MAX, str, format)); + CHECK(String20(STR("32767###############")) == etl::to_string(INT16_MAX, str, format)); + CHECK(String20(STR("2147483647##########")) == etl::to_string(INT32_MAX, str, format)); + CHECK(String20(STR("9223372036854775807#")) == etl::to_string(INT64_MAX, str, format)); - CHECK(etl::u32string<20>(STR("-128################")) == etl::to_string(INT8_MIN, str, format)); - CHECK(etl::u32string<20>(STR("-32768##############")) == etl::to_string(INT16_MIN, str, format)); - CHECK(etl::u32string<20>(STR("-2147483648#########")) == etl::to_string(INT32_MIN, str, format)); - CHECK(etl::u32string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); + CHECK(String20(STR("-128################")) == etl::to_string(INT8_MIN, str, format)); + CHECK(String20(STR("-32768##############")) == etl::to_string(INT16_MIN, str, format)); + CHECK(String20(STR("-2147483648#########")) == etl::to_string(INT32_MIN, str, format)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); } //************************************************************************* TEST(test_binary_format_no_append) { - etl::u32string<64> str; + String64 str; - CHECK(etl::u32string<64>(STR("00000000")) == etl::to_string(uint8_t(0), str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("0000000000000000")) == etl::to_string(uint16_t(0), str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("00000000000000000000000000000000")) - == etl::to_string(uint32_t(0), str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("0000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("00000000")) == etl::to_string(uint8_t(0), str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("0000000000000000")) == etl::to_string(uint16_t(0), str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("00000000000000000000000000000000")) == etl::to_string(uint32_t(0), str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("0000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(uint64_t(0), str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("10000000")) == etl::to_string(uint8_t(128), str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("1000000000000000")) == etl::to_string(uint16_t(32768), str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("10000000000000000000000000000000")) + CHECK(String64(STR("10000000")) == etl::to_string(uint8_t(128), str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("1000000000000000")) == etl::to_string(uint16_t(32768), str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("10000000000000000000000000000000")) == etl::to_string(uint32_t(2147483648ul), str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("1000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("1000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(uint64_t(9223372036854775808ull), str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("01111111")) == etl::to_string(INT8_MAX, str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("0111111111111111")) == etl::to_string(INT16_MAX, str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("01111111111111111111111111111111")) == etl::to_string(INT32_MAX, str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("0111111111111111111111111111111111111111111111111111111111111111")) + CHECK(String64(STR("01111111")) == etl::to_string(INT8_MAX, str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("0111111111111111")) == etl::to_string(INT16_MAX, str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("01111111111111111111111111111111")) == etl::to_string(INT32_MAX, str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("0111111111111111111111111111111111111111111111111111111111111111")) == etl::to_string(INT64_MAX, str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("10000000")) == etl::to_string(INT8_MIN, str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("1000000000000000")) == etl::to_string(INT16_MIN, str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("10000000000000000000000000000000")) == etl::to_string(INT32_MIN, str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u32string<64>(STR("1000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("10000000")) == etl::to_string(INT8_MIN, str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("1000000000000000")) == etl::to_string(INT16_MIN, str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("10000000000000000000000000000000")) == etl::to_string(INT32_MIN, str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("1000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(INT64_MIN, str, Format().base(2).width(64).fill(STR('0')))); } @@ -258,329 +260,329 @@ namespace //************************************************************************* TEST(test_floating_point_no_append) { - etl::u32string<20> str; + String20 str; - CHECK(etl::u32string<20>(STR(" 0.000000")) == etl::to_string(0.0, str, Format().precision(6).width(10).right())); - CHECK(etl::u32string<20>(STR("0.000000 ")) == etl::to_string(0.0, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 0.000000")) == etl::to_string(0.0, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("0.000000 ")) == etl::to_string(0.0, str, Format().precision(6).width(10).left())); - CHECK(etl::u32string<20>(STR(" 0.000001")) == etl::to_string(0.000001, str, Format().precision(6).width(10).right())); - CHECK(etl::u32string<20>(STR("0.000001 ")) == etl::to_string(0.000001, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 0.000001")) == etl::to_string(0.000001, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("0.000001 ")) == etl::to_string(0.000001, str, Format().precision(6).width(10).left())); - CHECK(etl::u32string<20>(STR(" 1.000000")) == etl::to_string(1.0, str, Format().precision(6).width(10).right())); - CHECK(etl::u32string<20>(STR("1.000000 ")) == etl::to_string(1.0, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 1.000000")) == etl::to_string(1.0, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("1.000000 ")) == etl::to_string(1.0, str, Format().precision(6).width(10).left())); - CHECK(etl::u32string<20>(STR(" 1.000001")) == etl::to_string(1.000001, str, Format().precision(6).width(10).right())); - CHECK(etl::u32string<20>(STR("1.000001 ")) == etl::to_string(1.000001, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 1.000001")) == etl::to_string(1.000001, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("1.000001 ")) == etl::to_string(1.000001, str, Format().precision(6).width(10).left())); - CHECK(etl::u32string<20>(STR(" 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right())); - CHECK(etl::u32string<20>(STR("12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left())); - CHECK(etl::u32string<20>(STR(" -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right())); - CHECK(etl::u32string<20>(STR("-12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left())); + CHECK(String20(STR(" -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right())); + CHECK(String20(STR("-12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left())); - CHECK(etl::u32string<20>(STR(" -0.123456")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).right())); - CHECK(etl::u32string<20>(STR("-0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" -0.123456")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("-0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left())); } //************************************************************************* TEST(test_floating_point_append) { - etl::u32string<20> str; + String20 str; str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right(), true)); + CHECK(String20(STR("Result 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right(), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result 12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left(), true)); + CHECK(String20(STR("Result 12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left(), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right(), true)); + CHECK(String20(STR("Result -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right(), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result -12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left(), true)); + CHECK(String20(STR("Result -12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left(), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result -0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left(), true)); + CHECK(String20(STR("Result -0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left(), true)); } //************************************************************************* TEST(test_floating_point_rounding) { - etl::u32string<20> str; + String20 str; - CHECK(etl::u32string<20>(STR("0.00001")) == etl::to_string(0.000009, str, Format().precision(5).width(7).right())); - CHECK(etl::u32string<20>(STR("0.0001")) == etl::to_string(0.000099, str, Format().precision(4).width(6).right())); - CHECK(etl::u32string<20>(STR("0.001")) == etl::to_string(0.000999, str, Format().precision(3).width(5).right())); - CHECK(etl::u32string<20>(STR("0.01")) == etl::to_string(0.009999, str, Format().precision(2).width(4).right())); - CHECK(etl::u32string<20>(STR("0.1")) == etl::to_string(0.099999, str, Format().precision(1).width(3).right())); - CHECK(etl::u32string<20>(STR("1.0")) == etl::to_string(0.999999, str, Format().precision(1).width(3).right())); - CHECK(etl::u32string<20>(STR("1")) == etl::to_string(0.999999, str, Format().precision(0).width(1).right())); - CHECK(etl::u32string<20>(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); - CHECK(etl::u32string<20>(STR("2")) == etl::to_string(1.999999, str, Format().precision(0).width(1).right())); - CHECK(etl::u32string<20>(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); - CHECK(etl::u32string<20>(STR("20.0")) == etl::to_string(19.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("0.00001")) == etl::to_string(0.000009, str, Format().precision(5).width(7).right())); + CHECK(String20(STR("0.0001")) == etl::to_string(0.000099, str, Format().precision(4).width(6).right())); + CHECK(String20(STR("0.001")) == etl::to_string(0.000999, str, Format().precision(3).width(5).right())); + CHECK(String20(STR("0.01")) == etl::to_string(0.009999, str, Format().precision(2).width(4).right())); + CHECK(String20(STR("0.1")) == etl::to_string(0.099999, str, Format().precision(1).width(3).right())); + CHECK(String20(STR("1.0")) == etl::to_string(0.999999, str, Format().precision(1).width(3).right())); + CHECK(String20(STR("1")) == etl::to_string(0.999999, str, Format().precision(0).width(1).right())); + CHECK(String20(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("2")) == etl::to_string(1.999999, str, Format().precision(0).width(1).right())); + CHECK(String20(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("20.0")) == etl::to_string(19.999999, str, Format().precision(1).width(4).right())); } //************************************************************************* TEST(test_bool_no_append) { - etl::u32string<20> str; + String20 str; - CHECK(etl::u32string<20>(STR(" 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false))); - CHECK(etl::u32string<20>(STR(" 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false))); - CHECK(etl::u32string<20>(STR("0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false))); - CHECK(etl::u32string<20>(STR("1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false))); + CHECK(String20(STR(" 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false))); + CHECK(String20(STR(" 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false))); + CHECK(String20(STR("0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false))); + CHECK(String20(STR("1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false))); - CHECK(etl::u32string<20>(STR(" false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true))); - CHECK(etl::u32string<20>(STR(" true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true))); - CHECK(etl::u32string<20>(STR("false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true))); - CHECK(etl::u32string<20>(STR("true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true))); + CHECK(String20(STR(" false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true))); + CHECK(String20(STR(" true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true))); + CHECK(String20(STR("false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true))); + CHECK(String20(STR("true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true))); } //************************************************************************* TEST(test_bool_append) { - etl::u32string<20> str; + String20 str; str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false), true)); + CHECK(String20(STR("Result 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false), true)); + CHECK(String20(STR("Result 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result 0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false), true)); + CHECK(String20(STR("Result 0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result 1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false), true)); + CHECK(String20(STR("Result 1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true), true)); + CHECK(String20(STR("Result false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true), true)); + CHECK(String20(STR("Result true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true), true)); + CHECK(String20(STR("Result false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::u32string<20>(STR("Result true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true), true)); + CHECK(String20(STR("Result true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true), true)); } //************************************************************************* TEST(test_integer_denominator_default_format) { - etl::u32string<20> result; - int value = -1234567; + String20 result; + int value = -1234567; etl::to_string(value, 6U, result); - CHECK(etl::u32string<20>(STR("-1")) == result); + CHECK(String20(STR("-1")) == result); } //************************************************************************* TEST(test_integer_denominator_huge_precision) { - etl::u32string<20> result; - int value = -1234560; + String20 result; + int value = -1234560; Format format = Format().precision(100); etl::to_string(value, 6U, result, format); - CHECK(etl::u32string<20>(STR("-1.234560")) == result); + CHECK(String20(STR("-1.234560")) == result); } //************************************************************************* TEST(test_integer_denominator_huge_precision_64bit) { - etl::u32string<20> result; - int64_t value = INT64_MIN; + String20 result; + int64_t value = INT64_MIN; Format format = Format().precision(100); etl::to_string(value, 12U, result, format); - CHECK(etl::u32string<20>(STR("-9223372.036854775808")) == result); + CHECK(String20(STR("-9223372.036854775808")) == result); } //************************************************************************* TEST(test_integer_denominator_zero_fractional) { - etl::u32string<20> result_i; - int value_i = -1000000; + String20 result_i; + int value_i = -1000000; - etl::u32string<20> result_d; - double value_d = -1.000000; + String20 result_d; + double value_d = -1.000000; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u32string<20>(STR("-1.0000")) == result_i); + CHECK(String20(STR("-1.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_zero_value) { - etl::u32string<20> result_i; - int value_i = 0; + String20 result_i; + int value_i = 0; - etl::u32string<20> result_d; - double value_d = -0.000000; + String20 result_d; + double value_d = -0.000000; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u32string<20>(STR("0.0000")) == result_i); + CHECK(String20(STR("0.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_zero_integral_small_fractional) { - etl::u32string<20> result_i; - int value_i = -400; + String20 result_i; + int value_i = -400; - etl::u32string<20> result_d; - double value_d = -0.000400; + String20 result_d; + double value_d = -0.000400; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u32string<20>(STR("-0.0004")) == result_i); + CHECK(String20(STR("-0.0004")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_small_fractional) { - etl::u32string<20> result_i; - int value_i = -123000400; + String20 result_i; + int value_i = -123000400; - etl::u32string<20> result_d; - double value_d = -123.000400; + String20 result_d; + double value_d = -123.000400; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u32string<20>(STR("-123.0004")) == result_i); + CHECK(String20(STR("-123.0004")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_very_small_fractional) { - etl::u32string<20> result_i; - int value_i = -123000004; + String20 result_i; + int value_i = -123000004; - etl::u32string<20> result_d; - double value_d = -123.000004; + String20 result_d; + double value_d = -123.000004; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u32string<20>(STR("-123.0000")) == result_i); + CHECK(String20(STR("-123.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_very_small_fractional_rounded_up) { - etl::u32string<20> result_i; - int value_i = -123000050; + String20 result_i; + int value_i = -123000050; - etl::u32string<20> result_d; - double value_d = -123.000050; + String20 result_d; + double value_d = -123.000050; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u32string<20>(STR("-123.0001")) == result_i); + CHECK(String20(STR("-123.0001")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_shorter_width) { - etl::u32string<20> result_i; - int value_i = -123456780; + String20 result_i; + int value_i = -123456780; - etl::u32string<20> result_d; - double value_d = -123.456780; + String20 result_d; + double value_d = -123.456780; Format format = Format().precision(4).width(6).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u32string<20>(STR("-123.4568")) == result_i); + CHECK(String20(STR("-123.4568")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_larger_width) { - etl::u32string<20> result_i; - int value_i = -123456780; + String20 result_i; + int value_i = -123456780; - etl::u32string<20> result_d; - double value_d = -123.456780; + String20 result_d; + double value_d = -123.456780; Format format = Format().precision(4).width(15).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u32string<20>(STR(" -123.4568")) == result_i); + CHECK(String20(STR(" -123.4568")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_positive_rollover) { - etl::u32string<20> result_i; - int value_i = 123999990; + String20 result_i; + int value_i = 123999990; - etl::u32string<20> result_d; - double value_d = 123.999990; + String20 result_d; + double value_d = 123.999990; Format format = Format().precision(4).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u32string<20>(STR("124.0000")) == result_i); + CHECK(String20(STR("124.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_negative_rollover) { - etl::u32string<20> result_i; - int value_i = -123999990; + String20 result_i; + int value_i = -123999990; - etl::u32string<20> result_d; - double value_d = -123.999990; + String20 result_d; + double value_d = -123.999990; Format format = Format().precision(4).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u32string<20>(STR("-124.0000")) == result_i); + CHECK(String20(STR("-124.0000")) == result_i); CHECK(result_d == result_i); } @@ -590,48 +592,48 @@ namespace if (std::numeric_limits::is_iec559) { // Check forced scientific formatting for smaller numbers. - etl::u32string<64> s0; + String64 s0; etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true)); - CHECK(etl::u32string<64>(STR(" 1.00000e+3")) == s0); + CHECK(String64(STR(" 1.00000e+3")) == s0); etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u32string<64>(STR(" 1.00000E+3")) == s0); + CHECK(String64(STR(" 1.00000E+3")) == s0); // Maximum double value is 1.7976931348623157e+308, which rounds to 1.79769e+308 with 5 digits of precision. - etl::u32string<64> s1; + String64 s1; etl::to_string(std::numeric_limits::max(), s1, Format().precision(5).width(15).right()); - CHECK(etl::u32string<64>(STR(" 1.79769e+308")) == s1); + CHECK(String64(STR(" 1.79769e+308")) == s1); // Negative maximum double value is -1.7976931348623157e+308, which rounds to -1.79769e+308 with 5 digits of precision. - etl::u32string<64> s2; + String64 s2; etl::to_string(-std::numeric_limits::max(), s2, Format().precision(5).width(15).right()); - CHECK(etl::u32string<64>(STR(" -1.79769e+308")) == s2); + CHECK(String64(STR(" -1.79769e+308")) == s2); } #if ETL_USING_64BIT_TYPES using workspace_t = etl::private_to_string::workspace_t; // Maximum workspace_t value is 9223372036854775807. - etl::u32string<64> s3; + String64 s3; etl::to_string(std::numeric_limits::max(), s3, Format().precision(5).width(21).right()); - CHECK(etl::u32string<64>(STR(" 9223372036854775807")) == s3); + CHECK(String64(STR(" 9223372036854775807")) == s3); // Minimum workspace_t value is -9223372036854775808. - etl::u32string<64> s4; + String64 s4; etl::to_string(std::numeric_limits::min(), s4, Format().precision(5).width(21).right()); - CHECK(etl::u32string<64>(STR(" -9223372036854775808")) == s4); + CHECK(String64(STR(" -9223372036854775808")) == s4); using uworkspace_t = etl::private_to_string::uworkspace_t; // Maximum uworkspace_t value is 18446744073709551615. - etl::u32string<64> s5; + String64 s5; etl::to_string(std::numeric_limits::max(), s5, Format().precision(5).width(21).right()); - CHECK(etl::u32string<64>(STR(" 18446744073709551615")) == s5); + CHECK(String64(STR(" 18446744073709551615")) == s5); // Minimum uworkspace_t value is 0. - etl::u32string<64> s6; + String64 s6; etl::to_string(std::numeric_limits::min(), s6, Format().precision(5).width(21).right()); - CHECK(etl::u32string<64>(STR(" 0")) == s6); + CHECK(String64(STR(" 0")) == s6); #endif } @@ -640,30 +642,48 @@ namespace { if (std::numeric_limits::is_iec559) { - etl::u32string<64> s1; + String64 s1; etl::to_string(std::numeric_limits::quiet_NaN(), s1, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::u32string<64>(STR(" nan")) == s1); + CHECK(String64(STR(" nan")) == s1); - etl::u32string<64> s2; + String64 s2; etl::to_string(std::numeric_limits::infinity(), s2, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::u32string<64>(STR(" inf")) == s2); + CHECK(String64(STR(" inf")) == s2); - etl::u32string<64> s3; + String64 s3; etl::to_string(-std::numeric_limits::infinity(), s3, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::u32string<64>(STR(" -inf")) == s3); + CHECK(String64(STR(" -inf")) == s3); - etl::u32string<64> s4; + String64 s4; etl::to_string(std::numeric_limits::quiet_NaN(), s4, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u32string<64>(STR(" NAN")) == s4); + CHECK(String64(STR(" NAN")) == s4); - etl::u32string<64> s5; + String64 s5; etl::to_string(std::numeric_limits::infinity(), s5, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u32string<64>(STR(" INF")) == s5); + CHECK(String64(STR(" INF")) == s5); - etl::u32string<64> s6; + String64 s6; etl::to_string(-std::numeric_limits::infinity(), s6, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u32string<64>(STR(" -INF")) == s6); + CHECK(String64(STR(" -INF")) == s6); } } + + //************************************************************************* + TEST(test_issue_1593_to_string_float_uses_double_math) + { + String64 s1; + + // Float + etl::to_string(3.1415927f, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + + // Double + etl::to_string(3.1415927, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + + // Long double + etl::to_string(3.1415927l, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + } } } // namespace diff --git a/test/test_to_u8string.cpp b/test/test_to_u8string.cpp index a1d4eece..de300609 100644 --- a/test/test_to_u8string.cpp +++ b/test/test_to_u8string.cpp @@ -43,32 +43,35 @@ namespace { typedef etl::u8format_spec Format; + using String20 = etl::u8string<20>; + using String64 = etl::u8string<64>; + SUITE(test_to_u8string) { //************************************************************************* TEST(test_default_format_no_append) { - etl::u8string<20> str; + String20 str; - CHECK(etl::u8string<20>(STR("0")) == etl::to_string(uint8_t(0), str)); - CHECK(etl::u8string<20>(STR("0")) == etl::to_string(uint16_t(0), str)); - CHECK(etl::u8string<20>(STR("0")) == etl::to_string(uint32_t(0), str)); - CHECK(etl::u8string<20>(STR("0")) == etl::to_string(uint64_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint8_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint16_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint32_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint64_t(0), str)); - CHECK(etl::u8string<20>(STR("128")) == etl::to_string(uint8_t(128), str)); - CHECK(etl::u8string<20>(STR("32768")) == etl::to_string(uint16_t(32768), str)); - CHECK(etl::u8string<20>(STR("2147483648")) == etl::to_string(uint32_t(2147483648ul), str)); - CHECK(etl::u8string<20>(STR("9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str)); + CHECK(String20(STR("128")) == etl::to_string(uint8_t(128), str)); + CHECK(String20(STR("32768")) == etl::to_string(uint16_t(32768), str)); + CHECK(String20(STR("2147483648")) == etl::to_string(uint32_t(2147483648ul), str)); + CHECK(String20(STR("9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str)); - CHECK(etl::u8string<20>(STR("127")) == etl::to_string(INT8_MAX, str)); - CHECK(etl::u8string<20>(STR("32767")) == etl::to_string(INT16_MAX, str)); - CHECK(etl::u8string<20>(STR("2147483647")) == etl::to_string(INT32_MAX, str)); - CHECK(etl::u8string<20>(STR("9223372036854775807")) == etl::to_string(INT64_MAX, str)); + CHECK(String20(STR("127")) == etl::to_string(INT8_MAX, str)); + CHECK(String20(STR("32767")) == etl::to_string(INT16_MAX, str)); + CHECK(String20(STR("2147483647")) == etl::to_string(INT32_MAX, str)); + CHECK(String20(STR("9223372036854775807")) == etl::to_string(INT64_MAX, str)); - CHECK(etl::u8string<20>(STR("-128")) == etl::to_string(INT8_MIN, str)); - CHECK(etl::u8string<20>(STR("-32768")) == etl::to_string(INT16_MIN, str)); - CHECK(etl::u8string<20>(STR("-2147483648")) == etl::to_string(INT32_MIN, str)); - CHECK(etl::u8string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str)); + CHECK(String20(STR("-128")) == etl::to_string(INT8_MIN, str)); + CHECK(String20(STR("-32768")) == etl::to_string(INT16_MIN, str)); + CHECK(String20(STR("-2147483648")) == etl::to_string(INT32_MIN, str)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str)); } //************************************************************************* @@ -106,88 +109,87 @@ namespace //************************************************************************* TEST(test_format_right_justified_no_append) { - etl::u8string<20> str; + String20 str; Format format = Format().base(10).width(20).fill(STR('#')); - CHECK(etl::u8string<20>(STR("###################0")) == etl::to_string(uint8_t(0), str, format)); - CHECK(etl::u8string<20>(STR("###################0")) == etl::to_string(uint16_t(0), str, format)); - CHECK(etl::u8string<20>(STR("###################0")) == etl::to_string(uint32_t(0), str, format)); - CHECK(etl::u8string<20>(STR("###################0")) == etl::to_string(uint64_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint8_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint16_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint32_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint64_t(0), str, format)); - CHECK(etl::u8string<20>(STR("#################128")) == etl::to_string(uint8_t(128), str, format)); - CHECK(etl::u8string<20>(STR("###############32768")) == etl::to_string(uint16_t(32768), str, format)); - CHECK(etl::u8string<20>(STR("##########2147483648")) == etl::to_string(uint32_t(2147483648ul), str, format)); - CHECK(etl::u8string<20>(STR("#9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); + CHECK(String20(STR("#################128")) == etl::to_string(uint8_t(128), str, format)); + CHECK(String20(STR("###############32768")) == etl::to_string(uint16_t(32768), str, format)); + CHECK(String20(STR("##########2147483648")) == etl::to_string(uint32_t(2147483648ul), str, format)); + CHECK(String20(STR("#9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); - CHECK(etl::u8string<20>(STR("#################127")) == etl::to_string(INT8_MAX, str, format)); - CHECK(etl::u8string<20>(STR("###############32767")) == etl::to_string(INT16_MAX, str, format)); - CHECK(etl::u8string<20>(STR("##########2147483647")) == etl::to_string(INT32_MAX, str, format)); - CHECK(etl::u8string<20>(STR("#9223372036854775807")) == etl::to_string(INT64_MAX, str, format)); + CHECK(String20(STR("#################127")) == etl::to_string(INT8_MAX, str, format)); + CHECK(String20(STR("###############32767")) == etl::to_string(INT16_MAX, str, format)); + CHECK(String20(STR("##########2147483647")) == etl::to_string(INT32_MAX, str, format)); + CHECK(String20(STR("#9223372036854775807")) == etl::to_string(INT64_MAX, str, format)); - CHECK(etl::u8string<20>(STR("################-128")) == etl::to_string(INT8_MIN, str, format)); - CHECK(etl::u8string<20>(STR("##############-32768")) == etl::to_string(INT16_MIN, str, format)); - CHECK(etl::u8string<20>(STR("#########-2147483648")) == etl::to_string(INT32_MIN, str, format)); - CHECK(etl::u8string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); + CHECK(String20(STR("################-128")) == etl::to_string(INT8_MIN, str, format)); + CHECK(String20(STR("##############-32768")) == etl::to_string(INT16_MIN, str, format)); + CHECK(String20(STR("#########-2147483648")) == etl::to_string(INT32_MIN, str, format)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); } //************************************************************************* TEST(test_format_left_justified_no_append) { - etl::u8string<20> str; + String20 str; Format format = Format().base(10).width(20).fill(STR('#')).left(); - CHECK(etl::u8string<20>(STR("0###################")) == etl::to_string(uint8_t(0), str, format)); - CHECK(etl::u8string<20>(STR("0###################")) == etl::to_string(uint16_t(0), str, format)); - CHECK(etl::u8string<20>(STR("0###################")) == etl::to_string(uint32_t(0), str, format)); - CHECK(etl::u8string<20>(STR("0###################")) == etl::to_string(uint64_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint8_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint16_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint32_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint64_t(0), str, format)); - CHECK(etl::u8string<20>(STR("128#################")) == etl::to_string(uint8_t(128), str, format)); - CHECK(etl::u8string<20>(STR("32768###############")) == etl::to_string(uint16_t(32768), str, format)); - CHECK(etl::u8string<20>(STR("2147483648##########")) == etl::to_string(uint32_t(2147483648ul), str, format)); - CHECK(etl::u8string<20>(STR("9223372036854775808#")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); + CHECK(String20(STR("128#################")) == etl::to_string(uint8_t(128), str, format)); + CHECK(String20(STR("32768###############")) == etl::to_string(uint16_t(32768), str, format)); + CHECK(String20(STR("2147483648##########")) == etl::to_string(uint32_t(2147483648ul), str, format)); + CHECK(String20(STR("9223372036854775808#")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); - CHECK(etl::u8string<20>(STR("127#################")) == etl::to_string(INT8_MAX, str, format)); - CHECK(etl::u8string<20>(STR("32767###############")) == etl::to_string(INT16_MAX, str, format)); - CHECK(etl::u8string<20>(STR("2147483647##########")) == etl::to_string(INT32_MAX, str, format)); - CHECK(etl::u8string<20>(STR("9223372036854775807#")) == etl::to_string(INT64_MAX, str, format)); + CHECK(String20(STR("127#################")) == etl::to_string(INT8_MAX, str, format)); + CHECK(String20(STR("32767###############")) == etl::to_string(INT16_MAX, str, format)); + CHECK(String20(STR("2147483647##########")) == etl::to_string(INT32_MAX, str, format)); + CHECK(String20(STR("9223372036854775807#")) == etl::to_string(INT64_MAX, str, format)); - CHECK(etl::u8string<20>(STR("-128################")) == etl::to_string(INT8_MIN, str, format)); - CHECK(etl::u8string<20>(STR("-32768##############")) == etl::to_string(INT16_MIN, str, format)); - CHECK(etl::u8string<20>(STR("-2147483648#########")) == etl::to_string(INT32_MIN, str, format)); - CHECK(etl::u8string<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); + CHECK(String20(STR("-128################")) == etl::to_string(INT8_MIN, str, format)); + CHECK(String20(STR("-32768##############")) == etl::to_string(INT16_MIN, str, format)); + CHECK(String20(STR("-2147483648#########")) == etl::to_string(INT32_MIN, str, format)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); } //************************************************************************* TEST(test_binary_format_no_append) { - etl::u8string<64> str; + String64 str; - CHECK(etl::u8string<64>(STR("00000000")) == etl::to_string(uint8_t(0), str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("0000000000000000")) == etl::to_string(uint16_t(0), str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("00000000000000000000000000000000")) - == etl::to_string(uint32_t(0), str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("0000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("00000000")) == etl::to_string(uint8_t(0), str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("0000000000000000")) == etl::to_string(uint16_t(0), str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("00000000000000000000000000000000")) == etl::to_string(uint32_t(0), str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("0000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(uint64_t(0), str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("10000000")) == etl::to_string(uint8_t(128), str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("1000000000000000")) == etl::to_string(uint16_t(32768), str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("10000000000000000000000000000000")) + CHECK(String64(STR("10000000")) == etl::to_string(uint8_t(128), str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("1000000000000000")) == etl::to_string(uint16_t(32768), str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("10000000000000000000000000000000")) == etl::to_string(uint32_t(2147483648ul), str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("1000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("1000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(uint64_t(9223372036854775808ull), str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("01111111")) == etl::to_string(INT8_MAX, str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("0111111111111111")) == etl::to_string(INT16_MAX, str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("01111111111111111111111111111111")) == etl::to_string(INT32_MAX, str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("0111111111111111111111111111111111111111111111111111111111111111")) + CHECK(String64(STR("01111111")) == etl::to_string(INT8_MAX, str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("0111111111111111")) == etl::to_string(INT16_MAX, str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("01111111111111111111111111111111")) == etl::to_string(INT32_MAX, str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("0111111111111111111111111111111111111111111111111111111111111111")) == etl::to_string(INT64_MAX, str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("10000000")) == etl::to_string(INT8_MIN, str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("1000000000000000")) == etl::to_string(INT16_MIN, str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("10000000000000000000000000000000")) == etl::to_string(INT32_MIN, str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::u8string<64>(STR("1000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("10000000")) == etl::to_string(INT8_MIN, str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("1000000000000000")) == etl::to_string(INT16_MIN, str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("10000000000000000000000000000000")) == etl::to_string(INT32_MIN, str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("1000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(INT64_MIN, str, Format().base(2).width(64).fill(STR('0')))); } @@ -259,328 +261,328 @@ namespace //************************************************************************* TEST(test_floating_point_no_append) { - etl::u8string<20> str; + String20 str; - CHECK(etl::u8string<20>(STR(" 0.000000")) == etl::to_string(0.0, str, Format().precision(6).width(10).right())); - CHECK(etl::u8string<20>(STR("0.000000 ")) == etl::to_string(0.0, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 0.000000")) == etl::to_string(0.0, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("0.000000 ")) == etl::to_string(0.0, str, Format().precision(6).width(10).left())); - CHECK(etl::u8string<20>(STR(" 0.000001")) == etl::to_string(0.000001, str, Format().precision(6).width(10).right())); - CHECK(etl::u8string<20>(STR("0.000001 ")) == etl::to_string(0.000001, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 0.000001")) == etl::to_string(0.000001, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("0.000001 ")) == etl::to_string(0.000001, str, Format().precision(6).width(10).left())); - CHECK(etl::u8string<20>(STR(" 1.000000")) == etl::to_string(1.0, str, Format().precision(6).width(10).right())); - CHECK(etl::u8string<20>(STR("1.000000 ")) == etl::to_string(1.0, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 1.000000")) == etl::to_string(1.0, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("1.000000 ")) == etl::to_string(1.0, str, Format().precision(6).width(10).left())); - CHECK(etl::u8string<20>(STR(" 1.000001")) == etl::to_string(1.000001, str, Format().precision(6).width(10).right())); - CHECK(etl::u8string<20>(STR("1.000001 ")) == etl::to_string(1.000001, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 1.000001")) == etl::to_string(1.000001, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("1.000001 ")) == etl::to_string(1.000001, str, Format().precision(6).width(10).left())); - CHECK(etl::u8string<20>(STR(" 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right())); - CHECK(etl::u8string<20>(STR("12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left())); - CHECK(etl::u8string<20>(STR(" -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right())); - CHECK(etl::u8string<20>(STR("-12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left())); + CHECK(String20(STR(" -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right())); + CHECK(String20(STR("-12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left())); - CHECK(etl::u8string<20>(STR(" -0.123456")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).right())); - CHECK(etl::u8string<20>(STR("-0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" -0.123456")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("-0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left())); } //************************************************************************* TEST(test_floating_point_append) { - etl::u8string<20> str; + String20 str; str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right(), true)); + CHECK(String20(STR("Result 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right(), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result 12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left(), true)); + CHECK(String20(STR("Result 12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left(), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right(), true)); + CHECK(String20(STR("Result -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right(), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result -12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left(), true)); + CHECK(String20(STR("Result -12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left(), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result -0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left(), true)); + CHECK(String20(STR("Result -0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left(), true)); } //************************************************************************* TEST(test_floating_point_rounding) { - etl::u8string<20> str; + String20 str; - CHECK(etl::u8string<20>(STR("0.00001")) == etl::to_string(0.000009, str, Format().precision(5).width(7).right())); - CHECK(etl::u8string<20>(STR("0.0001")) == etl::to_string(0.000099, str, Format().precision(4).width(6).right())); - CHECK(etl::u8string<20>(STR("0.001")) == etl::to_string(0.000999, str, Format().precision(3).width(5).right())); - CHECK(etl::u8string<20>(STR("0.01")) == etl::to_string(0.009999, str, Format().precision(2).width(4).right())); - CHECK(etl::u8string<20>(STR("0.1")) == etl::to_string(0.099999, str, Format().precision(1).width(3).right())); - CHECK(etl::u8string<20>(STR("1.0")) == etl::to_string(0.999999, str, Format().precision(1).width(3).right())); - CHECK(etl::u8string<20>(STR("1")) == etl::to_string(0.999999, str, Format().precision(0).width(1).right())); - CHECK(etl::u8string<20>(STR("2")) == etl::to_string(1.999999, str, Format().precision(0).width(1).right())); - CHECK(etl::u8string<20>(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); - CHECK(etl::u8string<20>(STR("20.0")) == etl::to_string(19.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("0.00001")) == etl::to_string(0.000009, str, Format().precision(5).width(7).right())); + CHECK(String20(STR("0.0001")) == etl::to_string(0.000099, str, Format().precision(4).width(6).right())); + CHECK(String20(STR("0.001")) == etl::to_string(0.000999, str, Format().precision(3).width(5).right())); + CHECK(String20(STR("0.01")) == etl::to_string(0.009999, str, Format().precision(2).width(4).right())); + CHECK(String20(STR("0.1")) == etl::to_string(0.099999, str, Format().precision(1).width(3).right())); + CHECK(String20(STR("1.0")) == etl::to_string(0.999999, str, Format().precision(1).width(3).right())); + CHECK(String20(STR("1")) == etl::to_string(0.999999, str, Format().precision(0).width(1).right())); + CHECK(String20(STR("2")) == etl::to_string(1.999999, str, Format().precision(0).width(1).right())); + CHECK(String20(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("20.0")) == etl::to_string(19.999999, str, Format().precision(1).width(4).right())); } //************************************************************************* TEST(test_bool_no_append) { - etl::u8string<20> str; + String20 str; - CHECK(etl::u8string<20>(STR(" 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false))); - CHECK(etl::u8string<20>(STR(" 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false))); - CHECK(etl::u8string<20>(STR("0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false))); - CHECK(etl::u8string<20>(STR("1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false))); + CHECK(String20(STR(" 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false))); + CHECK(String20(STR(" 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false))); + CHECK(String20(STR("0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false))); + CHECK(String20(STR("1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false))); - CHECK(etl::u8string<20>(STR(" false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true))); - CHECK(etl::u8string<20>(STR(" true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true))); - CHECK(etl::u8string<20>(STR("false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true))); - CHECK(etl::u8string<20>(STR("true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true))); + CHECK(String20(STR(" false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true))); + CHECK(String20(STR(" true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true))); + CHECK(String20(STR("false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true))); + CHECK(String20(STR("true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true))); } //************************************************************************* TEST(test_bool_append) { - etl::u8string<20> str; + String20 str; str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false), true)); + CHECK(String20(STR("Result 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false), true)); + CHECK(String20(STR("Result 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result 0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false), true)); + CHECK(String20(STR("Result 0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result 1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false), true)); + CHECK(String20(STR("Result 1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true), true)); + CHECK(String20(STR("Result false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true), true)); + CHECK(String20(STR("Result true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true), true)); + CHECK(String20(STR("Result false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::u8string<20>(STR("Result true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true), true)); + CHECK(String20(STR("Result true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true), true)); } //************************************************************************* TEST(test_integer_denominator_default_format) { - etl::u8string<20> result; - int value = -1234567; + String20 result; + int value = -1234567; etl::to_string(value, 6U, result); - CHECK(etl::u8string<20>(STR("-1")) == result); + CHECK(String20(STR("-1")) == result); } //************************************************************************* TEST(test_integer_denominator_huge_precision) { - etl::u8string<20> result; - int value = -1234560; + String20 result; + int value = -1234560; Format format = Format().precision(100); etl::to_string(value, 6U, result, format); - CHECK(etl::u8string<20>(STR("-1.234560")) == result); + CHECK(String20(STR("-1.234560")) == result); } //************************************************************************* TEST(test_integer_denominator_huge_precision_64bit) { - etl::u8string<20> result; - int64_t value = INT64_MIN; + String20 result; + int64_t value = INT64_MIN; Format format = Format().precision(100); etl::to_string(value, 12U, result, format); - CHECK(etl::u8string<20>(STR("-9223372.036854775808")) == result); + CHECK(String20(STR("-9223372.036854775808")) == result); } //************************************************************************* TEST(test_integer_denominator_zero_fractional) { - etl::u8string<20> result_i; - int value_i = -1000000; + String20 result_i; + int value_i = -1000000; - etl::u8string<20> result_d; - double value_d = -1.000000; + String20 result_d; + double value_d = -1.000000; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u8string<20>(STR("-1.0000")) == result_i); + CHECK(String20(STR("-1.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_zero_value) { - etl::u8string<20> result_i; - int value_i = 0; + String20 result_i; + int value_i = 0; - etl::u8string<20> result_d; - double value_d = -0.000000; + String20 result_d; + double value_d = -0.000000; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u8string<20>(STR("0.0000")) == result_i); + CHECK(String20(STR("0.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_zero_integral_small_fractional) { - etl::u8string<20> result_i; - int value_i = -400; + String20 result_i; + int value_i = -400; - etl::u8string<20> result_d; - double value_d = -0.000400; + String20 result_d; + double value_d = -0.000400; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u8string<20>(STR("-0.0004")) == result_i); + CHECK(String20(STR("-0.0004")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_small_fractional) { - etl::u8string<20> result_i; - int value_i = -123000400; + String20 result_i; + int value_i = -123000400; - etl::u8string<20> result_d; - double value_d = -123.000400; + String20 result_d; + double value_d = -123.000400; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u8string<20>(STR("-123.0004")) == result_i); + CHECK(String20(STR("-123.0004")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_very_small_fractional) { - etl::u8string<20> result_i; - int value_i = -123000004; + String20 result_i; + int value_i = -123000004; - etl::u8string<20> result_d; - double value_d = -123.000004; + String20 result_d; + double value_d = -123.000004; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u8string<20>(STR("-123.0000")) == result_i); + CHECK(String20(STR("-123.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_very_small_fractional_rounded_up) { - etl::u8string<20> result_i; - int value_i = -123000050; + String20 result_i; + int value_i = -123000050; - etl::u8string<20> result_d; - double value_d = -123.000050; + String20 result_d; + double value_d = -123.000050; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u8string<20>(STR("-123.0001")) == result_i); + CHECK(String20(STR("-123.0001")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_shorter_width) { - etl::u8string<20> result_i; - int value_i = -123456780; + String20 result_i; + int value_i = -123456780; - etl::u8string<20> result_d; - double value_d = -123.456780; + String20 result_d; + double value_d = -123.456780; Format format = Format().precision(4).width(6).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u8string<20>(STR("-123.4568")) == result_i); + CHECK(String20(STR("-123.4568")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_larger_width) { - etl::u8string<20> result_i; - int value_i = -123456780; + String20 result_i; + int value_i = -123456780; - etl::u8string<20> result_d; - double value_d = -123.456780; + String20 result_d; + double value_d = -123.456780; Format format = Format().precision(4).width(15).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u8string<20>(STR(" -123.4568")) == result_i); + CHECK(String20(STR(" -123.4568")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_positive_rollover) { - etl::u8string<20> result_i; - int value_i = 123999990; + String20 result_i; + int value_i = 123999990; - etl::u8string<20> result_d; - double value_d = 123.999990; + String20 result_d; + double value_d = 123.999990; Format format = Format().precision(4).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u8string<20>(STR("124.0000")) == result_i); + CHECK(String20(STR("124.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_negative_rollover) { - etl::u8string<20> result_i; - int value_i = -123999990; + String20 result_i; + int value_i = -123999990; - etl::u8string<20> result_d; - double value_d = -123.999990; + String20 result_d; + double value_d = -123.999990; Format format = Format().precision(4).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::u8string<20>(STR("-124.0000")) == result_i); + CHECK(String20(STR("-124.0000")) == result_i); CHECK(result_d == result_i); } @@ -590,48 +592,48 @@ namespace if (std::numeric_limits::is_iec559) { // Check forced scientific formatting for smaller numbers. - etl::u8string<64> s0; + String64 s0; etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true)); - CHECK(etl::u8string<64>(STR(" 1.00000e+3")) == s0); + CHECK(String64(STR(" 1.00000e+3")) == s0); etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u8string<64>(STR(" 1.00000E+3")) == s0); + CHECK(String64(STR(" 1.00000E+3")) == s0); // Maximum double value is 1.7976931348623157e+308, which rounds to 1.79769e+308 with 5 digits of precision. - etl::u8string<64> s1; + String64 s1; etl::to_string(std::numeric_limits::max(), s1, Format().precision(5).width(15).right()); - CHECK(etl::u8string<64>(STR(" 1.79769e+308")) == s1); + CHECK(String64(STR(" 1.79769e+308")) == s1); // Negative maximum double value is -1.7976931348623157e+308, which rounds to -1.79769e+308 with 5 digits of precision. - etl::u8string<64> s2; + String64 s2; etl::to_string(-std::numeric_limits::max(), s2, Format().precision(5).width(15).right()); - CHECK(etl::u8string<64>(STR(" -1.79769e+308")) == s2); + CHECK(String64(STR(" -1.79769e+308")) == s2); } #if ETL_USING_64BIT_TYPES using workspace_t = etl::private_to_string::workspace_t; // Maximum workspace_t value is 9223372036854775807. - etl::u8string<64> s3; + String64 s3; etl::to_string(std::numeric_limits::max(), s3, Format().precision(5).width(21).right()); - CHECK(etl::u8string<64>(STR(" 9223372036854775807")) == s3); + CHECK(String64(STR(" 9223372036854775807")) == s3); // Minimum workspace_t value is -9223372036854775808. - etl::u8string<64> s4; + String64 s4; etl::to_string(std::numeric_limits::min(), s4, Format().precision(5).width(21).right()); - CHECK(etl::u8string<64>(STR(" -9223372036854775808")) == s4); + CHECK(String64(STR(" -9223372036854775808")) == s4); using uworkspace_t = etl::private_to_string::uworkspace_t; // Maximum uworkspace_t value is 18446744073709551615. - etl::u8string<64> s5; + String64 s5; etl::to_string(std::numeric_limits::max(), s5, Format().precision(5).width(21).right()); - CHECK(etl::u8string<64>(STR(" 18446744073709551615")) == s5); + CHECK(String64(STR(" 18446744073709551615")) == s5); // Minimum uworkspace_t value is 0. - etl::u8string<64> s6; + String64 s6; etl::to_string(std::numeric_limits::min(), s6, Format().precision(5).width(21).right()); - CHECK(etl::u8string<64>(STR(" 0")) == s6); + CHECK(String64(STR(" 0")) == s6); #endif } @@ -640,31 +642,49 @@ namespace { if (std::numeric_limits::is_iec559) { - etl::u8string<64> s1; + String64 s1; etl::to_string(std::numeric_limits::quiet_NaN(), s1, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::u8string<64>(STR(" nan")) == s1); + CHECK(String64(STR(" nan")) == s1); - etl::u8string<64> s2; + String64 s2; etl::to_string(std::numeric_limits::infinity(), s2, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::u8string<64>(STR(" inf")) == s2); + CHECK(String64(STR(" inf")) == s2); - etl::u8string<64> s3; + String64 s3; etl::to_string(-std::numeric_limits::infinity(), s3, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::u8string<64>(STR(" -inf")) == s3); + CHECK(String64(STR(" -inf")) == s3); - etl::u8string<64> s4; + String64 s4; etl::to_string(std::numeric_limits::quiet_NaN(), s4, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u8string<64>(STR(" NAN")) == s4); + CHECK(String64(STR(" NAN")) == s4); - etl::u8string<64> s5; + String64 s5; etl::to_string(std::numeric_limits::infinity(), s5, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u8string<64>(STR(" INF")) == s5); + CHECK(String64(STR(" INF")) == s5); - etl::u8string<64> s6; + String64 s6; etl::to_string(-std::numeric_limits::infinity(), s6, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::u8string<64>(STR(" -INF")) == s6); + CHECK(String64(STR(" -INF")) == s6); } } + + //************************************************************************* + TEST(test_issue_1593_to_string_float_uses_double_math) + { + String64 s1; + + // Float + etl::to_string(3.1415927f, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + + // Double + etl::to_string(3.1415927, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + + // Long double + etl::to_string(3.1415927l, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + } } } // namespace diff --git a/test/test_to_wstring.cpp b/test/test_to_wstring.cpp index feb3b9cd..d2684cb4 100644 --- a/test/test_to_wstring.cpp +++ b/test/test_to_wstring.cpp @@ -44,32 +44,35 @@ namespace { typedef etl::wformat_spec Format; + using String20 = etl::wstring<20>; + using String64 = etl::wstring<64>; + SUITE(test_to_wstring) { //************************************************************************* TEST(test_default_format_no_append) { - etl::wstring<20> str; + String20 str; - CHECK(etl::wstring<20>(STR("0")) == etl::to_string(uint8_t(0), str)); - CHECK(etl::wstring<20>(STR("0")) == etl::to_string(uint16_t(0), str)); - CHECK(etl::wstring<20>(STR("0")) == etl::to_string(uint32_t(0), str)); - CHECK(etl::wstring<20>(STR("0")) == etl::to_string(uint64_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint8_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint16_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint32_t(0), str)); + CHECK(String20(STR("0")) == etl::to_string(uint64_t(0), str)); - CHECK(etl::wstring<20>(STR("128")) == etl::to_string(uint8_t(128), str)); - CHECK(etl::wstring<20>(STR("32768")) == etl::to_string(uint16_t(32768), str)); - CHECK(etl::wstring<20>(STR("2147483648")) == etl::to_string(uint32_t(2147483648ul), str)); - CHECK(etl::wstring<20>(STR("9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str)); + CHECK(String20(STR("128")) == etl::to_string(uint8_t(128), str)); + CHECK(String20(STR("32768")) == etl::to_string(uint16_t(32768), str)); + CHECK(String20(STR("2147483648")) == etl::to_string(uint32_t(2147483648ul), str)); + CHECK(String20(STR("9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str)); - CHECK(etl::wstring<20>(STR("127")) == etl::to_string(INT8_MAX, str)); - CHECK(etl::wstring<20>(STR("32767")) == etl::to_string(INT16_MAX, str)); - CHECK(etl::wstring<20>(STR("2147483647")) == etl::to_string(INT32_MAX, str)); - CHECK(etl::wstring<20>(STR("9223372036854775807")) == etl::to_string(INT64_MAX, str)); + CHECK(String20(STR("127")) == etl::to_string(INT8_MAX, str)); + CHECK(String20(STR("32767")) == etl::to_string(INT16_MAX, str)); + CHECK(String20(STR("2147483647")) == etl::to_string(INT32_MAX, str)); + CHECK(String20(STR("9223372036854775807")) == etl::to_string(INT64_MAX, str)); - CHECK(etl::wstring<20>(STR("-128")) == etl::to_string(INT8_MIN, str)); - CHECK(etl::wstring<20>(STR("-32768")) == etl::to_string(INT16_MIN, str)); - CHECK(etl::wstring<20>(STR("-2147483648")) == etl::to_string(INT32_MIN, str)); - CHECK(etl::wstring<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str)); + CHECK(String20(STR("-128")) == etl::to_string(INT8_MIN, str)); + CHECK(String20(STR("-32768")) == etl::to_string(INT16_MIN, str)); + CHECK(String20(STR("-2147483648")) == etl::to_string(INT32_MIN, str)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str)); } //************************************************************************* @@ -107,87 +110,87 @@ namespace //************************************************************************* TEST(test_format_right_justified_no_append) { - etl::wstring<20> str; + String20 str; Format format = Format().base(10).width(20).fill(STR('#')); - CHECK(etl::wstring<20>(STR("###################0")) == etl::to_string(uint8_t(0), str, format)); - CHECK(etl::wstring<20>(STR("###################0")) == etl::to_string(uint16_t(0), str, format)); - CHECK(etl::wstring<20>(STR("###################0")) == etl::to_string(uint32_t(0), str, format)); - CHECK(etl::wstring<20>(STR("###################0")) == etl::to_string(uint64_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint8_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint16_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint32_t(0), str, format)); + CHECK(String20(STR("###################0")) == etl::to_string(uint64_t(0), str, format)); - CHECK(etl::wstring<20>(STR("#################128")) == etl::to_string(uint8_t(128), str, format)); - CHECK(etl::wstring<20>(STR("###############32768")) == etl::to_string(uint16_t(32768), str, format)); - CHECK(etl::wstring<20>(STR("##########2147483648")) == etl::to_string(uint32_t(2147483648ul), str, format)); - CHECK(etl::wstring<20>(STR("#9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); + CHECK(String20(STR("#################128")) == etl::to_string(uint8_t(128), str, format)); + CHECK(String20(STR("###############32768")) == etl::to_string(uint16_t(32768), str, format)); + CHECK(String20(STR("##########2147483648")) == etl::to_string(uint32_t(2147483648ul), str, format)); + CHECK(String20(STR("#9223372036854775808")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); - CHECK(etl::wstring<20>(STR("#################127")) == etl::to_string(INT8_MAX, str, format)); - CHECK(etl::wstring<20>(STR("###############32767")) == etl::to_string(INT16_MAX, str, format)); - CHECK(etl::wstring<20>(STR("##########2147483647")) == etl::to_string(INT32_MAX, str, format)); - CHECK(etl::wstring<20>(STR("#9223372036854775807")) == etl::to_string(INT64_MAX, str, format)); + CHECK(String20(STR("#################127")) == etl::to_string(INT8_MAX, str, format)); + CHECK(String20(STR("###############32767")) == etl::to_string(INT16_MAX, str, format)); + CHECK(String20(STR("##########2147483647")) == etl::to_string(INT32_MAX, str, format)); + CHECK(String20(STR("#9223372036854775807")) == etl::to_string(INT64_MAX, str, format)); - CHECK(etl::wstring<20>(STR("################-128")) == etl::to_string(INT8_MIN, str, format)); - CHECK(etl::wstring<20>(STR("##############-32768")) == etl::to_string(INT16_MIN, str, format)); - CHECK(etl::wstring<20>(STR("#########-2147483648")) == etl::to_string(INT32_MIN, str, format)); - CHECK(etl::wstring<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); + CHECK(String20(STR("################-128")) == etl::to_string(INT8_MIN, str, format)); + CHECK(String20(STR("##############-32768")) == etl::to_string(INT16_MIN, str, format)); + CHECK(String20(STR("#########-2147483648")) == etl::to_string(INT32_MIN, str, format)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); } //************************************************************************* TEST(test_format_left_justified_no_append) { - etl::wstring<20> str; + String20 str; Format format = Format().base(10).width(20).fill(STR('#')).left(); - CHECK(etl::wstring<20>(STR("0###################")) == etl::to_string(uint8_t(0), str, format)); - CHECK(etl::wstring<20>(STR("0###################")) == etl::to_string(uint16_t(0), str, format)); - CHECK(etl::wstring<20>(STR("0###################")) == etl::to_string(uint32_t(0), str, format)); - CHECK(etl::wstring<20>(STR("0###################")) == etl::to_string(uint64_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint8_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint16_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint32_t(0), str, format)); + CHECK(String20(STR("0###################")) == etl::to_string(uint64_t(0), str, format)); - CHECK(etl::wstring<20>(STR("128#################")) == etl::to_string(uint8_t(128), str, format)); - CHECK(etl::wstring<20>(STR("32768###############")) == etl::to_string(uint16_t(32768), str, format)); - CHECK(etl::wstring<20>(STR("2147483648##########")) == etl::to_string(uint32_t(2147483648ul), str, format)); - CHECK(etl::wstring<20>(STR("9223372036854775808#")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); + CHECK(String20(STR("128#################")) == etl::to_string(uint8_t(128), str, format)); + CHECK(String20(STR("32768###############")) == etl::to_string(uint16_t(32768), str, format)); + CHECK(String20(STR("2147483648##########")) == etl::to_string(uint32_t(2147483648ul), str, format)); + CHECK(String20(STR("9223372036854775808#")) == etl::to_string(uint64_t(9223372036854775808ull), str, format)); - CHECK(etl::wstring<20>(STR("127#################")) == etl::to_string(INT8_MAX, str, format)); - CHECK(etl::wstring<20>(STR("32767###############")) == etl::to_string(INT16_MAX, str, format)); - CHECK(etl::wstring<20>(STR("2147483647##########")) == etl::to_string(INT32_MAX, str, format)); - CHECK(etl::wstring<20>(STR("9223372036854775807#")) == etl::to_string(INT64_MAX, str, format)); + CHECK(String20(STR("127#################")) == etl::to_string(INT8_MAX, str, format)); + CHECK(String20(STR("32767###############")) == etl::to_string(INT16_MAX, str, format)); + CHECK(String20(STR("2147483647##########")) == etl::to_string(INT32_MAX, str, format)); + CHECK(String20(STR("9223372036854775807#")) == etl::to_string(INT64_MAX, str, format)); - CHECK(etl::wstring<20>(STR("-128################")) == etl::to_string(INT8_MIN, str, format)); - CHECK(etl::wstring<20>(STR("-32768##############")) == etl::to_string(INT16_MIN, str, format)); - CHECK(etl::wstring<20>(STR("-2147483648#########")) == etl::to_string(INT32_MIN, str, format)); - CHECK(etl::wstring<20>(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); + CHECK(String20(STR("-128################")) == etl::to_string(INT8_MIN, str, format)); + CHECK(String20(STR("-32768##############")) == etl::to_string(INT16_MIN, str, format)); + CHECK(String20(STR("-2147483648#########")) == etl::to_string(INT32_MIN, str, format)); + CHECK(String20(STR("-9223372036854775808")) == etl::to_string(INT64_MIN, str, format)); } //************************************************************************* TEST(test_binary_format_no_append) { - etl::wstring<64> str; + String64 str; - CHECK(etl::wstring<64>(STR("00000000")) == etl::to_string(uint8_t(0), str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("0000000000000000")) == etl::to_string(uint16_t(0), str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("00000000000000000000000000000000")) == etl::to_string(uint32_t(0), str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("0000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("00000000")) == etl::to_string(uint8_t(0), str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("0000000000000000")) == etl::to_string(uint16_t(0), str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("00000000000000000000000000000000")) == etl::to_string(uint32_t(0), str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("0000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(uint64_t(0), str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("10000000")) == etl::to_string(uint8_t(128), str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("1000000000000000")) == etl::to_string(uint16_t(32768), str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("10000000000000000000000000000000")) + CHECK(String64(STR("10000000")) == etl::to_string(uint8_t(128), str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("1000000000000000")) == etl::to_string(uint16_t(32768), str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("10000000000000000000000000000000")) == etl::to_string(uint32_t(2147483648ul), str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("1000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("1000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(uint64_t(9223372036854775808ull), str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("01111111")) == etl::to_string(INT8_MAX, str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("0111111111111111")) == etl::to_string(INT16_MAX, str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("01111111111111111111111111111111")) == etl::to_string(INT32_MAX, str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("0111111111111111111111111111111111111111111111111111111111111111")) + CHECK(String64(STR("01111111")) == etl::to_string(INT8_MAX, str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("0111111111111111")) == etl::to_string(INT16_MAX, str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("01111111111111111111111111111111")) == etl::to_string(INT32_MAX, str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("0111111111111111111111111111111111111111111111111111111111111111")) == etl::to_string(INT64_MAX, str, Format().base(2).width(64).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("10000000")) == etl::to_string(INT8_MIN, str, Format().base(2).width(8).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("1000000000000000")) == etl::to_string(INT16_MIN, str, Format().base(2).width(16).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("10000000000000000000000000000000")) == etl::to_string(INT32_MIN, str, Format().base(2).width(32).fill(STR('0')))); - CHECK(etl::wstring<64>(STR("1000000000000000000000000000000000000000000000000000000000000000")) + CHECK(String64(STR("10000000")) == etl::to_string(INT8_MIN, str, Format().base(2).width(8).fill(STR('0')))); + CHECK(String64(STR("1000000000000000")) == etl::to_string(INT16_MIN, str, Format().base(2).width(16).fill(STR('0')))); + CHECK(String64(STR("10000000000000000000000000000000")) == etl::to_string(INT32_MIN, str, Format().base(2).width(32).fill(STR('0')))); + CHECK(String64(STR("1000000000000000000000000000000000000000000000000000000000000000")) == etl::to_string(INT64_MIN, str, Format().base(2).width(64).fill(STR('0')))); } @@ -259,119 +262,119 @@ namespace //************************************************************************* TEST(test_floating_point_no_append) { - etl::wstring<20> str; + String20 str; - CHECK(etl::wstring<20>(STR(" 0.000000")) == etl::to_string(0.0, str, Format().precision(6).width(10).right())); - CHECK(etl::wstring<20>(STR("0.000000 ")) == etl::to_string(0.0, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 0.000000")) == etl::to_string(0.0, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("0.000000 ")) == etl::to_string(0.0, str, Format().precision(6).width(10).left())); - CHECK(etl::wstring<20>(STR(" 0.000001")) == etl::to_string(0.000001, str, Format().precision(6).width(10).right())); - CHECK(etl::wstring<20>(STR("0.000001 ")) == etl::to_string(0.000001, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 0.000001")) == etl::to_string(0.000001, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("0.000001 ")) == etl::to_string(0.000001, str, Format().precision(6).width(10).left())); - CHECK(etl::wstring<20>(STR(" 1.000000")) == etl::to_string(1.0, str, Format().precision(6).width(10).right())); - CHECK(etl::wstring<20>(STR("1.000000 ")) == etl::to_string(1.0, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 1.000000")) == etl::to_string(1.0, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("1.000000 ")) == etl::to_string(1.0, str, Format().precision(6).width(10).left())); - CHECK(etl::wstring<20>(STR(" 1.000001")) == etl::to_string(1.000001, str, Format().precision(6).width(10).right())); - CHECK(etl::wstring<20>(STR("1.000001 ")) == etl::to_string(1.000001, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 1.000001")) == etl::to_string(1.000001, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("1.000001 ")) == etl::to_string(1.000001, str, Format().precision(6).width(10).left())); - CHECK(etl::wstring<20>(STR(" 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right())); - CHECK(etl::wstring<20>(STR("12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left())); - CHECK(etl::wstring<20>(STR(" -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right())); - CHECK(etl::wstring<20>(STR("-12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left())); + CHECK(String20(STR(" -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right())); + CHECK(String20(STR("-12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left())); - CHECK(etl::wstring<20>(STR(" -0.123456")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).right())); - CHECK(etl::wstring<20>(STR("-0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left())); + CHECK(String20(STR(" -0.123456")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).right())); + CHECK(String20(STR("-0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left())); } //************************************************************************* TEST(test_floating_point_append) { - etl::wstring<20> str; + String20 str; str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right(), true)); + CHECK(String20(STR("Result 12.345678")) == etl::to_string(12.345678, str, Format().precision(6).width(10).right(), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result 12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left(), true)); + CHECK(String20(STR("Result 12.345678 ")) == etl::to_string(12.345678, str, Format().precision(6).width(10).left(), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right(), true)); + CHECK(String20(STR("Result -12.345678")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).right(), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result -12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left(), true)); + CHECK(String20(STR("Result -12.345678 ")) == etl::to_string(-12.345678, str, Format().precision(6).width(11).left(), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result -0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left(), true)); + CHECK(String20(STR("Result -0.123456 ")) == etl::to_string(-0.123456, str, Format().precision(6).width(10).left(), true)); } //************************************************************************* TEST(test_floating_point_rounding) { - etl::wstring<20> str; + String20 str; - CHECK(etl::wstring<20>(STR("0.00001")) == etl::to_string(0.000009, str, Format().precision(5).width(7).right())); - CHECK(etl::wstring<20>(STR("0.0001")) == etl::to_string(0.000099, str, Format().precision(4).width(6).right())); - CHECK(etl::wstring<20>(STR("0.001")) == etl::to_string(0.000999, str, Format().precision(3).width(5).right())); - CHECK(etl::wstring<20>(STR("0.01")) == etl::to_string(0.009999, str, Format().precision(2).width(4).right())); - CHECK(etl::wstring<20>(STR("0.1")) == etl::to_string(0.099999, str, Format().precision(1).width(3).right())); - CHECK(etl::wstring<20>(STR("1.0")) == etl::to_string(0.999999, str, Format().precision(1).width(3).right())); - CHECK(etl::wstring<20>(STR("1")) == etl::to_string(0.999999, str, Format().precision(0).width(1).right())); - CHECK(etl::wstring<20>(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); - CHECK(etl::wstring<20>(STR("2")) == etl::to_string(1.999999, str, Format().precision(0).width(1).right())); - CHECK(etl::wstring<20>(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); - CHECK(etl::wstring<20>(STR("20.0")) == etl::to_string(19.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("0.00001")) == etl::to_string(0.000009, str, Format().precision(5).width(7).right())); + CHECK(String20(STR("0.0001")) == etl::to_string(0.000099, str, Format().precision(4).width(6).right())); + CHECK(String20(STR("0.001")) == etl::to_string(0.000999, str, Format().precision(3).width(5).right())); + CHECK(String20(STR("0.01")) == etl::to_string(0.009999, str, Format().precision(2).width(4).right())); + CHECK(String20(STR("0.1")) == etl::to_string(0.099999, str, Format().precision(1).width(3).right())); + CHECK(String20(STR("1.0")) == etl::to_string(0.999999, str, Format().precision(1).width(3).right())); + CHECK(String20(STR("1")) == etl::to_string(0.999999, str, Format().precision(0).width(1).right())); + CHECK(String20(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("2")) == etl::to_string(1.999999, str, Format().precision(0).width(1).right())); + CHECK(String20(STR("10.0")) == etl::to_string(9.999999, str, Format().precision(1).width(4).right())); + CHECK(String20(STR("20.0")) == etl::to_string(19.999999, str, Format().precision(1).width(4).right())); } //************************************************************************* TEST(test_bool_no_append) { - etl::wstring<20> str; + String20 str; - CHECK(etl::wstring<20>(STR(" 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false))); - CHECK(etl::wstring<20>(STR(" 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false))); - CHECK(etl::wstring<20>(STR("0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false))); - CHECK(etl::wstring<20>(STR("1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false))); + CHECK(String20(STR(" 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false))); + CHECK(String20(STR(" 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false))); + CHECK(String20(STR("0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false))); + CHECK(String20(STR("1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false))); - CHECK(etl::wstring<20>(STR(" false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true))); - CHECK(etl::wstring<20>(STR(" true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true))); - CHECK(etl::wstring<20>(STR("false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true))); - CHECK(etl::wstring<20>(STR("true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true))); + CHECK(String20(STR(" false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true))); + CHECK(String20(STR(" true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true))); + CHECK(String20(STR("false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true))); + CHECK(String20(STR("true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true))); } //************************************************************************* TEST(test_bool_append) { - etl::wstring<20> str; + String20 str; str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false), true)); + CHECK(String20(STR("Result 0")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false), true)); + CHECK(String20(STR("Result 1")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result 0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false), true)); + CHECK(String20(STR("Result 0 ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result 1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false), true)); + CHECK(String20(STR("Result 1 ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(false), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true), true)); + CHECK(String20(STR("Result false")) == to_string(false, str, Format().precision(6).width(10).right().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true), true)); + CHECK(String20(STR("Result true")) == to_string(true, str, Format().precision(6).width(10).right().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true), true)); + CHECK(String20(STR("Result false ")) == to_string(false, str, Format().precision(6).width(10).left().boolalpha(true), true)); str.assign(STR("Result ")); - CHECK(etl::wstring<20>(STR("Result true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true), true)); + CHECK(String20(STR("Result true ")) == to_string(true, str, Format().precision(6).width(10).left().boolalpha(true), true)); } //************************************************************************* TEST(test_pointer_no_append) { - etl::wstring<20> str; + String20 str; static const volatile int cvi = 0; @@ -379,8 +382,8 @@ namespace oss.width(10); oss.fill(STR('0')); oss << std::hex << std::right << uintptr_t(&cvi); - std::wstring temp(oss.str()); - etl::wstring<20> compare(temp.begin(), temp.end()); + std::wstring temp(oss.str()); + String20 compare(temp.begin(), temp.end()); to_string(&cvi, str, Format().hex().width(10).right().fill(STR('0'))); CHECK(compare == str); @@ -400,7 +403,7 @@ namespace //************************************************************************* TEST(test_pointer_append) { - etl::wstring<20> str; + String20 str; static const volatile int cvi = 0; @@ -410,7 +413,7 @@ namespace oss << std::hex << std::right << uintptr_t(&cvi); std::wstring temp(STR("Result ")); temp.append(oss.str()); - etl::wstring<20> compare(temp.begin(), temp.end()); + String20 compare(temp.begin(), temp.end()); str.assign(STR("Result ")); to_string(&cvi, str, Format().hex().width(10).right().fill(STR('0')), true); @@ -433,217 +436,217 @@ namespace //************************************************************************* TEST(test_integer_denominator_default_format) { - etl::wstring<20> result; - int value = -1234567; + String20 result; + int value = -1234567; etl::to_string(value, 6U, result); - CHECK(etl::wstring<20>(STR("-1")) == result); + CHECK(String20(STR("-1")) == result); } //************************************************************************* TEST(test_integer_denominator_huge_precision) { - etl::wstring<20> result; - int value = -1234560; + String20 result; + int value = -1234560; Format format = Format().precision(100); etl::to_string(value, 6U, result, format); - CHECK(etl::wstring<20>(STR("-1.234560")) == result); + CHECK(String20(STR("-1.234560")) == result); } //************************************************************************* TEST(test_integer_denominator_huge_precision_64bit) { - etl::wstring<20> result; - int64_t value = INT64_MIN; + String20 result; + int64_t value = INT64_MIN; Format format = Format().precision(100); etl::to_string(value, 12U, result, format); - CHECK(etl::wstring<20>(STR("-9223372.036854775808")) == result); + CHECK(String20(STR("-9223372.036854775808")) == result); } //************************************************************************* TEST(test_integer_denominator_zero_fractional) { - etl::wstring<20> result_i; - int value_i = -1000000; + String20 result_i; + int value_i = -1000000; - etl::wstring<20> result_d; - double value_d = -1.000000; + String20 result_d; + double value_d = -1.000000; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::wstring<20>(STR("-1.0000")) == result_i); + CHECK(String20(STR("-1.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_zero_value) { - etl::wstring<20> result_i; - int value_i = 0; + String20 result_i; + int value_i = 0; - etl::wstring<20> result_d; - double value_d = -0.000000; + String20 result_d; + double value_d = -0.000000; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::wstring<20>(STR("0.0000")) == result_i); + CHECK(String20(STR("0.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_zero_integral_small_fractional) { - etl::wstring<20> result_i; - int value_i = -400; + String20 result_i; + int value_i = -400; - etl::wstring<20> result_d; - double value_d = -0.000400; + String20 result_d; + double value_d = -0.000400; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::wstring<20>(STR("-0.0004")) == result_i); + CHECK(String20(STR("-0.0004")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_small_fractional) { - etl::wstring<20> result_i; - int value_i = -123000400; + String20 result_i; + int value_i = -123000400; - etl::wstring<20> result_d; - double value_d = -123.000400; + String20 result_d; + double value_d = -123.000400; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::wstring<20>(STR("-123.0004")) == result_i); + CHECK(String20(STR("-123.0004")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_very_small_fractional) { - etl::wstring<20> result_i; - int value_i = -123000004; + String20 result_i; + int value_i = -123000004; - etl::wstring<20> result_d; - double value_d = -123.000004; + String20 result_d; + double value_d = -123.000004; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::wstring<20>(STR("-123.0000")) == result_i); + CHECK(String20(STR("-123.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_very_small_fractional_rounded_up) { - etl::wstring<20> result_i; - int value_i = -123000050; + String20 result_i; + int value_i = -123000050; - etl::wstring<20> result_d; - double value_d = -123.000050; + String20 result_d; + double value_d = -123.000050; Format format = Format().precision(4); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::wstring<20>(STR("-123.0001")) == result_i); + CHECK(String20(STR("-123.0001")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_shorter_width) { - etl::wstring<20> result_i; - int value_i = -123456780; + String20 result_i; + int value_i = -123456780; - etl::wstring<20> result_d; - double value_d = -123.456780; + String20 result_d; + double value_d = -123.456780; Format format = Format().precision(4).width(6).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::wstring<20>(STR("-123.4568")) == result_i); + CHECK(String20(STR("-123.4568")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_larger_width) { - etl::wstring<20> result_i; - int value_i = -123456780; + String20 result_i; + int value_i = -123456780; - etl::wstring<20> result_d; - double value_d = -123.456780; + String20 result_d; + double value_d = -123.456780; Format format = Format().precision(4).width(15).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::wstring<20>(STR(" -123.4568")) == result_i); + CHECK(String20(STR(" -123.4568")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_positive_rollover) { - etl::wstring<20> result_i; - int value_i = 123999990; + String20 result_i; + int value_i = 123999990; - etl::wstring<20> result_d; - double value_d = 123.999990; + String20 result_d; + double value_d = 123.999990; Format format = Format().precision(4).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::wstring<20>(STR("124.0000")) == result_i); + CHECK(String20(STR("124.0000")) == result_i); CHECK(result_d == result_i); } //************************************************************************* TEST(test_integer_denominator_negative_rollover) { - etl::wstring<20> result_i; - int value_i = -123999990; + String20 result_i; + int value_i = -123999990; - etl::wstring<20> result_d; - double value_d = -123.999990; + String20 result_d; + double value_d = -123.999990; Format format = Format().precision(4).right(); etl::to_string(value_i, 6U, result_i, format); etl::to_string(value_d, result_d, format); - CHECK(etl::wstring<20>(STR("-124.0000")) == result_i); + CHECK(String20(STR("-124.0000")) == result_i); CHECK(result_d == result_i); } @@ -653,48 +656,48 @@ namespace if (std::numeric_limits::is_iec559) { // Check forced scientific formatting for smaller numbers. - etl::wstring<64> s0; + String64 s0; etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true)); - CHECK(etl::wstring<64>(STR(" 1.00000e+3")) == s0); + CHECK(String64(STR(" 1.00000e+3")) == s0); etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::wstring<64>(STR(" 1.00000E+3")) == s0); + CHECK(String64(STR(" 1.00000E+3")) == s0); // Maximum double value is 1.7976931348623157e+308, which rounds to 1.79769e+308 with 5 digits of precision. - etl::wstring<64> s1; + String64 s1; etl::to_string(std::numeric_limits::max(), s1, Format().precision(5).width(15).right()); - CHECK(etl::wstring<64>(STR(" 1.79769e+308")) == s1); + CHECK(String64(STR(" 1.79769e+308")) == s1); // Negative maximum double value is -1.7976931348623157e+308, which rounds to -1.79769e+308 with 5 digits of precision. - etl::wstring<64> s2; + String64 s2; etl::to_string(-std::numeric_limits::max(), s2, Format().precision(5).width(15).right()); - CHECK(etl::wstring<64>(STR(" -1.79769e+308")) == s2); + CHECK(String64(STR(" -1.79769e+308")) == s2); } #if ETL_USING_64BIT_TYPES using workspace_t = etl::private_to_string::workspace_t; // Maximum workspace_t value is 9223372036854775807. - etl::wstring<64> s3; + String64 s3; etl::to_string(std::numeric_limits::max(), s3, Format().precision(5).width(21).right()); - CHECK(etl::wstring<64>(STR(" 9223372036854775807")) == s3); + CHECK(String64(STR(" 9223372036854775807")) == s3); // Minimum workspace_t value is -9223372036854775808. - etl::wstring<64> s4; + String64 s4; etl::to_string(std::numeric_limits::min(), s4, Format().precision(5).width(21).right()); - CHECK(etl::wstring<64>(STR(" -9223372036854775808")) == s4); + CHECK(String64(STR(" -9223372036854775808")) == s4); using uworkspace_t = etl::private_to_string::uworkspace_t; // Maximum uworkspace_t value is 18446744073709551615. - etl::wstring<64> s5; + String64 s5; etl::to_string(std::numeric_limits::max(), s5, Format().precision(5).width(21).right()); - CHECK(etl::wstring<64>(STR(" 18446744073709551615")) == s5); + CHECK(String64(STR(" 18446744073709551615")) == s5); // Minimum uworkspace_t value is 0. - etl::wstring<64> s6; + String64 s6; etl::to_string(std::numeric_limits::min(), s6, Format().precision(5).width(21).right()); - CHECK(etl::wstring<64>(STR(" 0")) == s6); + CHECK(String64(STR(" 0")) == s6); #endif } @@ -703,30 +706,48 @@ namespace { if (std::numeric_limits::is_iec559) { - etl::wstring<64> s1; + String64 s1; etl::to_string(std::numeric_limits::quiet_NaN(), s1, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::wstring<64>(STR(" nan")) == s1); + CHECK(String64(STR(" nan")) == s1); - etl::wstring<64> s2; + String64 s2; etl::to_string(std::numeric_limits::infinity(), s2, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::wstring<64>(STR(" inf")) == s2); + CHECK(String64(STR(" inf")) == s2); - etl::wstring<64> s3; + String64 s3; etl::to_string(-std::numeric_limits::infinity(), s3, Format().precision(5).width(15).right().scientific(true).upper_case(false)); - CHECK(etl::wstring<64>(STR(" -inf")) == s3); + CHECK(String64(STR(" -inf")) == s3); - etl::wstring<64> s4; + String64 s4; etl::to_string(std::numeric_limits::quiet_NaN(), s4, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::wstring<64>(STR(" NAN")) == s4); + CHECK(String64(STR(" NAN")) == s4); - etl::wstring<64> s5; + String64 s5; etl::to_string(std::numeric_limits::infinity(), s5, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::wstring<64>(STR(" INF")) == s5); + CHECK(String64(STR(" INF")) == s5); - etl::wstring<64> s6; + String64 s6; etl::to_string(-std::numeric_limits::infinity(), s6, Format().precision(5).width(15).right().scientific(true).upper_case(true)); - CHECK(etl::wstring<64>(STR(" -INF")) == s6); + CHECK(String64(STR(" -INF")) == s6); } } + + //************************************************************************* + TEST(test_issue_1593_to_string_float_uses_double_math) + { + String64 s1; + + // Float + etl::to_string(3.1415927f, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + + // Double + etl::to_string(3.1415927, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + + // Long double + etl::to_string(3.1415927l, s1, Format().precision(4)); + CHECK_TRUE(String64(STR("3.1416")) == s1); + } } } // namespace diff --git a/test/vs2022/etl.vcxproj b/test/vs2022/etl.vcxproj index 7a69c37e..010846c4 100644 --- a/test/vs2022/etl.vcxproj +++ b/test/vs2022/etl.vcxproj @@ -6304,7 +6304,26 @@ true true - + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true true @@ -11807,4 +11826,4 @@ - + \ No newline at end of file diff --git a/test/vs2022/etl.vcxproj.filters b/test/vs2022/etl.vcxproj.filters index 10afee24..d27cedd4 100644 --- a/test/vs2022/etl.vcxproj.filters +++ b/test/vs2022/etl.vcxproj.filters @@ -4124,4 +4124,4 @@ Resource Files\Images - + \ No newline at end of file