From 14b3f4d33526c43a0a0c0ac26e8acb3ff58507e3 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sat, 12 Sep 2026 11:26:53 +0100 Subject: [PATCH 1/4] Sponsorship message updates --- docs/about.md | 2 +- ...nancial-case-for-commercial-sponsorship.md | 59 +++++++++++++++++++ hugo/layouts/shortcodes/safe-email.html | 18 ++++++ 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 docs/business-and-financial-case-for-commercial-sponsorship.md create mode 100644 hugo/layouts/shortcodes/safe-email.html diff --git a/docs/about.md b/docs/about.md index 4c2bf907..47317f47 100644 --- a/docs/about.md +++ b/docs/about.md @@ -1,6 +1,6 @@ --- title: "About" -weight: 3003 +weight: 3004 --- ## About me diff --git a/docs/business-and-financial-case-for-commercial-sponsorship.md b/docs/business-and-financial-case-for-commercial-sponsorship.md new file mode 100644 index 00000000..aa7683f4 --- /dev/null +++ b/docs/business-and-financial-case-for-commercial-sponsorship.md @@ -0,0 +1,59 @@ +--- +title: "Why CFOs should care about Open Source" +description: "The business and financial case for commercially sponsoring the Embedded Template Library (ETL)." +date: 2026-09-12 +weight: 3003 +--- + +If your engineering team is using the **Embedded Template Library (ETL)** in commercial products, your company is benefiting from a critical piece of software infrastructure. + +While the ETL is free to use under the permissive MIT license, relying on open-source dependencies in commercial firmware without a formal support mechanism introduces significant **operational and financial risk**. + +This page outlines the business case for transitioning from a passive consumer to a formal commercial sponsor of the ETL. + +## 1. Massive Cost Avoidance (Build vs. Buy) + +Developing, testing, and maintaining a custom, heap-free, deterministic C++ template library internally is a massive financial undertaking. To replicate the functionality, safety, and testing maturity of the ETL would require: + +* At least **1 to 2 senior embedded software engineers** working full-time for over a year. +* An estimated internal cost exceeding **$100,000 – $200,000+** in salaries, benefits, and overhead. + +By utilizing the ETL, your company has already saved six figures in upfront R&D costs. Contributing a fraction of those savings back to the project ensures the tool remains actively maintained. + +## 2. Supply Chain Risk Mitigation + +The ETL is an industry-standard library used in automotive, medical, aerospace, and consumer electronics globally. However, it is maintained by a single key architect. + +* **The "Key Person" Risk:** If the project is abandoned due to a lack of financial sustainability, your engineering team is left to maintain a massive, complex codebase alone. +* **Toolchain Evolution:** As compiler standards shift (such as transitioning codebases to C++20 and C++23), the library requires ongoing engineering to prevent compilation failures and optimization regressions. + +Sponsorship is not a charitable donation; it is a **risk mitigation premium** that secures the longevity of your product's software foundation. + +## 3. Compliance and Legal Safety + +Many corporate finance and legal departments are hesitant to use software that lacks a clear paper trail or accountability framework. + +* We provide standard, professional **corporate invoices** for accounting and tax compliance. +* We accept **direct bank transfers** across multiple major global currencies via standard SWIFT/IBAN channels. +* Funding the library ensures it stays compliant with modern software supply chain standards (such as SBOM tracking and security vulnerability scanning). + +## How to Establish Commercial Support + +To simplify corporate procurement, we offer a streamlined vendor onboarding path that fits standard corporate accounting: + +### Production Partner Tier +* **Financial Commitment:** An amount such as ~€300 / month (Available as an annual invoice to minimize recurring administrative paperwork). +* **Deliverables:** A formalized corporate invoice sent to your accounts payable department, legal recognition of your company as a commercial backer, and direct risk reduction for your engineering stack. + +### Frictionless Corporate Billing (Via GitHub) +If your engineering department already uses GitHub Enterprise, you can completely bypass corporate vendor onboarding. You can sponsor the ETL directly through **GitHub Sponsors for Organizations**. This adds the contribution directly to your **existing Microsoft/GitHub corporate invoice**, allowing middle managers to sign off on the expense immediately without involving procurement. + +--- + +> "Relying on a critical open-source dependency without supporting its creator is an unmitigated business risk. Securing your software supply chain is a fundamental fiduciary duty." + +--- + +For corporate invoicing setups please contact the author directly, or see the instructions [**here**]({{< relref "is-the-etl-free/#how-to-sponsor.md" >}}). + +**{{< safe-email email="corporate.support@etlcpp.com" >}}** diff --git a/hugo/layouts/shortcodes/safe-email.html b/hugo/layouts/shortcodes/safe-email.html new file mode 100644 index 00000000..34d737e6 --- /dev/null +++ b/hugo/layouts/shortcodes/safe-email.html @@ -0,0 +1,18 @@ +{{ $email := .Get "email" | base64Encode }} +Contact Me + + From 2e2c5d01999f2a9807f756fdf84b4a39fc492cc9 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Thu, 17 Sep 2026 12:38:18 +0200 Subject: [PATCH 2/4] Implement spaceship operator <=> for No-STL case (#1604) This was disabled explicitly in recent commit 232c948674cff1b4b72c2d6b3acfa7342d096dd1 to prevent using the header from STL. Now, re-enabling by implementing an ETL version of it in compare.h --- docs/utilities/compare.md | 103 ++++++- include/etl/chrono.h | 1 + include/etl/compare.h | 295 +++++++++++++++++++- include/etl/private/chrono/day.h | 4 +- include/etl/private/chrono/duration.h | 32 ++- include/etl/private/chrono/month.h | 4 +- include/etl/private/chrono/month_day.h | 6 +- include/etl/private/chrono/time_point.h | 2 +- include/etl/private/chrono/year.h | 4 +- include/etl/private/chrono/year_month.h | 2 +- include/etl/private/chrono/year_month_day.h | 4 +- test/test_chrono_day.cpp | 2 +- test/test_chrono_duration.cpp | 33 ++- test/test_chrono_month.cpp | 2 +- test/test_chrono_month_day.cpp | 8 +- test/test_chrono_month_day_last.cpp | 14 + test/test_chrono_time_point.cpp | 4 +- test/test_chrono_year.cpp | 2 +- test/test_chrono_year_month.cpp | 12 +- test/test_chrono_year_month_day.cpp | 16 +- test/test_chrono_year_month_day_last.cpp | 17 ++ test/test_compare.cpp | 148 ++++++++++ 22 files changed, 667 insertions(+), 48 deletions(-) diff --git a/docs/utilities/compare.md b/docs/utilities/compare.md index f6062c73..54d904e5 100644 --- a/docs/utilities/compare.md +++ b/docs/utilities/compare.md @@ -7,23 +7,32 @@ title: "compare" Since: `TBC` {{< /callout >}} -A helper class that defines `<=`, `>`, `>=` in terms of `etl::less` or a user supplied compare type. +A helper for deriving comparison operations from a strict weak ordering. +`etl::compare` exposes boolean predicates and a three-state comparison result. +`etl::three_way_compare` returns `-1`, `0`, or `1` using the same comparison policy. ```cpp template > struct compare ``` +```cpp +template > +ETL_CONSTEXPR14 int three_way_compare(const T& lhs, const T& rhs) +``` + ## Types ```cpp first_argument_type; second_argument_type; -result_type; +cmp_result; ``` +`cmp_result` has the values `Less`, `Equal`, and `Greater`. + ## Members ```cpp -static result_type lt(first_argument_type lhs, second_argument_type rhs) +static bool lt(first_argument_type lhs, second_argument_type rhs) ``` **Return** `true` if `lhs < rhs`, otherwise `false`. @@ -31,7 +40,7 @@ static result_type lt(first_argument_type lhs, second_argument_type rhs) --- ```cpp -static result_type lte(first_argument_type lhs, second_argument_type rhs) +static bool lte(first_argument_type lhs, second_argument_type rhs) ``` **Return** `true` if `lhs <= rhs`, otherwise `false`. @@ -39,7 +48,7 @@ static result_type lte(first_argument_type lhs, second_argument_type rhs) --- ```cpp -static result_type gt(first_argument_type lhs, second_argument_type rhs) +static bool gt(first_argument_type lhs, second_argument_type rhs) ``` **Return** `true` if `lhs > rhs`, otherwise `false`. @@ -47,14 +56,74 @@ static result_type gt(first_argument_type lhs, second_argument_type rhs) --- ```cpp -static result_type gte(first_argument_type lhs, second_argument_type rhs) +static bool gte(first_argument_type lhs, second_argument_type rhs) ``` **Return** `true` if `lhs >= rhs`, otherwise `false`. +--- + +```cpp +static bool eq(first_argument_type lhs, second_argument_type rhs) +``` +**Return** +`true` if `lhs == rhs`, otherwise `false`. + +--- + +```cpp +static bool ne(first_argument_type lhs, second_argument_type rhs) +``` +**Return** +`true` if `lhs != rhs`, otherwise `false`. + +--- + +```cpp +static cmp_result cmp(first_argument_type lhs, second_argument_type rhs) +``` +**Return** +`Less`, `Equal`, or `Greater`. + +## Three-way comparison + +`etl::three_way_compare(lhs, rhs)` returns the same ordering as `compare::cmp(lhs, rhs)`, +but as an `int` with the values `-1`, `0`, or `1`. + +## C++20 comparison categories + +When compiling as C++20, `compare.h` makes the comparison categories available in the `etl` +namespace, so ETL and user code can name them regardless of the STL configuration. + +```cpp +class strong_ordering; +class weak_ordering; +class partial_ordering; +``` + +* When the STL is available these are aliases for `std::strong_ordering`, `std::weak_ordering` + and `std::partial_ordering`. +* When the STL is not available, `compare.h` provides its own minimal implementations that + follow the standard semantics: the named values `less`, `equivalent`, `greater` + (plus `equal` for `strong_ordering` and `unordered` for `partial_ordering`), comparison + against the literal `0` only, and the implicit conversions + `strong_ordering` → `weak_ordering` → `partial_ordering`. + +An `unordered` result compares `false` against `0` for all of `<`, `<=`, `==`, `>=` and `>`. + +```cpp +ETL_CONSTEXPR strong_ordering make_strong_ordering(int value) ETL_NOEXCEPT; +ETL_CONSTEXPR weak_ordering make_weak_ordering(int value) ETL_NOEXCEPT; +ETL_CONSTEXPR partial_ordering make_partial_ordering(int value) ETL_NOEXCEPT; +``` + +These helpers convert a three-way comparison result, such as the one returned by +`etl::three_way_compare`, into a comparison category. Negative values map to `less`, positive +values to `greater`, and zero to `equal`/`equivalent`. `unordered` cannot be expressed as an +integer result, so use `etl::partial_ordering::unordered` directly for that case. + ## Example 1 ```cpp -using std::less struct Test { int a; @@ -84,6 +153,11 @@ bool operator >=(const Test& lhs, const Test& rhs) { return Compare::gte(lhs, rhs); } + +int operator <=>(const Test& lhs, const Test& rhs) +{ + return etl::three_way_compare(lhs, rhs); +} ``` ## Example 2 @@ -124,6 +198,11 @@ bool operator >=(const Test& lhs, const Test& rhs) { return Compare::gte(lhs, rhs); } + +int operator <=>(const Test& lhs, const Test& rhs) +{ + return etl::three_way_compare(lhs, rhs); +} ``` ## Example 3 @@ -158,3 +237,13 @@ bool operator >=(const Test& lhs, const Test& rhs) return Test::gte(lhs, rhs); } ``` + +## Example 4 +C++20, with or without the STL. + +```cpp +etl::strong_ordering operator <=>(const Test& lhs, const Test& rhs) +{ + return etl::make_strong_ordering(etl::three_way_compare(lhs, rhs)); +} +``` diff --git a/include/etl/chrono.h b/include/etl/chrono.h index b452ca4e..ab360108 100644 --- a/include/etl/chrono.h +++ b/include/etl/chrono.h @@ -37,6 +37,7 @@ SOFTWARE. #if ETL_USING_CPP11 + #include "compare.h" #include "hash.h" #include "integral_limits.h" #include "type_traits.h" diff --git a/include/etl/compare.h b/include/etl/compare.h index 918465bd..f909291c 100644 --- a/include/etl/compare.h +++ b/include/etl/compare.h @@ -35,6 +35,10 @@ SOFTWARE. #include "functional.h" #include "parameter_type.h" +#if ETL_USING_CPP20 && ETL_USING_STL + #include +#endif + //***************************************************************************** ///\defgroup compare compare /// Comparisons only using less than operator @@ -42,6 +46,295 @@ SOFTWARE. //***************************************************************************** namespace etl { +#if ETL_USING_CPP20 + #if ETL_USING_STL + //*************************************************************************** + /// The STL supplies the C++20 comparison categories, so simply expose them + /// through the etl namespace. This allows ETL code and user code to refer to + /// etl::strong_ordering (etc.) regardless of the STL configuration. + //*************************************************************************** + using std::partial_ordering; + using std::strong_ordering; + using std::weak_ordering; + #else + namespace private_compare + { + //************************************************************************* + /// A type that can only be constructed from the literal 0. + /// Used to constrain the comparison-category operators to comparisons + /// against the literal 0, as required by the standard. + //************************************************************************* + struct only_literal_zero + { + ETL_CONSTEXPR only_literal_zero(only_literal_zero*) ETL_NOEXCEPT {} + }; + + typedef signed char ordering_value_type; + + inline constexpr ordering_value_type ordering_less = -1; + inline constexpr ordering_value_type ordering_equivalent = 0; + inline constexpr ordering_value_type ordering_greater = 1; + inline constexpr ordering_value_type ordering_unordered = 2; + } // namespace private_compare + + //*************************************************************************** + /// Minimal partial-ordering category for C++20 three-way comparisons when the + /// STL is unavailable. + /// Unlike the other categories this one has an 'unordered' state, for which + /// every comparison against 0 is false. + //*************************************************************************** + class partial_ordering + { + public: + + static const partial_ordering less; + static const partial_ordering equivalent; + static const partial_ordering greater; + static const partial_ordering unordered; + + friend ETL_CONSTEXPR bool operator==(partial_ordering lhs, partial_ordering rhs) ETL_NOEXCEPT = default; + + friend ETL_CONSTEXPR bool operator==(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value == private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator<(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value == private_compare::ordering_less; + } + friend ETL_CONSTEXPR bool operator>(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value == private_compare::ordering_greater; + } + friend ETL_CONSTEXPR bool operator<=(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return (v.value == private_compare::ordering_less) || (v.value == private_compare::ordering_equivalent); + } + friend ETL_CONSTEXPR bool operator>=(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return (v.value == private_compare::ordering_greater) || (v.value == private_compare::ordering_equivalent); + } + + friend ETL_CONSTEXPR bool operator<(private_compare::only_literal_zero, partial_ordering v) ETL_NOEXCEPT + { + return v > 0; + } + friend ETL_CONSTEXPR bool operator>(private_compare::only_literal_zero, partial_ordering v) ETL_NOEXCEPT + { + return v < 0; + } + friend ETL_CONSTEXPR bool operator<=(private_compare::only_literal_zero, partial_ordering v) ETL_NOEXCEPT + { + return v >= 0; + } + friend ETL_CONSTEXPR bool operator>=(private_compare::only_literal_zero, partial_ordering v) ETL_NOEXCEPT + { + return v <= 0; + } + + private: + + explicit ETL_CONSTEXPR partial_ordering(private_compare::ordering_value_type value_) ETL_NOEXCEPT + : value(value_) + { + } + + friend class weak_ordering; + friend class strong_ordering; + + private_compare::ordering_value_type value; + }; + + inline constexpr partial_ordering partial_ordering::less(private_compare::ordering_less); + inline constexpr partial_ordering partial_ordering::equivalent(private_compare::ordering_equivalent); + inline constexpr partial_ordering partial_ordering::greater(private_compare::ordering_greater); + inline constexpr partial_ordering partial_ordering::unordered(private_compare::ordering_unordered); + + //*************************************************************************** + /// Minimal weak-ordering category for C++20 three-way comparisons when the + /// STL is unavailable. + //*************************************************************************** + class weak_ordering + { + public: + + static const weak_ordering less; + static const weak_ordering equivalent; + static const weak_ordering greater; + + ETL_CONSTEXPR operator partial_ordering() const ETL_NOEXCEPT + { + return partial_ordering(value); + } + + friend ETL_CONSTEXPR bool operator==(weak_ordering lhs, weak_ordering rhs) ETL_NOEXCEPT = default; + + friend ETL_CONSTEXPR bool operator==(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value == private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator<(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value < private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator>(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value > private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator<=(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value <= private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator>=(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value >= private_compare::ordering_equivalent; + } + + friend ETL_CONSTEXPR bool operator<(private_compare::only_literal_zero, weak_ordering v) ETL_NOEXCEPT + { + return v > 0; + } + friend ETL_CONSTEXPR bool operator>(private_compare::only_literal_zero, weak_ordering v) ETL_NOEXCEPT + { + return v < 0; + } + friend ETL_CONSTEXPR bool operator<=(private_compare::only_literal_zero, weak_ordering v) ETL_NOEXCEPT + { + return v >= 0; + } + friend ETL_CONSTEXPR bool operator>=(private_compare::only_literal_zero, weak_ordering v) ETL_NOEXCEPT + { + return v <= 0; + } + + private: + + explicit ETL_CONSTEXPR weak_ordering(private_compare::ordering_value_type value_) ETL_NOEXCEPT + : value(value_) + { + } + + friend class strong_ordering; + + private_compare::ordering_value_type value; + }; + + inline constexpr weak_ordering weak_ordering::less(private_compare::ordering_less); + inline constexpr weak_ordering weak_ordering::equivalent(private_compare::ordering_equivalent); + inline constexpr weak_ordering weak_ordering::greater(private_compare::ordering_greater); + + //*************************************************************************** + /// Minimal strong-ordering category for C++20 three-way comparisons when the + /// STL is unavailable. + //*************************************************************************** + class strong_ordering + { + public: + + static const strong_ordering less; + static const strong_ordering equal; + static const strong_ordering equivalent; + static const strong_ordering greater; + + ETL_CONSTEXPR operator partial_ordering() const ETL_NOEXCEPT + { + return partial_ordering(value); + } + + ETL_CONSTEXPR operator weak_ordering() const ETL_NOEXCEPT + { + return weak_ordering(value); + } + + friend ETL_CONSTEXPR bool operator==(strong_ordering lhs, strong_ordering rhs) ETL_NOEXCEPT = default; + + friend ETL_CONSTEXPR bool operator==(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value == private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator<(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value < private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator>(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value > private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator<=(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value <= private_compare::ordering_equivalent; + } + friend ETL_CONSTEXPR bool operator>=(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT + { + return v.value >= private_compare::ordering_equivalent; + } + + friend ETL_CONSTEXPR bool operator<(private_compare::only_literal_zero, strong_ordering v) ETL_NOEXCEPT + { + return v > 0; + } + friend ETL_CONSTEXPR bool operator>(private_compare::only_literal_zero, strong_ordering v) ETL_NOEXCEPT + { + return v < 0; + } + friend ETL_CONSTEXPR bool operator<=(private_compare::only_literal_zero, strong_ordering v) ETL_NOEXCEPT + { + return v >= 0; + } + friend ETL_CONSTEXPR bool operator>=(private_compare::only_literal_zero, strong_ordering v) ETL_NOEXCEPT + { + return v <= 0; + } + + private: + + explicit ETL_CONSTEXPR strong_ordering(private_compare::ordering_value_type value_) ETL_NOEXCEPT + : value(value_) + { + } + + private_compare::ordering_value_type value; + }; + + inline constexpr strong_ordering strong_ordering::less(private_compare::ordering_less); + inline constexpr strong_ordering strong_ordering::equal(private_compare::ordering_equivalent); + inline constexpr strong_ordering strong_ordering::equivalent(private_compare::ordering_equivalent); + inline constexpr strong_ordering strong_ordering::greater(private_compare::ordering_greater); + #endif + + //*************************************************************************** + /// Converts a three-way comparison result to an etl::strong_ordering. + /// Negative values map to 'less', positive values to 'greater' and zero to + /// 'equal'. + //*************************************************************************** + ETL_NODISCARD ETL_CONSTEXPR inline etl::strong_ordering make_strong_ordering(int value) ETL_NOEXCEPT + { + return (value < 0) ? etl::strong_ordering::less : (value > 0) ? etl::strong_ordering::greater : etl::strong_ordering::equal; + } + + //*************************************************************************** + /// Converts a three-way comparison result to an etl::weak_ordering. + /// Negative values map to 'less', positive values to 'greater' and zero to + /// 'equivalent'. + //*************************************************************************** + ETL_NODISCARD ETL_CONSTEXPR inline etl::weak_ordering make_weak_ordering(int value) ETL_NOEXCEPT + { + return (value < 0) ? etl::weak_ordering::less : (value > 0) ? etl::weak_ordering::greater : etl::weak_ordering::equivalent; + } + + //*************************************************************************** + /// Converts a three-way comparison result to an etl::partial_ordering. + /// Negative values map to 'less', positive values to 'greater' and zero to + /// 'equivalent'. + /// Note that 'unordered' cannot be expressed by an integer comparison result, + /// so use etl::partial_ordering::unordered directly for that case. + //*************************************************************************** + ETL_NODISCARD ETL_CONSTEXPR inline etl::partial_ordering make_partial_ordering(int value) ETL_NOEXCEPT + { + return (value < 0) ? etl::partial_ordering::less : (value > 0) ? etl::partial_ordering::greater : etl::partial_ordering::equivalent; + } +#endif + //*************************************************************************** /// Defines <=, >, >=, ==, !=, <=> in terms of < /// Default implementation of TLess is etl::less @@ -105,7 +398,7 @@ namespace etl #endif ETL_CONSTEXPR14 int three_way_compare(const T& lhs, const T& rhs) { - return etl::compare::cmp(lhs, rhs); + return etl::compare::cmp(lhs, rhs); } } // namespace etl diff --git a/include/etl/private/chrono/day.h b/include/etl/private/chrono/day.h index 1f903067..bbca6a59 100644 --- a/include/etl/private/chrono/day.h +++ b/include/etl/private/chrono/day.h @@ -211,11 +211,11 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::day& d1, const etl::chrono::day& d2) ETL_NOEXCEPT { - return (static_cast(d1) <=> static_cast(d2)); + return etl::make_strong_ordering(d1.compare(d2)); } #endif diff --git a/include/etl/private/chrono/duration.h b/include/etl/private/chrono/duration.h index cf48e170..fc0b6cb3 100644 --- a/include/etl/private/chrono/duration.h +++ b/include/etl/private/chrono/duration.h @@ -482,7 +482,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 template [[nodiscard]] constexpr auto operator<=>(const etl::chrono::duration& lhs, const etl::chrono::duration& rhs) ETL_NOEXCEPT @@ -492,7 +492,37 @@ namespace etl common_t l = etl::chrono::duration_cast(lhs); common_t r = etl::chrono::duration_cast(rhs); + #if ETL_USING_STL return (l.count() <=> r.count()); + #else + // The STL returns the ordering category of the representation type. + // Mirror that: a floating point representation is only partially ordered. + if constexpr (etl::is_floating_point::value) + { + if (l.count() < r.count()) + { + return etl::partial_ordering::less; + } + else if (r.count() < l.count()) + { + return etl::partial_ordering::greater; + } + else if ((l.count() <= r.count()) && (r.count() <= l.count())) + { + // Equivalent. Expressed without '==' so that -Wfloat-equal is not triggered. + return etl::partial_ordering::equivalent; + } + else + { + // At least one of the values is NaN. + return etl::partial_ordering::unordered; + } + } + else + { + return etl::make_strong_ordering((l.count() < r.count()) ? -1 : (r.count() < l.count()) ? 1 : 0); + } + #endif } #endif diff --git a/include/etl/private/chrono/month.h b/include/etl/private/chrono/month.h index 40c2e594..b166f509 100644 --- a/include/etl/private/chrono/month.h +++ b/include/etl/private/chrono/month.h @@ -224,11 +224,11 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::month& d1, const etl::chrono::month& d2) ETL_NOEXCEPT { - return (static_cast(d1) <=> static_cast(d2)); + return etl::make_strong_ordering(d1.compare(d2)); } #endif diff --git a/include/etl/private/chrono/month_day.h b/include/etl/private/chrono/month_day.h index f4f77628..e53f8ac8 100644 --- a/include/etl/private/chrono/month_day.h +++ b/include/etl/private/chrono/month_day.h @@ -178,7 +178,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::month_day& lhs, const etl::chrono::month_day& rhs) ETL_NOEXCEPT { @@ -298,11 +298,11 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::month_day_last& mdl1, const etl::chrono::month_day_last& mdl2) ETL_NOEXCEPT { - return (static_cast(mdl1.month()) <=> static_cast(mdl2.month())); + return mdl1.month() <=> mdl2.month(); } #endif } // namespace chrono diff --git a/include/etl/private/chrono/time_point.h b/include/etl/private/chrono/time_point.h index a0e79bb1..f90f1c61 100644 --- a/include/etl/private/chrono/time_point.h +++ b/include/etl/private/chrono/time_point.h @@ -353,7 +353,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 template [[nodiscard]] constexpr auto operator<=>(const etl::chrono::time_point& lhs, const etl::chrono::time_point& rhs) diff --git a/include/etl/private/chrono/year.h b/include/etl/private/chrono/year.h index e6cb3416..35f67853 100644 --- a/include/etl/private/chrono/year.h +++ b/include/etl/private/chrono/year.h @@ -240,11 +240,11 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::year& y1, const etl::chrono::year& y2) ETL_NOEXCEPT { - return (static_cast(y1) <=> static_cast(y2)); + return etl::make_strong_ordering(y1.compare(y2)); } #endif diff --git a/include/etl/private/chrono/year_month.h b/include/etl/private/chrono/year_month.h index 44bb9fd1..16b3a431 100644 --- a/include/etl/private/chrono/year_month.h +++ b/include/etl/private/chrono/year_month.h @@ -234,7 +234,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::year_month& lhs, const etl::chrono::year_month& rhs) ETL_NOEXCEPT { diff --git a/include/etl/private/chrono/year_month_day.h b/include/etl/private/chrono/year_month_day.h index 91088c3b..a0f42474 100644 --- a/include/etl/private/chrono/year_month_day.h +++ b/include/etl/private/chrono/year_month_day.h @@ -426,7 +426,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::year_month_day& lhs, const etl::chrono::year_month_day& rhs) ETL_NOEXCEPT { @@ -722,7 +722,7 @@ namespace etl //*********************************************************************** /// Spaceship operator //*********************************************************************** -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 [[nodiscard]] inline constexpr auto operator<=>(const etl::chrono::year_month_day_last& lhs, const etl::chrono::year_month_day_last& rhs) ETL_NOEXCEPT { diff --git a/test/test_chrono_day.cpp b/test/test_chrono_day.cpp index 47ab3fc8..706a3de9 100644 --- a/test/test_chrono_day.cpp +++ b/test/test_chrono_day.cpp @@ -278,7 +278,7 @@ namespace CHECK_TRUE(day10 >= day10); CHECK_TRUE(day20 >= day10); -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 CHECK_TRUE((day10 <=> day10) == 0); CHECK_TRUE((day10 <=> day20) < 0); CHECK_TRUE((day20 <=> day10) > 0); diff --git a/test/test_chrono_duration.cpp b/test/test_chrono_duration.cpp index 4122cdd5..1372d6f6 100644 --- a/test/test_chrono_duration.cpp +++ b/test/test_chrono_duration.cpp @@ -1045,16 +1045,41 @@ namespace CHECK_EQUAL(1, s1.count()); // 10 seconds % (-3) seconds = 1 second } -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 //************************************************************************* TEST(test_duration_spaceship_operator) { Chrono::seconds s1(2); // 2 seconds Chrono::milliseconds ms1(1500); // 1500 milliseconds - CHECK_TRUE(s1 <=> s1 == std::strong_ordering::equal); - CHECK_TRUE(ms1 <=> s1 == std::strong_ordering::less); - CHECK_TRUE(s1 <=> ms1 == std::strong_ordering::greater); + CHECK_TRUE(s1 <=> s1 == etl::strong_ordering::equal); + CHECK_TRUE(ms1 <=> s1 == etl::strong_ordering::less); + CHECK_TRUE(s1 <=> ms1 == etl::strong_ordering::greater); + + static_assert(etl::is_same ms1), etl::strong_ordering>::value, "Must return etl::strong_ordering"); + } + + //************************************************************************* + TEST(test_duration_spaceship_operator_floating_point_representation) + { + typedef Chrono::duration fp_seconds; + + fp_seconds fs1(1.5); + fp_seconds fs2(2.5); + + // A floating point representation is only partially ordered. + static_assert(etl::is_same fs2), etl::partial_ordering>::value, "Must return etl::partial_ordering"); + + CHECK_TRUE((fs1 <=> fs1) == etl::partial_ordering::equivalent); + CHECK_TRUE((fs1 <=> fs2) == etl::partial_ordering::less); + CHECK_TRUE((fs2 <=> fs1) == etl::partial_ordering::greater); + + fp_seconds nan_seconds(etl::numeric_limits::quiet_NaN()); + + CHECK_TRUE((nan_seconds <=> fs1) == etl::partial_ordering::unordered); + CHECK_FALSE((nan_seconds <=> fs1) < 0); + CHECK_FALSE((nan_seconds <=> fs1) == 0); + CHECK_FALSE((nan_seconds <=> fs1) > 0); } #endif diff --git a/test/test_chrono_month.cpp b/test/test_chrono_month.cpp index a4e61491..46522d4f 100644 --- a/test/test_chrono_month.cpp +++ b/test/test_chrono_month.cpp @@ -303,7 +303,7 @@ namespace CHECK_TRUE(month1 >= month1); CHECK_TRUE(month2 >= month1); -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 CHECK_TRUE((month1 <=> month1) == 0); CHECK_TRUE((month1 <=> month2) < 0); CHECK_TRUE((month2 <=> month1) > 0); diff --git a/test/test_chrono_month_day.cpp b/test/test_chrono_month_day.cpp index 7e7bf9b5..91a61482 100644 --- a/test/test_chrono_month_day.cpp +++ b/test/test_chrono_month_day.cpp @@ -89,7 +89,7 @@ namespace CHECK_FALSE(md.ok()); // Invalid month_day } -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 //************************************************************************* TEST(test_month_day_spaceship_operator) { @@ -97,9 +97,9 @@ namespace Chrono::month_day md2{Chrono::February, Chrono::day{10}}; Chrono::month_day md3{Chrono::January, Chrono::day{15}}; - CHECK_TRUE((md1 <=> md3) == std::strong_ordering::equal); // Same month and day - CHECK_TRUE((md1 <=> md2) == std::strong_ordering::less); // Different month and day - CHECK_TRUE((md2 <=> md1) == std::strong_ordering::greater); // Same month and day + CHECK_TRUE((md1 <=> md3) == etl::strong_ordering::equal); // Same month and day + CHECK_TRUE((md1 <=> md2) == etl::strong_ordering::less); // Different month and day + CHECK_TRUE((md2 <=> md1) == etl::strong_ordering::greater); // Same month and day } #endif diff --git a/test/test_chrono_month_day_last.cpp b/test/test_chrono_month_day_last.cpp index bd5bee7f..927a9369 100644 --- a/test/test_chrono_month_day_last.cpp +++ b/test/test_chrono_month_day_last.cpp @@ -100,6 +100,20 @@ namespace CHECK_TRUE(month_day_last1 != month_day_last3); } +#if ETL_USING_CPP20 + //************************************************************************* + TEST(test_month_day_last_spaceship_operator) + { + Chrono::month_day_last month_day_last1(Chrono::January); + Chrono::month_day_last month_day_last2(Chrono::February); + Chrono::month_day_last month_day_last3(Chrono::January); + + CHECK_TRUE((month_day_last1 <=> month_day_last3) == etl::strong_ordering::equal); + CHECK_TRUE((month_day_last1 <=> month_day_last2) == etl::strong_ordering::less); + CHECK_TRUE((month_day_last2 <=> month_day_last1) == etl::strong_ordering::greater); + } +#endif + #if ETL_USING_ETL_CHRONO //************************************************************************* TEST(test_month_day_last_hashes_are_unique) diff --git a/test/test_chrono_time_point.cpp b/test/test_chrono_time_point.cpp index a4866f0c..89a00227 100644 --- a/test/test_chrono_time_point.cpp +++ b/test/test_chrono_time_point.cpp @@ -365,7 +365,7 @@ namespace CHECK_FALSE(tp10d >= tp20h); CHECK_FALSE(tp10h >= tp20d); -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 CHECK_TRUE((tp10h <=> tp10h) == 0); CHECK_TRUE((tp10h <=> tp20h) < 0); CHECK_TRUE((tp20h <=> tp10h) > 0); @@ -377,6 +377,8 @@ namespace CHECK_TRUE((tp10d <=> tp10h) == 0); CHECK_TRUE((tp10h <=> tp20d) < 0); CHECK_TRUE((tp20d <=> tp10h) > 0); + + static_assert(etl::is_same tp20d), etl::strong_ordering>::value, "Must return etl::strong_ordering"); #endif } diff --git a/test/test_chrono_year.cpp b/test/test_chrono_year.cpp index 2b8dad29..8e3c9f1c 100644 --- a/test/test_chrono_year.cpp +++ b/test/test_chrono_year.cpp @@ -284,7 +284,7 @@ namespace CHECK_FALSE(year10 == year20); CHECK_TRUE(year10 != year20); -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 CHECK_TRUE((year10 <=> year10) == 0); CHECK_TRUE((year10 <=> year20) < 0); CHECK_TRUE((year20 <=> year10) > 0); diff --git a/test/test_chrono_year_month.cpp b/test/test_chrono_year_month.cpp index 73065121..ad051386 100644 --- a/test/test_chrono_year_month.cpp +++ b/test/test_chrono_year_month.cpp @@ -89,7 +89,7 @@ namespace CHECK_FALSE(ym.ok()); // Invalid year_month } -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 //************************************************************************* TEST(test_year_month_spaceship_operator) { @@ -97,11 +97,11 @@ namespace Chrono::year_month ym2{Chrono::year(2001), Chrono::January}; Chrono::year_month ym3{Chrono::year(2000), Chrono::February}; - CHECK_TRUE((ym1 <=> ym1) == std::strong_ordering::equal); - CHECK_TRUE((ym1 <=> ym2) == std::strong_ordering::less); - CHECK_TRUE((ym2 <=> ym1) == std::strong_ordering::greater); - CHECK_TRUE((ym1 <=> ym3) == std::strong_ordering::less); - CHECK_TRUE((ym3 <=> ym1) == std::strong_ordering::greater); + CHECK_TRUE((ym1 <=> ym1) == etl::strong_ordering::equal); + CHECK_TRUE((ym1 <=> ym2) == etl::strong_ordering::less); + CHECK_TRUE((ym2 <=> ym1) == etl::strong_ordering::greater); + CHECK_TRUE((ym1 <=> ym3) == etl::strong_ordering::less); + CHECK_TRUE((ym3 <=> ym1) == etl::strong_ordering::greater); } #endif diff --git a/test/test_chrono_year_month_day.cpp b/test/test_chrono_year_month_day.cpp index e44ee967..a83f7ebe 100644 --- a/test/test_chrono_year_month_day.cpp +++ b/test/test_chrono_year_month_day.cpp @@ -98,7 +98,7 @@ namespace CHECK_FALSE(ymd.ok()); // Invalid year_month_day } -#if ETL_USING_CPP20 && ETL_USING_STL +#if ETL_USING_CPP20 //************************************************************************* TEST(test_year_month_day_spaceship_operator) { @@ -107,13 +107,13 @@ namespace Chrono::year_month_day ym3{Chrono::year(2000), Chrono::February, Chrono::day(1)}; Chrono::year_month_day ym4{Chrono::year(2000), Chrono::January, Chrono::day(2)}; - CHECK_TRUE((ym1 <=> ym1) == std::strong_ordering::equal); - CHECK_TRUE((ym1 <=> ym2) == std::strong_ordering::less); - CHECK_TRUE((ym2 <=> ym1) == std::strong_ordering::greater); - CHECK_TRUE((ym1 <=> ym3) == std::strong_ordering::less); - CHECK_TRUE((ym3 <=> ym1) == std::strong_ordering::greater); - CHECK_TRUE((ym1 <=> ym4) == std::strong_ordering::less); - CHECK_TRUE((ym4 <=> ym1) == std::strong_ordering::greater); + CHECK_TRUE((ym1 <=> ym1) == etl::strong_ordering::equal); + CHECK_TRUE((ym1 <=> ym2) == etl::strong_ordering::less); + CHECK_TRUE((ym2 <=> ym1) == etl::strong_ordering::greater); + CHECK_TRUE((ym1 <=> ym3) == etl::strong_ordering::less); + CHECK_TRUE((ym3 <=> ym1) == etl::strong_ordering::greater); + CHECK_TRUE((ym1 <=> ym4) == etl::strong_ordering::less); + CHECK_TRUE((ym4 <=> ym1) == etl::strong_ordering::greater); } #endif diff --git a/test/test_chrono_year_month_day_last.cpp b/test/test_chrono_year_month_day_last.cpp index 53a556af..8edaca2f 100644 --- a/test/test_chrono_year_month_day_last.cpp +++ b/test/test_chrono_year_month_day_last.cpp @@ -160,6 +160,23 @@ namespace CHECK_TRUE(year_month_day_last1 != year_month_day_last3); } +#if ETL_USING_CPP20 + //************************************************************************* + TEST(test_year_month_day_last_spaceship_operator) + { + Chrono::year_month_day_last year_month_day_last1(Chrono::year(2000), Chrono::month_day_last(Chrono::January)); + Chrono::year_month_day_last year_month_day_last2(Chrono::year(2001), Chrono::month_day_last(Chrono::January)); + Chrono::year_month_day_last year_month_day_last3(Chrono::year(2000), Chrono::month_day_last(Chrono::February)); + Chrono::year_month_day_last year_month_day_last4(Chrono::year(2000), Chrono::month_day_last(Chrono::January)); + + CHECK_TRUE((year_month_day_last1 <=> year_month_day_last4) == etl::strong_ordering::equal); + CHECK_TRUE((year_month_day_last1 <=> year_month_day_last2) == etl::strong_ordering::less); + CHECK_TRUE((year_month_day_last2 <=> year_month_day_last1) == etl::strong_ordering::greater); + CHECK_TRUE((year_month_day_last1 <=> year_month_day_last3) == etl::strong_ordering::less); + CHECK_TRUE((year_month_day_last3 <=> year_month_day_last1) == etl::strong_ordering::greater); + } +#endif + #if ETL_USING_ETL_CHRONO //************************************************************************* TEST(test_year_month_day_last_hashes_are_unique) diff --git a/test/test_compare.cpp b/test/test_compare.cpp index 032cbd41..d0ec3b7c 100644 --- a/test/test_compare.cpp +++ b/test/test_compare.cpp @@ -91,6 +91,20 @@ namespace } }; + struct ComparatorOnlyObject + { + int a; + int b; + }; + + struct LessBySecond + { + bool constexpr operator()(const ComparatorOnlyObject& lhs, const ComparatorOnlyObject& rhs) const + { + return lhs.b < rhs.b; + } + }; + typedef etl::compare CompareTest; SUITE(test_compare) @@ -191,5 +205,139 @@ namespace CHECK_EQUAL(CompareInt::Greater, cmp_test_greater); CHECK_EQUAL(CompareInt::Equal, cmp_test_equal); } + +#if ETL_USING_CPP20 + //************************************************************************* + TEST(test_strong_ordering) + { + constexpr etl::strong_ordering ordering_less(etl::strong_ordering::less); + constexpr etl::strong_ordering ordering_equal(etl::strong_ordering::equal); + constexpr etl::strong_ordering ordering_greater(etl::strong_ordering::greater); + + CHECK(ordering_less < 0); + CHECK(ordering_less <= 0); + CHECK(!(ordering_less == 0)); + CHECK(!(ordering_less > 0)); + CHECK(ordering_equal == 0); + CHECK(ordering_equal <= 0); + CHECK(ordering_equal >= 0); + CHECK(ordering_greater > 0); + CHECK(ordering_greater >= 0); + CHECK(!(ordering_greater == 0)); + + // Reversed operands. + CHECK(0 > ordering_less); + CHECK(0 < ordering_greater); + + static_assert(etl::strong_ordering::equivalent == etl::strong_ordering::equal, "Equivalent must alias equal"); + static_assert(etl::is_same::value, + "Must convert to weak_ordering"); + CHECK(etl::weak_ordering(etl::strong_ordering::less) < 0); + CHECK(etl::partial_ordering(etl::strong_ordering::greater) > 0); + } + + //************************************************************************* + TEST(test_make_strong_ordering) + { + constexpr etl::strong_ordering ordering_less = etl::make_strong_ordering(-1); + constexpr etl::strong_ordering ordering_equal = etl::make_strong_ordering(0); + constexpr etl::strong_ordering ordering_greater = etl::make_strong_ordering(1); + + CHECK(ordering_less < 0); + CHECK(ordering_equal == 0); + CHECK(ordering_greater > 0); + + static_assert(etl::is_same::value, "Must return etl::strong_ordering"); + } + + //************************************************************************* + TEST(test_weak_ordering) + { + constexpr etl::weak_ordering ordering_less(etl::weak_ordering::less); + constexpr etl::weak_ordering ordering_equivalent(etl::weak_ordering::equivalent); + constexpr etl::weak_ordering ordering_greater(etl::weak_ordering::greater); + + CHECK(ordering_less < 0); + CHECK(ordering_equivalent == 0); + CHECK(ordering_greater > 0); + + CHECK(0 > ordering_less); + CHECK(0 < ordering_greater); + + static_assert(etl::weak_ordering::equivalent == 0, "Equivalent must be zero"); + CHECK(etl::partial_ordering(etl::weak_ordering::less) < 0); + } + + //************************************************************************* + TEST(test_make_weak_ordering) + { + constexpr etl::weak_ordering ordering_less = etl::make_weak_ordering(-1); + constexpr etl::weak_ordering ordering_equivalent = etl::make_weak_ordering(0); + constexpr etl::weak_ordering ordering_greater = etl::make_weak_ordering(1); + + CHECK(ordering_less < 0); + CHECK(ordering_equivalent == 0); + CHECK(ordering_greater > 0); + + static_assert(etl::is_same::value, "Must return etl::weak_ordering"); + } + + //************************************************************************* + TEST(test_partial_ordering) + { + constexpr etl::partial_ordering ordering_less(etl::partial_ordering::less); + constexpr etl::partial_ordering ordering_equivalent(etl::partial_ordering::equivalent); + constexpr etl::partial_ordering ordering_greater(etl::partial_ordering::greater); + constexpr etl::partial_ordering ordering_unordered(etl::partial_ordering::unordered); + + CHECK(ordering_less < 0); + CHECK(ordering_equivalent == 0); + CHECK(ordering_greater > 0); + + // An unordered result compares false against zero in every direction. + CHECK(!(ordering_unordered < 0)); + CHECK(!(ordering_unordered <= 0)); + CHECK(!(ordering_unordered == 0)); + CHECK(!(ordering_unordered >= 0)); + CHECK(!(ordering_unordered > 0)); + CHECK(ordering_unordered != 0); + + static_assert(etl::partial_ordering::equivalent == 0, "Equivalent must be zero"); + } + + //************************************************************************* + TEST(test_make_partial_ordering) + { + constexpr etl::partial_ordering ordering_less = etl::make_partial_ordering(-1); + constexpr etl::partial_ordering ordering_equivalent = etl::make_partial_ordering(0); + constexpr etl::partial_ordering ordering_greater = etl::make_partial_ordering(1); + + CHECK(ordering_less < 0); + CHECK(ordering_equivalent == 0); + CHECK(ordering_greater > 0); + + static_assert(etl::is_same::value, "Must return etl::partial_ordering"); + } + + //************************************************************************* + TEST(test_three_way_compare_custom_less) + { + constexpr ComparatorOnlyObject lhs = {5, 1}; + constexpr ComparatorOnlyObject rhs = {1, 2}; + constexpr ComparatorOnlyObject eq = {9, 1}; + + constexpr int cmp_less = etl::three_way_compare(lhs, rhs); + constexpr int cmp_greater = etl::three_way_compare(rhs, lhs); + constexpr int cmp_equal = etl::three_way_compare(lhs, eq); + + CHECK_EQUAL(-1, cmp_less); + CHECK_EQUAL(1, cmp_greater); + CHECK_EQUAL(0, cmp_equal); + + CHECK(etl::make_strong_ordering(cmp_less) < 0); + CHECK(etl::make_strong_ordering(cmp_greater) > 0); + CHECK(etl::make_strong_ordering(cmp_equal) == 0); + } +#endif } } // namespace From 525fd0304aabc8c61098a011d24483bb227269de Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sun, 13 Sep 2026 10:50:31 +0100 Subject: [PATCH 3/4] Updated ignore for hard link for CLion --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b5cc3865..035a6dfa 100644 --- a/.gitignore +++ b/.gitignore @@ -423,3 +423,4 @@ include/etl/iterator.h~RFfd5eda.TMP test/vs2022/Build docs/Messaging test/vs2026/Build +test/include From 22bdef2ee41279a75d92c91a7c831b73084e4c1f Mon Sep 17 00:00:00 2001 From: Mike Bloom <91038685+mike919192@users.noreply.github.com> Date: Thu, 17 Sep 2026 07:57:10 -0400 Subject: [PATCH 4/4] Simplify noexcept expressions using ETL_NOT_USING_EXCEPTIONS (#1606) * Simplify noexcept containing ETL_NOT_USING_EXCEPTIONS * Revert some format --------- Co-authored-by: John Wellbelove Co-authored-by: Roland Reichwein --- include/etl/array.h | 4 ++-- include/etl/array_view.h | 4 ++-- include/etl/array_wrapper.h | 4 ++-- include/etl/span.h | 26 +++++++++++--------------- include/etl/string_view.h | 2 +- include/etl/vector.h | 8 +++----- 6 files changed, 21 insertions(+), 27 deletions(-) diff --git a/include/etl/array.h b/include/etl/array.h index c1d7d6e4..a5f48e27 100644 --- a/include/etl/array.h +++ b/include/etl/array.h @@ -137,7 +137,7 @@ namespace etl /// Returns a reference to the value at index 'i'. ///\param i The index of the element to access. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR14 reference operator[](size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) + ETL_NODISCARD ETL_CONSTEXPR14 reference operator[](size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR) { ETL_ASSERT_CHECK_INDEX_OPERATOR(i < SIZE, ETL_ERROR(array_out_of_range)); @@ -149,7 +149,7 @@ namespace etl /// Returns a const reference to the value at index 'i'. ///\param i The index of the element to access. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR const_reference operator[](size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) + ETL_NODISCARD ETL_CONSTEXPR const_reference operator[](size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR) { // Throwing from c++11 constexpr requires special syntax #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_INDEX_OPERATOR diff --git a/include/etl/array_view.h b/include/etl/array_view.h index bb25dac0..7d20c5a2 100644 --- a/include/etl/array_view.h +++ b/include/etl/array_view.h @@ -518,7 +518,7 @@ namespace etl /// If asserts or exceptions are enabled, throws an etl::array_view_bounds /// if the index is out of bounds. //************************************************************************* - reference operator[](const size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) + reference operator[](const size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR) { ETL_ASSERT_CHECK_INDEX_OPERATOR(i < size(), ETL_ERROR(array_view_bounds)); @@ -531,7 +531,7 @@ namespace etl /// If asserts or exceptions are enabled, throws an etl::array_view_bounds /// if the index is out of bounds. //************************************************************************* - const_reference operator[](const size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) + const_reference operator[](const size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR) { ETL_ASSERT_CHECK_INDEX_OPERATOR(i < size(), ETL_ERROR(array_view_bounds)); diff --git a/include/etl/array_wrapper.h b/include/etl/array_wrapper.h index 2c434a05..97e198ec 100644 --- a/include/etl/array_wrapper.h +++ b/include/etl/array_wrapper.h @@ -271,7 +271,7 @@ namespace etl /// If asserts or exceptions are enabled, throws an /// etl::array_wrapper_bounds if the index is out of bounds. //************************************************************************* - reference operator[](size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) + reference operator[](size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR) { ETL_ASSERT_CHECK_INDEX_OPERATOR(i < SIZE, ETL_ERROR(etl::array_wrapper_bounds)); return ARRAY_[i]; @@ -282,7 +282,7 @@ namespace etl /// If asserts or exceptions are enabled, throws an /// etl::array_wrapper_bounds if the index is out of bounds. //************************************************************************* - ETL_CONSTEXPR const_reference operator[](size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) + ETL_CONSTEXPR const_reference operator[](size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR) { // Throwing from c++11 constexpr requires special syntax #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_INDEX_OPERATOR diff --git a/include/etl/span.h b/include/etl/span.h index d948c355..71c7e907 100644 --- a/include/etl/span.h +++ b/include/etl/span.h @@ -644,8 +644,7 @@ namespace etl /// Obtains a span that is a view over the first count elements of this /// span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span first(size_t count) const - ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span first(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return count <= size() ? etl::span(pbegin, pbegin + count) : throw(ETL_ERROR(span_out_of_range)); @@ -672,8 +671,7 @@ namespace etl //************************************************************************* /// Obtains a span that is a view over the last count elements of this span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span last(size_t count) const - ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span last(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return count <= size() ? etl::span((pbegin + Extent) - count, (pbegin + Extent)) @@ -735,7 +733,7 @@ namespace etl /// elements of this span. //************************************************************************* ETL_NODISCARD ETL_CONSTEXPR etl::span subspan(size_t offset, size_t count = etl::dynamic_extent) const - ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return (offset <= size()) && (count != etl::dynamic_extent ? count <= (size() - offset) : true) @@ -1003,7 +1001,7 @@ namespace etl //************************************************************************* /// Returns a reference to the first element. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR reference front() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR reference front() const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return size() > 0 ? *pbegin : throw(ETL_ERROR(span_out_of_range)); @@ -1017,7 +1015,7 @@ namespace etl //************************************************************************* /// Returns a reference to the last element. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR reference back() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR reference back() const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return size() > 0 ? *(pend - 1) : throw(ETL_ERROR(span_out_of_range)); @@ -1197,7 +1195,7 @@ namespace etl /// span. //************************************************************************* template - ETL_NODISCARD ETL_CONSTEXPR etl::span first() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span first() const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return COUNT <= size() ? etl::span(pbegin, pbegin + COUNT) : throw(ETL_ERROR(span_out_of_range)); @@ -1212,8 +1210,7 @@ namespace etl /// Obtains a span that is a view over the first count elements of this /// span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span first(size_t count) const - ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span first(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return count <= size() ? etl::span(pbegin, pbegin + count) : throw(ETL_ERROR(span_out_of_range)); @@ -1228,7 +1225,7 @@ namespace etl /// Obtains a span that is a view over the last COUNT elements of this span. //************************************************************************* template - ETL_NODISCARD ETL_CONSTEXPR etl::span last() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span last() const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return COUNT <= size() ? etl::span(pend - COUNT, pend) : throw(ETL_ERROR(span_out_of_range)); @@ -1242,8 +1239,7 @@ namespace etl //************************************************************************* /// Obtains a span that is a view over the last count elements of this span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span last(size_t count) const - ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span last(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return count <= size() ? etl::span(pend - count, pend) : throw(ETL_ERROR(span_out_of_range)); @@ -1261,7 +1257,7 @@ namespace etl //************************************************************************* template ETL_NODISCARD ETL_CONSTEXPR etl::span< element_type, COUNT != etl::dynamic_extent ? COUNT : etl::dynamic_extent> subspan() const - ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return (OFFSET <= size()) && (COUNT != etl::dynamic_extent ? COUNT <= (size() - OFFSET) : true) @@ -1305,7 +1301,7 @@ namespace etl /// elements of this span. //************************************************************************* ETL_NODISCARD ETL_CONSTEXPR14 etl::span subspan(size_t offset, size_t count = etl::dynamic_extent) const - ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA) { ETL_ASSERT_CHECK_EXTRA(offset <= size(), ETL_ERROR(span_out_of_range)); ETL_ASSERT_CHECK_EXTRA(count != etl::dynamic_extent ? count <= (size() - offset) : true, ETL_ERROR(span_out_of_range)); diff --git a/include/etl/string_view.h b/include/etl/string_view.h index 2cb20e37..0827636c 100644 --- a/include/etl/string_view.h +++ b/include/etl/string_view.h @@ -355,7 +355,7 @@ namespace etl /// If asserts or exceptions are enabled, throws an etl::string_view_bounds /// if the index is out of bounds. //************************************************************************* - ETL_CONSTEXPR const_reference operator[](size_type i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) + ETL_CONSTEXPR const_reference operator[](size_type i) const ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_INDEX_OPERATOR return i < size() ? mbegin[i] : throw(ETL_ERROR(string_view_bounds)); diff --git a/include/etl/vector.h b/include/etl/vector.h index beac6cdc..61ee6c26 100644 --- a/include/etl/vector.h +++ b/include/etl/vector.h @@ -393,7 +393,7 @@ namespace etl typename etl::enable_if::value && etl::is_convertible::value_type, T>::value, typename etl::enable_if::value, void>::type>::type - assign(TIterator first, TIterator last) ETL_NOEXCEPT_IF((etl::is_nothrow_copy_constructible::value && ETL_NOT_USING_EXCEPTIONS)) + assign(TIterator first, TIterator last) { #if ETL_USING_CPP11 ETL_STATIC_ASSERT((etl::is_same::type, @@ -2082,7 +2082,6 @@ namespace etl /// Copy constructor. //************************************************************************* vector_ext(const vector_ext& other, void* buffer, size_t max_size) - ETL_NOEXCEPT_IF((etl::is_nothrow_copy_constructible::value && ETL_NOT_USING_EXCEPTIONS)) : etl::ivector(reinterpret_cast(buffer), max_size) { if (&other != this) @@ -2094,7 +2093,7 @@ namespace etl //************************************************************************* /// Assignment operator. //************************************************************************* - vector_ext& operator=(const vector_ext& rhs) ETL_NOEXCEPT_IF((etl::is_nothrow_copy_constructible::value && ETL_NOT_USING_EXCEPTIONS)) + vector_ext& operator=(const vector_ext& rhs) { if (&rhs != this) { @@ -2109,7 +2108,6 @@ namespace etl /// Move constructor. //************************************************************************* vector_ext(vector_ext&& other, void* buffer, size_t max_size) - ETL_NOEXCEPT_IF((etl::is_nothrow_move_constructible::value && ETL_NOT_USING_EXCEPTIONS)) : etl::ivector(reinterpret_cast(buffer), max_size) { if (&other != this) @@ -2123,7 +2121,7 @@ namespace etl //************************************************************************* /// Move assignment operator. //************************************************************************* - vector_ext& operator=(vector_ext&& rhs) ETL_NOEXCEPT_IF((etl::is_nothrow_move_constructible::value && ETL_NOT_USING_EXCEPTIONS)) + vector_ext& operator=(vector_ext&& rhs) { if (&rhs != this) {