From 2f242e37f219cb784ffc8ee3ef7da0f8784708c1 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Thu, 26 Mar 2026 10:46:20 +0100 Subject: [PATCH] Restrict etl::atomic for general types (#1359) * Print test names at test time (#1343) * Restrict etl::atomic for general types Needs adding is_copy_assignable and is_move_assignable, and adjustments to is_trivially_copyable and is_assignable * Resolve mutable T value vs. volatile qualified methods * Remove volatile method overloads They are deprecated in C++20 because they don't work as users expect anyway. MSVC hinted for this. --------- Co-authored-by: John Wellbelove --- include/etl/atomic/atomic_gcc_sync.h | 193 ++---------------- .../etl/generators/type_traits_generator.h | 105 +++++++++- include/etl/type_traits.h | 105 +++++++++- test/test_atomic.cpp | 124 +++++++++++ test/test_type_traits.cpp | 60 ++++++ 5 files changed, 406 insertions(+), 181 deletions(-) diff --git a/include/etl/atomic/atomic_gcc_sync.h b/include/etl/atomic/atomic_gcc_sync.h index de7b8d5a..fab54974 100644 --- a/include/etl/atomic/atomic_gcc_sync.h +++ b/include/etl/atomic/atomic_gcc_sync.h @@ -807,6 +807,13 @@ namespace etl { public: + ETL_STATIC_ASSERT((etl::is_trivially_copyable::value), "atomic requires that T is trivially copyable"); + ETL_STATIC_ASSERT((etl::is_copy_constructible::value), "atomic requires that T is copy constructible"); + ETL_STATIC_ASSERT((etl::is_copy_assignable::value), "atomic requires that T is copy assignable"); + ETL_STATIC_ASSERT((etl::is_move_constructible::value), "atomic requires that T is move constructible"); + ETL_STATIC_ASSERT((etl::is_move_assignable::value), "atomic requires that T is move assignable"); + ETL_STATIC_ASSERT((etl::is_same::type>::value), "atomic requires that T is not const or volatile"); + atomic() : flag(0) , value(T()) @@ -827,13 +834,6 @@ namespace etl return v; } - T operator =(T v) volatile - { - store(v); - - return v; - } - // Conversion operator operator T () const { @@ -844,26 +844,12 @@ namespace etl return result; } - operator T() volatile const - { - ETL_BUILTIN_LOCK; - T result = value; - ETL_BUILTIN_UNLOCK; - - return result; - } - // Is lock free? bool is_lock_free() const { return false; } - bool is_lock_free() const volatile - { - return false; - } - // Store void store(T v, etl::memory_order order = etl::memory_order_seq_cst) { @@ -873,25 +859,6 @@ namespace etl ETL_BUILTIN_UNLOCK; } - void store(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile - { - (void)order; - ETL_BUILTIN_LOCK; - value = v; - ETL_BUILTIN_UNLOCK; - } - - // Load - T load(etl::memory_order order = etl::memory_order_seq_cst) const volatile - { - (void)order; - ETL_BUILTIN_LOCK; - T result = value; - ETL_BUILTIN_UNLOCK; - - return result; - } - // Load T load(etl::memory_order order = etl::memory_order_seq_cst) const { @@ -915,17 +882,6 @@ namespace etl return result; } - T exchange(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile - { - (void)order; - ETL_BUILTIN_LOCK; - T result = value; - value = v; - ETL_BUILTIN_UNLOCK; - - return result; - } - // Compare exchange weak bool compare_exchange_weak(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) { @@ -947,26 +903,6 @@ namespace etl return result; } - bool compare_exchange_weak(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) volatile - { - bool result; - - (void)order; - ETL_BUILTIN_LOCK; - if (memcmp(&value, &expected, sizeof(T)) == 0) - { - value = desired; - result = true; - } - else - { - result = false; - } - ETL_BUILTIN_UNLOCK; - - return result; - } - bool compare_exchange_weak(T& expected, T desired, etl::memory_order success, etl::memory_order failure) { (void)success; @@ -974,13 +910,6 @@ namespace etl return compare_exchange_weak(expected, desired); } - bool compare_exchange_weak(T& expected, T desired, etl::memory_order success, etl::memory_order failure) volatile - { - (void)success; - (void)failure; - return compare_exchange_weak(expected, desired); - } - // Compare exchange strong bool compare_exchange_strong(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) { @@ -988,12 +917,6 @@ namespace etl return compare_exchange_weak(expected, desired); } - bool compare_exchange_strong(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) volatile - { - (void)order; - return compare_exchange_weak(expected, desired); - } - bool compare_exchange_strong(T& expected, T desired, etl::memory_order success, etl::memory_order failure) { (void)success; @@ -1001,15 +924,11 @@ namespace etl return compare_exchange_weak(expected, desired); } - bool compare_exchange_strong(T& expected, T desired, etl::memory_order success, etl::memory_order failure) volatile - { - (void)success; - (void)failure; - return compare_exchange_weak(expected, desired); - } - private: + atomic& operator =(const atomic&) ETL_DELETE; + atomic& operator =(const atomic&) volatile ETL_DELETE; + mutable char flag; mutable T value; }; @@ -1995,6 +1914,13 @@ namespace etl { public: + ETL_STATIC_ASSERT((etl::is_trivially_copyable::value), "atomic requires that T is trivially copyable"); + ETL_STATIC_ASSERT((etl::is_copy_constructible::value), "atomic requires that T is copy constructible"); + ETL_STATIC_ASSERT((etl::is_copy_assignable::value), "atomic requires that T is copy assignable"); + ETL_STATIC_ASSERT((etl::is_move_constructible::value), "atomic requires that T is move constructible"); + ETL_STATIC_ASSERT((etl::is_move_assignable::value), "atomic requires that T is move assignable"); + ETL_STATIC_ASSERT((etl::is_same::type>::value), "atomic requires that T is not const or volatile"); + atomic() : flag(0) , value(T()) @@ -2015,13 +1941,6 @@ namespace etl return v; } - T operator =(T v) volatile - { - store(v); - - return v; - } - // Conversion operator operator T () const { @@ -2032,26 +1951,12 @@ namespace etl return result; } - operator T() volatile const - { - ETL_BUILTIN_LOCK; - T result = value; - ETL_BUILTIN_UNLOCK; - - return result; - } - // Is lock free? bool is_lock_free() const { return false; } - bool is_lock_free() const volatile - { - return false; - } - // Store void store(T v, etl::memory_order order = etl::memory_order_seq_cst) { @@ -2060,23 +1965,6 @@ namespace etl ETL_BUILTIN_UNLOCK; } - void store(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile - { - ETL_BUILTIN_LOCK; - value = v; - ETL_BUILTIN_UNLOCK; - } - - // Load - T load(etl::memory_order order = etl::memory_order_seq_cst) const volatile - { - ETL_BUILTIN_LOCK; - T result = value; - ETL_BUILTIN_UNLOCK; - - return result; - } - // Load T load(etl::memory_order order = etl::memory_order_seq_cst) const { @@ -2098,16 +1986,6 @@ namespace etl return result; } - T exchange(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile - { - ETL_BUILTIN_LOCK; - T result = value; - value = v; - ETL_BUILTIN_UNLOCK; - - return result; - } - // Compare exchange weak bool compare_exchange_weak(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) { @@ -2128,58 +2006,27 @@ namespace etl return result; } - bool compare_exchange_weak(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) volatile - { - bool result; - - ETL_BUILTIN_LOCK; - if (memcmp(&value, &expected, sizeof(T)) == 0) - { - value = desired; - result = true; - } - else - { - result = false; - } - ETL_BUILTIN_UNLOCK; - - return result; - } - bool compare_exchange_weak(T& expected, T desired, etl::memory_order success, etl::memory_order failure) { return compare_exchange_weak(expected, desired); } - bool compare_exchange_weak(T& expected, T desired, etl::memory_order success, etl::memory_order failure) volatile - { - return compare_exchange_weak(expected, desired); - } - // Compare exchange strong bool compare_exchange_strong(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) { return compare_exchange_weak(expected, desired); } - bool compare_exchange_strong(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) volatile - { - return compare_exchange_weak(expected, desired); - } - bool compare_exchange_strong(T& expected, T desired, etl::memory_order success, etl::memory_order failure) { return compare_exchange_weak(expected, desired); } - bool compare_exchange_strong(T& expected, T desired, etl::memory_order success, etl::memory_order failure) volatile - { - return compare_exchange_weak(expected, desired); - } - private: + atomic& operator =(const atomic&) ETL_DELETE; + atomic& operator =(const atomic&) volatile ETL_DELETE; + mutable char flag; mutable T value; }; diff --git a/include/etl/generators/type_traits_generator.h b/include/etl/generators/type_traits_generator.h index 373b5d0e..843ce343 100644 --- a/include/etl/generators/type_traits_generator.h +++ b/include/etl/generators/type_traits_generator.h @@ -1929,6 +1929,16 @@ typedef integral_constant true_type; template using is_move_constructible = std::is_move_constructible; + //********************************************* + // is_copy_assignable + template + using is_copy_assignable = std::is_copy_assignable; + + //********************************************* + // is_move_assignable + template + using is_move_assignable = std::is_move_assignable; + //********************************************* // is_trivially_constructible #if ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED @@ -1974,6 +1984,9 @@ typedef integral_constant true_type; #if ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED template using is_trivially_copyable = std::is_trivially_copyable; +#elif ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE + template + using is_trivially_copyable = etl::bool_constant<__is_trivially_copyable(T)>; #else template using is_trivially_copyable = etl::bool_constant::value || etl::is_pointer::value>; @@ -2033,6 +2046,29 @@ typedef integral_constant true_type; { }; + //********************************************* + // is_copy_assignable + template + struct is_copy_assignable : public etl::is_assignable::type, + typename etl::add_lvalue_reference::type> + { + }; + + //********************************************* + // is_move_assignable +#if ETL_USING_CPP11 + template + struct is_move_assignable : public etl::is_assignable::type, + typename etl::add_rvalue_reference::type> + { + }; +#else + template + struct is_move_assignable : public etl::is_assignable::type, T> + { + }; +#endif + #if ETL_USING_CPP11 //********************************************* // is_trivially_constructible @@ -2107,11 +2143,7 @@ typedef integral_constant true_type; template struct is_trivially_copyable { -#if defined(ETL_COMPILER_GCC) - static ETL_CONSTANT bool value = __has_trivial_copy(T); -#else static ETL_CONSTANT bool value = __is_trivially_copyable(T); -#endif }; #elif defined(ETL_USER_DEFINED_TYPE_TRAITS) && !defined(ETL_USE_TYPE_TRAITS_BUILTINS) @@ -2182,6 +2214,32 @@ typedef integral_constant true_type; template struct is_move_constructible; + //********************************************* + // is_copy_assignable + template ::value || etl::is_pointer::value> + struct is_copy_assignable; + + template + struct is_copy_assignable : public etl::true_type + { + }; + + template + struct is_copy_assignable; + + //********************************************* + // is_move_assignable + template ::value || etl::is_pointer::value> + struct is_move_assignable; + + template + struct is_move_assignable : public etl::true_type + { + }; + + template + struct is_move_assignable; + //********************************************* // is_trivially_constructible template ::value || etl::is_pointer::value> @@ -2257,7 +2315,11 @@ typedef integral_constant true_type; //********************************************* // is_assignable template +#if ETL_USING_BUILTIN_IS_ASSIGNABLE + struct is_assignable : public etl::bool_constant<__is_assignable(T1, T2)> +#else struct is_assignable : public etl::bool_constant<(etl::is_arithmetic::value || etl::is_pointer::value) && (etl::is_arithmetic::value || etl::is_pointer::value)> +#endif { }; @@ -2311,6 +2373,31 @@ typedef integral_constant true_type; }; #endif + //********************************************* + // is_copy_assignable + template + struct is_copy_assignable : public etl::is_assignable::type, + typename etl::add_lvalue_reference::type> + { + }; + +#if ETL_USING_CPP11 + //********************************************* + // is_move_assignable + template + struct is_move_assignable : public etl::is_assignable::type, + typename etl::add_rvalue_reference::type> + { + }; +#else + //********************************************* + // is_move_assignable + template + struct is_move_assignable : public etl::bool_constant::value || etl::is_pointer::value> + { + }; +#endif + //********************************************* // is_trivially_constructible template @@ -2342,7 +2429,11 @@ typedef integral_constant true_type; //********************************************* // is_trivially_copyable template +#if ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE + struct is_trivially_copyable : public etl::bool_constant<__is_trivially_copyable(T)> +#else struct is_trivially_copyable : public etl::bool_constant::value || etl::is_pointer::value> +#endif { }; @@ -2389,6 +2480,12 @@ typedef integral_constant true_type; template inline constexpr bool is_move_constructible_v = etl::is_move_constructible::value; + template + inline constexpr bool is_copy_assignable_v = etl::is_copy_assignable::value; + + template + inline constexpr bool is_move_assignable_v = etl::is_move_assignable::value; + template inline constexpr bool is_trivially_constructible_v = etl::is_trivially_constructible::value; diff --git a/include/etl/type_traits.h b/include/etl/type_traits.h index 521aaf48..34462e61 100644 --- a/include/etl/type_traits.h +++ b/include/etl/type_traits.h @@ -1922,6 +1922,16 @@ typedef integral_constant true_type; template using is_move_constructible = std::is_move_constructible; + //********************************************* + // is_copy_assignable + template + using is_copy_assignable = std::is_copy_assignable; + + //********************************************* + // is_move_assignable + template + using is_move_assignable = std::is_move_assignable; + //********************************************* // is_trivially_constructible #if ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED @@ -1967,6 +1977,9 @@ typedef integral_constant true_type; #if ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED template using is_trivially_copyable = std::is_trivially_copyable; +#elif ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE + template + using is_trivially_copyable = etl::bool_constant<__is_trivially_copyable(T)>; #else template using is_trivially_copyable = etl::bool_constant::value || etl::is_pointer::value>; @@ -2026,6 +2039,29 @@ typedef integral_constant true_type; { }; + //********************************************* + // is_copy_assignable + template + struct is_copy_assignable : public etl::is_assignable::type, + typename etl::add_lvalue_reference::type> + { + }; + + //********************************************* + // is_move_assignable +#if ETL_USING_CPP11 + template + struct is_move_assignable : public etl::is_assignable::type, + typename etl::add_rvalue_reference::type> + { + }; +#else + template + struct is_move_assignable : public etl::is_assignable::type, T> + { + }; +#endif + #if ETL_USING_CPP11 //********************************************* // is_trivially_constructible @@ -2100,11 +2136,7 @@ typedef integral_constant true_type; template struct is_trivially_copyable { -#if defined(ETL_COMPILER_GCC) - static ETL_CONSTANT bool value = __has_trivial_copy(T); -#else static ETL_CONSTANT bool value = __is_trivially_copyable(T); -#endif }; #elif defined(ETL_USER_DEFINED_TYPE_TRAITS) && !defined(ETL_USE_TYPE_TRAITS_BUILTINS) @@ -2175,6 +2207,32 @@ typedef integral_constant true_type; template struct is_move_constructible; + //********************************************* + // is_copy_assignable + template ::value || etl::is_pointer::value> + struct is_copy_assignable; + + template + struct is_copy_assignable : public etl::true_type + { + }; + + template + struct is_copy_assignable; + + //********************************************* + // is_move_assignable + template ::value || etl::is_pointer::value> + struct is_move_assignable; + + template + struct is_move_assignable : public etl::true_type + { + }; + + template + struct is_move_assignable; + //********************************************* // is_trivially_constructible template ::value || etl::is_pointer::value> @@ -2250,7 +2308,11 @@ typedef integral_constant true_type; //********************************************* // is_assignable template +#if ETL_USING_BUILTIN_IS_ASSIGNABLE + struct is_assignable : public etl::bool_constant<__is_assignable(T1, T2)> +#else struct is_assignable : public etl::bool_constant<(etl::is_arithmetic::value || etl::is_pointer::value) && (etl::is_arithmetic::value || etl::is_pointer::value)> +#endif { }; @@ -2304,6 +2366,31 @@ typedef integral_constant true_type; }; #endif + //********************************************* + // is_copy_assignable + template + struct is_copy_assignable : public etl::is_assignable::type, + typename etl::add_lvalue_reference::type> + { + }; + +#if ETL_USING_CPP11 + //********************************************* + // is_move_assignable + template + struct is_move_assignable : public etl::is_assignable::type, + typename etl::add_rvalue_reference::type> + { + }; +#else + //********************************************* + // is_move_assignable + template + struct is_move_assignable : public etl::bool_constant::value || etl::is_pointer::value> + { + }; +#endif + //********************************************* // is_trivially_constructible template @@ -2335,7 +2422,11 @@ typedef integral_constant true_type; //********************************************* // is_trivially_copyable template +#if ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE + struct is_trivially_copyable : public etl::bool_constant<__is_trivially_copyable(T)> +#else struct is_trivially_copyable : public etl::bool_constant::value || etl::is_pointer::value> +#endif { }; @@ -2382,6 +2473,12 @@ typedef integral_constant true_type; template inline constexpr bool is_move_constructible_v = etl::is_move_constructible::value; + template + inline constexpr bool is_copy_assignable_v = etl::is_copy_assignable::value; + + template + inline constexpr bool is_move_assignable_v = etl::is_move_assignable::value; + template inline constexpr bool is_trivially_constructible_v = etl::is_trivially_constructible::value; diff --git a/test/test_atomic.cpp b/test/test_atomic.cpp index a7442975..cdb79de0 100644 --- a/test/test_atomic.cpp +++ b/test/test_atomic.cpp @@ -711,6 +711,130 @@ namespace CHECK_EQUAL(compare.load(), test.load()); } + //************************************************************************* + TEST(test_atomic_non_scalar_trivially_copyable_struct) + { + struct Data + { + int x; + int y; + + bool operator ==(const Data& other) const + { + return (x == other.x) && (y == other.y); + } + }; + + // Default construction + etl::atomic test; + + Data d1 = { 1, 2 }; + Data d2 = { 3, 4 }; + Data d3 = { 5, 6 }; + + // Store and load + test.store(d1); + Data loaded = test.load(); + CHECK_EQUAL(d1.x, loaded.x); + CHECK_EQUAL(d1.y, loaded.y); + + // Assignment operator + test = d2; + loaded = test.load(); + CHECK_EQUAL(d2.x, loaded.x); + CHECK_EQUAL(d2.y, loaded.y); + + // Conversion operator + test.store(d1); + Data converted = static_cast(test); + CHECK_EQUAL(d1.x, converted.x); + CHECK_EQUAL(d1.y, converted.y); + + // Exchange + test.store(d1); + Data old_val = test.exchange(d2); + CHECK_EQUAL(d1.x, old_val.x); + CHECK_EQUAL(d1.y, old_val.y); + loaded = test.load(); + CHECK_EQUAL(d2.x, loaded.x); + CHECK_EQUAL(d2.y, loaded.y); + + // Compare exchange weak - pass (expected matches) + test.store(d1); + Data expected = d1; + bool result = test.compare_exchange_weak(expected, d3); + CHECK_TRUE(result); + loaded = test.load(); + CHECK_EQUAL(d3.x, loaded.x); + CHECK_EQUAL(d3.y, loaded.y); + + // Compare exchange weak - fail (expected does not match) + test.store(d1); + expected = d2; + result = test.compare_exchange_weak(expected, d3); + CHECK_FALSE(result); + loaded = test.load(); + CHECK_EQUAL(d1.x, loaded.x); + CHECK_EQUAL(d1.y, loaded.y); + + // Compare exchange weak with two memory order args - pass + test.store(d1); + expected = d1; + result = test.compare_exchange_weak(expected, d2, etl::memory_order_seq_cst, etl::memory_order_seq_cst); + CHECK_TRUE(result); + loaded = test.load(); + CHECK_EQUAL(d2.x, loaded.x); + CHECK_EQUAL(d2.y, loaded.y); + + // Compare exchange strong - pass (expected matches) + test.store(d1); + expected = d1; + result = test.compare_exchange_strong(expected, d3); + CHECK_TRUE(result); + loaded = test.load(); + CHECK_EQUAL(d3.x, loaded.x); + CHECK_EQUAL(d3.y, loaded.y); + + // Compare exchange strong - fail (expected does not match) + test.store(d1); + expected = d2; + result = test.compare_exchange_strong(expected, d3); + CHECK_FALSE(result); + loaded = test.load(); + CHECK_EQUAL(d1.x, loaded.x); + CHECK_EQUAL(d1.y, loaded.y); + + // Compare exchange strong with two memory order args - pass + test.store(d1); + expected = d1; + result = test.compare_exchange_strong(expected, d2, etl::memory_order_seq_cst, etl::memory_order_seq_cst); + CHECK_TRUE(result); + loaded = test.load(); + CHECK_EQUAL(d2.x, loaded.x); + CHECK_EQUAL(d2.y, loaded.y); + + // Explicit memory order on store/load + test.store(d3, etl::memory_order_release); + loaded = test.load(etl::memory_order_acquire); + CHECK_EQUAL(d3.x, loaded.x); + CHECK_EQUAL(d3.y, loaded.y); + + // Exchange with explicit memory order + test.store(d1); + old_val = test.exchange(d2, etl::memory_order_acq_rel); + CHECK_EQUAL(d1.x, old_val.x); + CHECK_EQUAL(d1.y, old_val.y); + loaded = test.load(); + CHECK_EQUAL(d2.x, loaded.x); + CHECK_EQUAL(d2.y, loaded.y); + + // Value construction + etl::atomic test2(d1); + loaded = test2.load(); + CHECK_EQUAL(d1.x, loaded.x); + CHECK_EQUAL(d1.y, loaded.y); + } + //************************************************************************* #if REALTIME_TEST diff --git a/test/test_type_traits.cpp b/test/test_type_traits.cpp index 73d83c22..fa320c4c 100644 --- a/test/test_type_traits.cpp +++ b/test/test_type_traits.cpp @@ -210,6 +210,8 @@ using etl::is_assignable; using etl::is_constructible; using etl::is_copy_constructible; using etl::is_move_constructible; +using etl::is_copy_assignable; +using etl::is_move_assignable; //************************* template <> @@ -227,6 +229,16 @@ struct etl::is_move_constructible : public etl::false_type { }; +template <> +struct etl::is_copy_assignable : public etl::true_type +{ +}; + +template <> +struct etl::is_move_assignable : public etl::true_type +{ +}; + //************************* template <> struct etl::is_assignable : public etl::true_type @@ -243,6 +255,16 @@ struct etl::is_move_constructible : public etl::true_type { }; +template <> +struct etl::is_copy_assignable : public etl::false_type +{ +}; + +template <> +struct etl::is_move_assignable : public etl::true_type +{ +}; + //************************* template <> struct etl::is_assignable : public etl::true_type @@ -258,6 +280,16 @@ template <> struct etl::is_move_constructible : public etl::true_type { }; + +template <> +struct etl::is_copy_assignable : public etl::true_type +{ +}; + +template <> +struct etl::is_move_assignable : public etl::true_type +{ +}; #endif namespace @@ -1296,6 +1328,34 @@ namespace #endif } + //************************************************************************* + TEST(test_is_copy_assignable) + { + #if ETL_USING_CPP17 + CHECK((etl::is_copy_assignable_v) == (std::is_copy_assignable_v)); + CHECK((etl::is_copy_assignable_v) == (std::is_copy_assignable_v)); + CHECK((etl::is_copy_assignable_v) == (std::is_copy_assignable_v)); + #else + CHECK((etl::is_copy_assignable::value) == (std::is_copy_assignable::value)); + CHECK((etl::is_copy_assignable::value) == (std::is_copy_assignable::value)); + CHECK((etl::is_copy_assignable::value) == (std::is_copy_assignable::value)); + #endif + } + + //************************************************************************* + TEST(test_is_move_assignable) + { + #if ETL_USING_CPP17 + CHECK((etl::is_move_assignable_v) == (std::is_move_assignable_v)); + CHECK((etl::is_move_assignable_v) == (std::is_move_assignable_v)); + CHECK((etl::is_move_assignable_v) == (std::is_move_assignable_v)); + #else + CHECK((etl::is_move_assignable::value) == (std::is_move_assignable::value)); + CHECK((etl::is_move_assignable::value) == (std::is_move_assignable::value)); + CHECK((etl::is_move_assignable::value) == (std::is_move_assignable::value)); + #endif + } + //************************************************************************* TEST(test_is_trivially_constructible) {