mirror of
https://github.com/ETLCPP/etl.git
synced 2026-04-30 19:09:10 +08:00
Add invocable and further missing concepts to concepts.h (#1412)
* Print test names at test time (#1343) * Fix operator| conflict with std::ranges (#1395) * Add concept invocable and further missing concepts to concepts.h --------- Co-authored-by: John Wellbelove <john.wellbelove@etlcpp.com> Co-authored-by: John Wellbelove <jwellbelove@users.noreply.github.com>
This commit is contained in:
parent
15ca12649e
commit
943e8e6089
@ -33,6 +33,7 @@ SOFTWARE.
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#include "invoke.h"
|
||||
#include "type_traits.h"
|
||||
#include "utility.h"
|
||||
|
||||
@ -50,12 +51,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
|
||||
@ -114,6 +134,112 @@ namespace etl
|
||||
{ lhs = etl::forward<RHS>(rhs) } -> etl::same_as<LHS>;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
template <typename F, typename... Args>
|
||||
concept invocable = etl::is_invocable_v<F, Args...>;
|
||||
|
||||
//***************************************************************************
|
||||
template <typename F, typename... Args>
|
||||
concept regular_invocable = etl::invocable<F, Args...>;
|
||||
|
||||
//***************************************************************************
|
||||
template <typename T>
|
||||
concept destructible = requires(T& t) {
|
||||
{ t.~T() } noexcept;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
template <typename T, typename... Args>
|
||||
concept constructible_from = etl::destructible<T> && etl::is_constructible_v<T, Args...>;
|
||||
|
||||
//***************************************************************************
|
||||
template <typename T>
|
||||
concept default_initializable = etl::constructible_from<T> && requires {
|
||||
T{};
|
||||
::new T;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
template <typename T>
|
||||
concept move_constructible = etl::constructible_from<T, T> && etl::convertible_to<T, T>;
|
||||
|
||||
//***************************************************************************
|
||||
template <typename T>
|
||||
concept copy_constructible =
|
||||
etl::move_constructible<T> && etl::constructible_from<T, T&> && etl::convertible_to<T&, T> && etl::constructible_from<T, const T&>
|
||||
&& etl::convertible_to<const T&, T> && etl::constructible_from<T, const T> && etl::convertible_to<const T, T>;
|
||||
|
||||
//***************************************************************************
|
||||
namespace private_concepts
|
||||
{
|
||||
template <typename T>
|
||||
concept boolean_testable = etl::convertible_to<T, bool> && requires(T&& t) {
|
||||
{ !etl::forward<T>(t) } -> etl::convertible_to<bool>;
|
||||
};
|
||||
} // namespace private_concepts
|
||||
|
||||
//***************************************************************************
|
||||
template <typename T>
|
||||
concept equality_comparable = requires(const etl::remove_reference_t<T>& a, const etl::remove_reference_t<T>& b) {
|
||||
{ a == b } -> private_concepts::boolean_testable;
|
||||
{ a != b } -> private_concepts::boolean_testable;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
template <typename T>
|
||||
concept totally_ordered = etl::equality_comparable<T> && requires(const etl::remove_reference_t<T>& a, const etl::remove_reference_t<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 <typename T>
|
||||
concept swappable = requires(T& a, T& b) { etl::swap(a, b); };
|
||||
|
||||
//***************************************************************************
|
||||
template <typename T, typename U>
|
||||
concept swappable_with = etl::common_reference_with<etl::remove_reference_t<T>&, etl::remove_reference_t<U>&> && requires(T&& t, U&& u) {
|
||||
etl::swap(etl::forward<T>(t), etl::forward<T>(t));
|
||||
etl::swap(etl::forward<U>(u), etl::forward<U>(u));
|
||||
etl::swap(etl::forward<T>(t), etl::forward<U>(u));
|
||||
etl::swap(etl::forward<U>(u), etl::forward<T>(t));
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
template <typename T>
|
||||
concept movable = etl::is_object_v<T> && etl::move_constructible<T> && etl::assignable_from<T&, T> && etl::swappable<T>;
|
||||
|
||||
//***************************************************************************
|
||||
template <typename T>
|
||||
concept copyable = etl::copy_constructible<T> && etl::movable<T> && etl::assignable_from<T&, T&> && etl::assignable_from<T&, const T&>
|
||||
&& etl::assignable_from<T&, const T>;
|
||||
|
||||
//***************************************************************************
|
||||
template <typename T>
|
||||
concept semiregular = etl::copyable<T> && etl::default_initializable<T>;
|
||||
|
||||
//***************************************************************************
|
||||
template <typename T>
|
||||
concept regular = etl::semiregular<T> && etl::equality_comparable<T>;
|
||||
|
||||
//***************************************************************************
|
||||
template <typename F, typename... Args>
|
||||
concept predicate = etl::regular_invocable<F, Args...> && private_concepts::boolean_testable<etl::invoke_result_t<F, Args...> >;
|
||||
|
||||
//***************************************************************************
|
||||
template <typename R, typename T, typename U>
|
||||
concept relation = etl::predicate<R, T, T> && etl::predicate<R, U, U> && etl::predicate<R, T, U> && etl::predicate<R, U, T>;
|
||||
|
||||
//***************************************************************************
|
||||
template <typename R, typename T, typename U>
|
||||
concept equivalence_relation = etl::relation<R, T, U>;
|
||||
|
||||
//***************************************************************************
|
||||
template <typename R, typename T, typename U>
|
||||
concept strict_weak_order = etl::relation<R, T, U>;
|
||||
|
||||
#endif
|
||||
} // namespace etl
|
||||
#endif
|
||||
|
||||
@ -649,6 +649,18 @@ namespace etl
|
||||
struct is_void<void> : true_type
|
||||
{
|
||||
};
|
||||
template <>
|
||||
struct is_void<const void> : true_type
|
||||
{
|
||||
};
|
||||
template <>
|
||||
struct is_void<volatile void> : true_type
|
||||
{
|
||||
};
|
||||
template <>
|
||||
struct is_void<const volatile void> : true_type
|
||||
{
|
||||
};
|
||||
|
||||
#if ETL_USING_CPP17
|
||||
template <typename T>
|
||||
@ -3977,6 +3989,19 @@ namespace etl
|
||||
template <typename T>
|
||||
inline constexpr bool is_function_v = etl::is_function<T>::value;
|
||||
#endif
|
||||
|
||||
//***************************************************************************
|
||||
/// is_object
|
||||
//***************************************************************************
|
||||
template <typename T>
|
||||
struct is_object : etl::bool_constant<!etl::is_function<T>::value && !etl::is_reference<T>::value && !etl::is_void<T>::value>
|
||||
{
|
||||
};
|
||||
|
||||
#if ETL_USING_CPP17
|
||||
template <typename T>
|
||||
inline constexpr bool is_object_v = etl::is_object<T>::value;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ETL_USING_CPP11
|
||||
|
||||
@ -29,6 +29,7 @@ SOFTWARE.
|
||||
#include "unit_test_framework.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#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<std::string, std::string> == false);
|
||||
static_assert(etl::assignable_from<std::atomic<int>&, int> == false);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_invocable)
|
||||
{
|
||||
struct Functor
|
||||
{
|
||||
void operator()() {}
|
||||
};
|
||||
struct FunctorWithArgs
|
||||
{
|
||||
int operator()(int, double)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(etl::invocable<void()> == true);
|
||||
static_assert(etl::invocable<void(int), int> == true);
|
||||
static_assert(etl::invocable<Functor> == true);
|
||||
static_assert(etl::invocable<FunctorWithArgs, int, double> == true);
|
||||
static_assert(etl::invocable<FunctorWithArgs> == false);
|
||||
static_assert(etl::invocable<int> == false);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_regular_invocable)
|
||||
{
|
||||
struct Functor
|
||||
{
|
||||
void operator()() {}
|
||||
};
|
||||
|
||||
static_assert(etl::regular_invocable<void()> == true);
|
||||
static_assert(etl::regular_invocable<Functor> == true);
|
||||
static_assert(etl::regular_invocable<int> == false);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_destructible)
|
||||
{
|
||||
static_assert(etl::destructible<int> == true);
|
||||
static_assert(etl::destructible<std::string> == true);
|
||||
static_assert(etl::destructible<NotDestructible> == false);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_constructible_from)
|
||||
{
|
||||
static_assert(etl::constructible_from<int> == true);
|
||||
static_assert(etl::constructible_from<int, int> == true);
|
||||
static_assert(etl::constructible_from<std::string, const char*> == true);
|
||||
static_assert(etl::constructible_from<NotDestructible> == false);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_default_initializable)
|
||||
{
|
||||
static_assert(etl::default_initializable<int> == true);
|
||||
static_assert(etl::default_initializable<std::string> == true);
|
||||
static_assert(etl::default_initializable<NotDefaultConstructible> == false);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_move_constructible)
|
||||
{
|
||||
static_assert(etl::move_constructible<int> == true);
|
||||
static_assert(etl::move_constructible<std::string> == true);
|
||||
static_assert(etl::move_constructible<NotMovable> == false);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_copy_constructible)
|
||||
{
|
||||
static_assert(etl::copy_constructible<int> == true);
|
||||
static_assert(etl::copy_constructible<std::string> == true);
|
||||
static_assert(etl::copy_constructible<NotCopyable> == false);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_equality_comparable)
|
||||
{
|
||||
static_assert(etl::equality_comparable<int> == true);
|
||||
static_assert(etl::equality_comparable<EqualityComparableType> == true);
|
||||
static_assert(etl::equality_comparable<NotEqualityComparable> == false);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_totally_ordered)
|
||||
{
|
||||
static_assert(etl::totally_ordered<int> == true);
|
||||
static_assert(etl::totally_ordered<OrderedType> == true);
|
||||
static_assert(etl::totally_ordered<EqualityComparableType> == false);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_swappable)
|
||||
{
|
||||
static_assert(etl::swappable<int> == true);
|
||||
static_assert(etl::swappable<std::string> == true);
|
||||
static_assert(etl::swappable<NotMovable> == false);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_swappable_with)
|
||||
{
|
||||
// Positive cases: same-type lvalue references that are swappable
|
||||
static_assert(etl::swappable_with<int&, int&> == true);
|
||||
static_assert(etl::swappable_with<std::string&, std::string&> == true);
|
||||
|
||||
// Negative cases: unrelated types (no valid swap overload)
|
||||
static_assert(etl::swappable_with<int, std::string> == false);
|
||||
|
||||
// Negative case: non-movable type cannot be swapped
|
||||
static_assert(etl::swappable_with<NotMovable&, NotMovable&> == false);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_movable)
|
||||
{
|
||||
static_assert(etl::movable<int> == true);
|
||||
static_assert(etl::movable<std::string> == true);
|
||||
static_assert(etl::movable<NotMovable> == false);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_copyable)
|
||||
{
|
||||
static_assert(etl::copyable<int> == true);
|
||||
static_assert(etl::copyable<std::string> == true);
|
||||
static_assert(etl::copyable<NotCopyable> == false);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_semiregular)
|
||||
{
|
||||
static_assert(etl::semiregular<int> == true);
|
||||
static_assert(etl::semiregular<std::string> == true);
|
||||
static_assert(etl::semiregular<NotDefaultConstructible> == false);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_regular)
|
||||
{
|
||||
static_assert(etl::regular<int> == true);
|
||||
static_assert(etl::regular<std::string> == true);
|
||||
static_assert(etl::regular<NotEqualityComparable> == false);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_predicate)
|
||||
{
|
||||
static_assert(etl::predicate<BoolPredicate, int> == true);
|
||||
static_assert(etl::predicate<bool(int), int> == true);
|
||||
static_assert(etl::predicate<void(int), int> == false);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_relation)
|
||||
{
|
||||
static_assert(etl::relation<IntRelation, int, int> == true);
|
||||
static_assert(etl::relation<std::less<int>, int, int> == true);
|
||||
static_assert(etl::relation<BoolPredicate, int, int> == false);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_equivalence_relation)
|
||||
{
|
||||
static_assert(etl::equivalence_relation<std::equal_to<int>, int, int> == true);
|
||||
static_assert(etl::equivalence_relation<BoolPredicate, int, int> == false);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_strict_weak_order)
|
||||
{
|
||||
static_assert(etl::strict_weak_order<std::less<int>, int, int> == true);
|
||||
static_assert(etl::strict_weak_order<BoolPredicate, int, int> == false);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
@ -2211,5 +2211,43 @@ namespace
|
||||
CHECK_FALSE((etl::is_function<int MF::*>::value));
|
||||
CHECK_FALSE((etl::is_function<int (MF::*)(int)>::value)); // pointer, not function
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_is_object)
|
||||
{
|
||||
CHECK_TRUE((etl::is_object<int>::value));
|
||||
CHECK_TRUE((etl::is_object<int*>::value));
|
||||
CHECK_TRUE((etl::is_object<int[]>::value));
|
||||
CHECK_TRUE((etl::is_object<int[3]>::value));
|
||||
CHECK_TRUE((etl::is_object<const int>::value));
|
||||
CHECK_TRUE((etl::is_object<volatile int>::value));
|
||||
CHECK_TRUE((etl::is_object<MF>::value));
|
||||
|
||||
CHECK_FALSE((etl::is_object<void>::value));
|
||||
CHECK_FALSE((etl::is_object<const void>::value));
|
||||
CHECK_FALSE((etl::is_object<volatile void>::value));
|
||||
CHECK_FALSE((etl::is_object<const volatile void>::value));
|
||||
CHECK_FALSE((etl::is_object<int&>::value));
|
||||
CHECK_FALSE((etl::is_object<int&&>::value));
|
||||
CHECK_FALSE((etl::is_object<decltype(f)>::value));
|
||||
|
||||
CHECK_TRUE((etl::is_void<const void>::value));
|
||||
CHECK_TRUE((etl::is_void<volatile void>::value));
|
||||
CHECK_TRUE((etl::is_void<const volatile void>::value));
|
||||
|
||||
#if ETL_USING_CPP17
|
||||
CHECK_TRUE((etl::is_object_v<int>));
|
||||
CHECK_TRUE((etl::is_object_v<int*>));
|
||||
CHECK_TRUE((etl::is_object_v<MF>));
|
||||
|
||||
CHECK_FALSE((etl::is_object_v<void>));
|
||||
CHECK_FALSE((etl::is_object_v<const void>));
|
||||
CHECK_FALSE((etl::is_object_v<volatile void>));
|
||||
CHECK_FALSE((etl::is_object_v<const volatile void>));
|
||||
CHECK_FALSE((etl::is_object_v<int&>));
|
||||
CHECK_FALSE((etl::is_object_v<int&&>));
|
||||
CHECK_FALSE((etl::is_object_v<decltype(f)>));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user