/*! * \file /utl/concepts/stl.h * \brief STL's Concepts */ #ifndef __utl_concepts_stl_h__ #define __utl_concepts_stl_h__ #include #include #include #include /*! * \brief * STL's core language concepts * * We provide std concepts in case host's stl does not provide them yet. * * For more information \see https://en.cppreference.com/w/cpp/concepts */ //! @{ namespace utl { template using remove_cvref_t = std::remove_cv_t< std::remove_reference_t >; template using cref_ = const std::remove_reference_t&; template using _ref_t = std::add_lvalue_reference_t; template using use_if_same_t = meta::eval< meta::enable_if< meta::same_<_T1, _T2>::value, _Ret > >; /*! * Same */ template _utlConcept Same = meta::same_::value; // template // _utlConcept Decayed = Same>; /*! * DerivedFrom */ template _utlConcept DerivedFrom = std::is_base_of::value && std::is_convertible::value; /*! * ConvertibleTo */ template #if CXX_CONCEPTS _utlConcept ConvertibleTo = std::is_convertible::value && requires(From (&f)()) { static_cast(f()); }; #else _utlConcept ConvertibleTo = std::is_convertible::value; #endif /*! * Common Reference */ //! @{ namespace common_impl { //! \see https://ericniebler.github.io/std/wg21/D0022.html // ========== common reference =========== template using __cond_res = decltype(false ? std::declval()() : std::declval()()); template struct __copy_cv_ { static_assert(!std::is_reference::value); template using apply = To; }; template struct __copy_cv_ { template using apply = const To; }; template struct __copy_cv_ { template using apply = volatile To; }; template struct __copy_cv_ { template using apply = const volatile To; }; template using __copy_cv = meta::invoke<__copy_cv_, To>; // CREF [meta.trans.other]/2.1 template using __cref = std::add_lvalue_reference_t>; // COMMON_REF [meta.trans.other]/2 template struct __common_ref_ { static_assert(std::is_reference::value, ""); static_assert(std::is_reference::value, ""); }; template using __common_ref = meta::eval<__common_ref_>; // [meta.trans.other]/2.5 template using __lref_res = __cond_res< __copy_cv &, __copy_cv & >; // [meta.trans.other]/2.6 template> using __rref_res = std::remove_reference_t&&; template struct __common_ref_, meta::when>::value>> > { using type = __lref_res; }; template struct __common_ref_, meta::when>>, meta::when>>> > { using type = __rref_res; }; // [meta.trans.other]/2.7 template struct __common_ref_, meta::when>>> > { using type = __common_ref; }; // [meta.trans.other]/2.8 template struct __common_ref_, meta::when>>> > { using type = __common_ref; }; template struct __xref { template using apply = U; }; template struct __xref { template using apply = const U; }; template struct __xref { template using apply = volatile U; }; template struct __xref { template using apply = const volatile U; }; template struct __xref { template using apply = std::add_lvalue_reference_t, U>>; }; template struct __xref { template using apply = std::add_rvalue_reference_t, U>>; }; template class, template class > struct basic_common_reference { }; template using __basic_common_reference_t = meta::eval< basic_common_reference< remove_cvref_t, remove_cvref_t, __xref::template apply, __xref::template apply > >; template struct common_reference {}; template using common_reference_t = meta::eval< common_reference >; // [meta.trans.other]/5.2 template struct common_reference { using type = T; }; // [meta.trans.other]/5.3.4 template struct __common_reference3 : std::common_type {}; // [meta.trans.other]/5.3.3 template struct __common_reference3>> { using type = __cond_res; }; template struct __common_reference2 : __common_reference3 {}; // [meta.trans.other]/5.3.2 template struct __common_reference2>> { using type = __basic_common_reference_t; }; template struct __common_reference : __common_reference2 { }; template struct __common_reference::value && std::is_reference::value>> { using type = __common_ref; }; template struct common_reference : __common_reference { }; // [meta.trans.other]/5.4 template //requires requires { typename common_reference_t; } struct common_reference : common_reference < common_reference_t, V, W... > {}; } template using common_reference = common_impl::common_reference; template using common_reference_t = meta::eval< common_reference >; //! @} //FIXME: CommonReference needs better implementation template _utlConcept CommonReference = Same, common_reference_t> && ConvertibleTo> && ConvertibleTo>; // != std::Common on CommonReference template _utlConcept Common = #if CXX_CONCEPTS Same, std::common_type_t> && requires { static_cast>(std::declval()); static_cast>(std::declval()); }; // } && // CommonReference< // std::add_lvalue_reference_t, // std::add_lvalue_reference_t> && // CommonReference< // std::add_lvalue_reference_t>, // std::common_reference_t< // std::add_lvalue_reference_t, // std::add_lvalue_reference_t // > // >; #else // meta::and_ < Same, std::common_type_t>; //> // meta::bool_, // std::add_lvalue_reference_t // >>, // meta::bool_< CommonReference< // std::add_lvalue_reference_t>, // common_reference_t< // std::add_lvalue_reference_t, // std::add_lvalue_reference_t // > // >> // >::value; #endif /*! * Integral */ template _utlConcept Integral = std::is_integral::value; /*! * Signed Integral */ template _utlConcept SignedIntegral = Integral && std::is_signed::value; /*! * Unsigned Integral */ template _utlConcept UnsignedIntegral = Integral && !std::is_signed::value; template _utlConcept MoveAssignable = std::is_move_assignable::value; template _utlConcept CopyAssignable = std::is_copy_assignable::value; /*! * Assignable * \note != std:: on CommonReference */ template _utlConcept Assignable = #if CXX_CONCEPTS std::is_lvalue_reference::value && // CommonReference< // const std::remove_reference_t&, // const std::remove_reference_t&> && requires(LHS lhs, RHS&& rhs) { lhs = std::forward(rhs); requires Same< decltype(lhs = std::forward(rhs)), LHS >; }; #else std::is_assignable::value; #endif /*! * Swappable, SwappableWith */ //! @{ #if CXX_VER < CXX_VER_STD_17 namespace swappable_with_impl { struct is_swappable_with_ { // can apply std::swap template(), std::declval<_Up&>())), typename = decltype(std::swap(std::declval<_Up&>(), std::declval<_Tp&>()))> static meta::true_ check(int); // can not apply std::swap template static meta::false_ check(...); }; } template struct is_swappable_with : swappable_with_impl::is_swappable_with_ { using type = decltype(check<_Tp, _Up>(0)); }; #else template using is_swappable = std::is_swappable; template using is_swappable_with = std::is_swappable_with; #endif // != std:: on CommonReference template _utlConcept SwappableWith = is_swappable_with::type::value && is_swappable_with::type::value && is_swappable_with::type::value && is_swappable_with::type::value; // std::CommonReference< // const std::remove_reference_t&, // const std::remove_reference_t& // >; // != std:: we use is_swappable_with now is_swappable template _utlConcept Swappable = is_swappable_with::type::value; //! @} /*! * Destructible */ template _utlConcept Destructible = std::is_nothrow_destructible::value; /*! * Constructible */ template _utlConcept Constructible = Destructible && std::is_constructible::value; /*! * DefaultConstructible */ template _utlConcept DefaultConstructible = Constructible; /*! * MoveConstructible * \note * Another approach would be std::is_move_constructible::value; */ template _utlConcept MoveConstructible = Constructible && ConvertibleTo; /*! * CopyConstructible */ template _utlConcept CopyConstructible = MoveConstructible && Constructible> && ConvertibleTo<_ref_t, T> && Constructible> && ConvertibleTo, T> && Constructible && ConvertibleTo; /*! * Movable */ template _utlConcept Movable = std::is_object::value && MoveConstructible && Assignable<_ref_t, T> && Swappable; /*! * Copyable */ template _utlConcept Copyable = CopyConstructible && Movable && Assignable<_ref_t, const _ref_t>; /*! * Boolean */ #if CXX_CONCEPTS template _utlConcept Boolean = Movable> && requires(const std::remove_reference_t& b1, const std::remove_reference_t& b2, const bool a) { requires ConvertibleTo&, bool>; !b1; requires ConvertibleTo; b1 && a; requires Same; b1 || a; requires Same; b1 && b2; requires Same; a && b2; requires Same; b1 || b2; requires Same; a || b2; requires Same; b1 == b2; requires ConvertibleTo; b1 == a; requires ConvertibleTo; a == b2; requires ConvertibleTo; b1 != b2; requires ConvertibleTo; b1 != a; requires ConvertibleTo; a != b2; requires ConvertibleTo; }; #else namespace details { // template using try_op_not_ = decltype(!std::declval>()); // template using try_op_eq_ = decltype(std::declval>() == std::declval>()); // template using try_op_neq_ = decltype(std::declval>() != std::declval>()); // template using try_op_and_ = decltype(std::declval>() && std::declval>()); // template using try_op_or_ = decltype(std::declval>() || std::declval>()); // // template // struct is_boolean__ { // using type = meta::and_ < // meta::is_detected, // meta::is_detected, // meta::is_detected, // meta::is_detected, // meta::is_detected // >; // }; template struct is_boolean_ { using type = meta::false_; }; template struct is_boolean_ >())>, meta::use_if_same_t>() == std::declval>())>, meta::use_if_same_t>() != std::declval>())>, meta::use_if_same_t>() && std::declval>())>, meta::use_if_same_t>() || std::declval>())> >> { using type = meta::true_; }; template using is_boolean_t = meta::eval < is_boolean_ >; } template _utlConcept Boolean = Movable> && //ConvertibleTo&, bool> && ConvertibleTo, bool> && Same>; #endif namespace details { template struct is_weakly_equality_comparable_with_ { using type = meta::false_; }; template struct is_weakly_equality_comparable_with_>() == std::declval>())>, meta::use_if_same_t>() != std::declval>())>, meta::use_if_same_t>() == std::declval>())>, meta::use_if_same_t>() != std::declval>())> >> { using type = meta::true_; }; template using is_weakly_equality_comparable_with_t = meta::eval< is_weakly_equality_comparable_with_ >; } template _utlConcept WeaklyEqualityComparableWith = #if CXX_CONCEPTS requires(const std::remove_reference_t& t, const std::remove_reference_t& u) { t == u; requires Boolean; t != u; requires Boolean; u == t; requires Boolean; u != t; requires Boolean; }; #else Same>; #endif template _utlConcept EqualityComparable = WeaklyEqualityComparableWith; template _utlConcept EqualityComparableWith = EqualityComparable && EqualityComparable && // CommonReference< // const std::remove_reference_t&, // const std::remove_reference_t&> && // EqualityComparable< // common_reference_t< // const std::remove_reference_t&, // const std::remove_reference_t&>> && WeaklyEqualityComparableWith; #if CXX_CONCEPTS template _utlConcept StrictTotallyOrdered = EqualityComparable && requires(const std::remove_reference_t& a, const std::remove_reference_t& b) { a < b; requires Boolean; a > b; requires Boolean b)>; a <= b; requires Boolean; a >= b; requires Boolean= b)>; }; #else namespace details { template struct is_strict_totally_ordered_ { using type = meta::false_; }; template struct is_strict_totally_ordered_ >() < std::declval>())>, meta::use_if_same_t>() > std::declval>())>, meta::use_if_same_t>() <= std::declval>())>, meta::use_if_same_t>() >= std::declval>())> >> { using type = meta::true_; }; template using is_strict_totally_ordered_t = meta::eval < is_strict_totally_ordered_ >; } template _utlConcept StrictTotallyOrdered = EqualityComparable && Same >; #endif #if CXX_CONCEPTS template _utlConcept StrictTotallyOrderedWith = StrictTotallyOrdered && StrictTotallyOrdered && // CommonReference< // const std::remove_reference_t&, // const std::remove_reference_t& // > && // StrictTotallyOrdered< // common_reference_t< // const std::remove_reference_t&, // const std::remove_reference_t& // > // > && EqualityComparableWith && requires(const std::remove_reference_t& t, const std::remove_reference_t& u) { t < u; requires Boolean; t > u; requires Boolean u)>; t <= u; requires Boolean; t >= u; requires Boolean= u)>; u < t; requires Boolean; u > t; requires Boolean t)>; u <= t; requires Boolean; u >= t; requires Boolean= t)>; }; #else namespace details { template struct is_strict_totally_ordered_with_ { using type = meta::false_; }; template struct is_strict_totally_ordered_with_ >() < std::declval>())>, meta::use_if_same_t>() > std::declval>())>, meta::use_if_same_t>() <= std::declval>())>, meta::use_if_same_t>() >= std::declval>())>, meta::use_if_same_t>() < std::declval>())>, meta::use_if_same_t>() > std::declval>())>, meta::use_if_same_t>() <= std::declval>())>, meta::use_if_same_t>() >= std::declval>())> >> { using type = meta::true_; }; template using is_strict_totally_ordered_with_t = meta::eval < is_strict_totally_ordered_with_ >; } template _utlConcept StrictTotallyOrderedWith = StrictTotallyOrdered && StrictTotallyOrdered && EqualityComparableWith && Same >; #endif /*! * Semiregular */ template _utlConcept Semiregular = Copyable && DefaultConstructible; /*! * Regular */ template _utlConcept Regular = Semiregular && EqualityComparable; /*! * Scalar */ template _utlConcept Scalar = std::is_scalar::value && Regular; /*! * Arithmetic */ template _utlConcept Arithmetic = std::is_arithmetic::value && Scalar && StrictTotallyOrdered; /*! * FloatingPoint */ template _utlConcept FloatingPoint = std::is_floating_point::value && Arithmetic; /*! * Invocable */ template _utlConcept Invocable = is_invocable::value; // requires(F&& f, Args&&... args) { // invoke(std::forward(f), std::forward(args)...); // }; template< class F, class... Args > _utlConcept RegularInvocable = Invocable; template < class F, class... Args > _utlConcept Predicate = RegularInvocable && Boolean>; template _utlConcept Relation = Predicate && Predicate && Predicate && Predicate; template < class R, class T, class U > _utlConcept StrictWeakOrder = Relation; } //! @} #endif /* __utl_concepts_stl_h__ */