From 993b49d9b0aba3a9e60ee2bc86aa72b5ee69d063 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sun, 29 Dec 2019 16:37:11 +0100 Subject: new_type: extract type_traits extensions --- include/newtype/impl/type_traits_extensions.hpp | 518 ++++++++++++++++++++++++ include/newtype/new_type.hpp | 98 +++-- 2 files changed, 565 insertions(+), 51 deletions(-) create mode 100644 include/newtype/impl/type_traits_extensions.hpp diff --git a/include/newtype/impl/type_traits_extensions.hpp b/include/newtype/impl/type_traits_extensions.hpp new file mode 100644 index 0000000..ae0c7d5 --- /dev/null +++ b/include/newtype/impl/type_traits_extensions.hpp @@ -0,0 +1,518 @@ +#ifndef NEWTYPE_IMPL_TYPE_TRAITS_EXTENSIONS_HPP +#define NEWTYPE_IMPL_TYPE_TRAITS_EXTENSIONS_HPP + +#include +#include + +namespace nt::impl +{ + + inline namespace equality_comparable + { + + /** + * @brief A trait to test if a given type is comparable using operator== + * + * @tparam T The type to test + * @note This specialization forms the base case for non-equals-comparable T + */ + template + struct is_equality_comparable : std::false_type + { + }; + + /** + * @brief A trait to test if a given type is comparable using operator== + * + * @tparam T The type to test + * @note This specialization forms the case for equals-comparable T + */ + template + struct is_equality_comparable() == std::declval())>> : std::true_type + { + }; + + /** + * @brief A variable template to test if a given type is comparable using operator== + * + * @tparam T The type to test + */ + template + auto constexpr is_equality_comparable_v = is_equality_comparable::value; + + /** + * @brief A trait to test if a given type is noexcept comparable using operator== + * + * @tparam T The type to test + * @note This specialization forms the base case for non-noexcept equals-comparable or non-equals-comparable T + */ + template + struct is_nothrow_equality_comparable : std::false_type + { + }; + + /** + * @brief A trait to test if a given type is noexcept comparable using operator== + * + * @tparam T The type to test + * @note This specialization forms the case for equals-comparable T detemining if T is noexcept comparable using operator== + */ + template + struct is_nothrow_equality_comparable() == std::declval())>> + : std::bool_constant() == std::declval())> + { + }; + + /** + * @brief A variable template to test if a given type is noexcept comparable using operator== + * + * @tparam T The type to test + */ + template + auto constexpr is_nothrow_equality_comparable_v = is_nothrow_equality_comparable::value; + + /** + * @brief A trait to test if a given type is comparable using operator!= + * + * @tparam T The type to test + * @note This specialization forms the base case for non-not-equals-comparable T + */ + template + struct is_inequality_comparable : std::false_type + { + }; + + /** + * @brief A trait to test if a given type is comparable using operator!= + * + * @tparam T The type to test + * @note This specialization forms the case for not-equals-comparable T + */ + template + struct is_inequality_comparable() != std::declval())>> : std::true_type + { + }; + + /** + * @brief A variable template to test if a given type is comparable using operator!= + * + * @tparam T The type to test + */ + template + auto constexpr is_inequality_comparable_v = is_inequality_comparable::value; + + /** + * @brief A trait to test if a given type is noexcept comparable using operator!= + * + * @tparam T The type to test + * @note This specialization forms the base case for non-noexcept not-equals-comparable or non-not-equals-comparable T + */ + template + struct is_nothrow_inequality_comparable : std::false_type + { + }; + + /** + * @brief A trait to test if a given type is noexcept comparable using operator== + * + * @tparam T The type to test + * @note This specialization forms the case for equals-comparable T detemining if T is noexcept comparable using operator!= + */ + template + struct is_nothrow_inequality_comparable() != std::declval())>> + : std::bool_constant() != std::declval())> + { + }; + + /** + * @brief A variable template to test if a given type is noexcept comparable using operator!= + * + * @tparam T The type to test + */ + template + auto constexpr is_nothrow_inequality_comparable_v = is_nothrow_inequality_comparable::value; + + } // namespace equality_comparable + + inline namespace relationally_comparable + { + + /** + * @brief A trait to test if a given type is comparable using operator< + * + * @tparam T The type to test + * @note This specialization forms the base case for non-less-than-comparable T + */ + template + struct is_less_than_comparable : std::false_type + { + }; + + /** + * @brief A trait to test if a given type is comparable using operator< + * + * @tparam T The type to test + * @note This specialization forms the case for less-than-comparable T + */ + template + struct is_less_than_comparable() < std::declval())>> : std::true_type + { + }; + + /** + * @brief A variable template to test if a given type is comparable using operator< + * + * @tparam T The type to test + */ + template + auto constexpr is_less_than_comparable_v = is_less_than_comparable::value; + + /** + * @brief A trait to test if a given type is noexcept comparable using operator< + * + * @tparam T The type to test + * @note This specialization forms the base case for non-noexcept less-than-comparable or non-less-than-comparable T + */ + template + struct is_nothrow_less_than_comparable : std::false_type + { + }; + + /** + * @brief A trait to test if a given type is noexcept comparable using operator< + * + * @tparam T The type to test + * @note This specialization forms the case for less-than-comparable T detemining if T is noexcept comparable using operator< + */ + template + struct is_nothrow_less_than_comparable() < std::declval())>> + : std::bool_constant() < std::declval())> + { + }; + + /** + * @brief A variable template to test if a given type is noexcept comparable using operator< + * + * @tparam T The type to test + */ + template + auto constexpr is_nothrow_less_than_comparable_v = is_nothrow_less_than_comparable::value; + + /** + * @brief A trait to test if a given type is comparable using operator> + * + * @tparam T The type to test + * @note This specialization forms the base case for non-greater-than-comparable T + */ + template + struct is_greater_than_comparable : std::false_type + { + }; + + /** + * @brief A trait to test if a given type is comparable using operator> + * + * @tparam T The type to test + * @note This specialization forms the case for greater-than-comparable T + */ + template + struct is_greater_than_comparable() > std::declval())>> : std::true_type + { + }; + + /** + * @brief A variable template to test if a given type is comparable using operator> + * + * @tparam T The type to test + */ + template + auto constexpr is_greater_than_comparable_v = is_greater_than_comparable::value; + + /** + * @brief A trait to test if a given type is noexcept comparable using operator> + * + * @tparam T The type to test + * @note This specialization forms the base case for non-noexcept greater-than-comparable or non-greater-than-comparable T + */ + template + struct is_nothrow_greater_than_comparable : std::false_type + { + }; + + /** + * @brief A trait to test if a given type is noexcept comparable using operator> + * + * @tparam T The type to test + * @note This specialization forms the case for greater-than-comparable T detemining if T is noexcept comparable using operator> + */ + template + struct is_nothrow_greater_than_comparable() > std::declval())>> + : std::bool_constant() > std::declval())> + { + }; + + /** + * @brief A variable template to test if a given type is noexcept comparable using operator> + * + * @tparam T The type to test + */ + template + auto constexpr is_nothrow_greater_than_comparable_v = is_nothrow_greater_than_comparable::value; + + /** + * @brief A trait to test if a given type is comparable using operator<= + * + * @tparam T The type to test + * @note This specialization forms the base case for non-less-than-or-equal-to-comparable T + */ + template + struct is_less_than_equal_to_comparable : std::false_type + { + }; + + /** + * @brief A trait to test if a given type is comparable using operator<= + * + * @tparam T The type to test + * @note This specialization forms the case for less-than-or-equal-to-comparable T + */ + template + struct is_less_than_equal_to_comparable() <= std::declval())>> : std::true_type + { + }; + + /** + * @brief A variable template to test if a given type is comparable using operator<= + * + * @tparam T The type to test + */ + template + auto constexpr is_less_than_equal_to_comparable_v = is_less_than_equal_to_comparable::value; + + /** + * @brief A trait to test if a given type is noexcept comparable using operator<= + * + * @tparam T The type to test + * @note This specialization forms the base case for non-noexcept less-than-or-equal-to-comparable or non-less-than-or-equal-to-comparable T + */ + template + struct is_nothrow_less_than_equal_to_comparable : std::false_type + { + }; + + /** + * @brief A trait to test if a given type is noexcept comparable using operator<= + * + * @tparam T The type to test + * @note This specialization forms the case for less-than-or-equal-to-comparable T detemining if T is noexcept comparable using operator<= + */ + template + struct is_nothrow_less_than_equal_to_comparable() <= std::declval())>> + : std::bool_constant() <= std::declval())> + { + }; + + /** + * @brief A variable template to test if a given type is noexcept comparable using operator<= + * + * @tparam T The type to test + */ + template + auto constexpr is_nothrow_less_than_equal_to_comparable_v = is_nothrow_less_than_equal_to_comparable::value; + + /** + * @brief A trait to test if a given type is comparable using operator>= + * + * @tparam T The type to test + * @note This specialization forms the base case for non-greater-than-or-equal-to-comparable T + */ + template + struct is_greater_than_equal_to_comparable : std::false_type + { + }; + + /** + * @brief A trait to test if a given type is comparable using operator>= + * + * @tparam T The type to test + * @note This specialization forms the case for greater-than-or-equal-to-comparable T + */ + template + struct is_greater_than_equal_to_comparable() >= std::declval())>> + : std::true_type + { + }; + + /** + * @brief A variable template to test if a given type is comparable using operator>= + * + * @tparam T The type to test + */ + template + auto constexpr is_greater_than_equal_to_comparable_v = is_greater_than_equal_to_comparable::value; + + /** + * @brief A trait to test if a given type is noexcept comparable using operator>= + * + * @tparam T The type to test + * @note This specialization forms the base case for non-noexcept greater-than-or-equal-to-comparable or + * non-greater-than-or-equal-to-comparable T + */ + template + struct is_nothrow_greater_than_equal_to_comparable : std::false_type + { + }; + + /** + * @brief A trait to test if a given type is noexcept comparable using operator>= + * + * @tparam T The type to test + * @note This specialization forms the case for greater-than-or-equal-to-comparable T detemining if T is noexcept comparable using + * operator>= + */ + template + struct is_nothrow_greater_than_equal_to_comparable() >= std::declval())>> + : std::bool_constant() >= std::declval())> + { + }; + + /** + * @brief A variable template to test if a given type is noexcept comparable using operator>= + * + * @tparam T The type to test + */ + template + auto constexpr is_nothrow_greater_than_equal_to_comparable_v = is_nothrow_greater_than_equal_to_comparable::value; + } // namespace relationally_comparable + + inline namespace iostreamable + { + + /** + * @brief A trait to test if a given type is output streamable + * + * @tparam T The type to test + * @note This specialization forms the base case for non-output-streamable T + */ + template + struct is_output_streamable : std::false_type + { + }; + + /** + * @brief A trait to test if a given type is output streamable + * + * @tparam T The type to test + * @note This specialization forms the case for output-streamable T + */ + template + struct is_output_streamable() << std::declval())>> + : std::true_type + { + }; + + /** + * @brief A variable template to test if a given type is output streamable + * + * @tparam T The type to test + */ + template + auto constexpr is_output_streamable_v = is_output_streamable::value; + + /** + * @brief A trait to test if a given type is noexcept output streamable + * + * @tparam T The type to test + * @note This specialization forms the base case for non-noexcept output-streamable or non-output-streamable T + */ + template + struct is_nothrow_output_streamable : std::false_type + { + }; + + /** + * @brief A trait to test if a given type is noexcept output streamable + * + * @tparam T The type to test + * @note This specialization forms the case for output-streamable T detemining if T is noexcept output-streamable + */ + template + struct is_nothrow_output_streamable() << std::declval())>> + : std::bool_constant() << std::declval())> + { + }; + + /** + * @brief A variable template to test if a given type is noexcept output streamable + * + * @tparam T The type to test + */ + template + auto constexpr is_nothrow_output_streamable_v = is_nothrow_output_streamable::value; + + /** + * @brief A trait to test if a given type is input streamable + * + * @tparam T The type to test + * @note This specialization forms the base case for non-input-streamable T + */ + template + struct is_input_streamable : std::false_type + { + }; + + /** + * @brief A trait to test if a given type is input streamable + * + * @tparam T The type to test + * @note This specialization forms the case for input-streamable T + */ + template + struct is_input_streamable() >> std::declval())>> : std::true_type + { + }; + + /** + * @brief A variable template to test if a given type is input streamable + * + * @tparam T The type to test + */ + template + auto constexpr is_input_streamable_v = is_input_streamable::value; + + /** + * @brief A trait to test if a given type is noexcept input streamable + * + * @tparam T The type to test + * @note This specialization forms the base case for non-noexcept input-streamable or non-input-streamable T + */ + template + struct is_nothrow_input_streamable : std::false_type + { + }; + + /** + * @brief A trait to test if a given type is noexcept input streamable + * + * @tparam T The type to test + * @note This specialization forms the case for input-streamable T detemining if T is noexcept input-streamable + */ + template + struct is_nothrow_input_streamable() >> std::declval())>> + : std::bool_constant() >> std::declval())> + { + }; + + /** + * @brief A variable template to test if a given type is noexcept input streamable + * + * @tparam T The type to test + */ + template + auto constexpr is_nothrow_input_streamable_v = is_nothrow_input_streamable::value; + + } // namespace iostreamable + +} // namespace nt::impl + +#endif \ No newline at end of file diff --git a/include/newtype/new_type.hpp b/include/newtype/new_type.hpp index 7f73daa..85f0d40 100644 --- a/include/newtype/new_type.hpp +++ b/include/newtype/new_type.hpp @@ -4,6 +4,7 @@ #include "newtype/derivable.hpp" #include "newtype/deriving.hpp" #include "newtype/impl/new_type_storage.hpp" +#include "newtype/impl/type_traits_extensions.hpp" #include "newtype/type.hpp" #include @@ -30,10 +31,10 @@ namespace nt static_assert(!std::is_void_v>, "The base type must not be possibly cv-qualified void"); template - auto friend - operator>>(std::basic_istream & input, new_type & target) noexcept( - noexcept(std::declval &>() >> std::declval())) - -> std::enable_if_t> &; + auto friend operator>>(std::basic_istream &, new_type &) noexcept( + impl::is_nothrow_input_streamable_v, BaseTypeT>) + -> std::enable_if_t, BaseTypeT>, + std::basic_istream> &; using super = impl::new_type_move_assignment; @@ -177,10 +178,10 @@ namespace nt * @return true iff. the base type comparison operator returns true, false otherwise. */ template - auto constexpr operator==(new_type const & lhs, - new_type const & rhs) noexcept(noexcept(std::declval() == - std::declval())) - -> bool + auto constexpr + operator==(new_type const & lhs, + new_type const & rhs) noexcept(impl::is_nothrow_equality_comparable_v) + -> std::enable_if_t, bool> { return lhs.decay() == rhs.decay(); } @@ -193,10 +194,10 @@ namespace nt * @return true iff. the base type comparison operator returns true, false otherwise. */ template - auto constexpr operator!=(new_type const & lhs, - new_type const & rhs) noexcept(noexcept(std::declval() != - std::declval())) - -> bool + auto constexpr + operator!=(new_type const & lhs, + new_type const & rhs) noexcept(impl::is_nothrow_inequality_comparable_v) + -> std::enable_if_t, bool> { return lhs.decay() != rhs.decay(); } @@ -204,50 +205,46 @@ namespace nt /** * @brief Check if one nt::new_type object is less-than an other * - * @note This operator is only avalaible if the the derivation clause of this nt::new_type does contains nt::Relational. Otherwise is is - * defined as deleted. + * @note This operator is only avalaible if the the derivation clause of this nt::new_type does contains nt::Relational and the base type is + * less-than comparable. Otherwise is is defined as deleted. * @throw This comparison operator throws any exception thrown by the base type comparison operator. It it noexcept iff. the base type * comparison operator is noexcept. * @return true iff. the base type comparison operator returns true, false otherwise. */ template - auto constexpr operator<(new_type const & lhs, - new_type const & rhs) noexcept(noexcept(std::declval() < - std::declval())) - -> std::enable_if_t + auto constexpr + operator<(new_type const & lhs, + new_type const & rhs) noexcept(impl::is_nothrow_less_than_comparable_v) + -> std::enable_if_t, bool> { return lhs.decay() < rhs.decay(); } template - auto constexpr operator<(new_type const & lhs, - new_type const & rhs) noexcept(noexcept(std::declval() < - std::declval())) - -> std::enable_if_t = delete; + auto constexpr operator<(new_type const &, new_type const &) + -> std::enable_if_t, bool> = delete; /** * Check if one nt::new_type object is greater-than an other * - * @note This operator is only avalaible if the the derivation clause of this nt::new_type does contains nt::Relational. Otherwise is is - * defined as deleted. + * @note This operator is only avalaible if the the derivation clause of this nt::new_type does contains nt::Relational and the base type is + * greater-than comparable. Otherwise is is defined as deleted. * @throw This comparison operator throws any exception thrown by the base type comparison operator. It it noexcept iff. the base type * comparison operator is noexcept. * @return true iff. the base type comparison operator returns true, false otherwise. */ template - auto constexpr operator>(new_type const & lhs, - new_type const & rhs) noexcept(noexcept(std::declval() > - std::declval())) - -> std::enable_if_t + auto constexpr + operator>(new_type const & lhs, + new_type const & rhs) noexcept(impl::is_nothrow_greater_than_comparable_v) + -> std::enable_if_t, bool> { return lhs.decay() > rhs.decay(); } template - auto constexpr operator>(new_type const & lhs, - new_type const & rhs) noexcept(noexcept(std::declval() > - std::declval())) - -> std::enable_if_t = delete; + auto constexpr operator>(new_type const &, new_type const &) + -> std::enable_if_t, bool> = delete; /** * Check if one nt::new_type object is less-than or equal-to an other @@ -258,19 +255,17 @@ namespace nt * @return true iff. the base type comparison operator returns true, false otherwise. */ template - auto constexpr operator<=(new_type const & lhs, - new_type const & rhs) noexcept(noexcept(std::declval() <= - std::declval())) - -> std::enable_if_t + auto constexpr + operator<=(new_type const & lhs, + new_type const & rhs) noexcept(impl::is_nothrow_less_than_equal_to_comparable_v) + -> std::enable_if_t, bool> { return lhs.decay() <= rhs.decay(); } template - auto constexpr operator<=(new_type const & lhs, - new_type const & rhs) noexcept(noexcept(std::declval() <= - std::declval())) - -> std::enable_if_t = delete; + auto constexpr operator<=(new_type const &, new_type const &) + -> std::enable_if_t, bool> = delete; /** * Check if one nt::new_type object is greater-than or equal-to an other @@ -281,19 +276,18 @@ namespace nt * @return true iff. the base type comparison operator returns true, false otherwise. */ template - auto constexpr operator>=(new_type const & lhs, - new_type const & rhs) noexcept(noexcept(std::declval() >= - std::declval())) - -> std::enable_if_t + auto constexpr + operator>=(new_type const & lhs, + new_type const & rhs) noexcept(impl::is_nothrow_greater_than_equal_to_comparable_v) + -> std::enable_if_t, bool> { return lhs.decay() >= rhs.decay(); } template auto constexpr operator>=(new_type const & lhs, - new_type const & rhs) noexcept(noexcept(std::declval() >= - std::declval())) - -> std::enable_if_t = delete; + new_type const & rhs) + -> std::enable_if_t, bool> = delete; /** * @brief Write the contained base type object to a standard output stream @@ -305,8 +299,9 @@ namespace nt */ template auto operator<<(std::basic_ostream & output, new_type const & source) noexcept( - noexcept(std::declval &>() << std::declval())) - -> std::enable_if_t> & + impl::is_nothrow_output_streamable_v, BaseType>) + -> std::enable_if_t, BaseType>, + std::basic_ostream> & { return output << source.decay(); } @@ -321,8 +316,9 @@ namespace nt */ template auto operator>>(std::basic_istream & input, new_type & target) noexcept( - noexcept(std::declval &>() >> std::declval())) - -> std::enable_if_t> & + impl::is_nothrow_input_streamable_v, BaseType>) + -> std::enable_if_t, BaseType>, + std::basic_istream> & { return input >> target.m_value; } -- cgit v1.2.3