aboutsummaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@gmail.com>2023-06-07 16:41:32 +0200
committerFelix Morgner <felix.morgner@gmail.com>2023-06-07 16:41:32 +0200
commitaa7c021962f529f3ed2f482fd6f02e5497532a8a (patch)
treec8a3eff796c94f757cc8ae7d3b2324f53038e0c3 /source
parent741b6d177865f20908b1b290c5170025964d3d9a (diff)
downloadnewtype-aa7c021962f529f3ed2f482fd6f02e5497532a8a.tar.xz
newtype-aa7c021962f529f3ed2f482fd6f02e5497532a8a.zip
project: begin restructuring
Diffstat (limited to 'source')
-rw-r--r--source/CMakeLists.txt88
-rw-r--r--source/examples/src/basic_usage.cpp39
-rw-r--r--source/examples/src/basic_usage_with_read.cpp39
-rw-r--r--source/examples/src/basic_usage_with_show.cpp41
-rw-r--r--source/include/newtype/derivable.hpp32
-rw-r--r--source/include/newtype/derivation_clause.hpp70
-rw-r--r--source/include/newtype/deriving.hpp21
-rw-r--r--source/include/newtype/impl/new_type_iterator_types.hpp66
-rw-r--r--source/include/newtype/impl/new_type_storage.hpp139
-rw-r--r--source/include/newtype/impl/type_traits_extensions.hpp853
-rw-r--r--source/include/newtype/newtype.hpp550
-rw-r--r--source/include/newtype/version.hpp23
-rw-r--r--source/test/src/arithmetic.cpp300
-rw-r--r--source/test/src/conversion_suite.cpp201
-rw-r--r--source/test/src/derivation_clause_suite.cpp312
-rw-r--r--source/test/src/equality_comparison_suite.cpp188
-rw-r--r--source/test/src/hash_suite.cpp69
-rw-r--r--source/test/src/io_operators_suite.cpp121
-rw-r--r--source/test/src/iterable_suite.cpp719
-rw-r--r--source/test/src/new_type_constructor_suite.cpp91
-rw-r--r--source/test/src/relational_operators_suite.cpp321
21 files changed, 4283 insertions, 0 deletions
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
new file mode 100644
index 0000000..0bd0876
--- /dev/null
+++ b/source/CMakeLists.txt
@@ -0,0 +1,88 @@
+cmake_minimum_required(VERSION "3.25.0")
+
+project("newtype"
+ LANGUAGES CXX
+ DESCRIPTION "A library of types and functions to create strong type aliases"
+)
+
+enable_testing()
+
+# Project dependencies
+
+find_package("Catch2" "3.1"
+ COMPONENTS "Catch2WithMain"
+ REQUIRED
+)
+
+include("Catch")
+
+# Project Options
+
+option(BUILD_EXAMPLES "Build the library examples" OFF)
+
+# 'newtype' library
+
+add_library("${PROJECT_NAME}" INTERFACE)
+
+target_include_directories("${PROJECT_NAME}" INTERFACE
+ $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
+ $<INSTALL_INTERFACE:include>
+)
+
+target_compile_features("${PROJECT_NAME}" INTERFACE
+ "cxx_std_20"
+)
+
+install(TARGETS "${PROJECT_NAME}"
+ EXPORT "${PROJECT_NAME}Targets"
+ PUBLIC_HEADER DESTINATION "include"
+)
+
+install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/"
+ DESTINATION "include"
+)
+
+add_library("${PROJECT_NAME}::${PROJECT_NAME}" ALIAS "${PROJECT_NAME}")
+
+# 'newtype' tests
+
+add_executable("${PROJECT_NAME}_tests"
+ "test/src/arithmetic.cpp"
+ # "test/src/conversion_suite.cpp"
+ # "test/src/derivation_clause_suite.cpp"
+ # "test/src/equality_comparison_suite.cpp"
+ # "test/src/hash_suite.cpp"
+ # "test/src/io_operators_suite.cpp"
+ # "test/src/iterable_suite.cpp"
+ # "test/src/new_type_constructor_suite.cpp"
+ # "test/src/relational_operators_suite.cpp"
+)
+
+target_link_libraries("${PROJECT_NAME}_tests"
+ "${PROJECT_NAME}::${PROJECT_NAME}"
+ "Catch2::Catch2WithMain"
+)
+
+target_compile_options("${PROJECT_NAME}_tests" PRIVATE
+ "$<$<CXX_COMPILER_ID:GNU,Clang>:-Wall>"
+ "$<$<CXX_COMPILER_ID:GNU,Clang>:-Wextra>"
+ "$<$<CXX_COMPILER_ID:GNU,Clang>:-Werror>"
+ "$<$<CXX_COMPILER_ID:GNU,Clang>:-pedantic-errors>"
+)
+
+catch_discover_tests("${PROJECT_NAME}_tests")
+
+# 'newtype' examples
+
+if(BUILD_EXAMPLES)
+ function(add_example NAME)
+ add_executable("ex_${NAME}"
+ "examples/src/${NAME}.cpp"
+ )
+ target_link_libraries("ex_${NAME}" "${PROJECT_NAME}")
+ endfunction()
+
+ add_example("basic_usage")
+ add_example("basic_usage_with_show")
+ add_example("basic_usage_with_read")
+endif()
diff --git a/source/examples/src/basic_usage.cpp b/source/examples/src/basic_usage.cpp
new file mode 100644
index 0000000..35d1d2c
--- /dev/null
+++ b/source/examples/src/basic_usage.cpp
@@ -0,0 +1,39 @@
+#include <newtype/newtype.hpp>
+
+#include <iostream>
+
+using Width = nt::new_type<unsigned int, struct width_tag>;
+using Height = nt::new_type<unsigned int, struct height_tag>;
+using Area = nt::new_type<unsigned int, struct area_tag>;
+
+struct Rectangle
+{
+ constexpr Rectangle(Width w, Height h)
+ : width{w}
+ , height{h}
+ {
+ }
+
+ auto constexpr area() const noexcept -> Area
+ {
+ return {width.decay() * height.decay()};
+ }
+
+private:
+ Width width;
+ Height height;
+};
+
+int main()
+{
+ auto w{0u}, h{0u};
+
+ std::cin >> w >> h;
+
+ auto width = Width{w};
+ auto height = Height{h};
+
+ auto rect = Rectangle{width, height};
+
+ std::cout << rect.area().decay() << '\n';
+} \ No newline at end of file
diff --git a/source/examples/src/basic_usage_with_read.cpp b/source/examples/src/basic_usage_with_read.cpp
new file mode 100644
index 0000000..2dabe2e
--- /dev/null
+++ b/source/examples/src/basic_usage_with_read.cpp
@@ -0,0 +1,39 @@
+#include <newtype/derivable.hpp>
+#include <newtype/deriving.hpp>
+#include <newtype/newtype.hpp>
+
+#include <iostream>
+
+using Width = nt::new_type<unsigned int, struct width_tag, deriving(nt::Read)>;
+using Height = nt::new_type<unsigned int, struct height_tag, deriving(nt::Read)>;
+using Area = nt::new_type<unsigned int, struct area_tag, deriving(nt::Show)>;
+
+struct Rectangle
+{
+ constexpr Rectangle(Width w, Height h)
+ : width{w}
+ , height{h}
+ {
+ }
+
+ auto constexpr area() const noexcept -> Area
+ {
+ return {width.decay() * height.decay()};
+ }
+
+private:
+ Width width;
+ Height height;
+};
+
+int main()
+{
+ auto width = Width{};
+ auto height = Height{};
+
+ std::cin >> width >> height;
+
+ auto rect = Rectangle{width, height};
+
+ std::cout << rect.area() << '\n';
+} \ No newline at end of file
diff --git a/source/examples/src/basic_usage_with_show.cpp b/source/examples/src/basic_usage_with_show.cpp
new file mode 100644
index 0000000..4bb68f6
--- /dev/null
+++ b/source/examples/src/basic_usage_with_show.cpp
@@ -0,0 +1,41 @@
+#include <newtype/derivable.hpp>
+#include <newtype/deriving.hpp>
+#include <newtype/newtype.hpp>
+
+#include <iostream>
+
+using Width = nt::new_type<unsigned int, struct width_tag>;
+using Height = nt::new_type<unsigned int, struct height_tag>;
+using Area = nt::new_type<unsigned int, struct area_tag, deriving(nt::Show)>;
+
+struct Rectangle
+{
+ constexpr Rectangle(Width w, Height h)
+ : width{w}
+ , height{h}
+ {
+ }
+
+ auto constexpr area() const noexcept -> Area
+ {
+ return {width.decay() * height.decay()};
+ }
+
+private:
+ Width width;
+ Height height;
+};
+
+int main()
+{
+ auto w{0u}, h{0u};
+
+ std::cin >> w >> h;
+
+ auto width = Width{w};
+ auto height = Height{h};
+
+ auto rect = Rectangle{width, height};
+
+ std::cout << rect.area() << '\n';
+} \ No newline at end of file
diff --git a/source/include/newtype/derivable.hpp b/source/include/newtype/derivable.hpp
new file mode 100644
index 0000000..c798c59
--- /dev/null
+++ b/source/include/newtype/derivable.hpp
@@ -0,0 +1,32 @@
+#ifndef NEWTYPE_DERIVABLE_HPP
+#define NEWTYPE_DERIVABLE_HPP
+
+#include "newtype/version.hpp"
+
+namespace nt
+{
+
+ template<typename DerivableTag>
+ struct derivable final
+ {
+ using tag_type = DerivableTag;
+ };
+
+ inline namespace derivables
+ {
+
+ auto constexpr Arithmetic = derivable<struct arithmetic_tag>{};
+ auto constexpr EqBase = derivable<struct eq_base_tag>{};
+ auto constexpr Hash = derivable<struct hash_tag>{};
+ auto constexpr ImplicitConversion = derivable<struct implicit_conversion_tag>{};
+ auto constexpr Indirection = derivable<struct indirection_tag>{};
+ auto constexpr Iterable = derivable<struct iterable_tag>{};
+ auto constexpr Read = derivable<struct read_tag>{};
+ auto constexpr Relational = derivable<struct relational_tag>{};
+ auto constexpr Show = derivable<struct show_tag>{};
+
+ } // namespace derivables
+
+} // namespace nt
+
+#endif \ No newline at end of file
diff --git a/source/include/newtype/derivation_clause.hpp b/source/include/newtype/derivation_clause.hpp
new file mode 100644
index 0000000..6de70e1
--- /dev/null
+++ b/source/include/newtype/derivation_clause.hpp
@@ -0,0 +1,70 @@
+#ifndef NEWTYPE_DERIVATION_CLAUSE_HPP
+#define NEWTYPE_DERIVATION_CLAUSE_HPP
+
+#include "newtype/derivable.hpp"
+#include "newtype/version.hpp"
+
+#include <type_traits>
+
+namespace nt
+{
+
+ template<typename... DerivableTags>
+ struct derivation_clause
+ {
+ constexpr derivation_clause(derivable<DerivableTags>...) noexcept
+ {
+ }
+
+ template<typename DerivableTag>
+ auto constexpr operator()(derivable<DerivableTag>) const noexcept -> bool
+ {
+ return (std::is_same_v<DerivableTags, DerivableTag> || ...);
+ }
+
+ template<typename DerivableTag, typename... RemainingDerivableTags>
+ auto constexpr operator()(derivable<DerivableTag>, derivable<RemainingDerivableTags>...) const noexcept -> bool
+ {
+ return (*this)(derivable<DerivableTag>{}) && (*this)(derivable<RemainingDerivableTags>{}...);
+ }
+
+ template<typename... OtherDerivableTags>
+ auto constexpr operator<(derivation_clause<OtherDerivableTags...> other) const noexcept -> bool
+ {
+ return (sizeof...(DerivableTags) < sizeof...(OtherDerivableTags)) && other(derivable<DerivableTags>{}...);
+ }
+
+ template<typename... OtherDerivableTags>
+ auto constexpr operator>(derivation_clause<OtherDerivableTags...> other) const noexcept -> bool
+ {
+ return other < *this;
+ }
+
+ template<typename... OtherDerivableTags>
+ auto constexpr operator==(derivation_clause<OtherDerivableTags...> other) const noexcept -> bool
+ {
+ return sizeof...(DerivableTags) == sizeof...(OtherDerivableTags) && other(derivable<DerivableTags>{}...);
+ }
+
+ template<typename... OtherDerivableTags>
+ auto constexpr operator!=(derivation_clause<OtherDerivableTags...> other) const noexcept -> bool
+ {
+ return !(*this == other);
+ }
+
+ template<typename... OtherDerivableTags>
+ auto constexpr operator<=(derivation_clause<OtherDerivableTags...> other) const noexcept -> bool
+ {
+ return *this < other || *this == other;
+ }
+
+ template<typename... OtherDerivableTags>
+ auto constexpr operator>=(derivation_clause<OtherDerivableTags...> other) const noexcept -> bool
+ {
+ return *this > other || *this == other;
+ }
+ };
+
+} // namespace nt
+
+#endif \ No newline at end of file
diff --git a/source/include/newtype/deriving.hpp b/source/include/newtype/deriving.hpp
new file mode 100644
index 0000000..ae10bab
--- /dev/null
+++ b/source/include/newtype/deriving.hpp
@@ -0,0 +1,21 @@
+#ifndef NEWTYPE_DERIVING_HPP
+#define NEWTYPE_DERIVING_HPP
+
+#include "newtype/derivable.hpp"
+#include "newtype/derivation_clause.hpp"
+#include "newtype/version.hpp"
+
+#include <type_traits>
+
+namespace nt
+{
+
+ template<typename... DerivableTags>
+ auto constexpr deriving(derivable<DerivableTags>... features) noexcept -> derivation_clause<DerivableTags...>
+ {
+ return {features...};
+ }
+
+} // namespace nt
+
+#endif \ No newline at end of file
diff --git a/source/include/newtype/impl/new_type_iterator_types.hpp b/source/include/newtype/impl/new_type_iterator_types.hpp
new file mode 100644
index 0000000..2ea8274
--- /dev/null
+++ b/source/include/newtype/impl/new_type_iterator_types.hpp
@@ -0,0 +1,66 @@
+#ifndef NEWTYPE_IMPL_NEW_TYPE_ITERATOR_TYPES_HPP
+#define NEWTYPE_IMPL_NEW_TYPE_ITERATOR_TYPES_HPP
+
+#include "newtype/version.hpp"
+
+#include <type_traits>
+
+namespace nt::impl
+{
+
+ template<typename T, bool = false, typename = std::void_t<>>
+ struct new_type_iterator
+ {
+ };
+
+ template<typename T>
+ struct new_type_iterator<T, true, std::void_t<typename T::iterator>>
+ {
+ using iterator = typename T::iterator;
+ };
+
+ template<typename T, bool = false, typename = std::void_t<>>
+ struct new_type_const_iterator
+ {
+ };
+
+ template<typename T>
+ struct new_type_const_iterator<T, true, std::void_t<typename T::const_iterator>>
+ {
+ using const_iterator = typename T::const_iterator;
+ };
+
+ template<typename T, bool = false, typename = std::void_t<>>
+ struct new_type_reverse_iterator
+ {
+ };
+
+ template<typename T>
+ struct new_type_reverse_iterator<T, true, std::void_t<typename T::reverse_iterator>>
+ {
+ using reverse_iterator = typename T::reverse_iterator;
+ };
+
+ template<typename T, bool = false, typename = std::void_t<>>
+ struct new_type_const_reverse_iterator
+ {
+ };
+
+ template<typename T>
+ struct new_type_const_reverse_iterator<T, true, std::void_t<typename T::const_reverse_iterator>>
+ {
+ using const_reverse_iterator = typename T::const_reverse_iterator;
+ };
+
+ template<typename T, bool Enabled>
+ struct new_type_iterator_types
+ : new_type_iterator<T, Enabled>
+ , new_type_const_iterator<T, Enabled>
+ , new_type_reverse_iterator<T, Enabled>
+ , new_type_const_reverse_iterator<T, Enabled>
+ {
+ };
+
+} // namespace nt::impl
+
+#endif \ No newline at end of file
diff --git a/source/include/newtype/impl/new_type_storage.hpp b/source/include/newtype/impl/new_type_storage.hpp
new file mode 100644
index 0000000..f7842af
--- /dev/null
+++ b/source/include/newtype/impl/new_type_storage.hpp
@@ -0,0 +1,139 @@
+#ifndef NEWTYPE_IMPL_NEW_TYPE_STORAGE_HPP
+#define NEWTYPE_IMPL_NEW_TYPE_STORAGE_HPP
+
+#include "newtype/version.hpp"
+
+#include <type_traits>
+#include <utility>
+
+namespace nt::impl
+{
+
+ template<typename BaseType, typename TagType>
+ struct new_type_storage
+ {
+ constexpr new_type_storage() noexcept(std::is_nothrow_default_constructible_v<BaseType>)
+ : m_value{}
+ {
+ }
+
+ constexpr new_type_storage(BaseType const & value)
+ : m_value{value}
+ {
+ }
+
+ constexpr new_type_storage(BaseType && value)
+ : m_value{std::move(value)}
+ {
+ }
+
+ BaseType m_value;
+ };
+
+ template<typename BaseType, typename TagType, bool = std::is_default_constructible_v<BaseType>>
+ struct new_type_constructor : new_type_storage<BaseType, TagType>
+ {
+ using new_type_storage<BaseType, TagType>::new_type_storage;
+ };
+
+ template<typename BaseType, typename TagType>
+ struct new_type_constructor<BaseType, TagType, false> : new_type_storage<BaseType, TagType>
+ {
+ using new_type_storage<BaseType, TagType>::new_type_storage;
+
+ constexpr new_type_constructor() = delete;
+ };
+
+ template<typename BaseType, typename TagType, bool = std::is_copy_constructible_v<BaseType>>
+ struct new_type_copy_constructor : new_type_constructor<BaseType, TagType>
+ {
+ using new_type_constructor<BaseType, TagType>::new_type_constructor;
+
+ constexpr new_type_copy_constructor(new_type_copy_constructor const &) = default;
+ constexpr new_type_copy_constructor(new_type_copy_constructor &&) = default;
+ auto constexpr operator=(new_type_copy_constructor &) -> new_type_copy_constructor & = default;
+ auto constexpr operator=(new_type_copy_constructor &&) -> new_type_copy_constructor & = default;
+ };
+
+ template<typename BaseType, typename TagType>
+ struct new_type_copy_constructor<BaseType, TagType, false> : new_type_constructor<BaseType, TagType>
+ {
+ using new_type_constructor<BaseType, TagType>::new_type_constructor;
+
+ constexpr new_type_copy_constructor(new_type_copy_constructor const &) = delete;
+ constexpr new_type_copy_constructor(new_type_copy_constructor &&) = default;
+ constexpr new_type_copy_constructor(BaseType const &) = delete;
+ auto constexpr operator=(new_type_copy_constructor &) -> new_type_copy_constructor & = default;
+ auto constexpr operator=(new_type_copy_constructor &&) -> new_type_copy_constructor & = default;
+ };
+
+ template<typename BaseType, typename TagType, bool = std::is_move_constructible_v<BaseType>>
+ struct new_type_move_constructor : new_type_copy_constructor<BaseType, TagType>
+ {
+ using new_type_copy_constructor<BaseType, TagType>::new_type_copy_constructor;
+
+ constexpr new_type_move_constructor(new_type_move_constructor const &) = default;
+ constexpr new_type_move_constructor(new_type_move_constructor &&) = default;
+ auto constexpr operator=(new_type_move_constructor &) -> new_type_move_constructor & = default;
+ auto constexpr operator=(new_type_move_constructor &&) -> new_type_move_constructor & = default;
+ };
+
+ template<typename BaseType, typename TagType>
+ struct new_type_move_constructor<BaseType, TagType, false> : new_type_copy_constructor<BaseType, TagType>
+ {
+ using new_type_copy_constructor<BaseType, TagType>::new_type_copy_constructor;
+
+ constexpr new_type_move_constructor(new_type_move_constructor const &) = default;
+ constexpr new_type_move_constructor(new_type_move_constructor &&) = delete;
+ constexpr new_type_move_constructor(BaseType &&) = delete;
+ auto constexpr operator=(new_type_move_constructor &) -> new_type_move_constructor & = default;
+ auto constexpr operator=(new_type_move_constructor &&) -> new_type_move_constructor & = default;
+ };
+
+ template<typename BaseType, typename TagType, bool = std::is_copy_assignable_v<BaseType>>
+ struct new_type_copy_assignment : new_type_move_constructor<BaseType, TagType>
+ {
+ using new_type_move_constructor<BaseType, TagType>::new_type_move_constructor;
+
+ constexpr new_type_copy_assignment(new_type_copy_assignment const &) = default;
+ constexpr new_type_copy_assignment(new_type_copy_assignment &&) = default;
+ auto constexpr operator=(new_type_copy_assignment &) -> new_type_copy_assignment & = default;
+ auto constexpr operator=(new_type_copy_assignment &&) -> new_type_copy_assignment & = default;
+ };
+
+ template<typename BaseType, typename TagType>
+ struct new_type_copy_assignment<BaseType, TagType, false> : new_type_move_constructor<BaseType, TagType>
+ {
+ using new_type_move_constructor<BaseType, TagType>::new_type_move_constructor;
+
+ constexpr new_type_copy_assignment(new_type_copy_assignment const &) = default;
+ constexpr new_type_copy_assignment(new_type_copy_assignment &&) = default;
+ auto constexpr operator=(new_type_copy_assignment &) -> new_type_copy_assignment & = default;
+ auto constexpr operator=(new_type_copy_assignment &&) -> new_type_copy_assignment & = delete;
+ };
+
+ template<typename BaseType, typename TagType, bool = std::is_move_assignable_v<BaseType>>
+ struct new_type_move_assignment : new_type_copy_assignment<BaseType, TagType>
+ {
+ using new_type_copy_assignment<BaseType, TagType>::new_type_copy_assignment;
+
+ constexpr new_type_move_assignment(new_type_move_assignment const &) = default;
+ constexpr new_type_move_assignment(new_type_move_assignment &&) = default;
+ auto constexpr operator=(new_type_move_assignment &) -> new_type_move_assignment & = default;
+ auto constexpr operator=(new_type_move_assignment &&) -> new_type_move_assignment & = default;
+ };
+
+ template<typename BaseType, typename TagType>
+ struct new_type_move_assignment<BaseType, TagType, false> : new_type_copy_assignment<BaseType, TagType>
+ {
+ using new_type_copy_assignment<BaseType, TagType>::new_type_copy_assignment;
+
+ constexpr new_type_move_assignment(new_type_move_assignment const &) = default;
+ constexpr new_type_move_assignment(new_type_move_assignment &&) = default;
+ auto constexpr operator=(new_type_move_assignment &) -> new_type_move_assignment & = default;
+ auto constexpr operator=(new_type_move_assignment &&) -> new_type_move_assignment & = delete;
+ };
+
+} // namespace nt::impl
+
+#endif
diff --git a/source/include/newtype/impl/type_traits_extensions.hpp b/source/include/newtype/impl/type_traits_extensions.hpp
new file mode 100644
index 0000000..dc41649
--- /dev/null
+++ b/source/include/newtype/impl/type_traits_extensions.hpp
@@ -0,0 +1,853 @@
+#ifndef NEWTYPE_IMPL_TYPE_TRAITS_EXTENSIONS_HPP
+#define NEWTYPE_IMPL_TYPE_TRAITS_EXTENSIONS_HPP
+
+#include "newtype/version.hpp"
+
+#include <cstddef>
+#include <functional>
+#include <iosfwd>
+#include <type_traits>
+
+namespace nt::impl
+{
+
+ inline namespace equality_comparable
+ {
+
+ template<typename T, typename = void>
+ struct is_equality_comparable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_equality_comparable<T, std::void_t<decltype(std::declval<T const &>() == std::declval<T const &>())>> : std::true_type
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_equality_comparable_v = is_equality_comparable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_nothrow_equality_comparable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_nothrow_equality_comparable<T, std::void_t<decltype(std::declval<T const &>() == std::declval<T const &>())>>
+ : std::bool_constant<noexcept(std::declval<T const &>() == std::declval<T const &>())>
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_nothrow_equality_comparable_v = is_nothrow_equality_comparable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_inequality_comparable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_inequality_comparable<T, std::void_t<decltype(std::declval<T const &>() != std::declval<T const &>())>> : std::true_type
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_inequality_comparable_v = is_inequality_comparable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_nothrow_inequality_comparable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_nothrow_inequality_comparable<T, std::void_t<decltype(std::declval<T const &>() != std::declval<T const &>())>>
+ : std::bool_constant<noexcept(std::declval<T const &>() != std::declval<T const &>())>
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_nothrow_inequality_comparable_v = is_nothrow_inequality_comparable<T>::value;
+
+ } // namespace equality_comparable
+
+ inline namespace relationally_comparable
+ {
+
+ template<typename T, typename = void>
+ struct is_less_than_comparable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_less_than_comparable<T, std::void_t<decltype(std::declval<T const &>() < std::declval<T const &>())>> : std::true_type
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_less_than_comparable_v = is_less_than_comparable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_nothrow_less_than_comparable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_nothrow_less_than_comparable<T, std::void_t<decltype(std::declval<T const &>() < std::declval<T const &>())>>
+ : std::bool_constant<noexcept(std::declval<T const &>() < std::declval<T const &>())>
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_nothrow_less_than_comparable_v = is_nothrow_less_than_comparable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_greater_than_comparable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_greater_than_comparable<T, std::void_t<decltype(std::declval<T const &>() > std::declval<T const &>())>> : std::true_type
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_greater_than_comparable_v = is_greater_than_comparable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_nothrow_greater_than_comparable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_nothrow_greater_than_comparable<T, std::void_t<decltype(std::declval<T const &>() > std::declval<T const &>())>>
+ : std::bool_constant<noexcept(std::declval<T const &>() > std::declval<T const &>())>
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_nothrow_greater_than_comparable_v = is_nothrow_greater_than_comparable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_less_than_equal_to_comparable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_less_than_equal_to_comparable<T, std::void_t<decltype(std::declval<T const &>() <= std::declval<T const &>())>> : std::true_type
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_less_than_equal_to_comparable_v = is_less_than_equal_to_comparable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_nothrow_less_than_equal_to_comparable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_nothrow_less_than_equal_to_comparable<T, std::void_t<decltype(std::declval<T const &>() <= std::declval<T const &>())>>
+ : std::bool_constant<noexcept(std::declval<T const &>() <= std::declval<T const &>())>
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_nothrow_less_than_equal_to_comparable_v = is_nothrow_less_than_equal_to_comparable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_greater_than_equal_to_comparable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_greater_than_equal_to_comparable<T, std::void_t<decltype(std::declval<T const &>() >= std::declval<T const &>())>>
+ : std::true_type
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_greater_than_equal_to_comparable_v = is_greater_than_equal_to_comparable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_nothrow_greater_than_equal_to_comparable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_nothrow_greater_than_equal_to_comparable<T, std::void_t<decltype(std::declval<T const &>() >= std::declval<T const &>())>>
+ : std::bool_constant<noexcept(std::declval<T const &>() >= std::declval<T const &>())>
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_nothrow_greater_than_equal_to_comparable_v = is_nothrow_greater_than_equal_to_comparable<T>::value;
+ } // namespace relationally_comparable
+
+ inline namespace iostreamable
+ {
+
+ template<typename StreamType, typename T, typename = void>
+ struct is_output_streamable : std::false_type
+ {
+ };
+
+ template<typename StreamType, typename T>
+ struct is_output_streamable<StreamType, T, std::void_t<decltype(std::declval<StreamType &>() << std::declval<T const &>())>>
+ : std::true_type
+ {
+ };
+
+ template<typename StreamType, typename T>
+ auto constexpr is_output_streamable_v = is_output_streamable<StreamType, T>::value;
+
+ template<typename StreamType, typename T, typename = void>
+ struct is_nothrow_output_streamable : std::false_type
+ {
+ };
+
+ template<typename StreamType, typename T>
+ struct is_nothrow_output_streamable<StreamType, T, std::void_t<decltype(std::declval<StreamType &>() << std::declval<T const &>())>>
+ : std::bool_constant<noexcept(std::declval<StreamType &>() << std::declval<T const &>())>
+ {
+ };
+
+ template<typename StreamType, typename T>
+ auto constexpr is_nothrow_output_streamable_v = is_nothrow_output_streamable<StreamType, T>::value;
+
+ template<typename StreamType, typename T, typename = void>
+ struct is_input_streamable : std::false_type
+ {
+ };
+
+ template<typename StreamType, typename T>
+ struct is_input_streamable<StreamType, T, std::void_t<decltype(std::declval<StreamType &>() >> std::declval<T &>())>> : std::true_type
+ {
+ };
+
+ template<typename StreamType, typename T>
+ auto constexpr is_input_streamable_v = is_input_streamable<StreamType, T>::value;
+
+ template<typename StreamType, typename T, typename = void>
+ struct is_nothrow_input_streamable : std::false_type
+ {
+ };
+
+ template<typename StreamType, typename T>
+ struct is_nothrow_input_streamable<StreamType, T, std::void_t<decltype(std::declval<StreamType &>() >> std::declval<T &>())>>
+ : std::bool_constant<noexcept(std::declval<StreamType &>() >> std::declval<T &>())>
+ {
+ };
+
+ template<typename StreamType, typename T>
+ auto constexpr is_nothrow_input_streamable_v = is_nothrow_input_streamable<StreamType, T>::value;
+
+ } // namespace iostreamable
+
+ inline namespace arithmetic
+ {
+
+ template<typename T, typename = void>
+ struct is_addable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_addable<T, std::void_t<decltype(std::declval<T const &>() + std::declval<T const &>())>> : std::true_type
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_addable_v = is_addable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_nothrow_addable : std::false_type
+ {
+ };
+
+ template<typename T>
<