From 2a79845dd5f78043135bbfec3bc09da781d8f636 Mon Sep 17 00:00:00 2001 From: Drew Rife Date: Fri, 6 Feb 2026 04:32:54 -0500 Subject: [PATCH 1/6] Add ref-qualifiers to basic_format_spec (#1292) * Remove AppVeyor build status badge Removed AppVeyor build status badge from README. * Update README.md * Update CONTRIBUTING.md Updated the instructions for contributing. * Fix for issue 1276 "Data corruption in the etl::bip_buffer_spsc_atomic" (#1277) * Reproduce data corruption bug in the `etl::bip_buffer_spsc_atomic`. * Fix data corruption bug in the `etl::bip_buffer_spsc_atomic`. * feat: use ref-qualifiers for basic_format_spec Converted the l-value methods to ref-qualified and also added r-value ref-qualified methods. --------- Co-authored-by: John Wellbelove Co-authored-by: Sergei --- include/etl/basic_format_spec.h | 177 ++++++++++++++++++++++++++------ include/etl/platform.h | 2 + test/test_format_spec.cpp | 87 ++++++++++++++++ 3 files changed, 232 insertions(+), 34 deletions(-) diff --git a/include/etl/basic_format_spec.h b/include/etl/basic_format_spec.h index 36c2d74c..c374e35f 100644 --- a/include/etl/basic_format_spec.h +++ b/include/etl/basic_format_spec.h @@ -36,6 +36,7 @@ SOFTWARE. #include "platform.h" #include "type_traits.h" #include "static_assert.h" +#include "utility.h" namespace etl { @@ -206,7 +207,7 @@ namespace etl //*************************************************************************** /// Default constructor. //*************************************************************************** - ETL_CONSTEXPR basic_format_spec() + ETL_CONSTEXPR basic_format_spec() ETL_NOEXCEPT : base_(10U) , width_(0U) , precision_(0U) @@ -228,7 +229,7 @@ namespace etl bool left_justified__, bool boolalpha__, bool show_base__, - typename TString::value_type fill__) + typename TString::value_type fill__) ETL_NOEXCEPT : base_(base__) , width_(width__) , precision_(precision__) @@ -243,7 +244,7 @@ namespace etl //*************************************************************************** /// Clears the format spec back to default. //*************************************************************************** - ETL_CONSTEXPR14 void clear() + ETL_CONSTEXPR14 void clear() ETL_NOEXCEPT { base_ = 10U; width_ = 0U; @@ -259,56 +260,93 @@ namespace etl /// Sets the base. /// \return A reference to the basic_format_spec. //*************************************************************************** - ETL_CONSTEXPR14 basic_format_spec& base(uint32_t b) + ETL_CONSTEXPR14 basic_format_spec& base(uint32_t b) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { base_ = static_cast(b); return *this; } +#if ETL_USING_CPP11 + /// @overload + ETL_CONSTEXPR14 basic_format_spec&& base(uint32_t b) ETL_RVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + base_ = static_cast(b); + return etl::move(*this); + } +#endif + //*************************************************************************** /// Sets the base to binary. /// \return A reference to the basic_format_spec. //*************************************************************************** - ETL_CONSTEXPR14 basic_format_spec& binary() + ETL_CONSTEXPR14 basic_format_spec& binary() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { - base(2); - return *this; + return base(2); } +#if ETL_USING_CPP11 + /// @overload + ETL_CONSTEXPR14 basic_format_spec&& binary() ETL_RVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + return etl::move(base(2)); + } +#endif + //*************************************************************************** /// Sets the base to octal. /// \return A reference to the basic_format_spec. //*************************************************************************** - ETL_CONSTEXPR14 basic_format_spec& octal() + ETL_CONSTEXPR14 basic_format_spec& octal() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { - base(8); - return *this; + return base(8); } +#if ETL_USING_CPP11 + /// @overload + ETL_CONSTEXPR14 basic_format_spec&& octal() ETL_RVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + return etl::move(base(8)); + } +#endif + //*************************************************************************** /// Sets the base to decimal. /// \return A reference to the basic_format_spec. //*************************************************************************** - ETL_CONSTEXPR14 basic_format_spec& decimal() + ETL_CONSTEXPR14 basic_format_spec& decimal() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { - base(10); - return *this; + return base(10); } +#if ETL_USING_CPP11 + /// @overload + ETL_CONSTEXPR14 basic_format_spec&& decimal() ETL_RVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + return etl::move(base(10)); + } +#endif + //*************************************************************************** /// Sets the base to hex. /// \return A reference to the basic_format_spec. //*************************************************************************** - ETL_CONSTEXPR14 basic_format_spec& hex() + ETL_CONSTEXPR14 basic_format_spec& hex() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { - base(16); - return *this; + return base(16); } +#if ETL_USING_CPP11 + /// @overload + ETL_CONSTEXPR14 basic_format_spec&& hex() ETL_RVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + return etl::move(base(16)); + } +#endif + //*************************************************************************** /// Gets the base. //*************************************************************************** - ETL_CONSTEXPR uint32_t get_base() const + ETL_CONSTEXPR uint32_t get_base() const ETL_NOEXCEPT { return base_; } @@ -317,16 +355,25 @@ namespace etl /// Sets the show base flag. /// \return A reference to the basic_format_spec. //*************************************************************************** - ETL_CONSTEXPR14 basic_format_spec& show_base(bool b) + ETL_CONSTEXPR14 basic_format_spec& show_base(bool b) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { show_base_ = b; return *this; } +#if ETL_USING_CPP11 + /// @overload + ETL_CONSTEXPR14 basic_format_spec&& show_base(bool b) ETL_RVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + show_base_ = b; + return etl::move(*this); + } +#endif + //*************************************************************************** /// Gets the show base flag. //*************************************************************************** - ETL_CONSTEXPR bool is_show_base() const + ETL_CONSTEXPR bool is_show_base() const ETL_NOEXCEPT { return show_base_; } @@ -335,16 +382,25 @@ namespace etl /// Sets the width. /// \return A reference to the basic_format_spec. //*************************************************************************** - ETL_CONSTEXPR14 basic_format_spec& width(uint32_t w) + ETL_CONSTEXPR14 basic_format_spec& width(uint32_t w) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { width_ = static_cast(w); return *this; } +#if ETL_USING_CPP11 + /// @overload + ETL_CONSTEXPR14 basic_format_spec&& width(uint32_t w) ETL_RVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + width_ = static_cast(w); + return etl::move(*this); + } +#endif + //*************************************************************************** /// Gets the width. //*************************************************************************** - ETL_CONSTEXPR uint32_t get_width() const + ETL_CONSTEXPR uint32_t get_width() const ETL_NOEXCEPT { return width_; } @@ -353,16 +409,25 @@ namespace etl /// Sets the precision. /// \return A reference to the basic_format_spec. //*************************************************************************** - ETL_CONSTEXPR14 basic_format_spec& precision(uint32_t p) + ETL_CONSTEXPR14 basic_format_spec& precision(uint32_t p) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { precision_ = static_cast(p); return *this; } +#if ETL_USING_CPP11 + /// @overload + ETL_CONSTEXPR14 basic_format_spec&& precision(uint32_t p) ETL_RVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + precision_ = static_cast(p); + return etl::move(*this); + } +#endif + //*************************************************************************** /// Gets the precision. //*************************************************************************** - ETL_CONSTEXPR uint32_t get_precision() const + ETL_CONSTEXPR uint32_t get_precision() const ETL_NOEXCEPT { return precision_; } @@ -371,16 +436,25 @@ namespace etl /// Sets the upper case flag. /// \return A reference to the basic_format_spec. //*************************************************************************** - ETL_CONSTEXPR14 basic_format_spec& upper_case(bool u) + ETL_CONSTEXPR14 basic_format_spec& upper_case(bool u) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { upper_case_ = u; return *this; } +#if ETL_USING_CPP11 + /// @overload + ETL_CONSTEXPR14 basic_format_spec&& upper_case(bool u) ETL_RVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + upper_case_ = u; + return etl::move(*this); + } +#endif + //*************************************************************************** /// Gets the upper case flag. //*************************************************************************** - ETL_CONSTEXPR bool is_upper_case() const + ETL_CONSTEXPR bool is_upper_case() const ETL_NOEXCEPT { return upper_case_; } @@ -389,16 +463,25 @@ namespace etl /// Sets the fill character. /// \return A reference to the basic_format_spec. //*************************************************************************** - ETL_CONSTEXPR14 basic_format_spec& fill(typename TString::value_type c) + ETL_CONSTEXPR14 basic_format_spec& fill(typename TString::value_type c) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { fill_ = c; return *this; } +#if ETL_USING_CPP11 + /// @overload + ETL_CONSTEXPR14 basic_format_spec&& fill(typename TString::value_type c) ETL_RVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + fill_ = c; + return etl::move(*this); + } +#endif + //*************************************************************************** /// Gets the fill character. //*************************************************************************** - ETL_CONSTEXPR typename TString::value_type get_fill() const + ETL_CONSTEXPR typename TString::value_type get_fill() const ETL_NOEXCEPT { return fill_; } @@ -407,16 +490,25 @@ namespace etl /// Sets the left justify flag. /// \return A reference to the basic_format_spec. //*************************************************************************** - ETL_CONSTEXPR14 basic_format_spec& left() + ETL_CONSTEXPR14 basic_format_spec& left() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { left_justified_ = true; return *this; } +#if ETL_USING_CPP11 + /// @overload + ETL_CONSTEXPR14 basic_format_spec&& left() ETL_RVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + left_justified_ = true; + return etl::move(*this); + } +#endif + //*************************************************************************** /// Gets the left justify flag. //*************************************************************************** - ETL_CONSTEXPR bool is_left() const + ETL_CONSTEXPR bool is_left() const ETL_NOEXCEPT { return left_justified_; } @@ -425,16 +517,25 @@ namespace etl /// Sets the right justify flag. /// \return A reference to the basic_format_spec. //*************************************************************************** - ETL_CONSTEXPR14 basic_format_spec& right() + ETL_CONSTEXPR14 basic_format_spec& right() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { left_justified_ = false; return *this; } +#if ETL_USING_CPP11 + /// @overload + ETL_CONSTEXPR14 basic_format_spec&& right() ETL_RVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + left_justified_ = false; + return etl::move(*this); + } +#endif + //*************************************************************************** /// Gets the right justify flag. //*************************************************************************** - ETL_CONSTEXPR bool is_right() const + ETL_CONSTEXPR bool is_right() const ETL_NOEXCEPT { return !left_justified_; } @@ -443,16 +544,25 @@ namespace etl /// Sets the bool alpha flag. /// \return A reference to the basic_format_spec. //*************************************************************************** - ETL_CONSTEXPR14 basic_format_spec& boolalpha(bool b) + ETL_CONSTEXPR14 basic_format_spec& boolalpha(bool b) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { boolalpha_ = b; return *this; } +#if ETL_USING_CPP11 + /// @overload + ETL_CONSTEXPR14 basic_format_spec&& boolalpha(bool b) ETL_RVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + boolalpha_ = b; + return etl::move(*this); + } +#endif + //*************************************************************************** /// Gets the boolalpha flag. //*************************************************************************** - ETL_CONSTEXPR bool is_boolalpha() const + ETL_CONSTEXPR bool is_boolalpha() const ETL_NOEXCEPT { return boolalpha_; } @@ -481,7 +591,6 @@ namespace etl } private: - uint_least8_t base_; uint_least8_t width_; uint_least8_t precision_; diff --git a/include/etl/platform.h b/include/etl/platform.h index 116daeb4..6db7c305 100644 --- a/include/etl/platform.h +++ b/include/etl/platform.h @@ -368,6 +368,7 @@ SOFTWARE. #define ETL_ENUM_CLASS(name) enum class name #define ETL_ENUM_CLASS_TYPE(name, type) enum class name : type #define ETL_LVALUE_REF_QUALIFIER & + #define ETL_RVALUE_REF_QUALIFIER && #if ETL_USING_EXCEPTIONS #define ETL_NOEXCEPT noexcept #define ETL_NOEXCEPT_EXPR(...) noexcept(__VA_ARGS__) @@ -393,6 +394,7 @@ SOFTWARE. #define ETL_ENUM_CLASS(name) enum name #define ETL_ENUM_CLASS_TYPE(name, type) enum name #define ETL_LVALUE_REF_QUALIFIER + #define ETL_RVALUE_REF_QUALIFIER #endif //************************************* diff --git a/test/test_format_spec.cpp b/test/test_format_spec.cpp index 57eb0841..d242dd79 100644 --- a/test/test_format_spec.cpp +++ b/test/test_format_spec.cpp @@ -91,6 +91,93 @@ namespace CHECK_EQUAL(true, format.is_upper_case()); } + //************************************************************************* +#if ETL_USING_CPP11 + TEST(test_format_rvalue_ref_qualifiers) + { + // Test chaining on temporary (rvalue) + auto format = etl::format_spec() + .base(16) + .boolalpha(true) + .fill('*') + .left() + .precision(3) + .show_base(true) + .upper_case(true) + .width(8); + + CHECK_EQUAL(16, format.get_base()); + CHECK_EQUAL('*', format.get_fill()); + CHECK_EQUAL(3, format.get_precision()); + CHECK_EQUAL(8, format.get_width()); + CHECK_EQUAL(true, format.is_boolalpha()); + CHECK_EQUAL(true, format.is_left()); + CHECK_EQUAL(false, format.is_right()); + CHECK_EQUAL(true, format.is_show_base()); + CHECK_EQUAL(true, format.is_upper_case()); + } + + //************************************************************************* + TEST(test_format_lvalue_ref_qualifiers) + { + // Test chaining on lvalue + etl::format_spec format; + + format.hex().boolalpha(true).fill('#').right().precision(5).show_base(false).upper_case(false).width(12); + + CHECK_EQUAL(16, format.get_base()); + CHECK_EQUAL('#', format.get_fill()); + CHECK_EQUAL(5, format.get_precision()); + CHECK_EQUAL(12, format.get_width()); + CHECK_EQUAL(true, format.is_boolalpha()); + CHECK_EQUAL(false, format.is_left()); + CHECK_EQUAL(true, format.is_right()); + CHECK_EQUAL(false, format.is_show_base()); + CHECK_EQUAL(false, format.is_upper_case()); + } + + //************************************************************************* + TEST(test_format_base_methods) + { + // Test binary + auto format_bin = etl::format_spec().binary().width(8).fill('0'); + CHECK_EQUAL(2, format_bin.get_base()); + CHECK_EQUAL(8, format_bin.get_width()); + CHECK_EQUAL('0', format_bin.get_fill()); + + // Test octal + auto format_oct = etl::format_spec().octal().width(6); + CHECK_EQUAL(8, format_oct.get_base()); + CHECK_EQUAL(6, format_oct.get_width()); + + // Test decimal + auto format_dec = etl::format_spec().decimal().precision(2); + CHECK_EQUAL(10, format_dec.get_base()); + CHECK_EQUAL(2, format_dec.get_precision()); + + // Test hex + auto format_hex = etl::format_spec().hex().upper_case(true); + CHECK_EQUAL(16, format_hex.get_base()); + CHECK_EQUAL(true, format_hex.is_upper_case()); + } + + //************************************************************************* + TEST(test_format_mixed_lvalue_rvalue) + { + // Create as rvalue, then use as lvalue + auto format = etl::format_spec().hex().width(8); + + // Continue chaining on lvalue + format.fill('0').upper_case(true).show_base(true); + + CHECK_EQUAL(16, format.get_base()); + CHECK_EQUAL('0', format.get_fill()); + CHECK_EQUAL(8, format.get_width()); + CHECK_EQUAL(true, format.is_upper_case()); + CHECK_EQUAL(true, format.is_show_base()); + } +#endif + //************************************************************************* #if ETL_USING_CPP14 TEST(test_format_constexpr) From 10fd81c2be8de595bf5ba326359c1c20484f4b64 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Fri, 6 Feb 2026 11:08:17 +0100 Subject: [PATCH 2/6] Add support for size_t and unsigned long to etl::format (#1290) * Remove AppVeyor build status badge Removed AppVeyor build status badge from README. * Update README.md * Update CONTRIBUTING.md Updated the instructions for contributing. * Fix for issue 1276 "Data corruption in the etl::bip_buffer_spsc_atomic" (#1277) * Reproduce data corruption bug in the `etl::bip_buffer_spsc_atomic`. * Fix data corruption bug in the `etl::bip_buffer_spsc_atomic`. * Add support for size_t and unsigned long to etl::format * Document list of supported types in etl::supported_format_types * Add further types and tests for etl::format --------- Co-authored-by: John Wellbelove Co-authored-by: Sergei --- include/etl/format.h | 40 ++++++++++++- test/test_format.cpp | 138 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 175 insertions(+), 3 deletions(-) diff --git a/include/etl/format.h b/include/etl/format.h index 95dafc8d..3268f43b 100644 --- a/include/etl/format.h +++ b/include/etl/format.h @@ -123,7 +123,13 @@ namespace etl template using format_string = basic_format_string...>; - // supported types to format + // Supported types to format + // + // This is the limited number of types as defined in std::basic_format_arg + // https://en.cppreference.com/w/cpp/utility/format/basic_format_arg.html + // + // Further types to be supported are added via converting constructors in + // etl::basic_format_arg using supported_format_types = etl::variant< etl::monostate, bool, @@ -239,6 +245,21 @@ namespace etl { } + basic_format_arg(const short v) + : data(static_cast(v)) + { + } + + basic_format_arg(const unsigned short v) + : data(static_cast(v)) + { + } + + basic_format_arg(const long int v) + : data(static_cast(v)) + { + } + basic_format_arg(const unsigned int v) : data(v) { @@ -254,6 +275,13 @@ namespace etl { } + // Additional type to list of basic types as defined for std::basic_format_arg: + // Mapping unsigned long to unsigned long long int + basic_format_arg(const unsigned long v) + : data(static_cast(v)) + { + } + basic_format_arg(const char* v) : data(v) { @@ -264,6 +292,16 @@ namespace etl { } + basic_format_arg(const signed char v) + : data(static_cast(v)) + { + } + + basic_format_arg(const unsigned char v) + : data(static_cast(v)) + { + } + basic_format_arg(const float v) : data(v) { diff --git a/test/test_format.cpp b/test/test_format.cpp index 2e148bb4..a6c25eab 100644 --- a/test/test_format.cpp +++ b/test/test_format.cpp @@ -86,6 +86,62 @@ namespace CHECK_EQUAL("-1", test_format(s, "{}", -1)); } + //************************************************************************* + TEST(test_format_short) + { + etl::string<100> s; + + CHECK_EQUAL("1", test_format(s, "{}", static_cast(1))); + CHECK_EQUAL("123", test_format(s, "{}", static_cast(123))); + CHECK_EQUAL("4123", test_format(s, "{}", static_cast(4123))); + CHECK_EQUAL("1 2", test_format(s, "{} {}", static_cast(1), static_cast(2))); + CHECK_EQUAL("-123", test_format(s, "{}", static_cast(-123))); + CHECK_EQUAL("0", test_format(s, "{}", static_cast(0))); + CHECK_EQUAL("-1", test_format(s, "{}", static_cast(-1))); + } + + //************************************************************************* + TEST(test_format_unsigned_short) + { + etl::string<100> s; + + CHECK_EQUAL("1", test_format(s, "{}", static_cast(1))); + CHECK_EQUAL("123", test_format(s, "{}", static_cast(123))); + CHECK_EQUAL("4123", test_format(s, "{}", static_cast(4123))); + CHECK_EQUAL("1 2", test_format(s, "{} {}", static_cast(1), static_cast(2))); + CHECK_EQUAL("60123", test_format(s, "{}", static_cast(60123))); + CHECK_EQUAL("0", test_format(s, "{}", static_cast(0))); + CHECK_EQUAL("65500", test_format(s, "{}", static_cast(65500))); + } + + //************************************************************************* + TEST(test_format_long_int) + { + etl::string<100> s; + + CHECK_EQUAL("1", test_format(s, "{}", static_cast(1))); + CHECK_EQUAL("123", test_format(s, "{}", static_cast(123))); + CHECK_EQUAL("4123", test_format(s, "{}", static_cast(4123))); + CHECK_EQUAL("1 2", test_format(s, "{} {}", static_cast(1), static_cast(2))); + CHECK_EQUAL("-123", test_format(s, "{}", static_cast(-123))); + CHECK_EQUAL("0", test_format(s, "{}", static_cast(0))); + CHECK_EQUAL("-1", test_format(s, "{}", static_cast(-1))); + } + + //************************************************************************* + TEST(test_format_unsigned_long_int) + { + etl::string<100> s; + + CHECK_EQUAL("1", test_format(s, "{}", static_cast(1))); + CHECK_EQUAL("123", test_format(s, "{}", static_cast(123))); + CHECK_EQUAL("4123", test_format(s, "{}", static_cast(4123))); + CHECK_EQUAL("1 2", test_format(s, "{} {}", static_cast(1), static_cast(2))); + CHECK_EQUAL("60123", test_format(s, "{}", static_cast(60123))); + CHECK_EQUAL("0", test_format(s, "{}", static_cast(0))); + CHECK_EQUAL("65500", test_format(s, "{}", static_cast(65500))); + } + //************************************************************************* TEST(test_format_unsigned_int) { @@ -131,8 +187,10 @@ namespace { etl::string<100> s; - CHECK_EQUAL("34", test_format(s, "{}", static_cast(34))); - CHECK_EQUAL("-14", test_format(s, "{}", static_cast(-14))); + // mapped to unsigned char + //CHECK_EQUAL("34", test_format(s, "{}", static_cast(34))); + // mapped to signed char + //CHECK_EQUAL("-14", test_format(s, "{}", static_cast(-14))); CHECK_EQUAL("6534", test_format(s, "{}", static_cast(6534))); CHECK_EQUAL("-9414", test_format(s, "{}", static_cast(-9414))); CHECK_EQUAL("236534", test_format(s, "{}", static_cast(236534))); @@ -398,6 +456,82 @@ namespace } } + //************************************************************************* + TEST(test_format_size_t) + { + etl::string<100> s; + + CHECK_EQUAL("0", test_format(s, "{}", static_cast(0LL))); + CHECK_EQUAL("1", test_format(s, "{}", static_cast(1LL))); + CHECK_EQUAL("12345678", test_format(s, "{}", static_cast(12345678LL))); + CHECK_EQUAL("4123456780", test_format(s, "{}", static_cast(4123456780LL))); +#if ETL_PLATFORM_64BIT + static_assert(sizeof(size_t) == 8, "size_t is expected to be 64 bit on 64 bit platforms"); + CHECK_EQUAL("18446744073709551615", test_format(s, "{}", static_cast(18446744073709551615ULL))); + CHECK_EQUAL("1311768467463790320", test_format(s, "{}", static_cast(0x123456789ABCDEF0ULL))); +#endif + } + + //************************************************************************* + TEST(test_format_unsigned_long) + { + etl::string<100> s; + + CHECK_EQUAL("0", test_format(s, "{}", static_cast(0LL))); + CHECK_EQUAL("1", test_format(s, "{}", static_cast(1LL))); + CHECK_EQUAL("12345678", test_format(s, "{}", static_cast(12345678LL))); + CHECK_EQUAL("4123456780", test_format(s, "{}", static_cast(4123456780LL))); +#if ETL_PLATFORM_64BIT + static_assert(sizeof(unsigned long) == 8, "size_t is expected to be 64 bit on 64 bit platforms"); + CHECK_EQUAL("18446744073709551615", test_format(s, "{}", static_cast(18446744073709551615ULL))); + CHECK_EQUAL("1311768467463790320", test_format(s, "{}", static_cast(0x123456789ABCDEF0ULL))); +#endif + } + + //************************************************************************* + TEST(test_format_signed_char) + { + etl::string<100> s; + + CHECK_EQUAL("a s b", test_format(s, "a {} b", static_cast('s'))); + CHECK_EQUAL("a s b", test_format(s, "a {:c} b", static_cast('s'))); + CHECK_EQUAL("a 's' b", test_format(s, "a {:?} b", static_cast('s'))); + CHECK_EQUAL("a \t b", test_format(s, "a {} b", static_cast('\t'))); + CHECK_EQUAL("a '\\t' b", test_format(s, "a {:?} b", static_cast('\t'))); + CHECK_EQUAL("a '\\n' b", test_format(s, "a {:?} b", static_cast('\n'))); + CHECK_EQUAL("a '\\r' b", test_format(s, "a {:?} b", static_cast('\r'))); + CHECK_EQUAL("a '\\\"' b", test_format(s, "a {:?} b", static_cast('"'))); + CHECK_EQUAL("a '\\'' b", test_format(s, "a {:?} b", static_cast('\''))); + CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast('\\'))); + CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast('\\'))); + CHECK_EQUAL("a 97 b", test_format(s, "a {:d} b", static_cast('a'))); + CHECK_EQUAL("a 61 b", test_format(s, "a {:X} b", static_cast('a'))); + CHECK_EQUAL("a 61 b", test_format(s, "a {:x} b", static_cast('a'))); + CHECK_EQUAL("a 0x61 b", test_format(s, "a {:#x} b", static_cast('a'))); + } + + //************************************************************************* + TEST(test_format_unsigned_char) + { + etl::string<100> s; + + CHECK_EQUAL("a s b", test_format(s, "a {} b", static_cast('s'))); + CHECK_EQUAL("a s b", test_format(s, "a {:c} b", static_cast('s'))); + CHECK_EQUAL("a 's' b", test_format(s, "a {:?} b", static_cast('s'))); + CHECK_EQUAL("a \t b", test_format(s, "a {} b", static_cast('\t'))); + CHECK_EQUAL("a '\\t' b", test_format(s, "a {:?} b", static_cast('\t'))); + CHECK_EQUAL("a '\\n' b", test_format(s, "a {:?} b", static_cast('\n'))); + CHECK_EQUAL("a '\\r' b", test_format(s, "a {:?} b", static_cast('\r'))); + CHECK_EQUAL("a '\\\"' b", test_format(s, "a {:?} b", static_cast('"'))); + CHECK_EQUAL("a '\\'' b", test_format(s, "a {:?} b", static_cast('\''))); + CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast('\\'))); + CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast('\\'))); + CHECK_EQUAL("a 97 b", test_format(s, "a {:d} b", static_cast('a'))); + CHECK_EQUAL("a 61 b", test_format(s, "a {:X} b", static_cast('a'))); + CHECK_EQUAL("a 61 b", test_format(s, "a {:x} b", static_cast('a'))); + CHECK_EQUAL("a 0x61 b", test_format(s, "a {:#x} b", static_cast('a'))); + } + //************************************************************************* TEST(test_format_limit) { From 45b352a059fdc9f8f43879f35ff6f5ab265ea4cd Mon Sep 17 00:00:00 2001 From: Marco Nilsson Date: Mon, 9 Feb 2026 12:01:20 +0100 Subject: [PATCH 3/6] Deduce underlying storage size when constructing string_ext from char[]. (#1269) * Deduce underlying storage size when constructing string_ext from char[]. This removes the need for passing sizeof(storage) to the constructor. * Add array constructors for the other string types. - u16string_ext - u32string_ext - u8string_ext - wstring_ext * Add additional constructors to match existing API. * Fix inconsistent test argument order. --------- Co-authored-by: John Wellbelove --- include/etl/string.h | 188 ++++++++++++++++++- include/etl/u16string.h | 188 ++++++++++++++++++- include/etl/u32string.h | 188 ++++++++++++++++++- include/etl/u8string.h | 188 ++++++++++++++++++- include/etl/wstring.h | 188 ++++++++++++++++++- test/test_string_char_external_buffer.cpp | 122 ++++++++++++ test/test_string_u16_external_buffer.cpp | 122 ++++++++++++ test/test_string_u32_external_buffer.cpp | 122 ++++++++++++ test/test_string_u8_external_buffer.cpp | 122 ++++++++++++ test/test_string_wchar_t_external_buffer.cpp | 122 ++++++++++++ 10 files changed, 1545 insertions(+), 5 deletions(-) diff --git a/include/etl/string.h b/include/etl/string.h index 9f462fdb..6fa1fccf 100644 --- a/include/etl/string.h +++ b/include/etl/string.h @@ -305,6 +305,17 @@ namespace etl this->initialise(); } + //************************************************************************* + /// Constructor, from array buffer. + ///\param buffer The array buffer. + //************************************************************************* + template + string_ext(value_type (&buffer)[Size]) + : istring(buffer, Size - 1U) + { + this->initialise(); + } + //************************************************************************* /// Copy constructor. ///\param other The other string_ext. @@ -341,6 +352,26 @@ namespace etl } } + //************************************************************************* + /// From other istring, from array buffer. + ///\param other The other istring. + ///\param buffer The array buffer. + //************************************************************************* + template + string_ext(const etl::istring& other, value_type (&buffer)[BufferSize]) + : istring(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(other.data())) + { + this->current_size = other.size(); + } + else + { + this->initialise(); + this->assign(other); + } + } + //************************************************************************* /// From other string_ext, position, length. ///\param other The other string_ext. @@ -363,6 +394,30 @@ namespace etl } } + //************************************************************************* + /// From other string_ext, position, length, from array buffer. + ///\param other The other string_ext. + ///\param buffer The array buffer. + ///\param position The position of the first character. + ///\param length The number of characters. Default = npos. + //************************************************************************* + template + string_ext(const etl::istring& other, value_type (&buffer)[BufferSize], size_type position, size_type length = npos) + : istring(buffer, BufferSize - 1U) + { + ETL_ASSERT(position < other.size(), ETL_ERROR(string_out_of_bounds)); + + if (this->is_within_buffer(other.data())) + { + this->current_size = other.size(); + } + else + { + this->initialise(); + this->assign(other, position, length); + } + } + //************************************************************************* /// Constructor, from null terminated text. ///\param text The initial text of the string_ext. @@ -383,6 +438,27 @@ namespace etl } } + //************************************************************************* + /// Constructor, from null terminated text, from array buffer. + ///\param text The initial text of the string_ext. + ///\param buffer The array buffer. + //************************************************************************* + template + string_ext(TPointer text, value_type (&buffer)[BufferSize], + typename etl::enable_if::value, int>::type* = ETL_NULLPTR) + : istring(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(text)) + { + this->current_size = etl::strlen(buffer); + } + else + { + this->initialise(); + this->assign(text, text + etl::strlen(text)); + } + } + //************************************************************************* /// Constructor, from null terminated literal text. ///\param text The initial text of the string_ext. @@ -402,6 +478,26 @@ namespace etl } } + //************************************************************************* + /// Constructor, from null terminated literal text, from array buffer. + ///\param literal The initial text of the string_ext. + ///\param buffer The array buffer. + //************************************************************************* + template + string_ext(const value_type (&literal)[LiteralSize], value_type (&buffer)[BufferSize]) + : istring(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(literal)) + { + this->current_size = etl::strlen(literal); + } + else + { + this->initialise(); + this->assign(literal); + } + } + //************************************************************************* /// Constructor, from null terminated text and count. ///\param text The initial text of the string_ext. @@ -421,6 +517,27 @@ namespace etl } } + //************************************************************************* + /// Constructor, from null terminated text and count, from array buffer. + ///\param text The initial text of the string_ext. + ///\param count The number of characters to copy. + ///\param buffer The array buffer. + //************************************************************************* + template + string_ext(const value_type* text, size_type count, value_type (&buffer)[BufferSize]) + : istring(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(text)) + { + this->current_size = count; + } + else + { + this->initialise(); + this->assign(text, text + count); + } + } + //************************************************************************* /// Constructor, from initial size and value. ///\param initialSize The initial size of the string_ext. @@ -433,6 +550,20 @@ namespace etl this->resize(count, c); } + //************************************************************************* + /// Constructor, from initial size and value, from array buffer. + ///\param count The initial size of the string_ext. + ///\param c The value to fill the string_ext with. + ///\param buffer The array buffer. + //************************************************************************* + template + string_ext(size_type count, value_type c, value_type (&buffer)[BufferSize]) + : istring(buffer, BufferSize - 1U) + { + this->initialise(); + this->resize(count, c); + } + //************************************************************************* /// From string_view. ///\param view The string_view. @@ -451,6 +582,26 @@ namespace etl } } + //************************************************************************* + /// From string_view, from array buffer. + ///\param view The string_view. + ///\param buffer The array buffer. + //************************************************************************* + template + explicit string_ext(const etl::string_view& view, value_type (&buffer)[BufferSize]) + : istring(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(view.data())) + { + this->current_size = view.size(); + } + else + { + this->initialise(); + this->assign(view.begin(), view.end()); + } + } + //************************************************************************* /// Constructor, from an iterator range. ///\tparam TIterator The iterator type. @@ -469,7 +620,29 @@ namespace etl { this->initialise(); this->assign(first, last); - } + } + } + + //************************************************************************* + /// Constructor, from an iterator range, from array buffer. + ///\tparam TIterator The iterator type. + ///\param first The iterator to the first element. + ///\param last The iterator to the last element + 1. + ///\param buffer The array buffer. + //************************************************************************* + template + string_ext(TIterator first, TIterator last, value_type (&buffer)[BufferSize], typename etl::enable_if::value, int>::type = 0) + : istring(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(etl::addressof(*first))) + { + this->current_size = etl::distance(first, last); + } + else + { + this->initialise(); + this->assign(first, last); + } } #if ETL_HAS_INITIALIZER_LIST @@ -482,6 +655,19 @@ namespace etl this->initialise(); this->assign(init.begin(), init.end()); } + + //************************************************************************* + /// Construct from initializer_list, from array buffer. + ///\param init The initializer_list. + ///\param buffer The array buffer. + //************************************************************************* + template + string_ext(std::initializer_list init, value_type (&buffer)[BufferSize]) + : istring(buffer, BufferSize - 1U) + { + this->initialise(); + this->assign(init.begin(), init.end()); + } #endif //************************************************************************* diff --git a/include/etl/u16string.h b/include/etl/u16string.h index 74eddeb2..6cc22299 100644 --- a/include/etl/u16string.h +++ b/include/etl/u16string.h @@ -285,6 +285,17 @@ namespace etl this->initialise(); } + //************************************************************************* + /// Constructor, from array buffer. + ///\param buffer The array buffer. + //************************************************************************* + template + u16string_ext(value_type (&buffer)[Size]) + : iu16string(buffer, Size - 1U) + { + this->initialise(); + } + //************************************************************************* /// Copy constructor. ///\param other The other u16string_ext. @@ -321,6 +332,26 @@ namespace etl } } + //************************************************************************* + /// From other iu16string, from array buffer. + ///\param other The other iu16string. + ///\param buffer The array buffer. + //************************************************************************* + template + u16string_ext(const etl::iu16string& other, value_type (&buffer)[BufferSize]) + : iu16string(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(other.data())) + { + this->current_size = other.size(); + } + else + { + this->initialise(); + this->assign(other); + } + } + //************************************************************************* /// From other u16string_ext, position, length. ///\param other The other u16string_ext. @@ -343,6 +374,30 @@ namespace etl } } + //************************************************************************* + /// From other u16string_ext, position, length, from array buffer. + ///\param other The other u16string_ext. + ///\param buffer The array buffer. + ///\param position The position of the first character. + ///\param length The number of characters. Default = npos. + //************************************************************************* + template + u16string_ext(const etl::iu16string& other, value_type (&buffer)[BufferSize], size_type position, size_type length = npos) + : iu16string(buffer, BufferSize - 1U) + { + ETL_ASSERT(position < other.size(), ETL_ERROR(string_out_of_bounds)); + + if (this->is_within_buffer(other.data())) + { + this->current_size = other.size(); + } + else + { + this->initialise(); + this->assign(other, position, length); + } + } + //************************************************************************* /// Constructor, from null terminated text. ///\param text The initial text of the u16string_ext. @@ -363,6 +418,27 @@ namespace etl } } + //************************************************************************* + /// Constructor, from null terminated text, from array buffer. + ///\param text The initial text of the u16string_ext. + ///\param buffer The array buffer. + //************************************************************************* + template + u16string_ext(TPointer text, value_type (&buffer)[BufferSize], + typename etl::enable_if::value, int>::type* = ETL_NULLPTR) + : iu16string(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(text)) + { + this->current_size = etl::strlen(buffer); + } + else + { + this->initialise(); + this->assign(text, text + etl::strlen(text)); + } + } + //************************************************************************* /// Constructor, from null terminated literal text. ///\param text The initial text of the u16string_ext. @@ -382,6 +458,26 @@ namespace etl } } + //************************************************************************* + /// Constructor, from null terminated literal text, from array buffer. + ///\param literal The initial text of the u16string_ext. + ///\param buffer The array buffer. + //************************************************************************* + template + u16string_ext(const value_type (&literal)[LiteralSize], value_type (&buffer)[BufferSize]) + : iu16string(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(literal)) + { + this->current_size = etl::strlen(literal); + } + else + { + this->initialise(); + this->assign(literal); + } + } + //************************************************************************* /// Constructor, from null terminated text and count. ///\param text The initial text of the u16string_ext. @@ -401,6 +497,27 @@ namespace etl } } + //************************************************************************* + /// Constructor, from null terminated text and count, from array buffer. + ///\param text The initial text of the u16string_ext. + ///\param count The number of characters to copy. + ///\param buffer The array buffer. + //************************************************************************* + template + u16string_ext(const value_type* text, size_type count, value_type (&buffer)[BufferSize]) + : iu16string(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(text)) + { + this->current_size = count; + } + else + { + this->initialise(); + this->assign(text, text + count); + } + } + //************************************************************************* /// Constructor, from initial size and value. ///\param initialSize The initial size of the u16string_ext. @@ -413,6 +530,20 @@ namespace etl this->resize(count, c); } + //************************************************************************* + /// Constructor, from initial size and value, from array buffer. + ///\param count The initial size of the u16string_ext. + ///\param c The value to fill the u16string_ext with. + ///\param buffer The array buffer. + //************************************************************************* + template + u16string_ext(size_type count, value_type c, value_type (&buffer)[BufferSize]) + : iu16string(buffer, BufferSize - 1U) + { + this->initialise(); + this->resize(count, c); + } + //************************************************************************* /// From u16string_view. ///\param view The u16string_view. @@ -431,6 +562,26 @@ namespace etl } } + //************************************************************************* + /// From u16string_view, from array buffer. + ///\param view The u16string_view. + ///\param buffer The array buffer. + //************************************************************************* + template + explicit u16string_ext(const etl::u16string_view& view, value_type (&buffer)[BufferSize]) + : iu16string(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(view.data())) + { + this->current_size = view.size(); + } + else + { + this->initialise(); + this->assign(view.begin(), view.end()); + } + } + //************************************************************************* /// Constructor, from an iterator range. ///\tparam TIterator The iterator type. @@ -449,7 +600,29 @@ namespace etl { this->initialise(); this->assign(first, last); - } + } + } + + //************************************************************************* + /// Constructor, from an iterator range, from array buffer. + ///\tparam TIterator The iterator type. + ///\param first The iterator to the first element. + ///\param last The iterator to the last element + 1. + ///\param buffer The array buffer. + //************************************************************************* + template + u16string_ext(TIterator first, TIterator last, value_type (&buffer)[BufferSize], typename etl::enable_if::value, int>::type = 0) + : iu16string(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(reinterpret_cast(etl::addressof(*first)))) + { + this->current_size = etl::distance(first, last); + } + else + { + this->initialise(); + this->assign(first, last); + } } #if ETL_HAS_INITIALIZER_LIST @@ -462,6 +635,19 @@ namespace etl this->initialise(); this->assign(init.begin(), init.end()); } + + //************************************************************************* + /// Construct from initializer_list, from array buffer. + ///\param init The initializer_list. + ///\param buffer The array buffer. + //************************************************************************* + template + u16string_ext(std::initializer_list init, value_type (&buffer)[BufferSize]) + : iu16string(buffer, BufferSize - 1U) + { + this->initialise(); + this->assign(init.begin(), init.end()); + } #endif //************************************************************************* diff --git a/include/etl/u32string.h b/include/etl/u32string.h index 2399b910..c4dc60e5 100644 --- a/include/etl/u32string.h +++ b/include/etl/u32string.h @@ -285,6 +285,17 @@ namespace etl this->initialise(); } + //************************************************************************* + /// Constructor, from array buffer. + ///\param buffer The array buffer. + //************************************************************************* + template + u32string_ext(value_type (&buffer)[Size]) + : iu32string(buffer, Size - 1U) + { + this->initialise(); + } + //************************************************************************* /// Copy constructor. ///\param other The other u32string_ext. @@ -321,6 +332,26 @@ namespace etl } } + //************************************************************************* + /// From other iu32string, from array buffer. + ///\param other The other iu32string. + ///\param buffer The array buffer. + //************************************************************************* + template + u32string_ext(const etl::iu32string& other, value_type (&buffer)[BufferSize]) + : iu32string(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(other.data())) + { + this->current_size = other.size(); + } + else + { + this->initialise(); + this->assign(other); + } + } + //************************************************************************* /// From other u32string_ext, position, length. ///\param other The other u32string_ext. @@ -343,6 +374,30 @@ namespace etl } } + //************************************************************************* + /// From other u32string_ext, position, length, from array buffer. + ///\param other The other u32string_ext. + ///\param buffer The array buffer. + ///\param position The position of the first character. + ///\param length The number of characters. Default = npos. + //************************************************************************* + template + u32string_ext(const etl::iu32string& other, value_type (&buffer)[BufferSize], size_type position, size_type length = npos) + : iu32string(buffer, BufferSize - 1U) + { + ETL_ASSERT(position < other.size(), ETL_ERROR(string_out_of_bounds)); + + if (this->is_within_buffer(other.data())) + { + this->current_size = other.size(); + } + else + { + this->initialise(); + this->assign(other, position, length); + } + } + //************************************************************************* /// Constructor, from null terminated text. ///\param text The initial text of the u32string_ext. @@ -363,6 +418,27 @@ namespace etl } } + //************************************************************************* + /// Constructor, from null terminated text, from array buffer. + ///\param text The initial text of the u32string_ext. + ///\param buffer The array buffer. + //************************************************************************* + template + u32string_ext(TPointer text, value_type (&buffer)[BufferSize], + typename etl::enable_if::value, int>::type* = ETL_NULLPTR) + : iu32string(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(text)) + { + this->current_size = etl::strlen(buffer); + } + else + { + this->initialise(); + this->assign(text, text + etl::strlen(text)); + } + } + //************************************************************************* /// Constructor, from null terminated literal text. ///\param text The initial text of the u32string_ext. @@ -382,6 +458,26 @@ namespace etl } } + //************************************************************************* + /// Constructor, from null terminated literal text, from array buffer. + ///\param literal The initial text of the u32string_ext. + ///\param buffer The array buffer. + //************************************************************************* + template + u32string_ext(const value_type (&literal)[LiteralSize], value_type (&buffer)[BufferSize]) + : iu32string(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(literal)) + { + this->current_size = etl::strlen(literal); + } + else + { + this->initialise(); + this->assign(literal); + } + } + //************************************************************************* /// Constructor, from null terminated text and count. ///\param text The initial text of the u32string_ext. @@ -401,6 +497,27 @@ namespace etl } } + //************************************************************************* + /// Constructor, from null terminated text and count, from array buffer. + ///\param text The initial text of the u32string_ext. + ///\param count The number of characters to copy. + ///\param buffer The array buffer. + //************************************************************************* + template + u32string_ext(const value_type* text, size_type count, value_type (&buffer)[BufferSize]) + : iu32string(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(text)) + { + this->current_size = count; + } + else + { + this->initialise(); + this->assign(text, text + count); + } + } + //************************************************************************* /// Constructor, from initial size and value. ///\param initialSize The initial size of the u32string_ext. @@ -413,6 +530,20 @@ namespace etl this->resize(count, c); } + //************************************************************************* + /// Constructor, from initial size and value, from array buffer. + ///\param count The initial size of the u32string_ext. + ///\param c The value to fill the u32string_ext with. + ///\param buffer The array buffer. + //************************************************************************* + template + u32string_ext(size_type count, value_type c, value_type (&buffer)[BufferSize]) + : iu32string(buffer, BufferSize - 1U) + { + this->initialise(); + this->resize(count, c); + } + //************************************************************************* /// From u32string_view. ///\param view The u32string_view. @@ -431,6 +562,26 @@ namespace etl } } + //************************************************************************* + /// From u32string_view, from array buffer. + ///\param view The u32string_view. + ///\param buffer The array buffer. + //************************************************************************* + template + explicit u32string_ext(const etl::u32string_view& view, value_type (&buffer)[BufferSize]) + : iu32string(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(view.data())) + { + this->current_size = view.size(); + } + else + { + this->initialise(); + this->assign(view.begin(), view.end()); + } + } + //************************************************************************* /// Constructor, from an iterator range. ///\tparam TIterator The iterator type. @@ -449,7 +600,29 @@ namespace etl { this->initialise(); this->assign(first, last); - } + } + } + + //************************************************************************* + /// Constructor, from an iterator range, from array buffer. + ///\tparam TIterator The iterator type. + ///\param first The iterator to the first element. + ///\param last The iterator to the last element + 1. + ///\param buffer The array buffer. + //************************************************************************* + template + u32string_ext(TIterator first, TIterator last, value_type (&buffer)[BufferSize], typename etl::enable_if::value, int>::type = 0) + : iu32string(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(reinterpret_cast(etl::addressof(*first)))) + { + this->current_size = etl::distance(first, last); + } + else + { + this->initialise(); + this->assign(first, last); + } } #if ETL_HAS_INITIALIZER_LIST @@ -462,6 +635,19 @@ namespace etl this->initialise(); this->assign(init.begin(), init.end()); } + + //************************************************************************* + /// Construct from initializer_list, from array buffer. + ///\param init The initializer_list. + ///\param buffer The array buffer. + //************************************************************************* + template + u32string_ext(std::initializer_list init, value_type (&buffer)[BufferSize]) + : iu32string(buffer, BufferSize - 1U) + { + this->initialise(); + this->assign(init.begin(), init.end()); + } #endif //************************************************************************* diff --git a/include/etl/u8string.h b/include/etl/u8string.h index f82bbadd..26199773 100644 --- a/include/etl/u8string.h +++ b/include/etl/u8string.h @@ -305,6 +305,17 @@ namespace etl this->initialise(); } + //************************************************************************* + /// Constructor, from array buffer. + ///\param buffer The array buffer. + //************************************************************************* + template + u8string_ext(value_type (&buffer)[Size]) + : iu8string(buffer, Size - 1U) + { + this->initialise(); + } + //************************************************************************* /// Copy constructor. ///\param other The other u8string_ext. @@ -341,6 +352,26 @@ namespace etl } } + //************************************************************************* + /// From other iu8string, from array buffer. + ///\param other The other iu8string. + ///\param buffer The array buffer. + //************************************************************************* + template + u8string_ext(const etl::iu8string& other, value_type (&buffer)[BufferSize]) + : iu8string(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(other.data())) + { + this->current_size = other.size(); + } + else + { + this->initialise(); + this->assign(other); + } + } + //************************************************************************* /// From other u8string_ext, position, length. ///\param other The other u8string_ext. @@ -363,6 +394,30 @@ namespace etl } } + //************************************************************************* + /// From other u8string_ext, position, length, from array buffer. + ///\param other The other u8string_ext. + ///\param buffer The array buffer. + ///\param position The position of the first character. + ///\param length The number of characters. Default = npos. + //************************************************************************* + template + u8string_ext(const etl::iu8string& other, value_type (&buffer)[BufferSize], size_type position, size_type length = npos) + : iu8string(buffer, BufferSize - 1U) + { + ETL_ASSERT(position < other.size(), ETL_ERROR(string_out_of_bounds)); + + if (this->is_within_buffer(other.data())) + { + this->current_size = other.size(); + } + else + { + this->initialise(); + this->assign(other, position, length); + } + } + //************************************************************************* /// Constructor, from null terminated text. ///\param text The initial text of the u8string_ext. @@ -383,6 +438,27 @@ namespace etl } } + //************************************************************************* + /// Constructor, from null terminated text, from array buffer. + ///\param text The initial text of the u8string_ext. + ///\param buffer The array buffer. + //************************************************************************* + template + u8string_ext(TPointer text, value_type (&buffer)[BufferSize], + typename etl::enable_if::value, int>::type* = ETL_NULLPTR) + : iu8string(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(text)) + { + this->current_size = etl::strlen(buffer); + } + else + { + this->initialise(); + this->assign(text, text + etl::strlen(text)); + } + } + //************************************************************************* /// Constructor, from null terminated literal text. ///\param text The initial text of the u8string_ext. @@ -402,6 +478,26 @@ namespace etl } } + //************************************************************************* + /// Constructor, from null terminated literal text, from array buffer. + ///\param literal The initial text of the u8string_ext. + ///\param buffer The array buffer. + //************************************************************************* + template + u8string_ext(const value_type (&literal)[LiteralSize], value_type (&buffer)[BufferSize]) + : iu8string(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(literal)) + { + this->current_size = etl::strlen(literal); + } + else + { + this->initialise(); + this->assign(literal); + } + } + //************************************************************************* /// Constructor, from null terminated text and count. ///\param text The initial text of the u8string_ext. @@ -421,6 +517,27 @@ namespace etl } } + //************************************************************************* + /// Constructor, from null terminated text and count, from array buffer. + ///\param text The initial text of the u8string_ext. + ///\param count The number of characters to copy. + ///\param buffer The array buffer. + //************************************************************************* + template + u8string_ext(const value_type* text, size_type count, value_type (&buffer)[BufferSize]) + : iu8string(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(text)) + { + this->current_size = count; + } + else + { + this->initialise(); + this->assign(text, text + count); + } + } + //************************************************************************* /// Constructor, from initial size and value. ///\param initialSize The initial size of the u8string_ext. @@ -433,6 +550,20 @@ namespace etl this->resize(count, c); } + //************************************************************************* + /// Constructor, from initial size and value, from array buffer. + ///\param count The initial size of the u8string_ext. + ///\param c The value to fill the u8string_ext with. + ///\param buffer The array buffer. + //************************************************************************* + template + u8string_ext(size_type count, value_type c, value_type (&buffer)[BufferSize]) + : iu8string(buffer, BufferSize - 1U) + { + this->initialise(); + this->resize(count, c); + } + //************************************************************************* /// From u8string_view. ///\param view The u8string_view. @@ -451,6 +582,26 @@ namespace etl } } + //************************************************************************* + /// From u8string_view, from array buffer. + ///\param view The u8string_view. + ///\param buffer The array buffer. + //************************************************************************* + template + explicit u8string_ext(const etl::u8string_view& view, value_type (&buffer)[BufferSize]) + : iu8string(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(view.data())) + { + this->current_size = view.size(); + } + else + { + this->initialise(); + this->assign(view.begin(), view.end()); + } + } + //************************************************************************* /// Constructor, from an iterator range. ///\tparam TIterator The iterator type. @@ -469,7 +620,29 @@ namespace etl { this->initialise(); this->assign(first, last); - } + } + } + + //************************************************************************* + /// Constructor, from an iterator range, from array buffer. + ///\tparam TIterator The iterator type. + ///\param first The iterator to the first element. + ///\param last The iterator to the last element + 1. + ///\param buffer The array buffer. + //************************************************************************* + template + u8string_ext(TIterator first, TIterator last, value_type (&buffer)[BufferSize], typename etl::enable_if::value, int>::type = 0) + : iu8string(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(etl::addressof(*first))) + { + this->current_size = etl::distance(first, last); + } + else + { + this->initialise(); + this->assign(first, last); + } } #if ETL_HAS_INITIALIZER_LIST @@ -482,6 +655,19 @@ namespace etl this->initialise(); this->assign(init.begin(), init.end()); } + + //************************************************************************* + /// Construct from initializer_list, from array buffer. + ///\param init The initializer_list. + ///\param buffer The array buffer. + //************************************************************************* + template + u8string_ext(std::initializer_list init, value_type (&buffer)[BufferSize]) + : iu8string(buffer, BufferSize - 1U) + { + this->initialise(); + this->assign(init.begin(), init.end()); + } #endif //************************************************************************* diff --git a/include/etl/wstring.h b/include/etl/wstring.h index d683a160..91405be7 100644 --- a/include/etl/wstring.h +++ b/include/etl/wstring.h @@ -285,6 +285,17 @@ namespace etl this->initialise(); } + //************************************************************************* + /// Constructor, from array buffer. + ///\param buffer The array buffer. + //************************************************************************* + template + wstring_ext(value_type (&buffer)[Size]) + : iwstring(buffer, Size - 1U) + { + this->initialise(); + } + //************************************************************************* /// Copy constructor. ///\param other The other wstring_ext. @@ -321,6 +332,26 @@ namespace etl } } + //************************************************************************* + /// From other iwstring, from array buffer. + ///\param other The other iwstring. + ///\param buffer The array buffer. + //************************************************************************* + template + wstring_ext(const etl::iwstring& other, value_type (&buffer)[BufferSize]) + : iwstring(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(other.data())) + { + this->current_size = other.size(); + } + else + { + this->initialise(); + this->assign(other); + } + } + //************************************************************************* /// From other wstring_ext, position, length. ///\param other The other wstring_ext. @@ -343,6 +374,30 @@ namespace etl } } + //************************************************************************* + /// From other wstring_ext, position, length, from array buffer. + ///\param other The other wstring_ext. + ///\param buffer The array buffer. + ///\param position The position of the first character. + ///\param length The number of characters. Default = npos. + //************************************************************************* + template + wstring_ext(const etl::iwstring& other, value_type (&buffer)[BufferSize], size_type position, size_type length = npos) + : iwstring(buffer, BufferSize - 1U) + { + ETL_ASSERT(position < other.size(), ETL_ERROR(string_out_of_bounds)); + + if (this->is_within_buffer(other.data())) + { + this->current_size = other.size(); + } + else + { + this->initialise(); + this->assign(other, position, length); + } + } + //************************************************************************* /// Constructor, from null terminated text. ///\param text The initial text of the wstring_ext. @@ -363,6 +418,27 @@ namespace etl } } + //************************************************************************* + /// Constructor, from null terminated text, from array buffer. + ///\param text The initial text of the wstring_ext. + ///\param buffer The array buffer. + //************************************************************************* + template + wstring_ext(TPointer text, value_type (&buffer)[BufferSize], + typename etl::enable_if::value, int>::type* = ETL_NULLPTR) + : iwstring(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(text)) + { + this->current_size = etl::strlen(buffer); + } + else + { + this->initialise(); + this->assign(text, text + etl::strlen(text)); + } + } + //************************************************************************* /// Constructor, from null terminated literal text. ///\param text The initial text of the wstring_ext. @@ -382,6 +458,26 @@ namespace etl } } + //************************************************************************* + /// Constructor, from null terminated literal text, from array buffer. + ///\param literal The initial text of the wstring_ext. + ///\param buffer The array buffer. + //************************************************************************* + template + wstring_ext(const value_type (&literal)[LiteralSize], value_type (&buffer)[BufferSize]) + : iwstring(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(literal)) + { + this->current_size = etl::strlen(literal); + } + else + { + this->initialise(); + this->assign(literal); + } + } + //************************************************************************* /// Constructor, from null terminated text and count. ///\param text The initial text of the wstring_ext. @@ -401,6 +497,27 @@ namespace etl } } + //************************************************************************* + /// Constructor, from null terminated text and count, from array buffer. + ///\param text The initial text of the wstring_ext. + ///\param count The number of characters to copy. + ///\param buffer The array buffer. + //************************************************************************* + template + wstring_ext(const value_type* text, size_type count, value_type (&buffer)[BufferSize]) + : iwstring(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(text)) + { + this->current_size = count; + } + else + { + this->initialise(); + this->assign(text, text + count); + } + } + //************************************************************************* /// Constructor, from initial size and value. ///\param initialSize The initial size of the wstring_ext. @@ -413,6 +530,20 @@ namespace etl this->resize(count, c); } + //************************************************************************* + /// Constructor, from initial size and value, from array buffer. + ///\param count The initial size of the wstring_ext. + ///\param c The value to fill the wstring_ext with. + ///\param buffer The array buffer. + //************************************************************************* + template + wstring_ext(size_type count, value_type c, value_type (&buffer)[BufferSize]) + : iwstring(buffer, BufferSize - 1U) + { + this->initialise(); + this->resize(count, c); + } + //************************************************************************* /// From wstring_view. ///\param view The wstring_view. @@ -431,6 +562,26 @@ namespace etl } } + //************************************************************************* + /// From wstring_view, from array buffer. + ///\param view The wstring_view. + ///\param buffer The array buffer. + //************************************************************************* + template + explicit wstring_ext(const etl::wstring_view& view, value_type (&buffer)[BufferSize]) + : iwstring(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(view.data())) + { + this->current_size = view.size(); + } + else + { + this->initialise(); + this->assign(view.begin(), view.end()); + } + } + //************************************************************************* /// Constructor, from an iterator range. ///\tparam TIterator The iterator type. @@ -449,7 +600,29 @@ namespace etl { this->initialise(); this->assign(first, last); - } + } + } + + //************************************************************************* + /// Constructor, from an iterator range, from array buffer. + ///\tparam TIterator The iterator type. + ///\param first The iterator to the first element. + ///\param last The iterator to the last element + 1. + ///\param buffer The array buffer. + //************************************************************************* + template + wstring_ext(TIterator first, TIterator last, value_type (&buffer)[BufferSize], typename etl::enable_if::value, int>::type = 0) + : iwstring(buffer, BufferSize - 1U) + { + if (this->is_within_buffer(reinterpret_cast(etl::addressof(*first)))) + { + this->current_size = etl::distance(first, last); + } + else + { + this->initialise(); + this->assign(first, last); + } } #if ETL_HAS_INITIALIZER_LIST @@ -462,6 +635,19 @@ namespace etl this->initialise(); this->assign(init.begin(), init.end()); } + + //************************************************************************* + /// Construct from initializer_list, from array buffer. + ///\param init The initializer_list. + ///\param buffer The array buffer. + //************************************************************************* + template + wstring_ext(std::initializer_list init, value_type (&buffer)[BufferSize]) + : iwstring(buffer, BufferSize - 1U) + { + this->initialise(); + this->assign(init.begin(), init.end()); + } #endif //************************************************************************* diff --git a/test/test_string_char_external_buffer.cpp b/test/test_string_char_external_buffer.cpp index 15790f43..9812a654 100644 --- a/test/test_string_char_external_buffer.cpp +++ b/test/test_string_char_external_buffer.cpp @@ -120,6 +120,20 @@ namespace CHECK_FALSE(text.is_truncated()); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_from_array) + { + value_t buffer[SIZE + 1]; + Text text(buffer); + + CHECK_EQUAL(0U, text.size()); + CHECK(text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_EQUAL(SIZE, text.max_size()); + CHECK(text.begin() == text.end()); + CHECK_FALSE(text.is_truncated()); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_default_constructor_use_buffer_and_size) { @@ -173,6 +187,114 @@ namespace CHECK_FALSE(text.is_truncated()); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_array_buffer_text_same_buffer) + { + // Test using the same buffer for source and storage, with size deduced from array + Text text(array_text, array_text); + + CHECK_EQUAL(etl::strlen(array_text), text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(ETL_OR_STD17::size(array_text) - 1, text.capacity()); + CHECK_EQUAL(ETL_OR_STD17::size(array_text) - 1, text.max_size()); + CHECK(text.begin() != text.end()); + CHECK_FALSE(text.is_truncated()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_istring_with_array_buffer) + { + value_t buffer[SIZE + 1]; + TextBuffer source_buffer{0}; + Text source(initial_text.c_str(), source_buffer.data(), source_buffer.size()); + + Text text(source, buffer); + + CHECK_EQUAL(initial_text.size(), text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_EQUAL(SIZE, text.max_size()); + CHECK_FALSE(text.is_truncated()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_text_count_with_array_buffer) + { + value_t buffer[SIZE + 1]; + + Text text(initial_text.c_str(), 5U, buffer); + + CHECK_EQUAL(5U, text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_size_value_with_array_buffer) + { + const size_t INITIAL_SIZE = 5UL; + const value_t INITIAL_VALUE = STR('A'); + value_t buffer[SIZE + 1]; + + TextSTD compare_text(INITIAL_SIZE, INITIAL_VALUE); + Text text(INITIAL_SIZE, INITIAL_VALUE, buffer); + + CHECK_EQUAL(INITIAL_SIZE, text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + + bool is_equal = Equal(compare_text, text); + CHECK(is_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_string_view_with_array_buffer) + { + value_t buffer[SIZE + 1]; + View view(initial_text.c_str()); + + Text text(view, buffer); + + CHECK_EQUAL(view.size(), text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_iterator_range_with_array_buffer) + { + value_t buffer[SIZE + 1]; + TextSTD compare_text(initial_text.begin(), initial_text.end()); + + Text text(initial_text.begin(), initial_text.end(), buffer); + + CHECK_EQUAL(initial_text.size(), text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + + bool is_equal = Equal(compare_text, text); + CHECK(is_equal); + } + +#if ETL_HAS_INITIALIZER_LIST + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_initializer_list_with_array_buffer) + { + value_t buffer[SIZE + 1]; + + Text text({ STR('H'), STR('e'), STR('l'), STR('l'), STR('o') }, buffer); + + CHECK_EQUAL(5U, text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + } +#endif + //************************************************************************* TEST(test_iterator_comparison_empty) { diff --git a/test/test_string_u16_external_buffer.cpp b/test/test_string_u16_external_buffer.cpp index b22f43dd..311e26f6 100644 --- a/test/test_string_u16_external_buffer.cpp +++ b/test/test_string_u16_external_buffer.cpp @@ -134,6 +134,20 @@ namespace CHECK_FALSE(text.is_truncated()); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_from_array) + { + value_t buffer[SIZE + 1]; + Text text(buffer); + + CHECK_EQUAL(0U, text.size()); + CHECK(text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_EQUAL(SIZE, text.max_size()); + CHECK(text.begin() == text.end()); + CHECK_FALSE(text.is_truncated()); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_default_constructor_use_buffer_and_size) { @@ -187,6 +201,114 @@ namespace CHECK_FALSE(text.is_truncated()); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_array_buffer_text_same_buffer) + { + // Test using the same buffer for source and storage, with size deduced from array + Text text(array_text, array_text); + + CHECK_EQUAL(etl::strlen(array_text), text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(ETL_OR_STD17::size(array_text) - 1, text.capacity()); + CHECK_EQUAL(ETL_OR_STD17::size(array_text) - 1, text.max_size()); + CHECK(text.begin() != text.end()); + CHECK_FALSE(text.is_truncated()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_istring_with_array_buffer) + { + value_t buffer[SIZE + 1]; + TextBuffer source_buffer{0}; + Text source(initial_text.c_str(), source_buffer.data(), source_buffer.size()); + + Text text(source, buffer); + + CHECK_EQUAL(initial_text.size(), text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_EQUAL(SIZE, text.max_size()); + CHECK_FALSE(text.is_truncated()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_text_count_with_array_buffer) + { + value_t buffer[SIZE + 1]; + + Text text(initial_text.c_str(), 5U, buffer); + + CHECK_EQUAL(5U, text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_size_value_with_array_buffer) + { + const size_t INITIAL_SIZE = 5UL; + const value_t INITIAL_VALUE = STR('A'); + value_t buffer[SIZE + 1]; + + TextSTD compare_text(INITIAL_SIZE, INITIAL_VALUE); + Text text(INITIAL_SIZE, INITIAL_VALUE, buffer); + + CHECK_EQUAL(INITIAL_SIZE, text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + + bool is_equal = Equal(compare_text, text); + CHECK(is_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_string_view_with_array_buffer) + { + value_t buffer[SIZE + 1]; + View view(initial_text.c_str()); + + Text text(view, buffer); + + CHECK_EQUAL(view.size(), text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_iterator_range_with_array_buffer) + { + value_t buffer[SIZE + 1]; + TextSTD compare_text(initial_text.begin(), initial_text.end()); + + Text text(initial_text.begin(), initial_text.end(), buffer); + + CHECK_EQUAL(initial_text.size(), text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + + bool is_equal = Equal(compare_text, text); + CHECK(is_equal); + } + +#if ETL_HAS_INITIALIZER_LIST + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_initializer_list_with_array_buffer) + { + value_t buffer[SIZE + 1]; + + Text text({ STR('H'), STR('e'), STR('l'), STR('l'), STR('o') }, buffer); + + CHECK_EQUAL(5U, text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + } +#endif + //************************************************************************* TEST(test_iterator_comparison_empty) { diff --git a/test/test_string_u32_external_buffer.cpp b/test/test_string_u32_external_buffer.cpp index c0b8150b..4ba37523 100644 --- a/test/test_string_u32_external_buffer.cpp +++ b/test/test_string_u32_external_buffer.cpp @@ -134,6 +134,20 @@ namespace CHECK_FALSE(text.is_truncated()); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_from_array) + { + value_t buffer[SIZE + 1]; + Text text(buffer); + + CHECK_EQUAL(0U, text.size()); + CHECK(text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_EQUAL(SIZE, text.max_size()); + CHECK(text.begin() == text.end()); + CHECK_FALSE(text.is_truncated()); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_default_constructor_use_buffer_and_size) { @@ -187,6 +201,114 @@ namespace CHECK_FALSE(text.is_truncated()); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_array_buffer_text_same_buffer) + { + // Test using the same buffer for source and storage, with size deduced from array + Text text(array_text, array_text); + + CHECK_EQUAL(etl::strlen(array_text), text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(ETL_OR_STD17::size(array_text) - 1, text.capacity()); + CHECK_EQUAL(ETL_OR_STD17::size(array_text) - 1, text.max_size()); + CHECK(text.begin() != text.end()); + CHECK_FALSE(text.is_truncated()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_istring_with_array_buffer) + { + value_t buffer[SIZE + 1]; + TextBuffer source_buffer{0}; + Text source(initial_text.c_str(), source_buffer.data(), source_buffer.size()); + + Text text(source, buffer); + + CHECK_EQUAL(initial_text.size(), text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_EQUAL(SIZE, text.max_size()); + CHECK_FALSE(text.is_truncated()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_text_count_with_array_buffer) + { + value_t buffer[SIZE + 1]; + + Text text(initial_text.c_str(), 5U, buffer); + + CHECK_EQUAL(5U, text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_size_value_with_array_buffer) + { + const size_t INITIAL_SIZE = 5UL; + const value_t INITIAL_VALUE = STR('A'); + value_t buffer[SIZE + 1]; + + TextSTD compare_text(INITIAL_SIZE, INITIAL_VALUE); + Text text(INITIAL_SIZE, INITIAL_VALUE, buffer); + + CHECK_EQUAL(INITIAL_SIZE, text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + + bool is_equal = Equal(compare_text, text); + CHECK(is_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_string_view_with_array_buffer) + { + value_t buffer[SIZE + 1]; + View view(initial_text.c_str()); + + Text text(view, buffer); + + CHECK_EQUAL(view.size(), text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_iterator_range_with_array_buffer) + { + value_t buffer[SIZE + 1]; + TextSTD compare_text(initial_text.begin(), initial_text.end()); + + Text text(initial_text.begin(), initial_text.end(), buffer); + + CHECK_EQUAL(initial_text.size(), text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + + bool is_equal = Equal(compare_text, text); + CHECK(is_equal); + } + +#if ETL_HAS_INITIALIZER_LIST + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_initializer_list_with_array_buffer) + { + value_t buffer[SIZE + 1]; + + Text text({ STR('H'), STR('e'), STR('l'), STR('l'), STR('o') }, buffer); + + CHECK_EQUAL(5U, text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + } +#endif + //************************************************************************* TEST(test_iterator_comparison_empty) { diff --git a/test/test_string_u8_external_buffer.cpp b/test/test_string_u8_external_buffer.cpp index e161bc13..41ad7fa1 100644 --- a/test/test_string_u8_external_buffer.cpp +++ b/test/test_string_u8_external_buffer.cpp @@ -137,6 +137,20 @@ namespace CHECK_FALSE(text.is_truncated()); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_from_array) + { + value_t buffer[SIZE + 1]; + Text text(buffer); + + CHECK_EQUAL(0U, text.size()); + CHECK(text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_EQUAL(SIZE, text.max_size()); + CHECK(text.begin() == text.end()); + CHECK_FALSE(text.is_truncated()); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_default_constructor_use_buffer_and_size) { @@ -190,6 +204,114 @@ namespace CHECK_FALSE(text.is_truncated()); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_array_buffer_text_same_buffer) + { + // Test using the same buffer for source and storage, with size deduced from array + Text text(array_text, array_text); + + CHECK_EQUAL(etl::strlen(array_text), text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(ETL_OR_STD17::size(array_text) - 1, text.capacity()); + CHECK_EQUAL(ETL_OR_STD17::size(array_text) - 1, text.max_size()); + CHECK(text.begin() != text.end()); + CHECK_FALSE(text.is_truncated()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_istring_with_array_buffer) + { + value_t buffer[SIZE + 1]; + TextBuffer source_buffer{0}; + Text source(initial_text.c_str(), source_buffer.data(), source_buffer.size()); + + Text text(source, buffer); + + CHECK_EQUAL(initial_text.size(), text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_EQUAL(SIZE, text.max_size()); + CHECK_FALSE(text.is_truncated()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_text_count_with_array_buffer) + { + value_t buffer[SIZE + 1]; + + Text text(initial_text.c_str(), 5U, buffer); + + CHECK_EQUAL(5U, text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_size_value_with_array_buffer) + { + const size_t INITIAL_SIZE = 5UL; + const value_t INITIAL_VALUE = STR('A'); + value_t buffer[SIZE + 1]; + + TextSTD compare_text(INITIAL_SIZE, INITIAL_VALUE); + Text text(INITIAL_SIZE, INITIAL_VALUE, buffer); + + CHECK_EQUAL(INITIAL_SIZE, text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + + bool is_equal = Equal(compare_text, text); + CHECK(is_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_string_view_with_array_buffer) + { + value_t buffer[SIZE + 1]; + View view(initial_text.c_str()); + + Text text(view, buffer); + + CHECK_EQUAL(view.size(), text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_iterator_range_with_array_buffer) + { + value_t buffer[SIZE + 1]; + TextSTD compare_text(initial_text.begin(), initial_text.end()); + + Text text(initial_text.begin(), initial_text.end(), buffer); + + CHECK_EQUAL(initial_text.size(), text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + + bool is_equal = Equal(compare_text, text); + CHECK(is_equal); + } + +#if ETL_HAS_INITIALIZER_LIST + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_initializer_list_with_array_buffer) + { + value_t buffer[SIZE + 1]; + + Text text({ STR('H'), STR('e'), STR('l'), STR('l'), STR('o') }, buffer); + + CHECK_EQUAL(5U, text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + } +#endif + //************************************************************************* TEST(test_iterator_comparison_empty) { diff --git a/test/test_string_wchar_t_external_buffer.cpp b/test/test_string_wchar_t_external_buffer.cpp index 7bc6ca8a..e57b8764 100644 --- a/test/test_string_wchar_t_external_buffer.cpp +++ b/test/test_string_wchar_t_external_buffer.cpp @@ -137,6 +137,20 @@ namespace CHECK_FALSE(text.is_truncated()); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_from_array) + { + value_t buffer[SIZE + 1]; + Text text(buffer); + + CHECK_EQUAL(0U, text.size()); + CHECK(text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_EQUAL(SIZE, text.max_size()); + CHECK(text.begin() == text.end()); + CHECK_FALSE(text.is_truncated()); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_default_constructor_use_buffer_and_size) { @@ -190,6 +204,114 @@ namespace CHECK_FALSE(text.is_truncated()); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_array_buffer_text_same_buffer) + { + // Test using the same buffer for source and storage, with size deduced from array + Text text(array_text, array_text); + + CHECK_EQUAL(etl::strlen(array_text), text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(ETL_OR_STD17::size(array_text) - 1, text.capacity()); + CHECK_EQUAL(ETL_OR_STD17::size(array_text) - 1, text.max_size()); + CHECK(text.begin() != text.end()); + CHECK_FALSE(text.is_truncated()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_istring_with_array_buffer) + { + value_t buffer[SIZE + 1]; + TextBuffer source_buffer{0}; + Text source(initial_text.c_str(), source_buffer.data(), source_buffer.size()); + + Text text(source, buffer); + + CHECK_EQUAL(initial_text.size(), text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_EQUAL(SIZE, text.max_size()); + CHECK_FALSE(text.is_truncated()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_text_count_with_array_buffer) + { + value_t buffer[SIZE + 1]; + + Text text(initial_text.c_str(), 5U, buffer); + + CHECK_EQUAL(5U, text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_size_value_with_array_buffer) + { + const size_t INITIAL_SIZE = 5UL; + const value_t INITIAL_VALUE = STR('A'); + value_t buffer[SIZE + 1]; + + TextSTD compare_text(INITIAL_SIZE, INITIAL_VALUE); + Text text(INITIAL_SIZE, INITIAL_VALUE, buffer); + + CHECK_EQUAL(INITIAL_SIZE, text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + + bool is_equal = Equal(compare_text, text); + CHECK(is_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_string_view_with_array_buffer) + { + value_t buffer[SIZE + 1]; + View view(initial_text.c_str()); + + Text text(view, buffer); + + CHECK_EQUAL(view.size(), text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_iterator_range_with_array_buffer) + { + value_t buffer[SIZE + 1]; + TextSTD compare_text(initial_text.begin(), initial_text.end()); + + Text text(initial_text.begin(), initial_text.end(), buffer); + + CHECK_EQUAL(initial_text.size(), text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + + bool is_equal = Equal(compare_text, text); + CHECK(is_equal); + } + +#if ETL_HAS_INITIALIZER_LIST + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_initializer_list_with_array_buffer) + { + value_t buffer[SIZE + 1]; + + Text text({ STR('H'), STR('e'), STR('l'), STR('l'), STR('o') }, buffer); + + CHECK_EQUAL(5U, text.size()); + CHECK(!text.empty()); + CHECK_EQUAL(SIZE, text.capacity()); + CHECK_FALSE(text.is_truncated()); + } +#endif + //************************************************************************* TEST(test_iterator_comparison_empty) { From 1b244ec4c0028fa7f8c7518bb2cdcd0094af08c3 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Wed, 18 Feb 2026 20:46:21 +0000 Subject: [PATCH 4/6] Fix link rotations for etl::tree_node (#1299) * Fixed tree node rotate Improved tree node rotate tests to test all links. * Removed trailing whitespace * Removed trailing whitespace --------- Co-authored-by: John Wellbelove --- include/etl/intrusive_links.h | 63 ++++++++++----- test/test_intrusive_links.cpp | 142 +++++++++++++++++++++++++++++----- 2 files changed, 169 insertions(+), 36 deletions(-) diff --git a/include/etl/intrusive_links.h b/include/etl/intrusive_links.h index 5c9544bf..69012668 100644 --- a/include/etl/intrusive_links.h +++ b/include/etl/intrusive_links.h @@ -944,7 +944,7 @@ namespace etl { TLink* current = &first; ((current->etl_next = &links, static_cast(links).etl_previous = current, current = &links), ...); - + return current; } @@ -1303,16 +1303,30 @@ namespace etl typename etl::enable_if >::value, void>::type link_rotate_left(TLink& parent, TLink& leaf) { - parent.etl_right = leaf.etl_left; + TLink* grandparent = parent.etl_parent; - if (parent.etl_right != ETL_NULLPTR) - { - parent.etl_right->etl_parent = &parent; - } + parent.etl_right = leaf.etl_left; - leaf.etl_parent = parent.etl_parent; - parent.etl_parent = &leaf; - leaf.etl_left = &parent; + if (parent.etl_right != ETL_NULLPTR) + { + parent.etl_right->etl_parent = &parent; + } + + leaf.etl_parent = grandparent; + parent.etl_parent = &leaf; + leaf.etl_left = &parent; + + if (grandparent != ETL_NULLPTR) + { + if (grandparent->etl_left == &parent) + { + grandparent->etl_left = &leaf; + } + else if (grandparent->etl_right == &parent) + { + grandparent->etl_right = &leaf; + } + } } //*********************************** @@ -1358,19 +1372,32 @@ namespace etl typename etl::enable_if >::value, void>::type link_rotate_right(TLink& parent, TLink& leaf) { - parent.etl_left = leaf.etl_right; + TLink* grandparent = parent.etl_parent; - if (parent.etl_left != ETL_NULLPTR) - { - parent.etl_left->etl_parent = &parent; - } + parent.etl_left = leaf.etl_right; - leaf.etl_parent = parent.etl_parent; - parent.etl_parent = &leaf; - leaf.etl_right = &parent; + if (parent.etl_left != ETL_NULLPTR) + { + parent.etl_left->etl_parent = &parent; + } + + leaf.etl_parent = grandparent; + parent.etl_parent = &leaf; + leaf.etl_right = &parent; + + if (grandparent != ETL_NULLPTR) + { + if (grandparent->etl_left == &parent) + { + grandparent->etl_left = &leaf; + } + else if (grandparent->etl_right == &parent) + { + grandparent->etl_right = &leaf; + } + } } - template typename etl::enable_if >::value, void>::type link_rotate_right(TLink* parent, TLink* leaf) diff --git a/test/test_intrusive_links.cpp b/test/test_intrusive_links.cpp index c5bb92c8..2407b724 100644 --- a/test/test_intrusive_links.cpp +++ b/test/test_intrusive_links.cpp @@ -554,7 +554,7 @@ namespace CHECK(data1.FLink0::etl_next == &data2); CHECK(data2.FLink0::etl_next == nullptr); CHECK(data3.FLink0::etl_next == nullptr); - + etl::link_clear_range(*start); CHECK(data0.FLink0::etl_next == &data3); CHECK(data1.FLink0::etl_next == nullptr); @@ -882,11 +882,11 @@ namespace BData data2(2); BData data3(3); - // Use reference interface + // Use reference interface data0.BLink0::set_next(data1); data1.BLink0::set_next(data2); data2.BLink0::set_next(data3); - + data1.BLink0::set_previous(data0); data2.BLink0::set_previous(data1); data3.BLink0::set_previous(data2); @@ -1625,17 +1625,45 @@ namespace etl::link_right(a, c); etl::link_left(a, e); + // r // + // / // + // b // + // / \ // + // d a // + // / \ // + // e c // + etl::link_rotate_left(b, a); + // r // + // / // + // a // + // / \ // + // b c // + // / \ // + // d e // + + CHECK(r.etl_left == &a); + CHECK(a.etl_parent == &r); - CHECK(b.etl_parent == &a); - CHECK(e.etl_parent == &b); - CHECK(d.etl_parent == &b); - CHECK(c.etl_parent == &a); CHECK(a.etl_left == &b); CHECK(a.etl_right == &c); + + CHECK(b.etl_parent == &a); CHECK(b.etl_left == &d); CHECK(b.etl_right == &e); + + CHECK(d.etl_parent == &b); + CHECK(d.etl_left == nullptr); + CHECK(d.etl_right == nullptr); + + CHECK(c.etl_parent == &a); + CHECK(c.etl_left == nullptr); + CHECK(c.etl_right == nullptr); + + CHECK(e.etl_parent == &b); + CHECK(e.etl_left == nullptr); + CHECK(e.etl_right == nullptr); } //************************************************************************* @@ -1656,16 +1684,41 @@ namespace etl::link_left(b, d); etl::link_right(a, c); + // r // + // / // + // b // + // / \ // + // d a // + // \ // + // c // + etl::link_rotate_left(b, a); + // r // + // / // + // a // + // / \ // + // b c // + // / // + // d // + + CHECK(r.etl_left == &a); + CHECK(a.etl_parent == &r); - CHECK(b.etl_parent == &a); - CHECK(d.etl_parent == &b); - CHECK(c.etl_parent == &a); CHECK(a.etl_left == &b); CHECK(a.etl_right == &c); + + CHECK(b.etl_parent == &a); CHECK(b.etl_left == &d); CHECK(b.etl_right == nullptr); + + CHECK(d.etl_parent == &b); + CHECK(d.etl_left == nullptr); + CHECK(d.etl_right == nullptr); + + CHECK(c.etl_parent == &a); + CHECK(c.etl_left == nullptr); + CHECK(c.etl_right == nullptr); } //************************************************************************* @@ -1684,21 +1737,49 @@ namespace e.clear(); etl::link_left(r, a); etl::link_left(a, b); - etl::link_left(b, d); etl::link_right(a, c); + etl::link_left(b, d); etl::link_right(b, e); + // r // + // / // + // a // + // / \ // + // b c // + // / \ // + // d e // + etl::link_rotate_right(a, b); + // r // + // / // + // b // + // / \ // + // d a // + // / \ // + // e c // + + CHECK(r.etl_left == &b); + CHECK(b.etl_parent == &r); - CHECK(d.etl_parent == &b); - CHECK(a.etl_parent == &b); - CHECK(e.etl_parent == &a); - CHECK(c.etl_parent == &a); CHECK(b.etl_left == &d); CHECK(b.etl_right == &a); + + CHECK(d.etl_parent == &b); + CHECK(d.etl_left == nullptr); + CHECK(d.etl_right == nullptr); + + CHECK(a.etl_parent == &b); CHECK(a.etl_left == &e); CHECK(a.etl_right == &c); + + CHECK(c.etl_parent == &a); + CHECK(c.etl_left == nullptr); + CHECK(c.etl_right == nullptr); + + CHECK(e.etl_parent == &a); + CHECK(e.etl_left == nullptr); + CHECK(e.etl_right == nullptr); } //************************************************************************* @@ -1719,16 +1800,41 @@ namespace etl::link_left(b, d); etl::link_right(a, c); + // r // + // / // + // a // + // / \ // + // b c // + // / // + // d // + etl::link_rotate_right(a, b); + // r // + // / // + // b // + // / \ // + // d a // + // \ // + // c // + + CHECK(r.etl_left == &b); + CHECK(b.etl_parent == &r); - CHECK(d.etl_parent == &b); - CHECK(a.etl_parent == &b); - CHECK(c.etl_parent == &a); CHECK(b.etl_left == &d); CHECK(b.etl_right == &a); + + CHECK(d.etl_parent == &b); + CHECK(d.etl_left == nullptr); + CHECK(d.etl_right == nullptr); + + CHECK(a.etl_parent == &b); CHECK(a.etl_left == nullptr); CHECK(a.etl_right == &c); + + CHECK(c.etl_parent == &a); + CHECK(c.etl_left == nullptr); + CHECK(c.etl_right == nullptr); } } } From 226117b972996e16ac478d7ead14d216be9824b1 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Thu, 19 Feb 2026 12:41:48 +0000 Subject: [PATCH 5/6] Add etl::type_list API to more classes. (#1275) * Remove AppVeyor build status badge Removed AppVeyor build status badge from README. * Update README.md * Update CONTRIBUTING.md Updated the instructions for contributing. * Added etl::type_list to etl::observer * Added etl::type_list to etl::nth_type * Added missing 'typename' to type_list nth_type * Fix for issue 1276 "Data corruption in the etl::bip_buffer_spsc_atomic" (#1277) * Reproduce data corruption bug in the `etl::bip_buffer_spsc_atomic`. * Fix data corruption bug in the `etl::bip_buffer_spsc_atomic`. * Added type_list definitions for nth_type and observer * Added etl::type_list to etl::variant * Updated comments * Addedetl::type~_list to message_router, observer, visitor # Conflicts: # include/etl/observer.h # test/vs2022/etl.vcxproj.filters * Added member type_list type to tuple * Work in progress * Copy changes from other source * Removed unused tests * Fix iter_swap namespace * Add type_list functionality to etl::variant using etl::variant_from_type_list * Add type_list functionality to etl::message_packet using etl::message_packet_from_type_list * Add type_list functionality to etl::message_router using etl::message_router_from_type_list * Add type_list functionality to etl::observer using etl::observer_from_type_list * Add type_list functionality to etl::tuple using etl::tuple_from_type_list * Allow etl::make_index_sequence to be created from an etl::type_list * Add type_list functionality to etl::visitor using etl::visitor_from_type_list * Fix iter_swap namespace * Allow creation of a message_packet with no message types * Allow creation of a message_router with no message types * Updated VS2022 project files * Added missing test files CMakeLists.txt * Fix C++03 compatibility Fixed unused aregument warnings * Synced message_packet generator to updated code * Synced message_router generator to updated code * Synced message_router generator to updated code # Conflicts: # include/etl/generators/message_router_generator.h # include/etl/message_router.h * Fixed missing zero message specialisation for <= C++14 * Fixed missing zero message specialisation for <= C++14 * Fix year_month arithmetic and correct chrono API behavior (#1257) * Fix & add more tests for year_month arithmetic * Minor addtions to previous commit * More missing values to be uninitialized * Update the default constructors to = default and correct default constructor tests accordingly * Fix & add more tests for year_month arithmetic * Minor addtions to previous commit * More missing values to be uninitialized * Update the default constructors to = default and correct default constructor tests accordingly * Restore default constructor behavior for chrono calender * Suppress warnings from std in optimized builds (#1259) When testing with ./run-tests.sh 23 3 10, some warnings from std surfaced which resulted in build errors. * Add template deduction guide for span from vector (#1264) * Create span from vector deduction * Use ivector for deduction. Add vector_ext to test * Add vector pointer to test * Finish tests * Initialize pdata_ext and others * Document how to implement platform specifics (#1262) Some interfaces need to be implemented in every project or platform using the ETL: * etl_get_high_resolution_clock * etl_get_system_clock * etl_get_steady_clock * etl_putchar Co-authored-by: John Wellbelove * Fix etl::as_bytes for etl::span (#1266) A missing 'const' in the etl::as_bytes implementation was causing a compile-time error when etl::as_bytes was called on a span of const values. Co-authored-by: John Wellbelove * Remove some UB in test_vector_non_trivial.cpp (#1268) Some of the tests' UB are detectable by Gcc15 and thus give a compile error due to warnings-as-error flag. Co-authored-by: John Wellbelove * Update C++26 deprecated constructs to ensure future standard compliance (#1267) * Update C++26 deprecated constructs to ensure future standard compliance I replaced std::is_trivial with a combination of std::is_trivially_default_constructible and std::is_trivially_copyable. Additionally, I added the required comma before the ellipsis in variadic functions to match updated language specifications. * Some additional is_trivial related changes not found directly when compiling tests in C++26 --------- Co-authored-by: John Wellbelove * Fix return value of get_token_list (#1271) Co-authored-by: John Wellbelove * Fix etl::tuple template signature error in pair assignment operator (#1265) * Fix etl::tuple template signature error in pair assignment operator * Remove AppVeyor build status badge Removed AppVeyor build status badge from README. * Update README.md * Update etl::tuple to explicitly use etl::pair or std::pair in assignment operator * Added tests for etl::tuple assignment from pair --------- Co-authored-by: Bryton Flecker Co-authored-by: John Wellbelove * Remove advance() on static spans (#1281) * Remove AppVeyor build status badge Removed AppVeyor build status badge from README. * Update README.md * Update CONTRIBUTING.md Updated the instructions for contributing. * Fix for issue 1276 "Data corruption in the etl::bip_buffer_spsc_atomic" (#1277) * Reproduce data corruption bug in the `etl::bip_buffer_spsc_atomic`. * Fix data corruption bug in the `etl::bip_buffer_spsc_atomic`. * Remove advance() on static spans Since the size of a static span is constant, we can't reasonably advance() on it. --------- Co-authored-by: John Wellbelove Co-authored-by: Sergei * Add missing includes (#1286) * Remove AppVeyor build status badge Removed AppVeyor build status badge from README. * Update README.md * Update CONTRIBUTING.md Updated the instructions for contributing. * Fix for issue 1276 "Data corruption in the etl::bip_buffer_spsc_atomic" (#1277) * Reproduce data corruption bug in the `etl::bip_buffer_spsc_atomic`. * Fix data corruption bug in the `etl::bip_buffer_spsc_atomic`. * Add missing includes Before this change, the includes needed to be done explicitly by files using basic_string_stream.h, and be included first. This was error prone, especially if includes are reordered (e.g. via the currently defined clang-format rules). --------- Co-authored-by: John Wellbelove Co-authored-by: Sergei * Move comparison operators of etl::expected to namespace etl (#1287) * Remove AppVeyor build status badge Removed AppVeyor build status badge from README. * Update README.md * Update CONTRIBUTING.md Updated the instructions for contributing. * Fix for issue 1276 "Data corruption in the etl::bip_buffer_spsc_atomic" (#1277) * Reproduce data corruption bug in the `etl::bip_buffer_spsc_atomic`. * Fix data corruption bug in the `etl::bip_buffer_spsc_atomic`. * Move comparison operators of etl::expected to namespace etl --------- Co-authored-by: John Wellbelove Co-authored-by: Sergei * Make typed_storage constructor constexpr (#1291) * Remove AppVeyor build status badge Removed AppVeyor build status badge from README. * Update README.md * Update CONTRIBUTING.md Updated the instructions for contributing. * Fix for issue 1276 "Data corruption in the etl::bip_buffer_spsc_atomic" (#1277) * Reproduce data corruption bug in the `etl::bip_buffer_spsc_atomic`. * Fix data corruption bug in the `etl::bip_buffer_spsc_atomic`. * Make typed_storage constructor constexpr --------- Co-authored-by: John Wellbelove Co-authored-by: Sergei * Add basic_format_arg constructor for ibasic_string (#1288) * Allow string as format arg * Remove AppVeyor build status badge Removed AppVeyor build status badge from README. * Update README.md * Update CONTRIBUTING.md Updated the instructions for contributing. * Fix for issue 1276 "Data corruption in the etl::bip_buffer_spsc_atomic" (#1277) * Reproduce data corruption bug in the `etl::bip_buffer_spsc_atomic`. * Fix data corruption bug in the `etl::bip_buffer_spsc_atomic`. * Added test string escaped * Add temporary string test --------- Co-authored-by: John Wellbelove Co-authored-by: Sergei * QR Code for Github * Added etl::visitor_from_type_list * accepts(id) for empty router passes on to a sucessor * Fixed incorrect comment from 'tuple' to 'message_router' * PR review changes * PR review changes * Fixed internal constexptr flag in message_packet * Fixed unused variable in unti test --------- Co-authored-by: John Wellbelove Co-authored-by: Sergei Co-authored-by: John Wellbelove Co-authored-by: Bo Rydberg <2945606+bolry@users.noreply.github.com> Co-authored-by: Roland Reichwein Co-authored-by: Mike Bloom <91038685+mike919192@users.noreply.github.com> Co-authored-by: taltenbach <92919739+taltenbach@users.noreply.github.com> Co-authored-by: Bryton Flecker Co-authored-by: Bryton Flecker --- .../etl/generators/message_packet_generator.h | 219 ++++++++++++- .../etl/generators/message_router_generator.h | 221 ++++++++++++- include/etl/message_packet.h | 291 ++++++++++++++++-- include/etl/message_router.h | 266 +++++++++++++++- include/etl/nth_type.h | 12 +- include/etl/observer.h | 20 ++ include/etl/private/variant_variadic.h | 19 +- include/etl/tuple.h | 17 + include/etl/type_list.h | 11 + include/etl/utility.h | 25 +- include/etl/visitor.h | 32 ++ test/CMakeLists.txt | 8 - test/etl_profile.h | 1 + test/test_message_packet.cpp | 18 +- test/test_message_router.cpp | 84 ++++- test/test_nth_type.cpp | 11 + test/test_observer.cpp | 107 ++++++- test/test_tuple.cpp | 21 ++ test/test_type_list.cpp | 18 ++ test/test_utility.cpp | 34 ++ test/test_variant_variadic.cpp | 52 +++- test/test_visitor.cpp | 26 ++ 22 files changed, 1452 insertions(+), 61 deletions(-) diff --git a/include/etl/generators/message_packet_generator.h b/include/etl/generators/message_packet_generator.h index ee55c48d..36430509 100644 --- a/include/etl/generators/message_packet_generator.h +++ b/include/etl/generators/message_packet_generator.h @@ -71,6 +71,7 @@ cog.outl("//******************************************************************** #include "largest.h" #include "alignment.h" #include "utility.h" +#include "type_list.h" #include @@ -87,19 +88,21 @@ namespace etl private: template - static constexpr bool IsMessagePacket = etl::is_same_v< etl::remove_const_t>, etl::message_packet>; + static constexpr bool IsMessagePacket = etl::is_same_v>, etl::message_packet>; template static constexpr bool IsInMessageList = etl::is_one_of_v>, TMessageTypes...>; template - static constexpr bool IsIMessage = etl::is_same_v>, etl::imessage>; + static constexpr bool IsIMessage = etl::is_same_v>, etl::imessage>; public: + using message_types = etl::type_list; + //******************************************** #include "private/diagnostic_uninitialized_push.h" - message_packet() + constexpr message_packet() noexcept : valid(false) { } @@ -260,7 +263,7 @@ namespace etl //********************************************** template static ETL_CONSTEXPR - typename etl::enable_if::value, bool>::type + typename etl::enable_if::value, bool>::type accepts() { return accepts(); @@ -392,6 +395,132 @@ namespace etl bool valid; }; + //*************************************************************************** + // The definition for no message types. + //*************************************************************************** + template <> + class message_packet<> + { + private: + + template + static constexpr bool IsMessagePacket = etl::is_same_v>, etl::message_packet<>>; + + template + static constexpr bool IsInMessageList = false; + + template + static constexpr bool IsIMessage = etl::is_same_v>, etl::imessage>; + + public: + + using message_types = etl::type_list<>; + + //******************************************** +#include "private/diagnostic_uninitialized_push.h" + constexpr message_packet() noexcept + { + } +#include "private/diagnostic_pop.h" + + //********************************************** + message_packet(const message_packet& /*other*/) + { + } + +#if ETL_USING_CPP11 + //********************************************** + message_packet(message_packet&& /*other*/) + { + } +#endif + + //********************************************** + void copy(const message_packet& /*other*/) + { + } + + //********************************************** + void copy(message_packet&& /*other*/) + { + } + + //********************************************** +#include "private/diagnostic_uninitialized_push.h" + message_packet& operator =(const message_packet& /*rhs*/) + { + return *this; + } +#include "private/diagnostic_pop.h" + + //********************************************** +#include "private/diagnostic_uninitialized_push.h" + message_packet& operator =(message_packet&& /*rhs*/) + { + return *this; + } +#include "private/diagnostic_pop.h" + + //******************************************** + ~message_packet() + { + } + + //******************************************** + bool is_valid() const + { + return false; + } + + //********************************************** + static ETL_CONSTEXPR bool accepts(etl::message_id_t /*id*/) + { + return false; + } + + //********************************************** + static ETL_CONSTEXPR bool accepts(const etl::imessage& /*msg*/) + { + return false; + } + + //********************************************** + template + static ETL_CONSTEXPR bool accepts() + { + return false; + } + + //********************************************** + template + static ETL_CONSTEXPR + typename etl::enable_if::value, bool>::type + accepts() + { + return false; + } + + enum + { + SIZE = 0, + ALIGNMENT = 1 + }; + }; + + //*************************************************************************** + /// Helper to turn etl::type_list into etl::message_packet + template + struct message_packet_from_type_list; + + template + struct message_packet_from_type_list> + { + using type = etl::message_packet; + }; + + template + using message_packet_from_type_list_t = typename message_packet_from_type_list::type; + #else /*[[[cog @@ -470,8 +599,7 @@ namespace etl cog.outl("// The definition for all %s message types." % Handlers) cog.outl("//***************************************************************************") cog.out("template <") - cog.out("typename T1, ") - for n in range(2, int(Handlers)): + for n in range(1, int(Handlers)): cog.out("typename T%s = void, " % n) if n % 4 == 0: cog.outl("") @@ -481,9 +609,18 @@ namespace etl cog.outl("{") cog.outl("public:") cog.outl("") + + cog.outl("#if ETL_USING_CPP11") + cog.out(" using message_types = etl::type_list<") + for n in range(1, int(Handlers)): + cog.out("T%s, " % n) + cog.outl("T%s>;" % int(Handlers)) + cog.outl("#endif") + cog.outl("") + cog.outl(" //********************************************") cog.outl("#include \"private/diagnostic_uninitialized_push.h\"") - cog.outl(" message_packet()") + cog.outl(" ETL_CONSTEXPR message_packet() ETL_NOEXCEPT") cog.outl(" : valid(false)") cog.outl(" {") cog.outl(" }") @@ -785,9 +922,19 @@ namespace etl cog.outl("{") cog.outl("public:") cog.outl("") + + cog.out("#if ETL_USING_CPP11") + cog.outl("") + cog.out(" using message_types = etl::type_list<") + for t in range(1, int(n)): + cog.out("T%s, " % t) + cog.outl("T%s>;" % int(n)) + cog.outl("#endif") + cog.outl("") + cog.outl(" //********************************************") cog.outl("#include \"private/diagnostic_uninitialized_push.h\"") - cog.outl(" message_packet()") + cog.outl(" ETL_CONSTEXPR message_packet() ETL_NOEXCEPT") cog.outl(" : valid(false)") cog.outl(" {") cog.outl(" }") @@ -1058,6 +1205,62 @@ namespace etl cog.outl("};") ]]]*/ /*[[[end]]]*/ + + //*************************************************************************** + // Specialisation for 0 message types. + //*************************************************************************** + template <> + class message_packet + { + public: + +#if ETL_USING_CPP11 + using message_types = etl::type_list<>; +#endif + + ETL_CONSTEXPR message_packet() ETL_NOEXCEPT + : valid(false) + { + } + + static ETL_CONSTEXPR bool accepts(etl::message_id_t) + { + return false; + } + + static ETL_CONSTEXPR bool accepts(const etl::imessage&) + { + return false; + } + + template + static ETL_CONSTEXPR bool accepts() + { + return false; + } + + template + static ETL_CONSTEXPR typename etl::enable_if::value, bool>::type accepts() + { + return false; + } + + bool is_valid() const + { + return valid; + } + + enum + { + SIZE = 0U, + ALIGNMENT = 1U + }; + + private: + + bool valid; + }; #endif } diff --git a/include/etl/generators/message_router_generator.h b/include/etl/generators/message_router_generator.h index 868a51ab..02140088 100644 --- a/include/etl/generators/message_router_generator.h +++ b/include/etl/generators/message_router_generator.h @@ -76,7 +76,7 @@ cog.outl("//******************************************************************** #include "placement_new.h" #include "successor.h" #include "type_traits.h" - +#include "type_list.h" #include namespace etl @@ -560,6 +560,114 @@ namespace etl } } }; + + //*************************************************************************** + // The definition for 0 message types. + //*************************************************************************** + template + class message_router : public imessage_router + { + public: + + using message_packet = etl::message_packet<>; + using message_types = etl::type_list<>; + + //********************************************** + message_router() + : imessage_router(etl::imessage_router::MESSAGE_ROUTER) + { + } + + //********************************************** + message_router(etl::imessage_router& successor_) + : imessage_router(etl::imessage_router::MESSAGE_ROUTER, successor_) + { + } + + //********************************************** + message_router(etl::message_router_id_t id_) + : imessage_router(id_) + { + ETL_ASSERT(id_ <= etl::imessage_router::MAX_MESSAGE_ROUTER, ETL_ERROR(etl::message_router_illegal_id)); + } + + //********************************************** + message_router(etl::message_router_id_t id_, etl::imessage_router& successor_) + : imessage_router(id_, successor_) + { + ETL_ASSERT(id_ <= etl::imessage_router::MAX_MESSAGE_ROUTER, ETL_ERROR(etl::message_router_illegal_id)); + } + + //********************************************** + using etl::imessage_router::receive; + + void receive(const etl::imessage& msg) ETL_OVERRIDE + { + if (has_successor()) + { + get_successor().receive(msg); + } + } + + template ::value, int>::type = 0> + void receive(const TMessage& msg) + { +#include "etl/private/diagnostic_array_bounds_push.h" + if (has_successor()) + { + get_successor().receive(msg); + } +#include "etl/private/diagnostic_pop.h" + } + + //********************************************** + using imessage_router::accepts; + + bool accepts(etl::message_id_t id) const ETL_OVERRIDE + { + if (has_successor()) + { + return get_successor().accepts(id); + } + else + { + return false; + } + } + + //******************************************** + ETL_DEPRECATED bool is_null_router() const ETL_OVERRIDE + { + return false; + } + + //******************************************** + bool is_producer() const ETL_OVERRIDE + { + return true; + } + + //******************************************** + bool is_consumer() const ETL_OVERRIDE + { + return true; + } + }; + + //*************************************************************************** + /// Helper to turn etl::type_list into etl::message_router + template + struct message_router_from_type_list; + + template + struct message_router_from_type_list> + { + using type = etl::message_router; + }; + + template + using message_router_from_type_list_t = typename message_router_from_type_list::type; + #else //************************************************************************************************* // For C++14 and below. @@ -574,8 +682,7 @@ namespace etl cog.outl("//***************************************************************************") cog.outl("template message_packet;" % int(Handlers)) + cog.outl("T%s> message_packet;" % int(Handlers)) cog.outl("") + + cog.outl("#if ETL_USING_CPP11") + cog.out(" using message_types = etl::type_list<") + for n in range(1, int(Handlers)): + cog.out("T%s, " % n) + cog.outl("T%s>;" % int(Handlers)) + cog.outl("#endif") + cog.outl("") + cog.outl(" //**********************************************") cog.outl(" message_router(etl::message_router_id_t id_)") cog.outl(" : imessage_router(id_)") @@ -765,6 +881,15 @@ namespace etl cog.out("T%s, " % t) cog.outl(" T%s> message_packet;" % n) cog.outl("") + + cog.outl("#if ETL_USING_CPP11") + cog.out(" using message_types = etl::type_list<") + for t in range(1, n): + cog.out("T%s, " % t) + cog.outl("T%s>;" % n) + cog.outl("#endif") + cog.outl("") + cog.outl(" //**********************************************") cog.outl(" message_router(etl::message_router_id_t id_)") cog.outl(" : imessage_router(id_)") @@ -902,6 +1027,94 @@ namespace etl cog.outl("};") ]]]*/ /*[[[end]]]*/ + + //*************************************************************************** + // Specialisation for 0 message types. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + typedef etl::message_packet<> message_packet; + +#if ETL_USING_CPP11 + using message_types = etl::type_list<>; +#endif + + //********************************************** + message_router(etl::message_router_id_t id_) + : imessage_router(id_) + { + ETL_ASSERT(id_ <= etl::imessage_router::MAX_MESSAGE_ROUTER, ETL_ERROR(etl::message_router_illegal_id)); + } + + //********************************************** + message_router(etl::message_router_id_t id_, etl::imessage_router& successor_) + : imessage_router(id_, successor_) + { + ETL_ASSERT(id_ <= etl::imessage_router::MAX_MESSAGE_ROUTER, ETL_ERROR(etl::message_router_illegal_id)); + } + + //********************************************** + message_router() + : imessage_router(etl::imessage_router::MESSAGE_ROUTER) + { + } + + //********************************************** + message_router(etl::imessage_router& successor_) + : imessage_router(etl::imessage_router::MESSAGE_ROUTER, successor_) + { + } + + //********************************************** + using etl::imessage_router::receive; + + void receive(const etl::imessage& msg) ETL_OVERRIDE + { +#include "etl/private/diagnostic_array_bounds_push.h" + if (has_successor()) + { + get_successor().receive(msg); + } +#include "etl/private/diagnostic_pop.h" + } + + //********************************************** + using imessage_router::accepts; + + bool accepts(etl::message_id_t id) const ETL_OVERRIDE + { + if (has_successor()) + { + return get_successor().accepts(id); + } + else + { + return false; + } + } + + //******************************************** + ETL_DEPRECATED bool is_null_router() const ETL_OVERRIDE + { + return false; + } + + //******************************************** + bool is_producer() const ETL_OVERRIDE + { + return true; + } + + //******************************************** + bool is_consumer() const ETL_OVERRIDE + { + return true; + } + }; #endif } diff --git a/include/etl/message_packet.h b/include/etl/message_packet.h index 8215eeb0..24f1e157 100644 --- a/include/etl/message_packet.h +++ b/include/etl/message_packet.h @@ -59,6 +59,7 @@ SOFTWARE. #include "largest.h" #include "alignment.h" #include "utility.h" +#include "type_list.h" #include @@ -75,19 +76,21 @@ namespace etl private: template - static constexpr bool IsMessagePacket = etl::is_same_v< etl::remove_const_t>, etl::message_packet>; + static constexpr bool IsMessagePacket = etl::is_same_v>, etl::message_packet>; template static constexpr bool IsInMessageList = etl::is_one_of_v>, TMessageTypes...>; template - static constexpr bool IsIMessage = etl::is_same_v>, etl::imessage>; + static constexpr bool IsIMessage = etl::is_same_v>, etl::imessage>; public: + using message_types = etl::type_list; + //******************************************** #include "private/diagnostic_uninitialized_push.h" - message_packet() + constexpr message_packet() noexcept : valid(false) { } @@ -248,7 +251,7 @@ namespace etl //********************************************** template static ETL_CONSTEXPR - typename etl::enable_if::value, bool>::type + typename etl::enable_if::value, bool>::type accepts() { return accepts(); @@ -380,12 +383,138 @@ namespace etl bool valid; }; + //*************************************************************************** + // The definition for no message types. + //*************************************************************************** + template <> + class message_packet<> + { + private: + + template + static constexpr bool IsMessagePacket = etl::is_same_v>, etl::message_packet<>>; + + template + static constexpr bool IsInMessageList = false; + + template + static constexpr bool IsIMessage = etl::is_same_v>, etl::imessage>; + + public: + + using message_types = etl::type_list<>; + + //******************************************** +#include "private/diagnostic_uninitialized_push.h" + constexpr message_packet() noexcept + { + } +#include "private/diagnostic_pop.h" + + //********************************************** + message_packet(const message_packet& /*other*/) + { + } + +#if ETL_USING_CPP11 + //********************************************** + message_packet(message_packet&& /*other*/) + { + } +#endif + + //********************************************** + void copy(const message_packet& /*other*/) + { + } + + //********************************************** + void copy(message_packet&& /*other*/) + { + } + + //********************************************** +#include "private/diagnostic_uninitialized_push.h" + message_packet& operator =(const message_packet& /*rhs*/) + { + return *this; + } +#include "private/diagnostic_pop.h" + + //********************************************** +#include "private/diagnostic_uninitialized_push.h" + message_packet& operator =(message_packet&& /*rhs*/) + { + return *this; + } +#include "private/diagnostic_pop.h" + + //******************************************** + ~message_packet() + { + } + + //******************************************** + bool is_valid() const + { + return false; + } + + //********************************************** + static ETL_CONSTEXPR bool accepts(etl::message_id_t /*id*/) + { + return false; + } + + //********************************************** + static ETL_CONSTEXPR bool accepts(const etl::imessage& /*msg*/) + { + return false; + } + + //********************************************** + template + static ETL_CONSTEXPR bool accepts() + { + return false; + } + + //********************************************** + template + static ETL_CONSTEXPR + typename etl::enable_if::value, bool>::type + accepts() + { + return false; + } + + enum + { + SIZE = 0, + ALIGNMENT = 1 + }; + }; + + //*************************************************************************** + /// Helper to turn etl::type_list into etl::message_packet + template + struct message_packet_from_type_list; + + template + struct message_packet_from_type_list> + { + using type = etl::message_packet; + }; + + template + using message_packet_from_type_list_t = typename message_packet_from_type_list::type; + #else //*************************************************************************** // The definition for all 16 message types. //*************************************************************************** - template @@ -393,9 +522,13 @@ namespace etl { public: + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //******************************************** #include "private/diagnostic_uninitialized_push.h" - message_packet() + ETL_CONSTEXPR message_packet() ETL_NOEXCEPT : valid(false) { } @@ -717,9 +850,13 @@ namespace etl { public: + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //******************************************** #include "private/diagnostic_uninitialized_push.h" - message_packet() + ETL_CONSTEXPR message_packet() ETL_NOEXCEPT : valid(false) { } @@ -1039,9 +1176,13 @@ namespace etl { public: + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //******************************************** #include "private/diagnostic_uninitialized_push.h" - message_packet() + ETL_CONSTEXPR message_packet() ETL_NOEXCEPT : valid(false) { } @@ -1358,9 +1499,13 @@ namespace etl { public: + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //******************************************** #include "private/diagnostic_uninitialized_push.h" - message_packet() + ETL_CONSTEXPR message_packet() ETL_NOEXCEPT : valid(false) { } @@ -1673,9 +1818,13 @@ namespace etl { public: + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //******************************************** #include "private/diagnostic_uninitialized_push.h" - message_packet() + ETL_CONSTEXPR message_packet() ETL_NOEXCEPT : valid(false) { } @@ -1982,9 +2131,13 @@ namespace etl { public: + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //******************************************** #include "private/diagnostic_uninitialized_push.h" - message_packet() + ETL_CONSTEXPR message_packet() ETL_NOEXCEPT : valid(false) { } @@ -2288,9 +2441,13 @@ namespace etl { public: + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //******************************************** #include "private/diagnostic_uninitialized_push.h" - message_packet() + ETL_CONSTEXPR message_packet() ETL_NOEXCEPT : valid(false) { } @@ -2591,9 +2748,13 @@ namespace etl { public: + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //******************************************** #include "private/diagnostic_uninitialized_push.h" - message_packet() + ETL_CONSTEXPR message_packet() ETL_NOEXCEPT : valid(false) { } @@ -2890,9 +3051,13 @@ namespace etl { public: + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //******************************************** #include "private/diagnostic_uninitialized_push.h" - message_packet() + ETL_CONSTEXPR message_packet() ETL_NOEXCEPT : valid(false) { } @@ -3183,9 +3348,13 @@ namespace etl { public: + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //******************************************** #include "private/diagnostic_uninitialized_push.h" - message_packet() + ETL_CONSTEXPR message_packet() ETL_NOEXCEPT : valid(false) { } @@ -3473,9 +3642,13 @@ namespace etl { public: + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //******************************************** #include "private/diagnostic_uninitialized_push.h" - message_packet() + ETL_CONSTEXPR message_packet() ETL_NOEXCEPT : valid(false) { } @@ -3760,9 +3933,13 @@ namespace etl { public: + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //******************************************** #include "private/diagnostic_uninitialized_push.h" - message_packet() + ETL_CONSTEXPR message_packet() ETL_NOEXCEPT : valid(false) { } @@ -4043,9 +4220,13 @@ namespace etl { public: + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //******************************************** #include "private/diagnostic_uninitialized_push.h" - message_packet() + ETL_CONSTEXPR message_packet() ETL_NOEXCEPT : valid(false) { } @@ -4320,9 +4501,13 @@ namespace etl { public: + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //******************************************** #include "private/diagnostic_uninitialized_push.h" - message_packet() + ETL_CONSTEXPR message_packet() ETL_NOEXCEPT : valid(false) { } @@ -4594,9 +4779,13 @@ namespace etl { public: + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //******************************************** #include "private/diagnostic_uninitialized_push.h" - message_packet() + ETL_CONSTEXPR message_packet() ETL_NOEXCEPT : valid(false) { } @@ -4865,9 +5054,13 @@ namespace etl { public: + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //******************************************** #include "private/diagnostic_uninitialized_push.h" - message_packet() + ETL_CONSTEXPR message_packet() ETL_NOEXCEPT : valid(false) { } @@ -5124,6 +5317,62 @@ namespace etl typename etl::aligned_storage::type data; bool valid; }; + + //*************************************************************************** + // Specialisation for 0 message types. + //*************************************************************************** + template <> + class message_packet + { + public: + +#if ETL_USING_CPP11 + using message_types = etl::type_list<>; +#endif + + ETL_CONSTEXPR message_packet() ETL_NOEXCEPT + : valid(false) + { + } + + static ETL_CONSTEXPR bool accepts(etl::message_id_t) + { + return false; + } + + static ETL_CONSTEXPR bool accepts(const etl::imessage&) + { + return false; + } + + template + static ETL_CONSTEXPR bool accepts() + { + return false; + } + + template + static ETL_CONSTEXPR typename etl::enable_if::value, bool>::type accepts() + { + return false; + } + + bool is_valid() const + { + return valid; + } + + enum + { + SIZE = 0U, + ALIGNMENT = 1U + }; + + private: + + bool valid; + }; #endif } diff --git a/include/etl/message_router.h b/include/etl/message_router.h index cc6ea40c..b53d9071 100644 --- a/include/etl/message_router.h +++ b/include/etl/message_router.h @@ -64,7 +64,7 @@ SOFTWARE. #include "placement_new.h" #include "successor.h" #include "type_traits.h" - +#include "type_list.h" #include namespace etl @@ -548,6 +548,114 @@ namespace etl } } }; + + //*************************************************************************** + // The definition for 0 message types. + //*************************************************************************** + template + class message_router : public imessage_router + { + public: + + using message_packet = etl::message_packet<>; + using message_types = etl::type_list<>; + + //********************************************** + message_router() + : imessage_router(etl::imessage_router::MESSAGE_ROUTER) + { + } + + //********************************************** + message_router(etl::imessage_router& successor_) + : imessage_router(etl::imessage_router::MESSAGE_ROUTER, successor_) + { + } + + //********************************************** + message_router(etl::message_router_id_t id_) + : imessage_router(id_) + { + ETL_ASSERT(id_ <= etl::imessage_router::MAX_MESSAGE_ROUTER, ETL_ERROR(etl::message_router_illegal_id)); + } + + //********************************************** + message_router(etl::message_router_id_t id_, etl::imessage_router& successor_) + : imessage_router(id_, successor_) + { + ETL_ASSERT(id_ <= etl::imessage_router::MAX_MESSAGE_ROUTER, ETL_ERROR(etl::message_router_illegal_id)); + } + + //********************************************** + using etl::imessage_router::receive; + + void receive(const etl::imessage& msg) ETL_OVERRIDE + { + if (has_successor()) + { + get_successor().receive(msg); + } + } + + template ::value, int>::type = 0> + void receive(const TMessage& msg) + { +#include "etl/private/diagnostic_array_bounds_push.h" + if (has_successor()) + { + get_successor().receive(msg); + } +#include "etl/private/diagnostic_pop.h" + } + + //********************************************** + using imessage_router::accepts; + + bool accepts(etl::message_id_t id) const ETL_OVERRIDE + { + if (has_successor()) + { + return get_successor().accepts(id); + } + else + { + return false; + } + } + + //******************************************** + ETL_DEPRECATED bool is_null_router() const ETL_OVERRIDE + { + return false; + } + + //******************************************** + bool is_producer() const ETL_OVERRIDE + { + return true; + } + + //******************************************** + bool is_consumer() const ETL_OVERRIDE + { + return true; + } + }; + + //*************************************************************************** + /// Helper to turn etl::type_list into etl::message_router + template + struct message_router_from_type_list; + + template + struct message_router_from_type_list> + { + using type = etl::message_router; + }; + + template + using message_router_from_type_list_t = typename message_router_from_type_list::type; + #else //************************************************************************************************* // For C++14 and below. @@ -556,7 +664,7 @@ namespace etl // The definition for all 16 message types. //*************************************************************************** template @@ -564,7 +672,11 @@ namespace etl { public: - typedef etl::message_packet message_packet; + typedef etl::message_packet message_packet; + + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif //********************************************** message_router(etl::message_router_id_t id_) @@ -717,6 +829,10 @@ namespace etl typedef etl::message_packet message_packet; + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //********************************************** message_router(etl::message_router_id_t id_) : imessage_router(id_) @@ -868,6 +984,10 @@ namespace etl typedef etl::message_packet message_packet; + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //********************************************** message_router(etl::message_router_id_t id_) : imessage_router(id_) @@ -1018,6 +1138,10 @@ namespace etl typedef etl::message_packet message_packet; + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //********************************************** message_router(etl::message_router_id_t id_) : imessage_router(id_) @@ -1166,6 +1290,10 @@ namespace etl typedef etl::message_packet message_packet; + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //********************************************** message_router(etl::message_router_id_t id_) : imessage_router(id_) @@ -1313,6 +1441,10 @@ namespace etl typedef etl::message_packet message_packet; + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //********************************************** message_router(etl::message_router_id_t id_) : imessage_router(id_) @@ -1459,6 +1591,10 @@ namespace etl typedef etl::message_packet message_packet; + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //********************************************** message_router(etl::message_router_id_t id_) : imessage_router(id_) @@ -1604,6 +1740,10 @@ namespace etl typedef etl::message_packet message_packet; + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //********************************************** message_router(etl::message_router_id_t id_) : imessage_router(id_) @@ -1747,6 +1887,10 @@ namespace etl typedef etl::message_packet message_packet; + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //********************************************** message_router(etl::message_router_id_t id_) : imessage_router(id_) @@ -1889,6 +2033,10 @@ namespace etl typedef etl::message_packet message_packet; + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //********************************************** message_router(etl::message_router_id_t id_) : imessage_router(id_) @@ -2029,6 +2177,10 @@ namespace etl typedef etl::message_packet message_packet; + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //********************************************** message_router(etl::message_router_id_t id_) : imessage_router(id_) @@ -2168,6 +2320,10 @@ namespace etl typedef etl::message_packet message_packet; + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //********************************************** message_router(etl::message_router_id_t id_) : imessage_router(id_) @@ -2305,6 +2461,10 @@ namespace etl typedef etl::message_packet message_packet; + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //********************************************** message_router(etl::message_router_id_t id_) : imessage_router(id_) @@ -2441,6 +2601,10 @@ namespace etl typedef etl::message_packet message_packet; + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //********************************************** message_router(etl::message_router_id_t id_) : imessage_router(id_) @@ -2576,6 +2740,10 @@ namespace etl typedef etl::message_packet message_packet; + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //********************************************** message_router(etl::message_router_id_t id_) : imessage_router(id_) @@ -2710,6 +2878,10 @@ namespace etl typedef etl::message_packet< T1> message_packet; + #if ETL_USING_CPP11 + using message_types = etl::type_list; + #endif + //********************************************** message_router(etl::message_router_id_t id_) : imessage_router(id_) @@ -2830,6 +3002,94 @@ namespace etl return true; } }; + + //*************************************************************************** + // Specialisation for 0 message types. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + typedef etl::message_packet<> message_packet; + +#if ETL_USING_CPP11 + using message_types = etl::type_list<>; +#endif + + //********************************************** + message_router(etl::message_router_id_t id_) + : imessage_router(id_) + { + ETL_ASSERT(id_ <= etl::imessage_router::MAX_MESSAGE_ROUTER, ETL_ERROR(etl::message_router_illegal_id)); + } + + //********************************************** + message_router(etl::message_router_id_t id_, etl::imessage_router& successor_) + : imessage_router(id_, successor_) + { + ETL_ASSERT(id_ <= etl::imessage_router::MAX_MESSAGE_ROUTER, ETL_ERROR(etl::message_router_illegal_id)); + } + + //********************************************** + message_router() + : imessage_router(etl::imessage_router::MESSAGE_ROUTER) + { + } + + //********************************************** + message_router(etl::imessage_router& successor_) + : imessage_router(etl::imessage_router::MESSAGE_ROUTER, successor_) + { + } + + //********************************************** + using etl::imessage_router::receive; + + void receive(const etl::imessage& msg) ETL_OVERRIDE + { +#include "etl/private/diagnostic_array_bounds_push.h" + if (has_successor()) + { + get_successor().receive(msg); + } +#include "etl/private/diagnostic_pop.h" + } + + //********************************************** + using imessage_router::accepts; + + bool accepts(etl::message_id_t id) const ETL_OVERRIDE + { + if (has_successor()) + { + return get_successor().accepts(id); + } + else + { + return false; + } + } + + //******************************************** + ETL_DEPRECATED bool is_null_router() const ETL_OVERRIDE + { + return false; + } + + //******************************************** + bool is_producer() const ETL_OVERRIDE + { + return true; + } + + //******************************************** + bool is_consumer() const ETL_OVERRIDE + { + return true; + } + }; #endif } diff --git a/include/etl/nth_type.h b/include/etl/nth_type.h index 01f2a6e8..82e58ef4 100644 --- a/include/etl/nth_type.h +++ b/include/etl/nth_type.h @@ -31,6 +31,7 @@ SOFTWARE. #include "platform.h" #include "static_assert.h" +#include "type_list.h" #if ETL_USING_CPP11 namespace etl @@ -57,7 +58,16 @@ namespace etl { ETL_STATIC_ASSERT(Index < sizeof...(TTypes), "etl::nth_type index 'Index' out of bounds"); - using type = typename private_nth_type::nth_type_helper::type; + using type = typename private_nth_type::nth_type_helper::type; + using type_list = etl::type_list; + }; + + //*********************************** + template + struct nth_type> + { + using type = typename nth_type::type; + using type_list = typename nth_type::type_list; }; //*********************************** diff --git a/include/etl/observer.h b/include/etl/observer.h index 3e915a59..26f88478 100644 --- a/include/etl/observer.h +++ b/include/etl/observer.h @@ -57,6 +57,7 @@ SOFTWARE. #include "exception.h" #include "error_handler.h" #include "utility.h" +#include "type_list.h" namespace etl { @@ -308,6 +309,7 @@ namespace etl using observer::notification; using observer::notification; + using type_list = etl::type_list; }; //***************************************************************** @@ -319,6 +321,8 @@ namespace etl { public: + using type_list = etl::type_list; + virtual ~observer() = default; virtual void notification(T1) = 0; @@ -333,11 +337,27 @@ namespace etl { public: + using type_list = etl::type_list<>; + virtual ~observer() = default; virtual void notification() = 0; }; + //*************************************************************************** + /// Helper to turn etl::type_list into etl::observer + template + struct observer_from_type_list; + + template + struct observer_from_type_list> + { + using type = etl::observer; + }; + + template + using observer_from_type_list_t = typename observer_from_type_list::type; + #else //********************************************************************* diff --git a/include/etl/private/variant_variadic.h b/include/etl/private/variant_variadic.h index 8ec3f195..4363ebfa 100644 --- a/include/etl/private/variant_variadic.h +++ b/include/etl/private/variant_variadic.h @@ -370,7 +370,6 @@ namespace etl //*************************************************************************** /// A template class that can store any of the types defined in the template parameter list. - /// Supports up to 8 types. ///\ingroup variant //*************************************************************************** template @@ -378,6 +377,8 @@ namespace etl { public: + using type_list = etl::type_list; + //*************************************************************************** /// get() is a friend function. //*************************************************************************** @@ -1222,7 +1223,7 @@ namespace etl //*************************************************************************** template ETL_CONSTEXPR14 bool holds_alternative(const etl::variant& v) ETL_NOEXCEPT - { + { constexpr size_t Index = etl::type_list_index_of_type, T>::value; return (Index == variant_npos) ? false : (v.index() == Index); @@ -1853,5 +1854,19 @@ namespace etl } } #endif + + //*************************************************************************** + /// Helper to turn etl::type_list into etl::variant + template + struct variant_from_type_list; + + template + struct variant_from_type_list> + { + using type = etl::variant; + }; + + template + using variant_from_type_list_t = typename variant_from_type_list::type; } #endif diff --git a/include/etl/tuple.h b/include/etl/tuple.h index ffc6db11..dde08d13 100644 --- a/include/etl/tuple.h +++ b/include/etl/tuple.h @@ -45,6 +45,7 @@ SOFTWARE. #include "type_traits.h" #include "utility.h" #include "functional.h" +#include "type_list.h" #include "private/tuple_element.h" #include "private/tuple_size.h" @@ -131,6 +132,7 @@ namespace etl using value_type = void; ///< The type contained by this tuple. using this_type = tuple<>; ///< The type of this tuple. using base_type = void; ///< The type of the base tuple. + using type_list = etl::type_list<>; ///< The type list for this tuple. using index_sequence_type = etl::make_index_sequence<0>; ///< The index_sequence type for this tuple. //********************************* @@ -233,6 +235,7 @@ namespace etl using value_type = THead; ///< The type contained by this tuple. using this_type = tuple; ///< The type of this tuple. using base_type = tuple; ///< The type of the base tuple. + using type_list = etl::type_list; ///< The type list for this tuple. using index_sequence_type = etl::make_index_sequence()>; ///< The index_sequence type for this tuple. //********************************* @@ -1273,6 +1276,20 @@ namespace etl { lhs.swap(rhs); } + + //*************************************************************************** + /// Helper to turn etl::type_list into etl::tuple + template + struct tuple_from_type_list; + + template + struct tuple_from_type_list> + { + using type = etl::tuple; + }; + + template + using tuple_from_type_list_t = typename tuple_from_type_list::type; } namespace std diff --git a/include/etl/type_list.h b/include/etl/type_list.h index 84e241ec..19eddc15 100644 --- a/include/etl/type_list.h +++ b/include/etl/type_list.h @@ -53,6 +53,17 @@ namespace etl template struct type_list; + template + struct is_type_list : etl::false_type {}; + + template + struct is_type_list> : etl::true_type {}; + +#if ETL_USING_CPP17 + template + inline constexpr bool is_type_list_v = is_type_list::value; +#endif + //*************************************************************************** /// The empty type list. //*************************************************************************** diff --git a/include/etl/utility.h b/include/etl/utility.h index c3ea993d..46dc6003 100644 --- a/include/etl/utility.h +++ b/include/etl/utility.h @@ -565,8 +565,31 @@ namespace etl template using make_index_sequence = typename private_integer_sequence::make_index_sequence>::type; + //*********************************** + // Helper to support both parameter packs and etl::type_list + // Forward declare etl::type_list to allow use without including type_list.h template - using make_index_sequence_for = typename private_integer_sequence::make_index_sequence>::type; + struct type_list; + + namespace private_make_index_sequence_for + { + // Generic pack form + template + struct impl + { + using type = typename private_integer_sequence::make_index_sequence>::type; + }; + + // etl::type_list form + template + struct impl> : impl + { + }; + } + + // Accepts either a parameter pack of types or a single etl::type_list + template + using make_index_sequence_for = typename private_make_index_sequence_for::impl::type; //*********************************** template diff --git a/include/etl/visitor.h b/include/etl/visitor.h index 2975c678..c9ee698e 100644 --- a/include/etl/visitor.h +++ b/include/etl/visitor.h @@ -33,6 +33,7 @@ SOFTWARE. #include "platform.h" #include "type_traits.h" +#include "type_list.h" //***************************************************************************** ///\defgroup visitor visitor @@ -64,6 +65,7 @@ namespace etl using visitable::accept; using visitable::accept; + using type_list = etl::type_list; }; //***************************************************************** @@ -75,11 +77,27 @@ namespace etl { public: + using type_list = etl::type_list; + virtual ~visitable() = default; virtual void accept(T1&) = 0; }; + //*************************************************************************** + /// Helper to turn etl::type_list into etl::visitable + template + struct visitable_from_type_list; + + template + struct visitable_from_type_list> + { + using type = etl::visitable; + }; + + template + using visitable_from_type_list_t = typename visitable_from_type_list::type; + #else //***************************************************************** @@ -187,6 +205,20 @@ namespace etl virtual void visit(T1) = 0; }; + //*************************************************************************** + /// Helper to turn etl::type_list into etl::visitor + template + struct visitor_from_type_list; + + template + struct visitor_from_type_list> + { + using type = etl::visitor; + }; + + template + using visitor_from_type_list_t = typename visitor_from_type_list::type; + #else //***************************************************************** diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 899ae047..aa4e2aac 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -86,7 +86,6 @@ add_executable(etl_tests test_closure.cpp test_closure_constexpr.cpp test_compare.cpp - test_concepts.cpp test_constant.cpp test_const_map.cpp test_const_map_constexpr.cpp @@ -184,7 +183,6 @@ add_executable(etl_tests test_flat_multiset.cpp test_flat_set.cpp test_fnv_1.cpp - test_format.cpp test_format_spec.cpp test_forward_list.cpp test_forward_list_shared_pool.cpp @@ -196,12 +194,10 @@ add_executable(etl_tests test_hash.cpp test_hfsm.cpp test_hfsm_recurse_to_inner_state_on_start.cpp - test_hfsm_transition_on_enter.cpp test_histogram.cpp test_index_of_type.cpp test_indirect_vector.cpp test_indirect_vector_external_buffer.cpp - test_inplace_function.cpp test_instance_count.cpp test_integral_limits.cpp test_intrusive_forward_list.cpp @@ -210,9 +206,7 @@ add_executable(etl_tests test_intrusive_queue.cpp test_intrusive_stack.cpp test_invert.cpp - test_invoke.cpp test_io_port.cpp - test_is_invocable.cpp test_iterator.cpp test_jenkins.cpp test_largest.cpp @@ -263,7 +257,6 @@ add_executable(etl_tests test_poly_span_fixed_extent.cpp test_pool.cpp test_pool_external_buffer.cpp - test_print.cpp test_priority_queue.cpp test_pseudo_moving_average.cpp test_quantize.cpp @@ -292,7 +285,6 @@ add_executable(etl_tests test_scaled_rounding.cpp test_set.cpp test_shared_message.cpp - test_signal.cpp test_singleton.cpp test_singleton_base.cpp test_smallest.cpp diff --git a/test/etl_profile.h b/test/etl_profile.h index b6089a40..16d73d1b 100644 --- a/test/etl_profile.h +++ b/test/etl_profile.h @@ -110,6 +110,7 @@ SOFTWARE. #define ETL_FLAT_SET_FORCE_CPP03_IMPLEMENTATION #define ETL_FLAT_MULTISET_FORCE_CPP03_IMPLEMENTATION #define ETL_VARIANT_POOL_FORCE_CPP03_IMPLEMENTATION + #define ETL_VISITOR_FORCE_CPP03_IMPLEMENTATION #endif #if defined(ETL_FORCE_TEST_CPP11) diff --git a/test/test_message_packet.cpp b/test/test_message_packet.cpp index 206d9f8c..b28191fa 100644 --- a/test/test_message_packet.cpp +++ b/test/test_message_packet.cpp @@ -29,7 +29,6 @@ SOFTWARE. #include "unit_test_framework.h" #include "etl/platform.h" - #include "etl/message_packet.h" #include @@ -200,8 +199,15 @@ namespace { }; + using NullPacket = etl::message_packet<>; + using Packet = etl::message_packet; +#if ETL_USING_CPP17 && !defined(ETL_MESSAGE_PACKET_FORCE_CPP03_IMPLEMENTATION) + using MessageTypes = etl::type_list; + using PacketFromMessageTypes = etl::message_packet_from_type_list_t; +#endif + struct Object { void Push(const etl::message_packet& p) @@ -224,12 +230,22 @@ namespace Packet packet1(message1); Packet packet2(message2); +#if ETL_USING_CPP17 && !defined(ETL_MESSAGE_PACKET_FORCE_CPP03_IMPLEMENTATION) + PacketFromMessageTypes packet3(message3); +#else Packet packet3(message3); +#endif // Should cause a static assert. //Packet packet4(message4); //Packet packet4((Message4())); + CHECK_TRUE((std::is_same, typename Packet::message_types>::value)); +#if ETL_USING_CPP17 && !defined(ETL_MESSAGE_PACKET_FORCE_CPP03_IMPLEMENTATION) + CHECK_TRUE((std::is_same, typename PacketFromMessageTypes::message_types>::value)); +#endif + CHECK_TRUE((std::is_same, typename NullPacket::message_types>::value)); + CHECK_EQUAL(MESSAGE1, packet1.get().get_message_id()); CHECK_EQUAL(MESSAGE2, packet2.get().get_message_id()); CHECK_EQUAL(MESSAGE3, packet3.get().get_message_id()); diff --git a/test/test_message_router.cpp b/test/test_message_router.cpp index e7d94ac1..cfe2e9b1 100644 --- a/test/test_message_router.cpp +++ b/test/test_message_router.cpp @@ -49,6 +49,7 @@ namespace enum { + ROUTER0, ROUTER1, ROUTER2, ROUTER3 @@ -120,9 +121,28 @@ namespace Message5 message5; //*************************************************************************** - // Router that handles messages 1, 2, 3, 4 and 5 and returns nothing. + // Router that handles no messages. //*************************************************************************** + class Router0 : public etl::message_router + { + public: + + Router0() + : message_router(ROUTER0) + { + } + }; + + //*************************************************************************** + // Router that handles messages 1, 2, 3, 4 and 5 and returns nothing. + // Created from a type list. + //*************************************************************************** +#if ETL_USING_CPP17 && !defined(ETL_MESSAGE_ROUTER_FORCE_CPP03_IMPLEMENTATION) + using Router1Messages = etl::type_list; + class Router1 : public etl::message_router_from_type_list_t +#else class Router1 : public etl::message_router +#endif { public: @@ -135,7 +155,6 @@ namespace message_unknown_count(0), callback_count(0) { - } void on_receive(const Message1& msg) @@ -334,14 +353,14 @@ namespace Router1 r1; Router2 r2; - p_router = &r1; - Message1 message1(r2); Message2 message2(r2); Message3 message3(r2); Message4 message4(r2); - // CHECK(!r1.is_null_router()); + p_router = &r1; + + CHECK(!r1.is_null_router()); CHECK(r1.is_producer()); CHECK(r1.is_consumer()); @@ -378,6 +397,59 @@ namespace CHECK_EQUAL(4, r2.callback_count); } + //************************************************************************* + TEST(message_router_with_no_message_types) + { + Router0 r0; + Router1 r1; + Router2 r2; + + Message1 message1(r2); + Message2 message2(r2); + Message3 message3(r2); + Message4 message4(r2); + + r0.append_successor(r1); // All messages are passed to r1. + + CHECK_TRUE((etl::is_same>::value)); + + CHECK(!r0.is_null_router()); + CHECK(r0.is_producer()); + CHECK(r0.is_consumer()); + + r1.receive(message1); + CHECK_EQUAL(1, r1.message1_count); + CHECK_EQUAL(0, r1.message2_count); + CHECK_EQUAL(0, r1.message3_count); + CHECK_EQUAL(0, r1.message4_count); + CHECK_EQUAL(0, r1.message_unknown_count); + CHECK_EQUAL(1, r2.callback_count); + + r1.receive(message2); + CHECK_EQUAL(1, r1.message1_count); + CHECK_EQUAL(1, r1.message2_count); + CHECK_EQUAL(0, r1.message3_count); + CHECK_EQUAL(0, r1.message4_count); + CHECK_EQUAL(0, r1.message_unknown_count); + CHECK_EQUAL(2, r2.callback_count); + + r1.receive(message3); + CHECK_EQUAL(1, r1.message1_count); + CHECK_EQUAL(1, r1.message2_count); + CHECK_EQUAL(1, r1.message3_count); + CHECK_EQUAL(0, r1.message4_count); + CHECK_EQUAL(0, r1.message_unknown_count); + CHECK_EQUAL(3, r2.callback_count); + + r1.receive(message4); + CHECK_EQUAL(1, r1.message1_count); + CHECK_EQUAL(1, r1.message2_count); + CHECK_EQUAL(1, r1.message3_count); + CHECK_EQUAL(1, r1.message4_count); + CHECK_EQUAL(0, r1.message_unknown_count); + CHECK_EQUAL(4, r2.callback_count); + } + //************************************************************************* TEST(message_null_router) { @@ -389,7 +461,7 @@ namespace Message3 message3(null_router); Message4 message4(null_router); - // CHECK(null_router.is_null_router()); + CHECK(null_router.is_null_router()); CHECK(!null_router.is_producer()); CHECK(!null_router.is_consumer()); diff --git a/test/test_nth_type.cpp b/test/test_nth_type.cpp index a5339292..843d7607 100644 --- a/test/test_nth_type.cpp +++ b/test/test_nth_type.cpp @@ -29,6 +29,7 @@ SOFTWARE. #include "unit_test_framework.h" #include "etl/nth_type.h" +#include "etl/type_list.h" #include namespace @@ -42,5 +43,15 @@ namespace CHECK((std::is_same, long>::value)); CHECK((std::is_same, double>::value)); } + + //************************************************************************* + TEST(test_nth_type_from_type_list) + { + using types = etl::type_list; + + CHECK((std::is_same, int>::value)); + CHECK((std::is_same, long>::value)); + CHECK((std::is_same, double>::value)); + } } } diff --git a/test/test_observer.cpp b/test/test_observer.cpp index bf08e17b..f8eb72ae 100644 --- a/test/test_observer.cpp +++ b/test/test_observer.cpp @@ -68,12 +68,24 @@ namespace // The Notification2 is passed by reference. // The Notification3 is passed by const reference. //***************************************************************************** - typedef etl::observer ObserverType; + using ObserverType = etl::observer; + +#if !defined(ETL_OBSERVER_FORCE_CPP03_IMPLEMENTATION) + //***************************************************************************** + // The observer base type. + // Declare what notifications you want to observe and how they are passed to 'notification'. + // The Notification1 is passed by value. + // The Notification2 is passed by reference. + // The Notification3 is passed by const reference. + //***************************************************************************** + using NotificationList = etl::type_list; + using ObserverFromTypeList = etl::observer_from_type_list_t; +#endif //***************************************************************************** // The observer base type that does not take a notification type. //***************************************************************************** - typedef etl::observer ObserverVoidIntType; + using ObserverVoidIntType = etl::observer; } //***************************************************************************** @@ -115,6 +127,29 @@ public: } }; +#if !defined(ETL_OBSERVER_FORCE_CPP03_IMPLEMENTATION) +//***************************************************************************** +// The concrete observable 3 class. +//***************************************************************************** +class Observable3 : public etl::observable +{ +public: + + Notification1 data1; + Notification2 data2; + Notification1& data3 = data1; + + //********************************* + // Notify all of the observers. + //********************************* + void send_notifications() + { + notify_observers(data3); + notify_observers(data2); + } +}; +#endif + //***************************************************************************** // The concrete observable 3 class. //***************************************************************************** @@ -229,6 +264,53 @@ public: int data3_count; }; +#if !defined(ETL_OBSERVER_FORCE_CPP03_IMPLEMENTATION) +//***************************************************************************** +// The third observer type. +// If any one of the overloads is missing or a parameter declaration is incorrect +// then the class will be 'abstract' and will not compile. +//***************************************************************************** +class Observer3 : public ObserverFromTypeList +{ +public: + + Observer3() + : data1_count(0) + , data2_count(0) + , data3_count(0) + { + } + + //******************************************* + // Notification1 is passed by value. + //******************************************* + void notification(Notification1 /*data1*/) + { + ++data1_count; + } + + //******************************************* + // Notification2 is passed by reference. + //******************************************* + void notification(Notification2& /*data2*/) + { + ++data2_count; + } + + //******************************************* + // Notification3 is passed by const reference. + //******************************************* + void notification(const Notification3& /*data3*/) + { + ++data3_count; + } + + int data1_count; + int data2_count; + int data3_count; +}; +#endif + //***************************************************************************** // The third observer type. // If any one of the overloads is missing or a parameter declaration is incorrect @@ -399,6 +481,27 @@ namespace CHECK_EQUAL(3, observer2.data1_count); } +#if !defined(ETL_OBSERVER_FORCE_CPP03_IMPLEMENTATION) + //************************************************************************* + TEST(test_observer_created_from_a_type_list) + { + // The observable objects. + Observable3 observable3; + + // The observer objects. + Observer3 observer3; + + observable3.add_observer(observer3); + + // Send the notifications. + observable3.send_notifications(); // Updates data1 & data2. + + CHECK_EQUAL(1, observer3.data1_count); + CHECK_EQUAL(1, observer3.data2_count); + CHECK_EQUAL(0, observer3.data3_count); + } +#endif + //************************************************************************* TEST(test_8_notifications) { diff --git a/test/test_tuple.cpp b/test/test_tuple.cpp index 1a292dcc..918541e0 100644 --- a/test/test_tuple.cpp +++ b/test/test_tuple.cpp @@ -31,6 +31,7 @@ SOFTWARE. #include "data.h" #include "etl/tuple.h" +#include "etl/type_list.h" #include #include @@ -103,6 +104,26 @@ namespace CHECK_TRUE((std::is_same>::value)); } + //************************************************************************* + TEST(test_tuple_from_type_list) + { + using TypeList = etl::type_list; + using TupleFromTypeList = etl::tuple_from_type_list_t; + using Tuple = etl::tuple; + + CHECK_TRUE((std::is_same::value)); + CHECK_TRUE((std::is_same::value)); + } + + //************************************************************************* + TEST(test_tuple_type_list) + { + using Tuple = etl::tuple; + using TupleTypes = etl::type_list; + + CHECK_TRUE((std::is_same::value)); + } + //************************************************************************* TEST(test_default_constructor) { diff --git a/test/test_type_list.cpp b/test/test_type_list.cpp index cd53981c..b8124dee 100644 --- a/test/test_type_list.cpp +++ b/test/test_type_list.cpp @@ -37,6 +37,24 @@ namespace #if ETL_USING_CPP11 SUITE(test_type_list) { + //************************************************************************* + TEST(test_is_type_list) + { + using t0 = etl::type_list<>; + using t1 = etl::type_list; + using t2 = int; + + CHECK_TRUE((etl::is_type_list::value)); + CHECK_TRUE((etl::is_type_list::value)); + CHECK_FALSE((etl::is_type_list::value)); + +#if ETL_USING_CPP17 + CHECK_TRUE((etl::is_type_list_v)); + CHECK_TRUE((etl::is_type_list_v)); + CHECK_FALSE((etl::is_type_list_v)); +#endif + } + //************************************************************************* TEST(test_type_list_select) { diff --git a/test/test_utility.cpp b/test/test_utility.cpp index 1757a499..2339587d 100644 --- a/test/test_utility.cpp +++ b/test/test_utility.cpp @@ -863,5 +863,39 @@ namespace CHECK_TRUE(E::B == (etl::nontype_t::value)); } #endif + + //********************************* + TEST(test_make_index_sequence_for_pack_matches_expected) + { + struct T1 {}; + struct T2 {}; + struct T3 {}; + + using seq12 = etl::make_index_sequence_for; + using seq123 = etl::make_index_sequence_for; + using expect12 = etl::index_sequence<0U, 1U>; + using expect123 = etl::index_sequence<0U, 1U, 2U>; + + CHECK_TRUE((std::is_same::value)); + CHECK_TRUE((std::is_same::value)); + } + + //********************************* + TEST(test_make_index_sequence_for_type_list_matches_expected) + { + struct T1 {}; + struct T2 {}; + struct T3 {}; + + using list12 = etl::type_list; + using list123 = etl::type_list; + using seq12 = etl::make_index_sequence_for; + using seq123 = etl::make_index_sequence_for; + using expect12 = etl::index_sequence<0U, 1U>; + using expect123 = etl::index_sequence<0U, 1U, 2U>; + + CHECK_TRUE((std::is_same::value)); + CHECK_TRUE((std::is_same::value)); + } } } diff --git a/test/test_variant_variadic.cpp b/test/test_variant_variadic.cpp index dc8373ac..bf684bcd 100644 --- a/test/test_variant_variadic.cpp +++ b/test/test_variant_variadic.cpp @@ -427,10 +427,10 @@ namespace { TEST(test_alignment) { - typedef etl::variant test_variant_a; - typedef etl::variant test_variant_b; - typedef etl::variant test_variant_c; - typedef etl::variant test_variant_d; + using test_variant_a = etl::variant; + using test_variant_b = etl::variant; + using test_variant_c = etl::variant; + using test_variant_d = etl::variant; static test_variant_a a(char('1')); static test_variant_b b(short(2)); @@ -443,6 +443,48 @@ namespace CHECK((uintptr_t(&etl::get(d)) % uintptr_t(etl::alignment_of::value)) == 0); } + //************************************************************************* + TEST(test_variant_from_type_list) + { + struct DefaultConstructible + { + DefaultConstructible() + : value(1) + { + } + + int value = 0; + }; + + using type_list = etl::type_list; + using test_variant_t = etl::variant_from_type_list_t; + using normal_variant_t = etl::variant; + + CHECK_NO_THROW(test_variant_t variant_etl); + + test_variant_t variant_etl; + + // Are the type lists the same? + CHECK_TRUE((std::is_same::value)); + + // Are the variants the same? + CHECK_TRUE((std::is_same::value)); + + CHECK_TRUE(etl::holds_alternative(variant_etl)); + CHECK_FALSE(etl::holds_alternative(variant_etl)); + CHECK_FALSE(etl::holds_alternative(variant_etl)); + CHECK_EQUAL(1, etl::get<0U>(variant_etl).value); + + CHECK_TRUE(etl::holds_alternative<0U>(variant_etl)); + CHECK_FALSE(etl::holds_alternative<1U>(variant_etl)); + CHECK_FALSE(etl::holds_alternative<2U>(variant_etl)); + + CHECK_TRUE(etl::holds_alternative(0U, variant_etl)); + CHECK_FALSE(etl::holds_alternative(1U, variant_etl)); + CHECK_FALSE(etl::holds_alternative(2U, variant_etl)); + CHECK_FALSE(etl::holds_alternative(99U, variant_etl)); + } + //************************************************************************* TEST(test_constructor_default) { @@ -462,6 +504,8 @@ namespace test_variant_t variant_etl; + CHECK_TRUE((std::is_same>::value)); + CHECK_TRUE(etl::holds_alternative(variant_etl)); CHECK_FALSE(etl::holds_alternative(variant_etl)); CHECK_FALSE(etl::holds_alternative(variant_etl)); diff --git a/test/test_visitor.cpp b/test/test_visitor.cpp index 4ea72356..1ab34f77 100644 --- a/test/test_visitor.cpp +++ b/test/test_visitor.cpp @@ -746,6 +746,32 @@ namespace CHECK_FALSE(etl::is_visitor::value); #endif } + +#if !defined(ETL_VISITOR_FORCE_CPP03_IMPLEMENTATION) + //************************************************************************* + TEST(test_visitor_from_type_list) + { + using Visitor1 = etl::visitor; + + using TypeList = etl::type_list; + using Visitor2 = etl::visitor_from_type_list_t; + + CHECK_TRUE((std::is_same::value)); + } +#endif + +#if !defined(ETL_VISITOR_FORCE_CPP03_IMPLEMENTATION) + //************************************************************************* + TEST(test_visitable_from_type_list) + { + using Visitable1 = etl::visitable; + + using TypeList = etl::type_list; + using Visitable2 = etl::visitable_from_type_list_t; + + CHECK_TRUE((std::is_same::value)); + } +#endif } } From 992348b4bb2d0de2bdc5fa3d35ee83625caff69a Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Thu, 19 Feb 2026 14:18:12 +0100 Subject: [PATCH 6/6] Add constexpr to array comparison operators (#1303) * Remove AppVeyor build status badge Removed AppVeyor build status badge from README. * Update README.md * Update CONTRIBUTING.md Updated the instructions for contributing. * Fix for issue 1276 "Data corruption in the etl::bip_buffer_spsc_atomic" (#1277) * Reproduce data corruption bug in the `etl::bip_buffer_spsc_atomic`. * Fix data corruption bug in the `etl::bip_buffer_spsc_atomic`. * Add constexpr to array comparison operators --------- Co-authored-by: John Wellbelove Co-authored-by: Sergei --- include/etl/array.h | 4 ++-- test/test_array.cpp | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/include/etl/array.h b/include/etl/array.h index efb2ce04..f1624209 100644 --- a/include/etl/array.h +++ b/include/etl/array.h @@ -1136,7 +1136,7 @@ namespace etl ///\return true if the arrays are equal, otherwise false //************************************************************************* template - bool operator ==(const etl::array& lhs, const etl::array& rhs) + ETL_CONSTEXPR14 bool operator ==(const etl::array& lhs, const etl::array& rhs) { return etl::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin()); } @@ -1148,7 +1148,7 @@ namespace etl ///\return true if the arrays are not equal, otherwise false //************************************************************************* template - bool operator !=(const etl::array& lhs, const etl::array& rhs) + ETL_CONSTEXPR14 bool operator !=(const etl::array& lhs, const etl::array& rhs) { return !(lhs == rhs); } diff --git a/test/test_array.cpp b/test/test_array.cpp index 9edd0719..27eaf674 100644 --- a/test/test_array.cpp +++ b/test/test_array.cpp @@ -631,6 +631,16 @@ namespace CHECK(data1 == data2); } + //************************************************************************* + TEST(test_equal_constexpr) + { + ETL_CONSTEXPR14 Data data1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + ETL_CONSTEXPR14 Data data2 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + ETL_CONSTEXPR14 bool result = (data1 == data2); + CHECK(result); + } + //************************************************************************* TEST(test_not_equal) { @@ -640,6 +650,16 @@ namespace CHECK(data1 != data2); } + //************************************************************************* + TEST(test_not_equal_constexpr) + { + ETL_CONSTEXPR14 Data data1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + ETL_CONSTEXPR14 Data data2 = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; + + ETL_CONSTEXPR14 bool result = (data1 != data2); + CHECK(result); + } + //************************************************************************* TEST(test_less_than) {