diff --git a/include/etl/concepts.h b/include/etl/concepts.h index ce5cec97..d64033c6 100644 --- a/include/etl/concepts.h +++ b/include/etl/concepts.h @@ -33,6 +33,7 @@ SOFTWARE. #include "platform.h" +#include "invoke.h" #include "type_traits.h" #include "utility.h" @@ -54,12 +55,31 @@ namespace etl using std::assignable_from; using std::common_reference_with; using std::common_with; + using std::constructible_from; using std::convertible_to; + using std::copy_constructible; + using std::copyable; + using std::default_initializable; using std::derived_from; + using std::destructible; + using std::equality_comparable; + using std::equivalence_relation; using std::floating_point; using std::integral; + using std::invocable; + using std::movable; + using std::move_constructible; + using std::predicate; + using std::regular; + using std::regular_invocable; + using std::relation; using std::same_as; + using std::semiregular; using std::signed_integral; + using std::strict_weak_order; + using std::swappable; + using std::swappable_with; + using std::totally_ordered; using std::unsigned_integral; #else // not ETL_USING_STL @@ -118,6 +138,112 @@ namespace etl { lhs = etl::forward(rhs) } -> etl::same_as; }; + //*************************************************************************** + template + concept invocable = etl::is_invocable_v; + + //*************************************************************************** + template + concept regular_invocable = etl::invocable; + + //*************************************************************************** + template + concept destructible = requires(T& t) { + { t.~T() } noexcept; + }; + + //*************************************************************************** + template + concept constructible_from = etl::destructible && etl::is_constructible_v; + + //*************************************************************************** + template + concept default_initializable = etl::constructible_from && requires { + T{}; + ::new T; + }; + + //*************************************************************************** + template + concept move_constructible = etl::constructible_from && etl::convertible_to; + + //*************************************************************************** + template + concept copy_constructible = + etl::move_constructible && etl::constructible_from && etl::convertible_to && etl::constructible_from + && etl::convertible_to && etl::constructible_from && etl::convertible_to; + + //*************************************************************************** + namespace private_concepts + { + template + concept boolean_testable = etl::convertible_to && requires(T&& t) { + { !etl::forward(t) } -> etl::convertible_to; + }; + } // namespace private_concepts + + //*************************************************************************** + template + concept equality_comparable = requires(const etl::remove_reference_t& a, const etl::remove_reference_t& b) { + { a == b } -> private_concepts::boolean_testable; + { a != b } -> private_concepts::boolean_testable; + }; + + //*************************************************************************** + template + concept totally_ordered = etl::equality_comparable && requires(const etl::remove_reference_t& a, const etl::remove_reference_t& b) { + { a < b } -> private_concepts::boolean_testable; + { a > b } -> private_concepts::boolean_testable; + { a <= b } -> private_concepts::boolean_testable; + { a >= b } -> private_concepts::boolean_testable; + }; + + //*************************************************************************** + template + concept swappable = requires(T& a, T& b) { etl::swap(a, b); }; + + //*************************************************************************** + template + concept swappable_with = etl::common_reference_with&, etl::remove_reference_t&> && requires(T&& t, U&& u) { + etl::swap(etl::forward(t), etl::forward(t)); + etl::swap(etl::forward(u), etl::forward(u)); + etl::swap(etl::forward(t), etl::forward(u)); + etl::swap(etl::forward(u), etl::forward(t)); + }; + + //*************************************************************************** + template + concept movable = etl::is_object_v && etl::move_constructible && etl::assignable_from && etl::swappable; + + //*************************************************************************** + template + concept copyable = etl::copy_constructible && etl::movable && etl::assignable_from && etl::assignable_from + && etl::assignable_from; + + //*************************************************************************** + template + concept semiregular = etl::copyable && etl::default_initializable; + + //*************************************************************************** + template + concept regular = etl::semiregular && etl::equality_comparable; + + //*************************************************************************** + template + concept predicate = etl::regular_invocable && private_concepts::boolean_testable >; + + //*************************************************************************** + template + concept relation = etl::predicate && etl::predicate && etl::predicate && etl::predicate; + + //*************************************************************************** + template + concept equivalence_relation = etl::relation; + + //*************************************************************************** + template + concept strict_weak_order = etl::relation; + #endif } // namespace etl #endif diff --git a/include/etl/type_traits.h b/include/etl/type_traits.h index 411632ff..bd7e8232 100644 --- a/include/etl/type_traits.h +++ b/include/etl/type_traits.h @@ -649,6 +649,18 @@ namespace etl struct is_void : true_type { }; + template <> + struct is_void : true_type + { + }; + template <> + struct is_void : true_type + { + }; + template <> + struct is_void : true_type + { + }; #if ETL_USING_CPP17 template @@ -3553,6 +3565,19 @@ namespace etl template inline constexpr bool is_function_v = etl::is_function::value; #endif + + //*************************************************************************** + /// is_object + //*************************************************************************** + template + struct is_object : etl::bool_constant::value && !etl::is_reference::value && !etl::is_void::value> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_object_v = etl::is_object::value; + #endif #endif #if ETL_USING_CPP11 diff --git a/test/test_concepts.cpp b/test/test_concepts.cpp index 347ec8fb..a6b03830 100644 --- a/test/test_concepts.cpp +++ b/test/test_concepts.cpp @@ -29,6 +29,7 @@ SOFTWARE. #include "unit_test_framework.h" #include +#include #include #if ETL_USING_CPP20 @@ -52,6 +53,65 @@ namespace { }; + struct NotDestructible + { + ~NotDestructible() = delete; + }; + + struct NotDefaultConstructible + { + NotDefaultConstructible(int) {} + }; + + struct NotCopyable + { + NotCopyable() = default; + NotCopyable(const NotCopyable&) = delete; + NotCopyable(NotCopyable&&) = default; + NotCopyable& operator=(const NotCopyable&) = delete; + NotCopyable& operator=(NotCopyable&&) = default; + }; + + struct NotMovable + { + NotMovable() = default; + NotMovable(const NotMovable&) = delete; + NotMovable(NotMovable&&) = delete; + NotMovable& operator=(const NotMovable&) = delete; + NotMovable& operator=(NotMovable&&) = delete; + }; + + struct NotEqualityComparable + { + }; + + struct EqualityComparableType + { + bool operator==(const EqualityComparableType&) const = default; + }; + + struct OrderedType + { + int value; + auto operator<=>(const OrderedType&) const = default; + }; + + struct BoolPredicate + { + bool operator()(int) const + { + return true; + } + }; + + struct IntRelation + { + bool operator()(int, int) const + { + return true; + } + }; + SUITE(test_concepts) { //************************************************************************* @@ -140,6 +200,183 @@ namespace static_assert(etl::assignable_from == false); static_assert(etl::assignable_from&, int> == false); } + + //************************************************************************* + TEST(test_invocable) + { + struct Functor + { + void operator()() {} + }; + struct FunctorWithArgs + { + int operator()(int, double) + { + return 0; + } + }; + + static_assert(etl::invocable == true); + static_assert(etl::invocable == true); + static_assert(etl::invocable == true); + static_assert(etl::invocable == true); + static_assert(etl::invocable == false); + static_assert(etl::invocable == false); + } + + //************************************************************************* + TEST(test_regular_invocable) + { + struct Functor + { + void operator()() {} + }; + + static_assert(etl::regular_invocable == true); + static_assert(etl::regular_invocable == true); + static_assert(etl::regular_invocable == false); + } + + //************************************************************************* + TEST(test_destructible) + { + static_assert(etl::destructible == true); + static_assert(etl::destructible == true); + static_assert(etl::destructible == false); + } + + //************************************************************************* + TEST(test_constructible_from) + { + static_assert(etl::constructible_from == true); + static_assert(etl::constructible_from == true); + static_assert(etl::constructible_from == true); + static_assert(etl::constructible_from == false); + } + + //************************************************************************* + TEST(test_default_initializable) + { + static_assert(etl::default_initializable == true); + static_assert(etl::default_initializable == true); + static_assert(etl::default_initializable == false); + } + + //************************************************************************* + TEST(test_move_constructible) + { + static_assert(etl::move_constructible == true); + static_assert(etl::move_constructible == true); + static_assert(etl::move_constructible == false); + } + + //************************************************************************* + TEST(test_copy_constructible) + { + static_assert(etl::copy_constructible == true); + static_assert(etl::copy_constructible == true); + static_assert(etl::copy_constructible == false); + } + + //************************************************************************* + TEST(test_equality_comparable) + { + static_assert(etl::equality_comparable == true); + static_assert(etl::equality_comparable == true); + static_assert(etl::equality_comparable == false); + } + + //************************************************************************* + TEST(test_totally_ordered) + { + static_assert(etl::totally_ordered == true); + static_assert(etl::totally_ordered == true); + static_assert(etl::totally_ordered == false); + } + + //************************************************************************* + TEST(test_swappable) + { + static_assert(etl::swappable == true); + static_assert(etl::swappable == true); + static_assert(etl::swappable == false); + } + + //************************************************************************* + TEST(test_swappable_with) + { + // Positive cases: same-type lvalue references that are swappable + static_assert(etl::swappable_with == true); + static_assert(etl::swappable_with == true); + + // Negative cases: unrelated types (no valid swap overload) + static_assert(etl::swappable_with == false); + + // Negative case: non-movable type cannot be swapped + static_assert(etl::swappable_with == false); + } + + //************************************************************************* + TEST(test_movable) + { + static_assert(etl::movable == true); + static_assert(etl::movable == true); + static_assert(etl::movable == false); + } + + //************************************************************************* + TEST(test_copyable) + { + static_assert(etl::copyable == true); + static_assert(etl::copyable == true); + static_assert(etl::copyable == false); + } + + //************************************************************************* + TEST(test_semiregular) + { + static_assert(etl::semiregular == true); + static_assert(etl::semiregular == true); + static_assert(etl::semiregular == false); + } + + //************************************************************************* + TEST(test_regular) + { + static_assert(etl::regular == true); + static_assert(etl::regular == true); + static_assert(etl::regular == false); + } + + //************************************************************************* + TEST(test_predicate) + { + static_assert(etl::predicate == true); + static_assert(etl::predicate == true); + static_assert(etl::predicate == false); + } + + //************************************************************************* + TEST(test_relation) + { + static_assert(etl::relation == true); + static_assert(etl::relation, int, int> == true); + static_assert(etl::relation == false); + } + + //************************************************************************* + TEST(test_equivalence_relation) + { + static_assert(etl::equivalence_relation, int, int> == true); + static_assert(etl::equivalence_relation == false); + } + + //************************************************************************* + TEST(test_strict_weak_order) + { + static_assert(etl::strict_weak_order, int, int> == true); + static_assert(etl::strict_weak_order == false); + } } } // namespace #endif diff --git a/test/test_type_traits.cpp b/test/test_type_traits.cpp index c49b2eda..026bbb9f 100644 --- a/test/test_type_traits.cpp +++ b/test/test_type_traits.cpp @@ -1900,5 +1900,43 @@ namespace CHECK_FALSE((etl::is_function::value)); CHECK_FALSE((etl::is_function::value)); // pointer, not function } + + //************************************************************************* + TEST(test_is_object) + { + CHECK_TRUE((etl::is_object::value)); + CHECK_TRUE((etl::is_object::value)); + CHECK_TRUE((etl::is_object::value)); + CHECK_TRUE((etl::is_object::value)); + CHECK_TRUE((etl::is_object::value)); + CHECK_TRUE((etl::is_object::value)); + CHECK_TRUE((etl::is_object::value)); + + CHECK_FALSE((etl::is_object::value)); + CHECK_FALSE((etl::is_object::value)); + CHECK_FALSE((etl::is_object::value)); + CHECK_FALSE((etl::is_object::value)); + CHECK_FALSE((etl::is_object::value)); + CHECK_FALSE((etl::is_object::value)); + CHECK_FALSE((etl::is_object::value)); + + CHECK_TRUE((etl::is_void::value)); + CHECK_TRUE((etl::is_void::value)); + CHECK_TRUE((etl::is_void::value)); + +#if ETL_USING_CPP17 + CHECK_TRUE((etl::is_object_v)); + CHECK_TRUE((etl::is_object_v)); + CHECK_TRUE((etl::is_object_v)); + + CHECK_FALSE((etl::is_object_v)); + CHECK_FALSE((etl::is_object_v)); + CHECK_FALSE((etl::is_object_v)); + CHECK_FALSE((etl::is_object_v)); + CHECK_FALSE((etl::is_object_v)); + CHECK_FALSE((etl::is_object_v)); + CHECK_FALSE((etl::is_object_v)); +#endif + } } } // namespace