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>
+ struct is_nothrow_addable<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_addable_v = is_nothrow_addable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_subtractable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_subtractable<T, std::void_t<decltype(std::declval<T const &>() - std::declval<T const &>())>> : std::true_type
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_subtractable_v = is_subtractable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_nothrow_subtractable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_nothrow_subtractable<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_subtractable_v = is_nothrow_subtractable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_multipliable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_multipliable<T, std::void_t<decltype(std::declval<T const &>() * std::declval<T const &>())>> : std::true_type
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_multipliable_v = is_multipliable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_nothrow_multipliable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_nothrow_multipliable<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_multipliable_v = is_nothrow_multipliable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_dividable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_dividable<T, std::void_t<decltype(std::declval<T const &>() / std::declval<T const &>())>> : std::true_type
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_dividable_v = is_dividable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_nothrow_dividable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_nothrow_dividable<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_dividable_v = is_nothrow_dividable<T>::value;
+
+ } // namespace arithmetic
+
+ inline namespace compound_arithmetic
+ {
+
+ template<typename T, typename = void>
+ struct is_add_assignable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_add_assignable<T, std::void_t<decltype(std::declval<T &>() += std::declval<T const &>())>> : std::true_type
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_add_assignable_v = is_add_assignable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_nothrow_add_assignable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_nothrow_add_assignable<T, std::void_t<decltype(std::declval<T &>() += std::declval<T const &>())>>
+ : std::bool_constant<noexcept(std::declval<T &>() += std::declval<T const &>())>
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_nothrow_add_assignable_v = is_nothrow_add_assignable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_subtract_assignable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_subtract_assignable<T, std::void_t<decltype(std::declval<T &>() -= std::declval<T const &>())>> : std::true_type
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_subtract_assignable_v = is_subtract_assignable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_nothrow_subtract_assignable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_nothrow_subtract_assignable<T, std::void_t<decltype(std::declval<T &>() -= std::declval<T const &>())>>
+ : std::bool_constant<noexcept(std::declval<T &>() -= std::declval<T const &>())>
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_nothrow_subtract_assignable_v = is_nothrow_subtract_assignable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_multiply_assignable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_multiply_assignable<T, std::void_t<decltype(std::declval<T &>() *= std::declval<T const &>())>> : std::true_type
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_multiply_assignable_v = is_multiply_assignable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_nothrow_multiply_assignable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_nothrow_multiply_assignable<T, std::void_t<decltype(std::declval<T &>() *= std::declval<T const &>())>>
+ : std::bool_constant<noexcept(std::declval<T &>() *= std::declval<T const &>())>
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_nothrow_multiply_assignable_v = is_nothrow_multiply_assignable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_divide_assignable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_divide_assignable<T, std::void_t<decltype(std::declval<T &>() /= std::declval<T const &>())>> : std::true_type
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_divide_assignable_v = is_divide_assignable<T>::value;
+
+ template<typename T, typename = void>
+ struct is_nothrow_divide_assignable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_nothrow_divide_assignable<T, std::void_t<decltype(std::declval<T &>() /= std::declval<T const &>())>>
+ : std::bool_constant<noexcept(std::declval<T &>() /= std::declval<T const &>())>
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_nothrow_divide_assignable_v = is_nothrow_divide_assignable<T>::value;
+
+ } // namespace compound_arithmetic
+
+ inline namespace std_support
+ {
+
+ template<typename T, typename = void>
+ struct is_hashable : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct is_hashable<T, std::void_t<decltype(std::declval<std::hash<T> const &>()(std::declval<T const &>()))>>
+ : std::is_same<std::size_t, decltype(std::declval<std::hash<T> const &>()(std::declval<T const &>()))>
+ {
+ };
+
+ template<typename T>
+ auto constexpr is_hashable_v = is_hashable<T>::value;
+
+ } // namespace std_support
+
+ inline namespace iterable_begin
+ {
+ template<typename T, typename = void>
+ struct has_free_begin : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_free_begin<T, std::void_t<decltype(begin(std::declval<T &>()))>>
+ : std::is_same<typename T::iterator, std::remove_cvref_t<decltype(begin(std::declval<T &>()))>>
+ {
+ };
+
+ template<typename T>
+ struct has_free_begin<T const, std::void_t<decltype(begin(std::declval<T const &>()))>>
+ : std::is_same<typename T::const_iterator, std::remove_cvref_t<decltype(begin(std::declval<T const &>()))>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_free_begin_v = has_free_begin<T>::value;
+
+ template<typename T, typename = void>
+ struct has_member_begin : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_member_begin<T, std::void_t<decltype(std::declval<T &>().begin())>>
+ : std::is_same<typename T::iterator, std::remove_cvref_t<decltype(std::declval<T &>().begin())>>
+ {
+ };
+
+ template<typename T>
+ struct has_member_begin<T const, std::void_t<decltype(std::declval<T const &>().begin())>>
+ : std::is_same<typename T::const_iterator, std::remove_cvref_t<decltype(std::declval<T const &>().begin())>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_member_begin_v = has_member_begin<T>::value;
+
+ template<typename T>
+ struct has_begin : std::disjunction<has_free_begin<T>, has_member_begin<T>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_begin_v = has_begin<T>::value;
+ } // namespace iterable_begin
+
+ inline namespace iterable_cbegin
+ {
+ template<typename T, typename = void>
+ struct has_free_cbegin : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_free_cbegin<T, std::void_t<decltype(cbegin(std::declval<T const &>()))>>
+ : std::is_same<typename T::const_iterator, std::remove_cvref_t<decltype(cbegin(std::declval<T const &>()))>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_free_cbegin_v = has_free_cbegin<T>::value;
+
+ template<typename T, typename = void>
+ struct has_member_cbegin : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_member_cbegin<T, std::void_t<decltype(std::declval<T const &>().cbegin())>>
+ : std::is_same<typename T::const_iterator, decltype(std::declval<T const &>().cbegin())>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_member_cbegin_v = has_member_cbegin<T>::value;
+
+ template<typename T>
+ struct has_cbegin : std::disjunction<has_free_cbegin<T>, has_member_cbegin<T>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_cbegin_v = has_cbegin<T>::value;
+ } // namespace iterable_cbegin
+
+ inline namespace iterable_rbegin
+ {
+ template<typename T, typename = void>
+ struct has_free_rbegin : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_free_rbegin<T, std::void_t<decltype(rbegin(std::declval<T &>()))>>
+ : std::is_same<typename T::reverse_iterator, std::remove_cvref_t<decltype(rbegin(std::declval<T &>()))>>
+ {
+ };
+
+ template<typename T>
+ struct has_free_rbegin<T const, std::void_t<decltype(rbegin(std::declval<T const &>()))>>
+ : std::is_same<typename T::const_reverse_iterator, std::remove_cvref_t<decltype(rbegin(std::declval<T const &>()))>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_free_rbegin_v = has_free_rbegin<T>::value;
+
+ template<typename T, typename = void>
+ struct has_member_rbegin : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_member_rbegin<T, std::void_t<decltype(std::declval<T &>().rbegin())>>
+ : std::is_same<typename T::reverse_iterator, std::remove_cvref_t<decltype(std::declval<T &>().rbegin())>>
+ {
+ };
+
+ template<typename T>
+ struct has_member_rbegin<T const, std::void_t<decltype(std::declval<T const &>().rbegin())>>
+ : std::is_same<typename T::const_reverse_iterator, std::remove_cvref_t<decltype(std::declval<T const &>().rbegin())>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_member_rbegin_v = has_member_rbegin<T>::value;
+
+ template<typename T>
+ struct has_rbegin : std::disjunction<has_free_rbegin<T>, has_member_rbegin<T>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_rbegin_v = has_rbegin<T>::value;
+ } // namespace iterable_rbegin
+
+ inline namespace iterable_crbegin
+ {
+ template<typename T, typename = void>
+ struct has_free_crbegin : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_free_crbegin<T, std::void_t<decltype(crbegin(std::declval<T const &>()))>>
+ : std::is_same<typename T::const_reverse_iterator, std::remove_cvref_t<decltype(crbegin(std::declval<T const &>()))>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_free_crbegin_v = has_free_crbegin<T>::value;
+
+ template<typename T, typename = void>
+ struct has_member_crbegin : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_member_crbegin<T, std::void_t<decltype(std::declval<T const &>().crbegin())>>
+ : std::is_same<typename T::const_reverse_iterator, std::remove_cvref_t<decltype(std::declval<T const &>().crbegin())>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_member_crbegin_v = has_member_crbegin<T>::value;
+
+ template<typename T>
+ struct has_crbegin : std::disjunction<has_free_crbegin<T>, has_member_crbegin<T>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_crbegin_v = has_crbegin<T>::value;
+ } // namespace iterable_crbegin
+
+ inline namespace iterable_end
+ {
+ template<typename T, typename = void>
+ struct has_free_end : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_free_end<T, std::void_t<decltype(end(std::declval<T &>()))>>
+ : std::is_same<typename T::iterator, std::remove_cvref_t<decltype(end(std::declval<T &>()))>>
+ {
+ };
+
+ template<typename T>
+ struct has_free_end<T const, std::void_t<decltype(end(std::declval<T const &>()))>>
+ : std::is_same<typename T::const_iterator, std::remove_cvref_t<decltype(end(std::declval<T const &>()))>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_free_end_v = has_free_end<T>::value;
+
+ template<typename T, typename = void>
+ struct has_member_end : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_member_end<T, std::void_t<decltype(std::declval<T &>().end())>>
+ : std::is_same<typename T::iterator, std::remove_cvref_t<decltype(std::declval<T &>().end())>>
+ {
+ };
+
+ template<typename T>
+ struct has_member_end<T const, std::void_t<decltype(std::declval<T const &>().end())>>
+ : std::is_same<typename T::const_iterator, std::remove_cvref_t<decltype(std::declval<T const &>().end())>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_member_end_v = has_member_end<T>::value;
+
+ template<typename T>
+ struct has_end : std::disjunction<has_free_end<T>, has_member_end<T>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_end_v = has_end<T>::value;
+ } // namespace iterable_end
+
+ inline namespace iterable_cend
+ {
+ template<typename T, typename = void>
+ struct has_free_cend : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_free_cend<T, std::void_t<decltype(cend(std::declval<T const &>()))>>
+ : std::is_same<typename T::const_iterator, std::remove_cvref_t<decltype(cend(std::declval<T const &>()))>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_free_cend_v = has_free_cend<T>::value;
+
+ template<typename T, typename = void>
+ struct has_member_cend : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_member_cend<T, std::void_t<decltype(std::declval<T const &>().cend())>>
+ : std::is_same<typename T::const_iterator, decltype(std::declval<T const &>().cend())>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_member_cend_v = has_member_cend<T>::value;
+
+ template<typename T>
+ struct has_cend : std::disjunction<has_free_cend<T>, has_member_cend<T>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_cend_v = has_cend<T>::value;
+ } // namespace iterable_cend
+
+ inline namespace iterable_rend
+ {
+ template<typename T, typename = void>
+ struct has_free_rend : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_free_rend<T, std::void_t<decltype(rend(std::declval<T &>()))>>
+ : std::is_same<typename T::reverse_iterator, std::remove_cvref_t<decltype(rend(std::declval<T &>()))>>
+ {
+ };
+
+ template<typename T>
+ struct has_free_rend<T const, std::void_t<decltype(rend(std::declval<T const &>()))>>
+ : std::is_same<typename T::const_reverse_iterator, std::remove_cvref_t<decltype(rend(std::declval<T const &>()))>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_free_rend_v = has_free_rend<T>::value;
+
+ template<typename T, typename = void>
+ struct has_member_rend : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_member_rend<T, std::void_t<decltype(std::declval<T &>().rend())>>
+ : std::is_same<typename T::reverse_iterator, std::remove_cvref_t<decltype(std::declval<T &>().rend())>>
+ {
+ };
+
+ template<typename T>
+ struct has_member_rend<T const, std::void_t<decltype(std::declval<T const &>().rend())>>
+ : std::is_same<typename T::const_reverse_iterator, std::remove_cvref_t<decltype(std::declval<T const &>().rend())>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_member_rend_v = has_member_rend<T>::value;
+
+ template<typename T>
+ struct has_rend : std::disjunction<has_free_rend<T>, has_member_rend<T>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_rend_v = has_rend<T>::value;
+ } // namespace iterable_rend
+
+ inline namespace iterable_crend
+ {
+ template<typename T, typename = void>
+ struct has_free_crend : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_free_crend<T, std::void_t<decltype(crend(std::declval<T const &>()))>>
+ : std::is_same<typename T::const_reverse_iterator, std::remove_cvref_t<decltype(crend(std::declval<T const &>()))>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_free_crend_v = has_free_crend<T>::value;
+
+ template<typename T, typename = void>
+ struct has_member_crend : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_member_crend<T, std::void_t<decltype(std::declval<T const &>().crend())>>
+ : std::is_same<typename T::const_reverse_iterator, std::remove_cvref_t<decltype(std::declval<T const &>().crend())>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_member_crend_v = has_member_crend<T>::value;
+
+ template<typename T>
+ struct has_crend : std::disjunction<has_free_crend<T>, has_member_crend<T>>
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_crend_v = has_crend<T>::value;
+ } // namespace iterable_crend
+
+} // namespace nt::impl
+
+#endif \ No newline at end of file
diff --git a/source/include/newtype/newtype.hpp b/source/include/newtype/newtype.hpp
new file mode 100644
index 0000000..e2704f3
--- /dev/null
+++ b/source/include/newtype/newtype.hpp
@@ -0,0 +1,550 @@
+#ifndef NEWTYPE_NEWTYPE_HPP
+#define NEWTYPE_NEWTYPE_HPP
+
+#include "newtype/derivable.hpp"
+#include "newtype/deriving.hpp"
+#include "newtype/impl/new_type_iterator_types.hpp"
+#include "newtype/impl/new_type_storage.hpp"
+#include "newtype/impl/type_traits_extensions.hpp"
+#include "newtype/version.hpp"
+
+#include <functional>
+#include <istream>
+#include <ostream>
+#include <type_traits>
+
+namespace nt
+{
+
+ template<typename BaseType, typename TagType, auto DerivationClause = deriving()>
+ class new_type
+ : impl::new_type_move_assignment<BaseType, TagType>
+ , public impl::new_type_iterator_types<BaseType, DerivationClause(nt::Iterable)>
+ {
+ static_assert(!std::is_reference_v<BaseType>, "The base type must not be a reference type");
+ static_assert(!std::is_void_v<std::remove_cv_t<BaseType>>, "The base type must not be possibly cv-qualified void");
+
+ template<typename BaseTypeT, typename TagTypeT, auto DerivationClauseV, typename CharType, typename StreamTraits>
+ auto friend operator>>(std::basic_istream<CharType, StreamTraits> &, new_type<BaseTypeT, TagTypeT, DerivationClauseV> &) noexcept(
+ impl::is_nothrow_input_streamable_v<std::basic_istream<CharType, StreamTraits>, BaseTypeT>)
+ -> std::enable_if_t<DerivationClauseV(nt::Read) && impl::is_input_streamable_v<std::basic_istream<CharType, StreamTraits>, BaseTypeT>,
+ std::basic_istream<CharType, StreamTraits>> &;
+
+ template<typename BaseTypeT, typename TagTypeT, auto DerivationClauseV>
+ auto constexpr friend
+ operator+=(new_type<BaseTypeT, TagTypeT, DerivationClauseV> & lhs,
+ new_type<BaseTypeT, TagTypeT, DerivationClauseV> const & rhs) noexcept(impl::is_nothrow_add_assignable_v<BaseTypeT>)
+ -> std::enable_if_t<DerivationClauseV(nt::Arithmetic) && impl::is_add_assignable_v<BaseTypeT>,
+ new_type<BaseTypeT, TagTypeT, DerivationClauseV> &>;
+
+ template<typename BaseTypeT, typename TagTypeT, auto DerivationClauseV>
+ auto constexpr friend
+ operator-=(new_type<BaseTypeT, TagTypeT, DerivationClauseV> & lhs,
+ new_type<BaseTypeT, TagTypeT, DerivationClauseV> const & rhs) noexcept(impl::is_nothrow_subtract_assignable_v<BaseTypeT>)
+ -> std::enable_if_t<DerivationClauseV(nt::Arithmetic) && impl::is_subtract_assignable_v<BaseTypeT>,
+ new_type<BaseTypeT, TagTypeT, DerivationClauseV> &>;
+
+ template<typename BaseTypeT, typename TagTypeT, auto DerivationClauseV>
+ auto constexpr friend
+ operator*=(new_type<BaseTypeT, TagTypeT, DerivationClauseV> & lhs,
+ new_type<BaseTypeT, TagTypeT, DerivationClauseV> const & rhs) noexcept(impl::is_nothrow_multiply_assignable_v<BaseTypeT>)
+ -> std::enable_if_t<DerivationClauseV(nt::Arithmetic) && impl::is_multiply_assignable_v<BaseTypeT>,
+ new_type<BaseTypeT, TagTypeT, DerivationClauseV> &>;
+
+ template<typename BaseTypeT, typename TagTypeT, auto DerivationClauseV>
+ auto constexpr friend
+ operator/=(new_type<BaseTypeT, TagTypeT, DerivationClauseV> & lhs,
+ new_type<BaseTypeT, TagTypeT, DerivationClauseV> const & rhs) noexcept(impl::is_nothrow_divide_assignable_v<BaseTypeT>)
+ -> std::enable_if_t<DerivationClauseV(nt::Arithmetic) && impl::is_divide_assignable_v<BaseTypeT>,
+ new_type<BaseTypeT, TagTypeT, DerivationClauseV> &>;
+
+ template<typename BaseTypeT, typename TagTypeT, auto DerivationClauseV>
+ auto constexpr friend begin(new_type<BaseTypeT, TagTypeT, DerivationClauseV> & obj)
+ -> std::enable_if_t<DerivationClauseV(nt::Iterable) && impl::has_free_begin_v<BaseTypeT>,
+ typename new_type<BaseTypeT, TagTypeT, DerivationClauseV>::iterator>;
+
+ template<typename BaseTypeT, typename TagTypeT, auto DerivationClauseV>
+ auto constexpr friend begin(new_type<BaseTypeT, TagTypeT, DerivationClauseV> const & obj)
+ -> std::enable_if_t<DerivationClauseV(nt::Iterable) && impl::has_free_begin_v<BaseTypeT const>,
+ typename new_type<BaseTypeT, TagTypeT, DerivationClauseV>::const_iterator>;
+
+ template<typename BaseTypeT, typename TagTypeT, auto DerivationClauseV>
+ auto constexpr friend cbegin(new_type<BaseTypeT, TagTypeT, DerivationClauseV> const & obj)
+ -> std::enable_if_t<DerivationClauseV(nt::Iterable) && impl::has_free_cbegin_v<BaseTypeT const>,
+ typename new_type<BaseTypeT, TagTypeT, DerivationClauseV>::const_iterator>;
+
+ template<typename BaseTypeT, typename TagTypeT, auto DerivationClauseV>
+ auto constexpr friend rbegin(new_type<BaseTypeT, TagTypeT, DerivationClauseV> & obj)
+ -> std::enable_if_t<DerivationClauseV(nt::Iterable) && impl::has_free_rbegin_v<BaseTypeT>,
+ typename new_type<BaseTypeT, TagTypeT, DerivationClauseV>::reverse_iterator>;
+
+ template<typename BaseTypeT, typename TagTypeT, auto DerivationClauseV>
+ auto constexpr friend rbegin(new_type<BaseTypeT, TagTypeT, DerivationClauseV> const & obj)
+ -> std::enable_if_t<DerivationClauseV(nt::Iterable) && impl::has_free_rbegin_v<BaseTypeT const>,
+ typename new_type<BaseTypeT, TagTypeT, DerivationClauseV>::const_reverse_iterator>;
+
+ template<typename BaseTypeT, typename TagTypeT, auto DerivationClauseV>
+ auto constexpr friend crbegin(new_type<BaseTypeT, TagTypeT, DerivationClauseV> const & obj)
+ -> std::enable_if_t<DerivationClauseV(nt::Iterable) && impl::has_free_crbegin_v<BaseTypeT const>,
+ typename new_type<BaseTypeT, TagTypeT, DerivationClauseV>::const_reverse_iterator>;
+
+ template<typename BaseTypeT, typename TagTypeT, auto DerivationClauseV>
+ auto constexpr friend end(new_type<BaseTypeT, TagTypeT, DerivationClauseV> & obj)
+ -> std::enable_if_t<DerivationClauseV(nt::Iterable) && impl::has_free_end_v<BaseTypeT>,
+ typename new_type<BaseTypeT, TagTypeT, DerivationClauseV>::iterator>;
+
+ template<typename BaseTypeT, typename TagTypeT, auto DerivationClauseV>
+ auto constexpr friend end(new_type<BaseTypeT, TagTypeT, DerivationClauseV> const & obj)
+ -> std::enable_if_t<DerivationClauseV(nt::Iterable) && impl::has_free_end_v<BaseTypeT const>,
+ typename new_type<BaseTypeT, TagTypeT, DerivationClauseV>::const_iterator>;
+
+ template<typename BaseTypeT, typename TagTypeT, auto DerivationClauseV>
+ auto constexpr friend cend(new_type<BaseTypeT, TagTypeT, DerivationClauseV> const & obj)
+ -> std::enable_if_t<DerivationClauseV(nt::Iterable) && impl::has_free_cend_v<BaseTypeT const>,
+ typename new_type<BaseTypeT, TagTypeT, DerivationClauseV>::const_iterator>;
+
+ template<typename BaseTypeT, typename TagTypeT, auto DerivationClauseV>
+ auto constexpr friend rend(new_type<BaseTypeT, TagTypeT, DerivationClauseV> & obj)
+ -> std::enable_if_t<DerivationClauseV(nt::Iterable) && impl::has_free_rend_v<BaseTypeT>,
+ typename new_type<BaseTypeT, TagTypeT, DerivationClauseV>::reverse_iterator>;
+
+ template<typename BaseTypeT, typename TagTypeT, auto DerivationClauseV>
+ auto constexpr friend rend(new_type<BaseTypeT, TagTypeT, DerivationClauseV> const & obj)
+ -> std::enable_if_t<DerivationClauseV(nt::Iterable) && impl::has_free_rend_v<BaseTypeT const>,
+ typename new_type<BaseTypeT, TagTypeT, DerivationClauseV>::const_reverse_iterator>;
+
+ template<typename BaseTypeT, typename TagTypeT, auto DerivationClauseV>
+ auto constexpr friend crend(new_type<BaseTypeT, TagTypeT, DerivationClauseV> const & obj)
+ -> std::enable_if_t<DerivationClauseV(nt::Iterable) && impl::has_free_crend_v<BaseTypeT const>,
+ typename new_type<BaseTypeT, TagTypeT, DerivationClauseV>::const_reverse_iterator>;
+
+ using super = impl::new_type_move_assignment<BaseType, TagType>;
+
+ public:
+ using base_type = BaseType;
+ using tag_type = TagType;
+ using derivation_clause_type = decltype(DerivationClause);
+
+ auto constexpr static derivation_clause = DerivationClause;
+
+ using super::super;
+
+ constexpr new_type() noexcept(std::is_nothrow_default_constructible_v<BaseType>) = default;
+ constexpr new_type(new_type const &) noexcept(std::is_nothrow_copy_constructible_v<BaseType>) = default;
+ constexpr new_type(new_type &&) noexcept(std::is_nothrow_move_constructible_v<BaseType>) = default;
+
+ auto constexpr operator=(new_type const &) noexcept(std::is_nothrow_copy_assignable_v<BaseType>) -> new_type & = default;
+ auto constexpr operator=(new_type &&) noexcept(std::is_nothrow_move_assignable_v<BaseType>) -> new_type & = default;
+
+ auto constexpr decay() const noexcept(std::is_nothrow_copy_constructible_v<BaseType>) -> BaseType
+ {
+ return this->m_value;
+ }
+
+ template<typename NewType = new_type, std::enable_if_t<NewType::derivation_clause(nt::ImplicitConversion)> * = nullptr>
+ constexpr operator base_type() const noexcept(std::is_nothrow_copy_constructible_v<base_type>)
+ {
+ return decay();
+ }
+
+ template<typename NewType = new_type, std::enable_if_t<!NewType::derivation_clause(nt::ImplicitConversion)> * = nullptr>
+ explicit constexpr operator base_type() const noexcept(std::is_nothrow_copy_constructible_v<base_type>)
+ {
+ return decay();
+ }
+
+ template<typename NewType = new_type>
+ auto constexpr operator->() noexcept -> std::enable_if_t<NewType::derivation_clause(nt::Indirection), BaseType *>
+ {
+ return std::addressof(this->m_value);
+ }
+
+ template<typename NewType = new_type>
+ auto constexpr operator->() const noexcept -> std::enable_if_t<NewType::derivation_clause(nt::Indirection), BaseType const *>
+ {
+ return std::addressof(this->m_value);
+ }
+
+ template<typename NewType = new_type, std::enable_if_t<NewType::derivation_clause(nt::Iterable)> * = nullptr>
+ auto constexpr begin()
+ -> std::enable_if_t<NewType::derivation_clause(nt::Iterable) && impl::has_member_begin_v<BaseType>, typename NewType::iterator>
+ {
+ return this->m_value.begin();
+ }
+
+ template<typename NewType = new_type>
+ auto constexpr begin() const -> std::enable_if_t<NewType::derivation_clause(nt::Iterable) && impl::has_member_begin_v<BaseType const>,
+ typename NewType::const_iterator>
+ {
+ return this->m_value.begin();
+ }
+
+ template<typename NewType = new_type>
+ auto constexpr cbegin() const -> std::enable_if_t<NewType::derivation_clause(nt::Iterable) && impl::has_member_cbegin_v<BaseType const>,
+ typename NewType::const_iterator>
+ {
+ return this->m_value.cbegin();
+ }
+
+ template<typename NewType = new_type, std::enable_if_t<NewType::derivation_clause(nt::Iterable)> * = nullptr>
+ auto constexpr rbegin()
+ -> std::enable_if_t<NewType::derivation_clause(nt::Iterable) && impl::has_member_rbegin_v<BaseType>, typename NewType::reverse_iterator>
+ {
+ return this->m_value.rbegin();
+ }
+
+ template<typename NewType = new_type>
+ auto constexpr rbegin() const -> std::enable_if_t<NewType::derivation_clause(nt::Iterable) && impl::has_member_rbegin_v<BaseType const>,
+ typename NewType::const_reverse_iterator>
+ {
+ return this->m_value.rbegin();
+ }
+
+ template<typename NewType = new_type>
+ auto constexpr crbegin() const -> std::enable_if_t<NewType::derivation_clause(nt::Iterable) && impl::has_member_crbegin_v<BaseType const>,
+ typename NewType::const_reverse_iterator>
+ {
+ return this->m_value.crbegin();
+ }
+
+ template<typename NewType = new_type, std::enable_if_t<NewType::derivation_clause(nt::Iterable)> * = nullptr>
+ auto constexpr end()
+ -> std::enable_if_t<NewType::derivation_clause(nt::Iterable) && impl::has_member_end_v<BaseType>, typename NewType::iterator>
+ {
+ return this->m_value.end();
+ }
+
+ template<typename NewType = new_type>
+ auto constexpr end() const -> std::enable_if_t<NewType::derivation_clause(nt::Iterable) && impl::has_member_end_v<BaseType const>,
+ typename NewType::const_iterator>
+ {
+ return this->m_value.end();
+ }
+
+ template<typename NewType = new_type>
+ auto constexpr cend() const -> std::enable_if_t<NewType::derivation_clause(nt::Iterable) && impl::has_member_cend_v<BaseType const>,
+ typename NewType::const_iterator>
+ {
+ return this->m_value.cend();
+ }
+
+ template<typename NewType = new_type, std::enable_if_t<NewType::derivation_clause(nt::Iterable)> * = nullptr>
+ auto constexpr rend()
+ -> std::enable_if_t<NewType::derivation_clause(nt::Iterable) && impl::has_member_rend_v<BaseType>, typename NewType::reverse_iterator>
+ {
+ return this->m_value.rend();
+ }
+
+ template<typename NewType = new_type>
+ auto constexpr rend() const -> std::enable_if_t<NewType::derivation_clause(nt::Iterable) && impl::has_member_rend_v<BaseType const>,
+ typename NewType::const_reverse_iterator>
+ {
+ return this->m_value.rend();
+ }
+
+ template<typename NewType = new_type>
+ auto constexpr crend() const -> std::enable_if_t<NewType::derivation_clause(nt::Iterable) && impl::has_member_crend_v<BaseType const>,
+ typename NewType::const_reverse_iterator>
+ {
+ return this->m_value.crend();
+ }
+ };
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr
+ operator==(new_type<BaseType, TagType, DerivationClause> const & lhs,
+ new_type<BaseType, TagType, DerivationClause> const & rhs) noexcept(impl::is_nothrow_equality_comparable_v<BaseType>)
+ -> std::enable_if_t<impl::is_equality_comparable_v<BaseType>, bool>
+ {
+ return lhs.decay() == rhs.decay();
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr operator==(new_type<BaseType, TagType, DerivationClause> const & lhs,
+ BaseType const & rhs) noexcept(impl::is_nothrow_equality_comparable_v<BaseType>)
+ -> std::enable_if_t<DerivationClause(nt::EqBase) && impl::is_equality_comparable_v<BaseType>, bool>
+ {
+ return lhs.decay() == rhs;
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr
+ operator==(BaseType const & lhs,
+ new_type<BaseType, TagType, DerivationClause> const & rhs) noexcept(impl::is_nothrow_equality_comparable_v<BaseType>)
+ -> std::enable_if_t<DerivationClause(nt::EqBase) && impl::is_equality_comparable_v<BaseType>, bool>
+ {
+ return lhs == rhs.decay();
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr
+ operator!=(new_type<BaseType, TagType, DerivationClause> const & lhs,
+ new_type<BaseType, TagType, DerivationClause> const & rhs) noexcept(impl::is_nothrow_inequality_comparable_v<BaseType>)
+ -> std::enable_if_t<impl::is_inequality_comparable_v<BaseType>, bool>
+ {
+ return lhs.decay() != rhs.decay();
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr operator!=(new_type<BaseType, TagType, DerivationClause> const & lhs,
+ BaseType const & rhs) noexcept(impl::is_nothrow_inequality_comparable_v<BaseType>)
+ -> std::enable_if_t<DerivationClause(nt::EqBase) && impl::is_inequality_comparable_v<BaseType>, bool>
+ {
+ return lhs.decay() != rhs;
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr
+ operator!=(BaseType const & lhs,
+ new_type<BaseType, TagType, DerivationClause> const & rhs) noexcept(impl::is_nothrow_inequality_comparable_v<BaseType>)
+ -> std::enable_if_t<DerivationClause(nt::EqBase) && impl::is_inequality_comparable_v<BaseType>, bool>
+ {
+ return lhs != rhs.decay();
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr
+ operator<(new_type<BaseType, TagType, DerivationClause> const & lhs,
+ new_type<BaseType, TagType, DerivationClause> const & rhs) noexcept(impl::is_nothrow_less_than_comparable_v<BaseType>)
+ -> std::enable_if_t<DerivationClause(nt::Relational) && impl::is_less_than_comparable_v<BaseType>, bool>
+ {
+ return lhs.decay() < rhs.decay();
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr
+ operator>(new_type<BaseType, TagType, DerivationClause> const & lhs,
+ new_type<BaseType, TagType, DerivationClause> const & rhs) noexcept(impl::is_nothrow_greater_than_comparable_v<BaseType>)
+ -> std::enable_if_t<DerivationClause(nt::Relational) && impl::is_greater_than_comparable_v<BaseType>, bool>
+ {
+ return lhs.decay() > rhs.decay();
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr
+ operator<=(new_type<BaseType, TagType, DerivationClause> const & lhs,
+ new_type<BaseType, TagType, DerivationClause> const & rhs) noexcept(impl::is_nothrow_less_than_equal_to_comparable_v<BaseType>)
+ -> std::enable_if_t<DerivationClause(nt::Relational) && impl::is_less_than_equal_to_comparable_v<BaseType>, bool>
+ {
+ return lhs.decay() <= rhs.decay();
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr
+ operator>=(new_type<BaseType, TagType, DerivationClause> const & lhs,
+ new_type<BaseType, TagType, DerivationClause> const & rhs) noexcept(impl::is_nothrow_greater_than_equal_to_comparable_v<BaseType>)
+ -> std::enable_if_t<DerivationClause(nt::Relational) && impl::is_greater_than_equal_to_comparable_v<BaseType>, bool>
+ {
+ return lhs.decay() >= rhs.decay();
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause, typename CharType, typename StreamTraits>
+ auto operator<<(std::basic_ostream<CharType, StreamTraits> & output, new_type<BaseType, TagType, DerivationClause> const & source) noexcept(
+ impl::is_nothrow_output_streamable_v<std::basic_ostream<CharType, StreamTraits>, BaseType>)
+ -> std::enable_if_t<DerivationClause(nt::Show) && impl::is_output_streamable_v<std::basic_ostream<CharType, StreamTraits>, BaseType>,
+ std::basic_ostream<CharType, StreamTraits>> &
+ {
+ return output << source.decay();
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause, typename CharType, typename StreamTraits>
+ auto operator>>(std::basic_istream<CharType, StreamTraits> & input, new_type<BaseType, TagType, DerivationClause> & target) noexcept(
+ impl::is_nothrow_input_streamable_v<std::basic_istream<CharType, StreamTraits>, BaseType>)
+ -> std::enable_if_t<DerivationClause(nt::Read) && impl::is_input_streamable_v<std::basic_istream<CharType, StreamTraits>, BaseType>,
+ std::basic_istream<CharType, StreamTraits>> &
+ {
+ return input >> target.m_value;
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr
+ operator+(new_type<BaseType, TagType, DerivationClause> const & lhs, new_type<BaseType, TagType, DerivationClause> const & rhs) noexcept(
+ impl::is_nothrow_addable_v<BaseType> && std::is_nothrow_copy_constructible_v<BaseType>)
+ -> std::enable_if_t<DerivationClause(nt::Arithmetic) && impl::is_addable_v<BaseType>, new_type<BaseType, TagType, DerivationClause>>
+ {
+ return {lhs.decay() + rhs.decay()};
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr operator+=(new_type<BaseType, TagType, DerivationClause> & lhs,
+ new_type<BaseType, TagType, DerivationClause> const & rhs) noexcept(impl::is_nothrow_add_assignable_v<BaseType>)
+ -> std::enable_if_t<DerivationClause(nt::Arithmetic) && impl::is_add_assignable_v<BaseType>,
+ new_type<BaseType, TagType, DerivationClause> &>
+ {
+ lhs.m_value += rhs.m_value;
+ return lhs;
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr
+ operator-(new_type<BaseType, TagType, DerivationClause> const & lhs, new_type<BaseType, TagType, DerivationClause> const & rhs) noexcept(
+ impl::is_nothrow_subtractable_v<BaseType> && std::is_nothrow_copy_constructible_v<BaseType>)
+ -> std::enable_if_t<DerivationClause(nt::Arithmetic) && impl::is_subtractable_v<BaseType>, new_type<BaseType, TagType, DerivationClause>>
+ {
+ return {lhs.decay() - rhs.decay()};
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr
+ operator-=(new_type<BaseType, TagType, DerivationClause> & lhs,
+ new_type<BaseType, TagType, DerivationClause> const & rhs) noexcept(impl::is_nothrow_subtract_assignable_v<BaseType>)
+ -> std::enable_if_t<DerivationClause(nt::Arithmetic) && impl::is_subtract_assignable_v<BaseType>,
+ new_type<BaseType, TagType, DerivationClause> &>
+ {
+ lhs.m_value -= rhs.m_value;
+ return lhs;
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr
+ operator*(new_type<BaseType, TagType, DerivationClause> const & lhs, new_type<BaseType, TagType, DerivationClause> const & rhs) noexcept(
+ impl::is_nothrow_multipliable_v<BaseType> && std::is_nothrow_copy_constructible_v<BaseType>)
+ -> std::enable_if_t<DerivationClause(nt::Arithmetic) && impl::is_multipliable_v<BaseType>, new_type<BaseType, TagType, DerivationClause>>
+ {
+ return {lhs.decay() * rhs.decay()};
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr
+ operator*=(new_type<BaseType, TagType, DerivationClause> & lhs,
+ new_type<BaseType, TagType, DerivationClause> const & rhs) noexcept(impl::is_nothrow_multiply_assignable_v<BaseType>)
+ -> std::enable_if_t<DerivationClause(nt::Arithmetic) && impl::is_multiply_assignable_v<BaseType>,
+ new_type<BaseType, TagType, DerivationClause> &>
+ {
+ lhs.m_value *= rhs.m_value;
+ return lhs;
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr
+ operator/(new_type<BaseType, TagType, DerivationClause> const & lhs, new_type<BaseType, TagType, DerivationClause> const & rhs) noexcept(
+ impl::is_nothrow_dividable_v<BaseType> && std::is_nothrow_copy_constructible_v<BaseType>)
+ -> std::enable_if_t<DerivationClause(nt::Arithmetic) && impl::is_dividable_v<BaseType>, new_type<BaseType, TagType, DerivationClause>>
+ {
+ return {lhs.decay() / rhs.decay()};
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr operator/=(new_type<BaseType, TagType, DerivationClause> & lhs,
+ new_type<BaseType, TagType, DerivationClause> const & rhs) noexcept(impl::is_nothrow_divide_assignable_v<BaseType>)
+ -> std::enable_if_t<DerivationClause(nt::Arithmetic) && impl::is_divide_assignable_v<BaseType>,
+ new_type<BaseType, TagType, DerivationClause> &>
+ {
+ lhs.m_value /= rhs.m_value;
+ return lhs;
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr begin(new_type<BaseType, TagType, DerivationClause> & obj)
+ -> std::enable_if_t<DerivationClause(nt::Iterable) && impl::has_free_begin_v<BaseType>,
+ typename new_type<BaseType, TagType, DerivationClause>::iterator>
+ {
+ return begin(obj.m_value);
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr begin(new_type<BaseType, TagType, DerivationClause> const & obj)
+ -> std::enable_if_t<DerivationClause(nt::Iterable) && impl::has_free_begin_v<BaseType const>,
+ typename new_type<BaseType, TagType, DerivationClause>::const_iterator>
+ {
+ return begin(obj.m_value);
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr cbegin(new_type<BaseType, TagType, DerivationClause> const & obj)
+ -> std::enable_if_t<DerivationClause(nt::Iterable) && impl::has_free_cbegin_v<BaseType const>,
+ typename new_type<BaseType, TagType, DerivationClause>::const_iterator>
+ {
+ return cbegin(obj.m_value);
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr rbegin(new_type<BaseType, TagType, DerivationClause> & obj)
+ -> std::enable_if_t<DerivationClause(nt::Iterable) && impl::has_free_rbegin_v<BaseType>,
+ typename new_type<BaseType, TagType, DerivationClause>::reverse_iterator>
+ {
+ return rbegin(obj.m_value);
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr rbegin(new_type<BaseType, TagType, DerivationClause> const & obj)
+ -> std::enable_if_t<DerivationClause(nt::Iterable) && impl::has_free_rbegin_v<BaseType const>,
+ typename new_type<BaseType, TagType, DerivationClause>::const_reverse_iterator>
+ {
+ return rbegin(obj.m_value);
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr crbegin(new_type<BaseType, TagType, DerivationClause> const & obj)
+ -> std::enable_if_t<DerivationClause(nt::Iterable) && impl::has_free_crbegin_v<BaseType const>,
+ typename new_type<BaseType, TagType, DerivationClause>::const_reverse_iterator>
+ {
+ return crbegin(obj.m_value);
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr end(new_type<BaseType, TagType, DerivationClause> & obj)
+ -> std::enable_if_t<DerivationClause(nt::Iterable) && impl::has_free_end_v<BaseType>,
+ typename new_type<BaseType, TagType, DerivationClause>::iterator>
+ {
+ return end(obj.m_value);
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr end(new_type<BaseType, TagType, DerivationClause> const & obj)
+ -> std::enable_if_t<DerivationClause(nt::Iterable) && impl::has_free_end_v<BaseType const>,
+ typename new_type<BaseType, TagType, DerivationClause>::const_iterator>
+ {
+ return end(obj.m_value);
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr cend(new_type<BaseType, TagType, DerivationClause> const & obj)
+ -> std::enable_if_t<DerivationClause(nt::Iterable) && impl::has_free_cend_v<BaseType const>,
+ typename new_type<BaseType, TagType, DerivationClause>::const_iterator>
+ {
+ return cend(obj.m_value);
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr rend(new_type<BaseType, TagType, DerivationClause> & obj)
+ -> std::enable_if_t<DerivationClause(nt::Iterable) && impl::has_free_rend_v<BaseType>,
+ typename new_type<BaseType, TagType, DerivationClause>::reverse_iterator>
+ {
+ return rend(obj.m_value);
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr rend(new_type<BaseType, TagType, DerivationClause> const & obj)
+ -> std::enable_if_t<DerivationClause(nt::Iterable) && impl::has_free_rend_v<BaseType const>,
+ typename new_type<BaseType, TagType, DerivationClause>::const_reverse_iterator>
+ {
+ return rend(obj.m_value);
+ }
+
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ auto constexpr crend(new_type<BaseType, TagType, DerivationClause> const & obj)
+ -> std::enable_if_t<DerivationClause(nt::Iterable) && impl::has_free_crend_v<BaseType const>,
+ typename new_type<BaseType, TagType, DerivationClause>::const_reverse_iterator>
+ {
+ return crend(obj.m_value);
+ }
+
+} // namespace nt
+
+namespace std
+{
+ template<typename BaseType, typename TagType, auto DerivationClause>
+ struct hash<nt::new_type<BaseType, TagType, DerivationClause>>
+ {
+ template<typename BaseTypeT = BaseType, auto DerivationClauseV = DerivationClause>
+ auto constexpr operator()(nt::new_type<BaseType, TagType, DerivationClause> const & object,
+ std::enable_if_t<DerivationClauseV(nt::Hash) && nt::impl::is_hashable_v<BaseTypeT>> * = nullptr) const
+ -> std::size_t
+ {
+ return std::hash<BaseType>{}(object.decay());
+ }
+ };
+} // namespace std
+
+#endif
diff --git a/source/include/newtype/version.hpp b/source/include/newtype/version.hpp
new file mode 100644
index 0000000..8678514
--- /dev/null
+++ b/source/include/newtype/version.hpp
@@ -0,0 +1,23 @@
+#ifndef NEWTYPE_VERSION_HPP
+#define NEWTYPE_VERSION_HPP
+
+namespace nt
+{
+
+ constexpr struct
+ {
+ int const major;
+ int const minor;
+ int const patch;
+
+ char const * const name;
+ } version{
+ .major = 1,
+ .minor = 1,
+ .patch = 0,
+ .name = "Anastasia",
+ };
+
+} // namespace nt
+
+#endif \ No newline at end of file
diff --git a/source/test/src/arithmetic.cpp b/source/test/src/arithmetic.cpp
new file mode 100644
index 0000000..5048393
--- /dev/null
+++ b/source/test/src/arithmetic.cpp
@@ -0,0 +1,300 @@
+#include "newtype/derivable.hpp"
+#include "newtype/deriving.hpp"
+#include "newtype/impl/type_traits_extensions.hpp"
+#include "newtype/newtype.hpp"
+
+#include <catch2/catch_test_macros.hpp>
+
+#include <type_traits>
+
+SCENARIO("Addition", "[arithmetic]")
+{
+ struct addable_type
+ {
+ auto constexpr operator+(addable_type const &) const -> addable_type
+ {
+ return {};
+ };
+ };
+
+ GIVEN("A new_type instance not deriving nt::Arithmetic")
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+
+ THEN("it is not addable")
+ {
+ REQUIRE(!nt::impl::is_addable_v<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type deriving nt::Arithmetic")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>;
+
+ THEN("it is addable")
+ {
+ REQUIRE(nt::impl::is_addable_v<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over a non-addable class type deriving nt::Arithmetic")
+ {
+ using type_alias = nt::new_type<void *, struct tag, deriving(nt::Arithmetic)>;
+
+ THEN("it is not addable")
+ {
+ REQUIRE(!nt::impl::is_addable_v<addable_type> == nt::impl::is_addable_v<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over an addable class type deriving nt::Arithmetic")
+ {
+ using type_alias = nt::new_type<addable_type, struct tag, deriving(nt::Arithmetic)>;
+
+ THEN("it is addable")
+ {
+ REQUIRE(nt::impl::is_addable_v<addable_type> == nt::impl::is_addable_v<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type deriving nt::Arithmetic")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>;
+
+ THEN("addition produces the same type")
+ {
+ REQUIRE(std::is_same_v<type_alias, decltype(std::declval<type_alias const &>() + std::declval<type_alias const &>())>);
+ }
+ }
+
+ GIVEN("Two objects of a new_type deriving nt::Arithmetic")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>;
+ auto lhs = type_alias{24};
+ auto rhs = type_alias{18};
+
+ THEN("addition produces the correct result with respect to the base type")
+ {
+ REQUIRE((lhs + rhs).decay() == 24 + 18);
+ }
+ }
+}
+
+SCENARIO("Subtraction", "[arithmetic]")
+{
+ struct subtractable_type
+ {
+ auto constexpr operator-(subtractable_type const &) const -> subtractable_type
+ {
+ return {};
+ };
+ };
+
+ GIVEN("A new_type not deriving nt::arithmetic")
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+
+ THEN("it is not subtractable")
+ {
+ REQUIRE(!nt::impl::is_subtractable_v<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type deriving nt::Arithmetic")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>;
+
+ THEN("it is subtractable")
+ {
+ REQUIRE(nt::impl::is_subtractable_v<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over a non-subtractable class type deriving nt::Arithmetic")
+ {
+ using type_alias = nt::new_type<void *, struct tag, deriving(nt::Arithmetic)>;
+
+ THEN("it is not addable")
+ {
+ REQUIRE(!nt::impl::is_subtractable_v<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over a subtractable class type deriving nt::Arithmetic")
+ {
+ using type_alias = nt::new_type<subtractable_type, struct tag, deriving(nt::Arithmetic)>;
+
+ THEN("it is subtractable")
+ {
+ REQUIRE(nt::impl::is_subtractable_v<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type deriving nt::Arithmetic")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>;
+
+ THEN("subtraction produces the same type")
+ {
+ REQUIRE(std::is_same_v<type_alias, decltype(std::declval<type_alias const &>() - std::declval<type_alias const &>())>);
+ }
+ }
+
+ GIVEN("Two objects of a new_type deriving nt::Arithmetic")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>;
+ auto lhs = type_alias{24};
+ auto rhs = type_alias{18};
+
+ THEN("subtraction produces the correct result with respect to the base type")
+ {
+ REQUIRE((lhs - rhs).decay() == 24 - 18);
+ }
+ }
+}
+
+SCENARIO("Multiplication", "[arithmetic]")
+{
+ struct multipliable_type
+ {
+ auto constexpr operator*(multipliable_type const &) const -> multipliable_type
+ {
+ return {};
+ };
+ };
+
+ GIVEN("A new_type not deriving nt::arithmetic")
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+
+ THEN("it is not multipliable")
+ {
+ REQUIRE(!nt::impl::is_multipliable_v<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type deriving nt::Arithmetic")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>;
+
+ THEN("it is multipliable")
+ {
+ REQUIRE(nt::impl::is_multipliable_v<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over a non-multipliable class type deriving nt::Arithmetic")
+ {
+ using type_alias = nt::new_type<void *, struct tag, deriving(nt::Arithmetic)>;
+
+ THEN("it is not multipliable")
+ {
+ REQUIRE(!nt::impl::is_multipliable_v<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over a multipliable class type deriving nt::Arithmetic")
+ {
+ using type_alias = nt::new_type<multipliable_type, struct tag, deriving(nt::Arithmetic)>;
+
+ THEN("it is multipliable")
+ {
+ REQUIRE(nt::impl::is_multipliable_v<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type deriving nt::Arithmetic")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>;
+
+ THEN("multiplication produces the same type")
+ {
+ REQUIRE(std::is_same_v<type_alias, decltype(std::declval<type_alias const &>() * std::declval<type_alias const &>())>);
+ }
+ }
+
+ GIVEN("Two objects of a new_type deriving nt::Arithmetic")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>;
+ auto lhs = type_alias{24};
+ auto rhs = type_alias{18};
+
+ THEN("multiplication produces the correct result with respect to the base type")
+ {
+ REQUIRE((lhs * rhs).decay() == 24 * 18);
+ }
+ }
+}
+
+SCENARIO("Division", "[arithmetic]")
+{
+ struct dividable_type
+ {
+ auto constexpr operator/(dividable_type const &) const -> dividable_type
+ {
+ return {};
+ };
+ };
+
+ GIVEN("A new_type not deriving nt::arithmetic")
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+
+ THEN("it is not divisible")
+ {
+ REQUIRE(!nt::impl::is_dividable_v<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type deriving nt::Arithmetic")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>;
+
+ THEN("it is divisible")
+ {
+ REQUIRE(nt::impl::is_dividable_v<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over a non-divisible class type deriving nt::Arithmetic")
+ {
+ using type_alias = nt::new_type<void *, struct tag, deriving(nt::Arithmetic)>;
+
+ THEN("it is not divisible")
+ {
+ REQUIRE(!nt::impl::is_dividable_v<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over a divisible class type deriving nt::Arithmetic")
+ {
+ using type_alias = nt::new_type<dividable_type, struct tag, deriving(nt::Arithmetic)>;
+
+ THEN("it is divisible")
+ {
+ REQUIRE(nt::impl::is_dividable_v<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type deriving nt::Arithmetic")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>;
+
+ THEN("division produces the same type")
+ {
+ REQUIRE(std::is_same_v<type_alias, decltype(std::declval<type_alias const &>() / std::declval<type_alias const &>())>);
+ }
+ }
+
+ GIVEN("Two objects of a new_type deriving nt::Arithmetic")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>;
+ auto lhs = type_alias{30};
+ auto rhs = type_alias{15};
+
+ THEN("division produces the correct result with respect to the base type")
+ {
+ REQUIRE((lhs / rhs).decay() == 30 / 15);
+ }
+ }
+}
diff --git a/source/test/src/conversion_suite.cpp b/source/test/src/conversion_suite.cpp
new file mode 100644
index 0000000..cbe2463
--- /dev/null
+++ b/source/test/src/conversion_suite.cpp
@@ -0,0 +1,201 @@
+#include "conversion_suite.hpp"
+
+#include "kawaii.hpp"
+#include "newtype/derivable.hpp"
+#include "newtype/deriving.hpp"
+#include "newtype/newtype.hpp"
+
+#include <cute/cute.h>
+
+#include <algorithm>
+#include <iterator>
+#include <limits>
+#include <random>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+inline namespace traits_extensions
+{
+
+ template<typename To, typename From, typename = void>
+ struct is_implicitly_convertible : std::false_type
+ {
+ };
+
+ template<typename To, typename From>
+ struct is_implicitly_convertible<To, From, std::void_t<decltype(std::declval<To &>() = std::declval<From &>())>> : std::true_type
+ {
+ };
+
+ template<typename To, typename From>
+ auto constexpr is_implicitly_convertible_v = is_implicitly_convertible<To, From>::value;
+
+} // namespace traits_extensions
+
+inline namespace ddt_support
+{
+
+ template<typename T>
+ struct datum
+ {
+ T value;
+ cute::test_failure failure;
+ };
+
+ template<typename ValueType>
+ auto generate_test_set(std::size_t size) -> std::vector<datum<ValueType>>
+ {
+ auto device = std::random_device{};
+ auto generator = std::mt19937{device()};
+ auto distribution = [&] {
+ auto min = std::numeric_limits<ValueType>::min();
+ auto max = std::numeric_limits<ValueType>::max();
+ if constexpr (std::is_floating_point_v<ValueType>)
+ {
+ return std::uniform_real_distribution<ValueType>(min, max);
+ }
+ else
+ {
+ return std::uniform_int_distribution<ValueType>(min, max);
+ }
+ }();
+
+ auto data = std::vector<datum<ValueType>>{};
+ generate_n(std::back_inserter(data), size, [&] {
+ auto point = distribution(generator);
+ return datum<ValueType>{point, DDT()};
+ });
+ return data;
+ }
+
+} // namespace ddt_support
+
+inline namespace implicit_conversion_tests
+{
+
+ template<typename T>
+ auto a_new__type_without_deriving_implicit_conversion_is_not_implicitly_convertible_to_the_base_type() -> void
+ {
+ using type_alias = nt::new_type<T, struct conversion_test_1_tag>;
+ ASSERT(!(is_implicitly_convertible_v<T, type_alias>));
+ }
+
+ template<typename T>
+ auto a_new__type_with_deriving_implicit_conversion_is_implicitly_convertible_to_the_base_type() -> void
+ {
+ using type_alias = nt::new_type<T, struct conversion_test_2_tag, deriving(nt::ImplicitConversion)>;
+ ASSERT((is_implicitly_convertible_v<T, type_alias>));
+ }
+
+} // namespace implicit_conversion_tests
+
+inline namespace decay_tests
+{
+
+ template<typename T>
+ auto decay_on_a_new__type_has_a_return_type_equal_to_the_base_type() -> void
+ {
+ using type_alias = nt::new_type<T, struct conversion_test_3_tag>;
+ ASSERT((std::is_same_v<T, decltype(std::declval<type_alias &>().decay())>));
+ }
+
+ template<typename T>
+ auto decay_on_a_new__type_returns_the_underlying_value() -> void
+ {
+ using type_alias = nt::new_type<T, struct conversion_test_4_tag>;
+ auto data = generate_test_set<T>(2 << 16);
+ for_each(begin(data), end(data), [](auto & test) { ASSERT_EQUAL_DDT(test.value, type_alias{test.value}.decay(), test.failure); });
+ }
+
+} // namespace decay_tests
+
+inline namespace decay_noexcept_tests
+{
+
+ auto decay_on_a_new__type_is_noexcept_if_the_base_type_can_be_copied_without_throwing() -> void
+ {
+ static_assert(std::is_nothrow_copy_constructible_v<int>, "Sanity check");
+ using type_alias = nt::new_type<int, struct conversion_test_5_tag>;
+ ASSERT(noexcept(type_alias{}.decay()));
+ }
+
+ auto decay_on_a_new__type_is_not_noexcept_if_the_base_type_can_be_not_copied_without_throwing() -> void
+ {
+ struct strange_type
+ {
+ strange_type() = default;
+ strange_type(strange_type const &) noexcept(false)
+ {
+ }
+ };
+
+ static_assert(!std::is_nothrow_copy_constructible_v<strange_type>, "Sanity check");
+ using type_alias = nt::new_type<strange_type, struct conversion_test_6_tag>;
+ ASSERT(!noexcept(type_alias{}.decay()));
+ }
+
+} // namespace decay_noexcept_tests
+
+inline namespace conversion_operator_noexcept_tests
+{
+
+ auto new__type_conversion_operator_is_noexcept_if_the_base_type_can_be_copied_without_throwing() -> void
+ {
+ static_assert(std::is_nothrow_copy_constructible_v<int>, "Sanity check");
+ using type_alias = nt::new_type<int, struct conversion_test_5_tag>;
+ ASSERT(noexcept(static_cast<int>(type_alias{})));
+ }
+
+ auto new__type_conversion_operator_is_not_noexcept_if_the_base_type_can_not_be_copied_without_throwing() -> void
+ {
+ struct strange_type
+ {
+ strange_type() = default;
+ strange_type(strange_type const &) noexcept(false)
+ {
+ }
+ };
+
+ static_assert(!std::is_nothrow_copy_constructible_v<strange_type>, "Sanity check");
+ using type_alias = nt::new_type<strange_type, struct conversion_test_5_tag>;
+ ASSERT(!noexcept(static_cast<strange_type>(type_alias{})));
+ }
+
+} // namespace conversion_operator_noexcept_tests
+
+auto conversion_suite() -> std::pair<cute::suite, std::string>
+{
+ return {{
+ /// Implicit Conversion Tests
+ KAWAII(a_new__type_without_deriving_implicit_conversion_is_not_implicitly_convertible_to_the_base_type<bool>),
+ KAWAII(a_new__type_without_deriving_implicit_conversion_is_not_implicitly_convertible_to_the_base_type<char>),
+ KAWAII(a_new__type_without_deriving_implicit_conversion_is_not_implicitly_convertible_to_the_base_type<int>),
+ KAWAII(a_new__type_without_deriving_implicit_conversion_is_not_implicitly_convertible_to_the_base_type<double>),
+ KAWAII(a_new__type_without_deriving_implicit_conversion_is_not_implicitly_convertible_to_the_base_type<std::string>),
+ KAWAII(a_new__type_with_deriving_implicit_conversion_is_implicitly_convertible_to_the_base_type<bool>),
+ KAWAII(a_new__type_with_deriving_implicit_conversion_is_implicitly_convertible_to_the_base_type<char>),
+ KAWAII(a_new__type_with_deriving_implicit_conversion_is_implicitly_convertible_to_the_base_type<int>),
+ KAWAII(a_new__type_with_deriving_implicit_conversion_is_implicitly_convertible_to_the_base_type<double>),
+ KAWAII(a_new__type_with_deriving_implicit_conversion_is_implicitly_convertible_to_the_base_type<std::string>),
+
+ /// Decay Tests
+ KAWAII(decay_on_a_new__type_has_a_return_type_equal_to_the_base_type<bool>),
+ KAWAII(decay_on_a_new__type_has_a_return_type_equal_to_the_base_type<char>),
+ KAWAII(decay_on_a_new__type_has_a_return_type_equal_to_the_base_type<int>),
+ KAWAII(decay_on_a_new__type_has_a_return_type_equal_to_the_base_type<double>),
+ KAWAII(decay_on_a_new__type_has_a_return_type_equal_to_the_base_type<std::string>),
+ KAWAII(decay_on_a_new__type_returns_the_underlying_value<char>),
+ KAWAII(decay_on_a_new__type_returns_the_underlying_value<int>),
+ KAWAII(decay_on_a_new__type_returns_the_underlying_value<double>),
+
+ /// Decay noexcept Tests
+ KAWAII(decay_on_a_new__type_is_noexcept_if_the_base_type_can_be_copied_without_throwing),
+ KAWAII(decay_on_a_new__type_is_not_noexcept_if_the_base_type_can_be_not_copied_without_throwing),
+
+ /// Conversion Operator noexcept Tests
+ KAWAII(new__type_conversion_operator_is_noexcept_if_the_base_type_can_be_copied_without_throwing),
+ KAWAII(new__type_conversion_operator_is_not_noexcept_if_the_base_type_can_not_be_copied_without_throwing),
+ },
+ "Conversion Tests"};
+} \ No newline at end of file
diff --git a/source/test/src/derivation_clause_suite.cpp b/source/test/src/derivation_clause_suite.cpp
new file mode 100644
index 0000000..86a34ab
--- /dev/null
+++ b/source/test/src/derivation_clause_suite.cpp
@@ -0,0 +1,312 @@
+#include "derivation_clause_suite.hpp"
+
+#include "kawaii.hpp"
+#include "newtype/derivable.hpp"
+#include "newtype/deriving.hpp"
+
+#include <cute/cute.h>
+
+#include <string>
+
+inline namespace subset_tests
+{
+
+ auto an_empty_derivation_clause_does_not_contain_any_derivable() -> void
+ {
+ auto derivation_clause = nt::deriving();
+ ASSERT(!derivation_clause(nt::Show));
+ }
+
+ auto a_derivation_clause_containing_only_show_does_not_contain_eqbase() -> void
+ {
+ auto derivation_clause = deriving(nt::Show);
+ ASSERT(!derivation_clause(nt::EqBase));
+ }
+
+ auto a_derivation_clause_containing_only_show_does_contain_show() -> void
+ {
+ auto derivation_clause = deriving(nt::Show);
+ ASSERT(derivation_clause(nt::Show));
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_does_contain_show() -> void
+ {
+ auto derivation_clause = deriving(nt::Show, nt::EqBase);
+ ASSERT(derivation_clause(nt::Show));
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_does_contain_both_show_and_eqbase() -> void
+ {
+ auto derivation_clause = deriving(nt::Show, nt::EqBase);
+ ASSERT(derivation_clause(nt::Show, nt::EqBase));
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_does_not_contain_arithmetic() -> void
+ {
+ auto derivation_clause = deriving(nt::Show, nt::EqBase);
+ ASSERT(!derivation_clause(nt::Arithmetic));
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_does_not_contain_both_show_and_arithmetic() -> void
+ {
+ auto derivation_clause = deriving(nt::Show, nt::EqBase);
+ ASSERT(!derivation_clause(nt::Show, nt::Arithmetic));
+ }
+
+} // namespace subset_tests
+
+inline namespace less_than_tests
+{
+
+ auto a_derivation_clause_containing_only_show_compares_less_than_one_containing_show_and_eqbase() -> void
+ {
+ auto only_show = deriving(nt::Show);
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT(only_show < show_and_eqbase);
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_does_not_compare_less_than_one_containing_show_and_eqbase() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto also_show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT(!(show_and_eqbase < also_show_and_eqbase));
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_does_not_compare_less_than_one_containing_eqbase_and_show() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto eqbase_and_show = deriving(nt::EqBase, nt::Show);
+
+ ASSERT(!(show_and_eqbase < eqbase_and_show));
+ }
+
+ auto a_derivation_clause_containing_only_show_does_not_compare_less_than_one_containing_only_eqbase() -> void
+ {
+ auto show = deriving(nt::Show);
+ auto eqbase = deriving(nt::EqBase);
+
+ ASSERT(!(show < eqbase));
+ }
+
+} // namespace less_than_tests
+
+inline namespace greater_than_tests
+{
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_compares_greater_than_one_containing_only_show() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto show = deriving(nt::Show);
+
+ ASSERT(show_and_eqbase > show);
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_does_not_compare_greater_than_one_containing_only_show_and_eqbase() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto also_show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT(!(show_and_eqbase > also_show_and_eqbase));
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_does_not_compare_greater_than_one_containing_only_eqbase_and_show() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto eqbase_and_show = deriving(nt::EqBase, nt::Show);
+
+ ASSERT(!(show_and_eqbase > eqbase_and_show));
+ }
+
+ auto a_derivation_clause_containing_only_show_does_not_compare_greater_than_one_containing_only_eqbase() -> void
+ {
+ auto show = deriving(nt::Show);
+ auto eqbase = deriving(nt::EqBase);
+
+ ASSERT(!(show > eqbase));
+ }
+
+} // namespace greater_than_tests
+
+inline namespace eqbaseuals_tests
+{
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_is_eqbaseual_to_one_containing_only_show_and_eqbase() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto also_show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT_EQUAL(show_and_eqbase, also_show_and_eqbase);
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_is_eqbaseual_to_one_containing_only_eqbase_and_show() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto eqbase_and_show = deriving(nt::EqBase, nt::Show);
+
+ ASSERT_EQUAL(show_and_eqbase, eqbase_and_show);
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_is_not_eqbaseual_to_one_containing_only_arithmetic() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto arithmetic = deriving(nt::Arithmetic);
+
+ ASSERT(!(show_and_eqbase == arithmetic));
+ }
+
+ auto a_derivation_clause_containing_only_show_is_not_eqbaseual_to_one_containing_only_arithmetic() -> void
+ {
+ auto show = deriving(nt::Show);
+ auto arithmetic = deriving(nt::Arithmetic);
+
+ ASSERT(!(show == arithmetic));
+ }
+
+} // namespace eqbaseuals_tests
+
+inline namespace not_eqbaseuals_tests
+{
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_is_not_not_eqbaseual_to_one_containing_only_show_and_eqbase() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto also_show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT(!(show_and_eqbase != also_show_and_eqbase));
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_is_not_not_eqbaseual_to_one_containing_only_eqbase_and_show() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto eqbase_and_show = deriving(nt::EqBase, nt::Show);
+
+ ASSERT(!(show_and_eqbase != eqbase_and_show));
+ }
+
+ auto a_derivation_clause_containing_only_eqbase_and_show_is_not_eqbaseual_to_one_containing_only_arithmetic() -> void
+ {
+ auto eqbase_and_show = deriving(nt::EqBase, nt::Show);
+ auto arithmetic = deriving(nt::Arithmetic);
+
+ ASSERT(eqbase_and_show != arithmetic);
+ }
+
+ auto a_derivation_clause_containing_only_eqbase_is_not_eqbaseual_to_one_containing_only_arithmetic() -> void
+ {
+ auto eqbase = deriving(nt::EqBase);
+ auto arithmetic = deriving(nt::Arithmetic);
+
+ ASSERT(eqbase != arithmetic);
+ }
+
+} // namespace not_eqbaseuals_tests
+
+inline namespace less_than_or_eqbaseual_tests
+{
+
+ auto a_derivation_clause_containing_only_show_is_less_than_or_eqbaseual_to_one_containing_only_show_and_eqbase() -> void
+ {
+ auto only_show = deriving(nt::Show);
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT(only_show <= show_and_eqbase);
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_is_less_than_or_eqbaseual_to_one_containing_only_show_and_eqbase() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto also_show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT(show_and_eqbase <= also_show_and_eqbase);
+ }
+
+ auto a_derivation_clause_containing_only_arithmetic_is_neither_less_than_nor_eqbaseual_to_on_containing_only_show_and_eqbase() -> void
+ {
+ auto arithmetic = deriving(nt::Arithmetic);
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT(!(arithmetic <= show_and_eqbase));
+ }
+
+} // namespace less_than_or_eqbaseual_tests
+
+inline namespace greater_than_or_eqbaseual_tests
+{
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_is_greater_than_or_eqbaseual_to_one_containing_only_show() -> void
+ {
+ auto only_show = deriving(nt::Show);
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT(show_and_eqbase >= only_show);
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_is_greater_than_or_eqbaseual_to_one_containing_only_show_and_eqbase() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto also_show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT(show_and_eqbase >= also_show_and_eqbase);
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_is_neither_greater_than_nor_eqbaseual_to_on_containing_only_arithmetic() -> void
+ {
+ auto arithmetic = deriving(nt::Arithmetic);
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT(!(show_and_eqbase >= arithmetic));
+ }
+
+} // namespace greater_than_or_eqbaseual_tests
+
+auto derivation_clause_suite() -> std::pair<cute::suite, std::string>
+{
+ return {
+ {
+ /// Subset tests
+ KAWAII(an_empty_derivation_clause_does_not_contain_any_derivable),
+ KAWAII(a_derivation_clause_containing_only_show_does_not_contain_eqbase),
+ KAWAII(a_derivation_clause_containing_only_show_does_contain_show),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_does_contain_show),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_does_contain_both_show_and_eqbase),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_does_not_contain_arithmetic),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_does_not_contain_both_show_and_arithmetic),
+
+ /// Less-than tests
+ KAWAII(a_derivation_clause_containing_only_show_compares_less_than_one_containing_show_and_eqbase),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_does_not_compare_less_than_one_containing_show_and_eqbase),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_does_not_compare_less_than_one_containing_eqbase_and_show),
+ KAWAII(a_derivation_clause_containing_only_show_does_not_compare_less_than_one_containing_only_eqbase),
+
+ /// Greater-than tests
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_compares_greater_than_one_containing_only_show),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_does_not_compare_greater_than_one_containing_only_show_and_eqbase),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_does_not_compare_greater_than_one_containing_only_eqbase_and_show),
+ KAWAII(a_derivation_clause_containing_only_show_does_not_compare_greater_than_one_containing_only_eqbase),
+
+ /// Equals tests
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_is_eqbaseual_to_one_containing_only_show_and_eqbase),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_is_eqbaseual_to_one_containing_only_eqbase_and_show),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_is_not_eqbaseual_to_one_containing_only_arithmetic),
+ KAWAII(a_derivation_clause_containing_only_show_is_not_eqbaseual_to_one_containing_only_arithmetic),
+
+ /// Not-Equals tests
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_is_not_not_eqbaseual_to_one_containing_only_show_and_eqbase),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_is_not_not_eqbaseual_to_one_containing_only_eqbase_and_show),
+ KAWAII(a_derivation_clause_containing_only_eqbase_and_show_is_not_eqbaseual_to_one_containing_only_arithmetic),
+ KAWAII(a_derivation_clause_containing_only_eqbase_is_not_eqbaseual_to_one_containing_only_arithmetic),
+
+ /// Less-than or Equals tests
+ KAWAII(a_derivation_clause_containing_only_show_is_less_than_or_eqbaseual_to_one_containing_only_show_and_eqbase),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_is_less_than_or_eqbaseual_to_one_containing_only_show_and_eqbase),
+ KAWAII(a_derivation_clause_containing_only_arithmetic_is_neither_less_than_nor_eqbaseual_to_on_containing_only_show_and_eqbase),
+
+ /// Greater-than or Equals tests
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_is_greater_than_or_eqbaseual_to_one_containing_only_show),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_is_greater_than_or_eqbaseual_to_one_containing_only_show_and_eqbase),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_is_neither_greater_than_nor_eqbaseual_to_on_containing_only_arithmetic),
+ },
+ "Derivation Clause Tests"};
+} \ No newline at end of file
diff --git a/source/test/src/equality_comparison_suite.cpp b/source/test/src/equality_comparison_suite.cpp
new file mode 100644
index 0000000..4e484f4
--- /dev/null
+++ b/source/test/src/equality_comparison_suite.cpp
@@ -0,0 +1,188 @@
+#include "equality_comparison_suite.hpp"
+
+#include "kawaii.hpp"
+#include "newtype/derivable.hpp"
+#include "newtype/deriving.hpp"
+#include "newtype/newtype.hpp"
+
+#include <cute/cute.h>
+
+#include <string>
+#include <type_traits>
+#include <utility>
+
+inline namespace basic_equality_comparsion_tests
+{
+
+ auto two_instances_of_the_same_new__type_with_the_same_value_compare_equal() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+
+ auto constexpr lhs = type_alias{42};
+ auto constexpr rhs = type_alias{42};
+
+ ASSERT_EQUAL(lhs, rhs);
+ }
+
+ auto two_instances_of_the_same_new__type_with_the_same_value_do_not_compare_not_equal() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+
+ auto constexpr lhs = type_alias{42};
+ auto constexpr rhs = type_alias{42};
+
+ ASSERT(!(lhs != rhs));
+ }
+
+ auto two_instances_of_the_same_new__type_with_differing_values_do_compare_not_equal() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+
+ auto constexpr lhs = type_alias{42};
+ auto constexpr rhs = type_alias{43};
+
+ ASSERT_NOT_EQUAL_TO(lhs, rhs);
+ }
+
+ auto two_instances_of_the_same_new__type_with_differing_values_do_not_compare_equal() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+
+ auto constexpr lhs = type_alias{42};
+ auto constexpr rhs = type_alias{43};
+
+ ASSERT(!(lhs == rhs));
+ }
+
+} // namespace basic_equality_comparsion_tests
+
+inline namespace equality_comparsion_noexcept_tests
+{
+
+ auto equality_comparison_on_a_new__type_is_noexcept_if_the_base_type_is_noexcept_comparable() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ static_assert(noexcept(std::declval<int &>() == std::declval<int &>()), "Sanity Check");
+ ASSERT(noexcept(std::declval<type_alias &>() == std::declval<type_alias &>()));
+ }
+
+ auto equality_comparison_on_a_new__type_is_not_noexcept_if_the_base_type_is_not_noexcept_comparable() -> void
+ {
+ struct strange_type
+ {
+ auto constexpr operator==(strange_type const &) const noexcept(false) -> bool
+ {
+ return false;
+ }
+ };
+
+ using type_alias = nt::new_type<strange_type, struct tag>;
+ static_assert(!noexcept(std::declval<strange_type &>() == std::declval<strange_type &>()), "Sanity Check");
+ ASSERT(!noexcept(std::declval<type_alias &>() == std::declval<type_alias &>()));
+ }
+
+ auto inequality_comparison_on_a_new__type_is_noexcept_if_the_base_type_is_noexcept_comparable() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ static_assert(noexcept(std::declval<int &>() != std::declval<int &>()), "Sanity Check");
+ ASSERT(noexcept(std::declval<type_alias &>() != std::declval<type_alias &>()));
+ }
+
+ auto inequality_comparison_on_a_new__type_is_not_noexcept_if_the_base_type_is_not_noexcept_comparable() -> void
+ {
+ struct strange_type
+ {
+ auto constexpr operator!=(strange_type const &) const noexcept(false) -> bool
+ {
+ return false;
+ }
+ };
+
+ using type_alias = nt::new_type<strange_type, struct tag>;
+ static_assert(!noexcept(std::declval<strange_type &>() != std::declval<strange_type &>()), "Sanity Check");
+ ASSERT(!noexcept(std::declval<type_alias &>() != std::declval<type_alias &>()));
+ }
+
+} // namespace equality_comparsion_noexcept_tests
+
+inline namespace equality_comparison_return_type_tests
+{
+
+ auto equality_comparsion_of_two_new__type_instances_returns_bool() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ ASSERT((std::is_same_v<bool, decltype(std::declval<type_alias &>() == std::declval<type_alias &>())>));
+ }
+
+ auto inequality_comparsion_of_two_new__type_instances_returns_bool() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ ASSERT((std::is_same_v<bool, decltype(std::declval<type_alias &>() != std::declval<type_alias &>())>));
+ }
+
+} // namespace equality_comparison_return_type_tests
+
+inline namespace base_type_equality_comparison_tests
+{
+
+ auto an_instance_of_a_new__type_compares_equal_to_an_instance_of_its_base_type_with_the_same_value() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::EqBase)>;
+ auto lhs = type_alias{42};
+ auto rhs = 42;
+ ASSERT_EQUAL(lhs, rhs);
+ }
+
+ auto an_instance_of_the_base_type_of_a_new__type_compares_equal_to_an_instance_of_the_new__type_with_the_same_value() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::EqBase)>;
+ auto lhs = 42;
+ auto rhs = type_alias{42};
+ ASSERT_EQUAL(lhs, rhs);
+ }
+
+ auto an_instance_of_a_new__type_compares_not_equal_to_an_instance_of_its_base_type_with_a_differing_value() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::EqBase)>;
+ auto lhs = type_alias{42};
+ auto rhs = 43;
+ ASSERT(lhs != rhs);
+ }
+
+ auto an_instance_of_the_base_type_of_a_new__type_compares_not_equal_to_an_instance_of_the_new__type_with_a_differing_value() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::EqBase)>;
+ auto lhs = 43;
+ auto rhs = type_alias{42};
+ ASSERT(lhs != rhs);
+ }
+
+} // namespace base_type_equality_comparison_tests
+
+auto equality_comparison_suite() -> std::pair<cute::suite, std::string>
+{
+ return {{
+ // Basic Equality Comparison Tests
+ KAWAII(two_instances_of_the_same_new__type_with_the_same_value_compare_equal),
+ KAWAII(two_instances_of_the_same_new__type_with_the_same_value_do_not_compare_not_equal),
+ KAWAII(two_instances_of_the_same_new__type_with_differing_values_do_compare_not_equal),
+ KAWAII(two_instances_of_the_same_new__type_with_differing_values_do_not_compare_equal),
+
+ // Equality Comparison noexcept Tests
+ KAWAII(equality_comparison_on_a_new__type_is_noexcept_if_the_base_type_is_noexcept_comparable),
+ KAWAII(equality_comparison_on_a_new__type_is_not_noexcept_if_the_base_type_is_not_noexcept_comparable),
+ KAWAII(inequality_comparison_on_a_new__type_is_noexcept_if_the_base_type_is_noexcept_comparable),
+ KAWAII(inequality_comparison_on_a_new__type_is_not_noexcept_if_the_base_type_is_not_noexcept_comparable),
+
+ // Equality Comparison Return Type Tests
+ KAWAII(equality_comparsion_of_two_new__type_instances_returns_bool),
+ KAWAII(inequality_comparsion_of_two_new__type_instances_returns_bool),
+
+ // Base-type Equality Comparison Tests
+ KAWAII(an_instance_of_a_new__type_compares_equal_to_an_instance_of_its_base_type_with_the_same_value),
+ KAWAII(an_instance_of_the_base_type_of_a_new__type_compares_equal_to_an_instance_of_the_new__type_with_the_same_value),
+ KAWAII(an_instance_of_a_new__type_compares_not_equal_to_an_instance_of_its_base_type_with_a_differing_value),
+ KAWAII(an_instance_of_the_base_type_of_a_new__type_compares_not_equal_to_an_instance_of_the_new__type_with_a_differing_value),
+ },
+ "Equality Comparison Tests"};
+} \ No newline at end of file
diff --git a/source/test/src/hash_suite.cpp b/source/test/src/hash_suite.cpp
new file mode 100644
index 0000000..b482414
--- /dev/null
+++ b/source/test/src/hash_suite.cpp
@@ -0,0 +1,69 @@
+#include "hash_suite.hpp"
+
+#include "kawaii.hpp"
+#include "newtype/derivable.hpp"
+#include "newtype/deriving.hpp"
+#include "newtype/impl/type_traits_extensions.hpp"
+#include "newtype/newtype.hpp"
+
+#include <cute/cute.h>
+
+#include <unordered_map>
+
+inline namespace hashable_tests
+{
+
+ auto a_new__type_that_does_not_include_hash_in_its_derivation_clause_is_not_hashable() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ ASSERT(!nt::impl::is_hashable_v<type_alias>);
+ }
+
+ auto a_new__type_that_does_include_hash_in_its_derivation_clause_is_hashable() -> void
+ {
+ static_assert(nt::impl::is_hashable_v<int>, "Sanity Check");
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Hash)>;
+ ASSERT(nt::impl::is_hashable_v<type_alias>);
+ }
+
+ auto a_new__type_that_does_include_hash_in_its_derivation_clause_but_whose_base_type_is_not_hashable_is_also_not_hashable() -> void
+ {
+ struct not_hashable
+ {
+ };
+
+ static_assert(!nt::impl::is_hashable_v<not_hashable>, "Sanity Check");
+ using type_alias = nt::new_type<not_hashable, struct tag, deriving(nt::Hash)>;
+ ASSERT(!nt::impl::is_hashable_v<type_alias>);
+ }
+
+} // namespace hashable_tests
+
+inline namespace usage_tests
+{
+
+ auto a_new__type_that_is_hashable_can_be_used_in_an_unordered__map() -> void
+ {
+ static_assert(nt::impl::is_hashable_v<int>, "Sanity Check");
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Hash)>;
+
+ auto map = std::unordered_map<type_alias, int>{};
+ map[type_alias{42}] = 43;
+ ASSERT_EQUAL(43, map[type_alias{42}]);
+ }
+
+} // namespace usage_tests
+
+auto hash_suite() -> std::pair<cute::suite, std::string>
+{
+ return {{
+ // Hashable Tests
+ KAWAII(a_new__type_that_does_not_include_hash_in_its_derivation_clause_is_not_hashable),
+ KAWAII(a_new__type_that_does_include_hash_in_its_derivation_clause_is_hashable),
+ KAWAII(a_new__type_that_does_include_hash_in_its_derivation_clause_but_whose_base_type_is_not_hashable_is_also_not_hashable),
+
+ // Usage Tests
+ KAWAII(a_new__type_that_is_hashable_can_be_used_in_an_unordered__map),
+ },
+ "std::hash Support Tests"};
+} \ No newline at end of file
diff --git a/source/test/src/io_operators_suite.cpp b/source/test/src/io_operators_suite.cpp
new file mode 100644
index 0000000..4e146a7
--- /dev/null
+++ b/source/test/src/io_operators_suite.cpp
@@ -0,0 +1,121 @@
+#include "io_operators_suite.hpp"
+
+#include "kawaii.hpp"
+#include "newtype/derivable.hpp"
+#include "newtype/deriving.hpp"
+#include "newtype/newtype.hpp"
+
+#include <cute/cute.h>
+
+#include <iosfwd>
+#include <sstream>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+inline namespace traits_extensions
+{
+
+ template<typename T, typename = void>
+ struct has_stream_input : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_stream_input<T, std::void_t<decltype(std::declval<std::istream &>() >> std::declval<T &>())>> : std::true_type
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_stream_input_v = has_stream_input<T>::value;
+
+ template<typename T, typename = void>
+ struct has_stream_output : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_stream_output<T, std::void_t<decltype(std::declval<std::ostream &>() << std::declval<T &>())>> : std::true_type
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_stream_output_v = has_stream_output<T>::value;
+
+} // namespace traits_extensions
+
+inline namespace stream_input_operator_tests
+{
+
+ auto a_new__type_has_the_stream_input_operator_if_the_derivation_clause_contains_read() -> void
+ {
+ using type_alias = nt::new_type<int, struct input_op_test_tag_1, deriving(nt::Read)>;
+ ASSERT(has_stream_input_v<type_alias>);
+ }
+
+ auto a_new__type_does_not_have_the_stream_input_operator_if_the_derivation_clause_does_not_contain_read() -> void
+ {
+ using type_alias = nt::new_type<int, struct input_op_test_tag_2>;
+ ASSERT(!has_stream_input_v<type_alias>);
+ }
+
+ auto a_instance_of_a_new__type_can_be_read_from_an_istream_if_the_base_type_can_be_read_from_an_istream() -> void
+ {
+ static_assert(has_stream_input_v<int>, "Sanity Check");
+ using type_alias = nt::new_type<int, struct input_op_test_tag_3, deriving(nt::Read)>;
+
+ auto obj = type_alias{};
+ auto input = std::istringstream{"42"};
+
+ input >> obj;
+
+ ASSERT_EQUAL(42, obj.decay());
+ }
+
+} // namespace stream_input_operator_tests
+
+inline namespace stream_output_operator_tests
+{
+
+ auto a_new__type_has_the_stream_output_operator_if_the_derivation_clause_contains_show() -> void
+ {
+ using type_alias = nt::new_type<int, struct output_op_test_tag_4, deriving(nt::Show)>;
+ ASSERT(has_stream_output_v<type_alias>);
+ }
+
+ auto a_new__type_does_not_have_the_stream_output_operator_if_the_derivation_clause_does_not_contain_show() -> void
+ {
+ using type_alias = nt::new_type<int, struct output_op_test_tag_5>;
+ ASSERT(!has_stream_output_v<type_alias>);
+ }
+
+ auto a_instance_of_a_new__type_can_be_written_to_an_ostream_if_the_base_type_can_be_written_to_an_ostream() -> void
+ {
+ static_assert(has_stream_output_v<int>, "Sanity Check");
+ using type_alias = nt::new_type<int, struct output_op_test_tag_6, deriving(nt::Show)>;
+
+ auto obj = type_alias{42};
+ auto output = std::ostringstream{};
+
+ output << obj;
+
+ ASSERT_EQUAL("42", output.str());
+ }
+
+} // namespace stream_output_operator_tests
+
+auto io_operators_suite() -> std::pair<cute::suite, std::string>
+{
+ return {{
+ // Stream Input Operator Tests
+ KAWAII(a_new__type_has_the_stream_input_operator_if_the_derivation_clause_contains_read),
+ KAWAII(a_new__type_does_not_have_the_stream_input_operator_if_the_derivation_clause_does_not_contain_read),
+ KAWAII(a_instance_of_a_new__type_can_be_read_from_an_istream_if_the_base_type_can_be_read_from_an_istream),
+
+ // Stream Ouput Operator Tests
+ KAWAII(a_new__type_has_the_stream_output_operator_if_the_derivation_clause_contains_show),
+ KAWAII(a_new__type_does_not_have_the_stream_output_operator_if_the_derivation_clause_does_not_contain_show),
+ KAWAII(a_instance_of_a_new__type_can_be_written_to_an_ostream_if_the_base_type_can_be_written_to_an_ostream),
+ },
+ "I/O Operators Tests"};
+} \ No newline at end of file
diff --git a/source/test/src/iterable_suite.cpp b/source/test/src/iterable_suite.cpp
new file mode 100644
index 0000000..add4696
--- /dev/null
+++ b/source/test/src/iterable_suite.cpp
@@ -0,0 +1,719 @@
+#include "iterable_suite.hpp"
+
+#include "kawaii.hpp"
+#include "newtype/derivable.hpp"
+#include "newtype/deriving.hpp"
+#include "newtype/impl/type_traits_extensions.hpp"
+#include "newtype/newtype.hpp"
+
+#include <cute/cute.h>
+
+#include <algorithm>
+#include <array>
+#include <iterator>
+#include <numeric>
+
+namespace
+{
+
+ struct with_member
+ {
+ using iterator = char *;
+ using const_iterator = char const *;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ auto begin() -> iterator;
+ auto begin() const -> const_iterator;
+ auto cbegin() const -> const_iterator;
+ auto rbegin() -> reverse_iterator;
+ auto rbegin() const -> const_reverse_iterator;
+ auto crbegin() const -> const_reverse_iterator;
+
+ auto end() -> iterator;
+ auto end() const -> const_iterator;
+ auto cend() const -> const_iterator;
+ auto rend() -> reverse_iterator;
+ auto rend() const -> const_reverse_iterator;
+ auto crend() const -> const_reverse_iterator;
+ };
+
+ struct with_free
+ {
+ using iterator = char *;
+ using const_iterator = char const *;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ };
+
+ auto begin(with_free &) -> with_free::iterator
+ {
+ return {};
+ }
+
+ auto begin(with_free const &) -> with_free::const_iterator
+ {
+ return {};
+ }
+
+ auto cbegin(with_free const &) -> with_free::const_iterator
+ {
+ return {};
+ }
+
+ auto rbegin(with_free &) -> with_free::reverse_iterator
+ {
+ return {};
+ }
+
+ auto rbegin(with_free const &) -> with_free::const_reverse_iterator
+ {
+ return {};
+ }
+
+ auto crbegin(with_free const &) -> with_free::const_reverse_iterator
+ {
+ return {};
+ }
+
+ auto end(with_free &) -> with_free::iterator
+ {
+ return {};
+ }
+
+ auto end(with_free const &) -> with_free::const_iterator
+ {
+ return {};
+ }
+
+ auto cend(with_free const &) -> with_free::const_iterator
+ {
+ return {};
+ }
+
+ auto rend(with_free &) -> with_free::reverse_iterator
+ {
+ return {};
+ }
+
+ auto rend(with_free const &) -> with_free::const_reverse_iterator
+ {
+ return {};
+ }
+
+ auto crend(with_free const &) -> with_free::const_reverse_iterator
+ {
+ return {};
+ }
+
+} // namespace
+
+inline namespace combined_enablement_tests
+{
+
+ auto a_new__type_not_deriving_iterable_has_no_begin() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ ASSERT(!(nt::impl::has_begin_v<type_alias>));
+ }
+
+ auto a_new__type_not_deriving_iterable_has_no_constant_begin() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ ASSERT(!(nt::impl::has_begin_v<type_alias const>));
+ }
+
+ auto a_new__type_not_deriving_iterable_has_no_cbegin() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ ASSERT(!(nt::impl::has_cbegin_v<type_alias>));
+ }
+
+ auto a_new__type_not_deriving_iterable_has_no_rbegin() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ ASSERT(!(nt::impl::has_rbegin_v<type_alias>));
+ }
+
+ auto a_new__type_not_deriving_iterable_has_no_constant_rbegin() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ ASSERT(!(nt::impl::has_rbegin_v<type_alias const>));
+ }
+
+ auto a_new__type_not_deriving_iterable_has_no_crbegin() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ ASSERT(!(nt::impl::has_crbegin_v<type_alias>));
+ }
+
+ auto a_new__type_not_deriving_iterable_has_no_end() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ ASSERT(!(nt::impl::has_end_v<type_alias>));
+ }
+
+ auto a_new__type_not_deriving_iterable_has_no_constant_end() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ ASSERT(!(nt::impl::has_end_v<type_alias const>));
+ }
+
+ auto a_new__type_not_deriving_iterable_has_no_cend() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ ASSERT(!(nt::impl::has_cend_v<type_alias>));
+ }
+
+ auto a_new__type_not_deriving_iterable_has_no_rend() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ ASSERT(!(nt::impl::has_rend_v<type_alias>));
+ }
+
+ auto a_new__type_not_deriving_iterable_has_no_constant_rend() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ ASSERT(!(nt::impl::has_rend_v<type_alias const>));
+ }
+
+ auto a_new__type_not_deriving_iterable_has_no_crend() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ ASSERT(!(nt::impl::has_crend_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_begin() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_begin_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_constant_begin() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_begin_v<type_alias const>));
+ }
+
+ auto a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_cbegin() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_cbegin_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_rbegin() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_rbegin_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_constant_rbegin() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_rbegin_v<type_alias const>));
+ }
+
+ auto a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_crbegin() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_crbegin_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_end() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_end_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_constant_end() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_end_v<type_alias const>));
+ }
+
+ auto a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_cend() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_cend_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_rend() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_rend_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_constant_rend() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_rend_v<type_alias const>));
+ }
+
+ auto a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_crend() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_crend_v<type_alias>));
+ }
+
+} // namespace combined_enablement_tests
+
+inline namespace member_enablement_tests
+{
+
+ auto a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_begin() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_member_begin_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_constant_begin() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_member_begin_v<type_alias const>));
+ }
+
+ auto a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_cbegin() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_member_cbegin_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_rbegin() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_member_rbegin_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_constant_rbegin() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_member_rbegin_v<type_alias const>));
+ }
+
+ auto a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_crbegin() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_member_crbegin_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_end() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_member_end_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_constant_end() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_member_end_v<type_alias const>));
+ }
+
+ auto a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_cend() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_member_cend_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_rend() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_member_rend_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_constant_rend() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_member_rend_v<type_alias const>));
+ }
+
+ auto a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_crend() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_member_crend_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_begin() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_member_begin_v<type_alias>);
+ }
+
+ auto a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_constant_begin() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_member_begin_v<type_alias const>);
+ }
+
+ auto a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_cbegin() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_member_cbegin_v<type_alias>);
+ }
+
+ auto a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_rbegin() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_member_rbegin_v<type_alias>);
+ }
+
+ auto a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_constant_rbegin() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_member_rbegin_v<type_alias const>);
+ }
+
+ auto a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_crbegin() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_member_crbegin_v<type_alias>);
+ }
+
+ auto a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_end() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_member_end_v<type_alias>);
+ }
+
+ auto a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_constant_end() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_member_end_v<type_alias const>);
+ }
+
+ auto a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_cend() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_member_cend_v<type_alias>);
+ }
+
+ auto a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_rend() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_member_rend_v<type_alias>);
+ }
+
+ auto a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_constant_rend() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_member_rend_v<type_alias const>);
+ }
+
+ auto a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_crend() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_member_crend_v<type_alias>);
+ }
+
+} // namespace member_enablement_tests
+
+inline namespace free_enablement_tests
+{
+ auto a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_begin() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_free_begin_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_constant_begin() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_free_begin_v<type_alias const>));
+ }
+
+ auto a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_cbegin() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_free_cbegin_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_rbegin() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_free_rbegin_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_constant_rbegin() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_free_rbegin_v<type_alias const>));
+ }
+
+ auto a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_crbegin() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_free_crbegin_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_end() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_free_end_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_constant_end() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_free_end_v<type_alias const>));
+ }
+
+ auto a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_cend() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_free_cend_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_rend() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_free_rend_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_constant_rend() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_free_rend_v<type_alias const>));
+ }
+
+ auto a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_crend() -> void
+ {
+ using type_alias = nt::new_type<with_member, struct tag, deriving(nt::Iterable)>;
+ ASSERT(!(nt::impl::has_free_crend_v<type_alias>));
+ }
+
+ auto a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_begin() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_free_begin_v<type_alias>);
+ }
+
+ auto a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_constant_begin() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_free_begin_v<type_alias const>);
+ }
+
+ auto a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_cbegin() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_free_cbegin_v<type_alias>);
+ }
+
+ auto a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_rbegin() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_free_rbegin_v<type_alias>);
+ }
+
+ auto a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_constant_rbegin() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_free_rbegin_v<type_alias const>);
+ }
+
+ auto a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_crbegin() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_free_crbegin_v<type_alias>);
+ }
+
+ auto a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_end() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_free_end_v<type_alias>);
+ }
+
+ auto a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_constant_end() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_free_end_v<type_alias const>);
+ }
+
+ auto a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_cend() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_free_cend_v<type_alias>);
+ }
+
+ auto a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_rend() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_free_rend_v<type_alias>);
+ }
+
+ auto a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_constant_rend() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_free_rend_v<type_alias const>);
+ }
+
+ auto a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_crend() -> void
+ {
+ using type_alias = nt::new_type<with_free, struct tag, deriving(nt::Iterable)>;
+ ASSERT(nt::impl::has_free_crend_v<type_alias>);
+ }
+
+} // namespace free_enablement_tests
+
+inline namespace semantic_tests
+{
+
+ auto a_non_const_object_of_iterable_new__type_can_be_used_in_value_range_for() -> void
+ {
+ using type_alias = nt::new_type<std::array<int, 3>, struct tag, deriving(nt::Iterable)>;
+ auto object = type_alias{{1, 2, 3}};
+ for (auto e : object)
+ {
+ (void)e;
+ }
+ ASSERT(true);
+ }
+
+ auto a_const_object_of_iterable_new__type_can_be_used_in_value_range_for() -> void
+ {
+ using type_alias = nt::new_type<std::array<int, 3>, struct tag, deriving(nt::Iterable)>;
+ auto const object = type_alias{{1, 2, 3}};
+ for (auto e : object)
+ {
+ (void)e;
+ }
+ ASSERT(true);
+ }
+
+ auto a_non_const_object_of_iterable_new__type_can_be_used_in_reference_range_for() -> void
+ {
+ using type_alias = nt::new_type<std::array<int, 3>, struct tag, deriving(nt::Iterable)>;
+ auto object = type_alias{{1, 2, 3}};
+ for (auto & e : object)
+ {
+ (void)e;
+ }
+ ASSERT(true);
+ }
+
+ auto a_const_object_of_iterable_new__type_can_be_used_in_const_reference_range_for() -> void
+ {
+ using type_alias = nt::new_type<std::array<int, 3>, struct tag, deriving(nt::Iterable)>;
+ auto const object = type_alias{{1, 2, 3}};
+ for (auto const & e : object)
+ {
+ (void)e;
+ }
+ ASSERT(true);
+ }
+
+ auto applying_accumulate_to_an_interable_new__type_yields_the_same_result_as_when_applied_to_the_base_type() -> void
+ {
+ using type_alias = nt::new_type<std::array<int, 3>, struct tag, deriving(nt::Iterable)>;
+ auto const nt_object = type_alias{{1, 2, 3}};
+ auto const bt_object = type_alias::base_type{1, 2, 3};
+
+ auto nt_result = std::accumulate(std::cbegin(nt_object), std::cend(nt_object), 0);
+ auto bt_result = std::accumulate(std::cbegin(bt_object), std::cend(bt_object), 0);
+
+ ASSERT_EQUAL(bt_result, nt_result);
+ }
+
+ auto iterating_over_an_iterable_new__type_yields_the_elements_in_the_same_order_as_when_iterating_the_base_type() -> void
+ {
+ using std::cbegin, std::cend, std::equal;
+ using type_alias = nt::new_type<std::array<int, 3>, struct tag, deriving(nt::Iterable)>;
+ auto const nt_object = type_alias{{1, 2, 3}};
+ auto const bt_object = type_alias::base_type{1, 2, 3};
+
+ ASSERT(equal(cbegin(nt_object), cend(nt_object), cbegin(bt_object), cend(bt_object)));
+ }
+
+} // namespace semantic_tests
+
+auto iterable_suite() -> std::pair<cute::suite, std::string>
+{
+ return {{
+ /// Combined Enablement Tests
+ KAWAII(a_new__type_not_deriving_iterable_has_no_begin),
+ KAWAII(a_new__type_not_deriving_iterable_has_no_constant_begin),
+ KAWAII(a_new__type_not_deriving_iterable_has_no_cbegin),
+ KAWAII(a_new__type_not_deriving_iterable_has_no_rbegin),
+ KAWAII(a_new__type_not_deriving_iterable_has_no_constant_rbegin),
+ KAWAII(a_new__type_not_deriving_iterable_has_no_crbegin),
+ KAWAII(a_new__type_not_deriving_iterable_has_no_end),
+ KAWAII(a_new__type_not_deriving_iterable_has_no_constant_end),
+ KAWAII(a_new__type_not_deriving_iterable_has_no_cend),
+ KAWAII(a_new__type_not_deriving_iterable_has_no_rend),
+ KAWAII(a_new__type_not_deriving_iterable_has_no_constant_rend),
+ KAWAII(a_new__type_not_deriving_iterable_has_no_crend),
+ KAWAII(a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_begin),
+ KAWAII(a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_constant_begin),
+ KAWAII(a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_cbegin),
+ KAWAII(a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_rbegin),
+ KAWAII(a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_constant_rbegin),
+ KAWAII(a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_crbegin),
+ KAWAII(a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_end),
+ KAWAII(a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_constant_end),
+ KAWAII(a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_cend),
+ KAWAII(a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_rend),
+ KAWAII(a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_constant_rend),
+ KAWAII(a_new__type_on_a_non_iterable_type_deriving_iterable_has_no_crend),
+
+ /// Member Enablement Tests
+ KAWAII(a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_begin),
+ KAWAII(a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_constant_begin),
+ KAWAII(a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_cbegin),
+ KAWAII(a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_rbegin),
+ KAWAII(a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_constant_rbegin),
+ KAWAII(a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_crbegin),
+ KAWAII(a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_end),
+ KAWAII(a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_constant_end),
+ KAWAII(a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_cend),
+ KAWAII(a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_rend),
+ KAWAII(a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_constant_rend),
+ KAWAII(a_new__type_on_a_non_member_iterable_type_deriving_iterable_has_no_member_crend),
+ KAWAII(a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_begin),
+ KAWAII(a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_constant_begin),
+ KAWAII(a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_cbegin),
+ KAWAII(a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_rbegin),
+ KAWAII(a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_constant_rbegin),
+ KAWAII(a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_crbegin),
+ KAWAII(a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_end),
+ KAWAII(a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_constant_end),
+ KAWAII(a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_cend),
+ KAWAII(a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_rend),
+ KAWAII(a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_constant_rend),
+ KAWAII(a_new__type_on_a_member_iterable_type_deriving_iterable_has_member_crend),
+
+ /// Free Enablement Tests
+ KAWAII(a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_begin),
+ KAWAII(a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_constant_begin),
+ KAWAII(a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_cbegin),
+ KAWAII(a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_rbegin),
+ KAWAII(a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_constant_rbegin),
+ KAWAII(a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_crbegin),
+ KAWAII(a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_end),
+ KAWAII(a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_constant_end),
+ KAWAII(a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_cend),
+ KAWAII(a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_rend),
+ KAWAII(a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_constant_rend),
+ KAWAII(a_new__type_on_a_non_free_iterable_type_deriving_iterable_has_no_free_crend),
+ KAWAII(a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_begin),
+ KAWAII(a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_constant_begin),
+ KAWAII(a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_cbegin),
+ KAWAII(a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_rbegin),
+ KAWAII(a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_constant_rbegin),
+ KAWAII(a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_crbegin),
+ KAWAII(a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_end),
+ KAWAII(a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_constant_end),
+ KAWAII(a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_cend),
+ KAWAII(a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_rend),
+ KAWAII(a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_constant_rend),
+ KAWAII(a_new__type_on_a_free_iterable_type_deriving_iterable_has_free_crend),
+
+ /// Semantic Tests
+ KAWAII(a_non_const_object_of_iterable_new__type_can_be_used_in_value_range_for),
+ KAWAII(a_const_object_of_iterable_new__type_can_be_used_in_value_range_for),
+ KAWAII(a_non_const_object_of_iterable_new__type_can_be_used_in_reference_range_for),
+ KAWAII(a_const_object_of_iterable_new__type_can_be_used_in_const_reference_range_for),
+ KAWAII(applying_accumulate_to_an_interable_new__type_yields_the_same_result_as_when_applied_to_the_base_type),
+ KAWAII(iterating_over_an_iterable_new__type_yields_the_elements_in_the_same_order_as_when_iterating_the_base_type),
+ },
+
+ "Iterable Tests"};
+} \ No newline at end of file
diff --git a/source/test/src/new_type_constructor_suite.cpp b/source/test/src/new_type_constructor_suite.cpp
new file mode 100644
index 0000000..fc3e9f6
--- /dev/null
+++ b/source/test/src/new_type_constructor_suite.cpp
@@ -0,0 +1,91 @@
+#include "new_type_constructor_suite.hpp"
+
+#include "kawaii.hpp"
+#include "newtype/derivable.hpp"
+#include "newtype/newtype.hpp"
+
+#include <cute/cute.h>
+
+#include <type_traits>
+
+inline namespace constructor_tests
+{
+
+ struct not_default_constructible
+ {
+ not_default_constructible() = delete;
+ };
+
+ auto a_new__type_based_on_a_type_that_is_default_constructible_is_default_constructible_too() -> void
+ {
+ using nt_float = nt::new_type<float, struct nt_float_tag>;
+
+ ASSERT(std::is_default_constructible_v<nt_float>);
+ }
+
+ auto a_new__type_based_on_a_type_that_is_not_default_constructible_is_not_default_constructible_too() -> void
+ {
+ using nt_not_default_constructible = nt::new_type<not_default_constructible, struct nt_not_default_constructible_tag>;
+
+ ASSERT(!std::is_default_constructible_v<nt_not_default_constructible>);
+ }
+
+ template<typename OldType>
+ auto a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type() -> void
+ {
+ using nt_old = nt::new_type<OldType, struct nt_old_tag>;
+
+ ASSERT((std::is_constructible_v<nt_old, OldType>));
+ }
+
+ auto a_new__type_instance_can_be_copy_constructed_if_the_base_type_can_be_copy_constructed() -> void
+ {
+ using type_alias = nt::new_type<int, struct tag_type>;
+ ASSERT((std::is_copy_constructible_v<type_alias>));
+ }
+
+ auto a_new__type_instance_can_not_be_copy_constructed_if_the_base_type_can_not_be_copy_constructed() -> void
+ {
+ struct not_copy_constructible
+ {
+ not_copy_constructible() = default;
+ not_copy_constructible(not_copy_constructible const &) = delete;
+ not_copy_constructible(not_copy_constructible &&) = default;
+ auto operator=(not_copy_constructible const &) -> not_copy_constructible & = default;
+ auto operator=(not_copy_constructible &&) -> not_copy_constructible & = default;
+ };
+
+ using type_alias = nt::new_type<not_copy_constructible, struct tag_type>;
+ ASSERT(!(std::is_copy_constructible_v<type_alias>));
+ }
+
+} // namespace constructor_tests
+
+auto new_type_constructor_suite() -> std::pair<cute::suite, std::string>
+{
+ return {{
+ KAWAII(a_new__type_based_on_a_type_that_is_default_constructible_is_default_constructible_too),
+ KAWAII(a_new__type_based_on_a_type_that_is_not_default_constructible_is_not_default_constructible_too),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<bool>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<char>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<unsigned char>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<signed char>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<wchar_t>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<char16_t>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<char32_t>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<short>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<unsigned short>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<int>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<unsigned int>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<long>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<unsigned long>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<long long>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<unsigned long long>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<float>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<double>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<long double>),
+ KAWAII(a_new__type_instance_can_be_copy_constructed_if_the_base_type_can_be_copy_constructed),
+ KAWAII(a_new__type_instance_can_not_be_copy_constructed_if_the_base_type_can_not_be_copy_constructed),
+ },
+ "new_type Constructor Tests"};
+} \ No newline at end of file
diff --git a/source/test/src/relational_operators_suite.cpp b/source/test/src/relational_operators_suite.cpp
new file mode 100644
index 0000000..64fcba0
--- /dev/null
+++ b/source/test/src/relational_operators_suite.cpp
@@ -0,0 +1,321 @@
+#include "relational_operators_suite.hpp"
+
+#include "kawaii.hpp"
+#include "newtype/derivable.hpp"
+#include "newtype/deriving.hpp"
+#include "newtype/newtype.hpp"
+
+#include <cute/cute.h>
+
+#include <string>
+#include <type_traits>
+#include <utility>
+
+inline namespace traits_extensions
+{
+
+ template<typename T, typename = void>
+ struct has_less_than : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_less_than<T, std::void_t<decltype(std::declval<T const &>() < std::declval<T const &>())>> : std::true_type
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_less_than_v = has_less_than<T>::value;
+
+ template<typename T, typename = void>
+ struct has_greater_than : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_greater_than<T, std::void_t<decltype(std::declval<T const &>() > std::declval<T const &>())>> : std::true_type
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_greater_than_v = has_greater_than<T>::value;
+
+ template<typename T, typename = void>
+ struct has_less_than_equal_to : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_less_than_equal_to<T, std::void_t<decltype(std::declval<T const &>() <= std::declval<T const &>())>> : std::true_type
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_less_than_equal_to_v = has_less_than_equal_to<T>::value;
+
+ template<typename T, typename = void>
+ struct has_greater_than_equal_to : std::false_type
+ {
+ };
+
+ template<typename T>
+ struct has_greater_than_equal_to<T, std::void_t<decltype(std::declval<T const &>() >= std::declval<T const &>())>> : std::true_type
+ {
+ };
+
+ template<typename T>
+ auto constexpr has_greater_than_equal_to_v = has_greater_than_equal_to<T>::value;
+
+} // namespace traits_extensions
+
+inline namespace relational_operator_presence_tests
+{
+
+ auto an_instance_of_new__type_has_less_than_if_its_derivation_clause_contains_relational() -> void
+ {
+ using type_alias = nt::new_type<int, struct relop_presensence_test_tag_1, deriving(nt::Relational)>;
+ ASSERT(has_less_than_v<type_alias>);
+ }
+
+ auto an_instance_of_new__type_does_not_have_less_than_if_its_derivation_clause_does_not_contain_relational() -> void
+ {
+ using type_alias = nt::new_type<int, struct relop_presensence_test_tag_2>;
+ ASSERT(!has_less_than_v<type_alias>);
+ }
+
+ auto an_instance_of_new__type_has_greater_than_if_its_derivation_clause_contains_relational() -> void
+ {
+ using type_alias = nt::new_type<int, struct relop_presensence_test_tag_3, deriving(nt::Relational)>;
+ ASSERT(has_greater_than_v<type_alias>);
+ }
+
+ auto an_instance_of_new__type_does_not_have_greater_than_if_its_derivation_clause_does_not_contain_relational() -> void
+ {
+ using type_alias = nt::new_type<int, struct relop_presensence_test_tag_4>;
+ ASSERT(!has_greater_than_v<type_alias>);
+ }
+
+ auto an_instance_of_new__type_has_less_than_or_equal_to_if_its_derivation_clause_contains_relational() -> void
+ {
+ using type_alias = nt::new_type<int, struct relop_presensence_test_tag_5, deriving(nt::Relational)>;
+ ASSERT(has_less_than_equal_to_v<type_alias>);
+ }
+
+ auto an_instance_of_new__type_does_not_have_less_than_or_equal_to_if_its_derivation_clause_does_not_contain_relational() -> void
+ {
+ using type_alias = nt::new_type<int, struct relop_presensence_test_tag_6>;
+ ASSERT(!has_less_than_equal_to_v<type_alias>);
+ }
+
+ auto an_instance_of_new__type_has_greater_than_or_equal_to_if_its_derivation_clause_contains_relational() -> void
+ {
+ using type_alias = nt::new_type<int, struct relop_presensence_test_tag_7, deriving(nt::Relational)>;
+ ASSERT(has_greater_than_equal_to_v<type_alias>);
+ }
+
+ auto an_instance_of_new__type_does_not_have_greater_than_or_equal_to_if_its_derivation_clause_does_not_contain_relational() -> void
+ {
+ using type_alias = nt::new_type<int, struct relop_presensence_test_tag_8>;
+ ASSERT(!has_greater_than_equal_to_v<type_alias>);
+ }
+
+} // namespace relational_operator_presence_tests
+
+inline namespace less_than_tests
+{
+
+ auto two_instances_of_the_same_new__type_compare_less_than_if_both_contained_values_would_compare_less_than() -> void
+ {
+ using type_alias = nt::new_type<int, struct less_than_test_tag_9, deriving(nt::Relational)>;
+ auto constexpr lhs{21};
+ auto constexpr rhs{42};
+ static_assert(lhs < rhs, "Sanity Check");
+ ASSERT_LESS(type_alias{lhs}, type_alias{rhs});
+ }
+
+ auto two_instances_of_the_same_new__type_do_not_compare_less_than_if_both_contained_values_would_not_compare_less_than() -> void
+ {
+ using type_alias = nt::new_type<int, struct less_than_test_tag_10, deriving(nt::Relational)>;
+ auto constexpr lhs{44};
+ auto constexpr rhs{42};
+ static_assert(!(lhs < rhs), "Sanity Check");
+ ASSERT(!(type_alias{lhs} < type_alias{rhs}));
+ }
+
+ auto two_instances_of_the_same_new__type_compare_less_than_or_equal_if_both_contained_values_would_compare_less_than_or_equal() -> void
+ {
+ using type_alias = nt::new_type<int, struct less_than_test_tag_11, deriving(nt::Relational)>;
+ auto constexpr lhs{33};
+ auto constexpr rhs{42};
+ static_assert(lhs <= rhs, "Sanity Check");
+ ASSERT_LESS_EQUAL(type_alias{lhs}, type_alias{rhs});
+ }
+
+ auto two_instances_of_the_same_new__type_do_not_compare_less_than_or_equal_if_both_contained_values_would_not_compare_less_than_or_equal()
+ -> void
+ {
+ using type_alias = nt::new_type<int, struct less_than_test_tag_12, deriving(nt::Relational)>;
+ auto constexpr lhs{87};
+ auto constexpr rhs{42};
+ static_assert(!(lhs < rhs), "Sanity Check");
+ ASSERT(!(type_alias{lhs} < type_alias{rhs}));
+ }
+
+} // namespace less_than_tests
+
+inline namespace greater_than_tests
+{
+
+ auto two_instances_of_the_same_new__type_compare_greater_than_if_both_contained_values_would_compare_greater_than() -> void
+ {
+ using type_alias = nt::new_type<int, struct greater_than_test_tag_13, deriving(nt::Relational)>;
+ auto constexpr lhs{42};
+ auto constexpr rhs{21};
+ static_assert(lhs > rhs, "Sanity Check");
+ ASSERT_GREATER(type_alias{lhs}, type_alias{rhs});
+ }
+
+ auto two_instances_of_the_same_new__type_do_not_compare_greater_than_if_both_contained_values_would_not_compare_greater_than() -> void
+ {
+ using type_alias = nt::new_type<int, struct greater_than_test_tag_14, deriving(nt::Relational)>;
+ auto constexpr lhs{42};
+ auto constexpr rhs{44};
+ static_assert(!(lhs > rhs), "Sanity Check");
+ ASSERT(!(type_alias{lhs} > type_alias{rhs}));
+ }
+
+ auto two_instances_of_the_same_new__type_compare_greater_than_or_equal_if_both_contained_values_would_compare_greater_than_or_equal() -> void
+ {
+ using type_alias = nt::new_type<int, struct greater_than_test_tag_15, deriving(nt::Relational)>;
+ auto constexpr lhs{42};
+ auto constexpr rhs{33};
+ static_assert(lhs >= rhs, "Sanity Check");
+ ASSERT_GREATER_EQUAL(type_alias{lhs}, type_alias{rhs});
+ }
+
+ auto
+ two_instances_of_the_same_new__type_do_not_compare_greater_than_or_equal_if_both_contained_values_would_not_compare_greater_than_or_equal()
+ -> void
+ {
+ using type_alias = nt::new_type<int, struct greater_than_test_tag_16, deriving(nt::Relational)>;
+ auto constexpr lhs{42};
+ auto constexpr rhs{87};
+ static_assert(!(lhs > rhs), "Sanity Check");
+ ASSERT(!(type_alias{lhs} > type_alias{rhs}));
+ }
+
+} // namespace greater_than_tests
+
+inline namespace relational_operator_noexcept_tests
+{
+
+ struct strange_type
+ {
+ auto constexpr operator<(strange_type const & other) const noexcept(false) -> bool;
+ auto constexpr operator>(strange_type const & other) const noexcept(false) -> bool;
+ auto constexpr operator<=(strange_type const & other) const noexcept(false) -> bool;
+ auto constexpr operator>=(strange_type const & other) const noexcept(false) -> bool;
+ };
+
+ auto less_than_comparison_of_two_intances_of_the_same_new__type_is_noexcept_if_comparison_of_the_base_type_is_noexcept() -> void
+ {
+ using type_alias = nt::new_type<int, struct greater_than_test_tag_17, deriving(nt::Relational)>;
+ static_assert(noexcept(std::declval<int const &>() < std::declval<int const &>()), "Sanity Check");
+ ASSERT(noexcept(std::declval<type_alias const &>() < std::declval<type_alias const &>()));
+ }
+
+ auto less_than_comparison_of_two_intances_of_the_same_new__type_is_not_noexcept_if_comparison_of_the_base_type_is_not_noexcept() -> void
+ {
+ using type_alias = nt::new_type<strange_type, struct greater_than_test_tag_18, deriving(nt::Relational)>;
+ static_assert(!noexcept(std::declval<strange_type const &>() < std::declval<strange_type const &>()), "Sanity Check");
+ ASSERT(!noexcept(std::declval<type_alias const &>() < std::declval<type_alias const &>()));
+ }
+
+ auto less_than_equal_to_comparison_of_two_intances_of_the_same_new__type_is_noexcept_if_comparison_of_the_base_type_is_noexcept() -> void
+ {
+ using type_alias = nt::new_type<int, struct greater_than_test_tag_19, deriving(nt::Relational)>;
+ static_assert(noexcept(std::declval<int const &>() <= std::declval<int const &>()), "Sanity Check");
+ ASSERT(noexcept(std::declval<type_alias const &>() <= std::declval<type_alias const &>()));
+ }
+
+ auto less_than_equal_to_comparison_of_two_intances_of_the_same_new__type_is_not_noexcept_if_comparison_of_the_base_type_is_not_noexcept()
+ -> void
+ {
+ using type_alias = nt::new_type<strange_type, struct greater_than_test_tag_20, deriving(nt::Relational)>;
+ static_assert(!noexcept(std::declval<strange_type const &>() <= std::declval<strange_type const &>()), "Sanity Check");
+ ASSERT(!noexcept(std::declval<type_alias const &>() <= std::declval<type_alias const &>()));
+ }
+
+ auto greater_than_comparison_of_two_intances_of_the_same_new__type_is_noexcept_if_comparison_of_the_base_type_is_noexcept() -> void
+ {
+ using type_alias = nt::new_type<int, struct greater_than_test_tag_21, deriving(nt::Relational)>;
+ static_assert(noexcept(std::declval<int const &>() > std::declval<int const &>()), "Sanity Check");
+ ASSERT(noexcept(std::declval<type_alias const &>() > std::declval<type_alias const &>()));
+ }
+
+ auto greater_than_comparison_of_two_intances_of_the_same_new__type_is_not_noexcept_if_comparison_of_the_base_type_is_not_noexcept() -> void
+ {
+ using type_alias = nt::new_type<strange_type, struct greater_than_test_tag_22, deriving(nt::Relational)>;
+ static_assert(!noexcept(std::declval<strange_type const &>() > std::declval<strange_type const &>()), "Sanity Check");
+ ASSERT(!noexcept(std::declval<type_alias const &>() > std::declval<type_alias const &>()));
+ }
+
+ auto greater_than_equal_to_comparison_of_two_intances_of_the_same_new__type_is_noexcept_if_comparison_of_the_base_type_is_noexcept() -> void
+ {
+ using type_alias = nt::new_type<int, struct greater_than_test_tag_23, deriving(nt::Relational)>;
+ static_assert(noexcept(std::declval<int const &>() >= std::declval<int const &>()), "Sanity Check");
+ ASSERT(noexcept(std::declval<type_alias const &>() >= std::declval<type_alias const &>()));
+ }
+
+ auto greater_than_equal_to_comparison_of_two_intances_of_the_same_new__type_is_not_noexcept_if_comparison_of_the_base_type_is_not_noexcept()
+ -> void
+ {
+ using type_alias = nt::new_type<strange_type, struct greater_than_test_tag_24, deriving(nt::Relational)>;
+ static_assert(!noexcept(std::declval<strange_type const &>() >= std::declval<strange_type const &>()), "Sanity Check");
+ ASSERT(!noexcept(std::declval<type_alias const &>() >= std::declval<type_alias const &>()));
+ }
+
+} // namespace relational_operator_noexcept_tests
+
+auto relational_operators_suite() -> std::pair<cute::suite, std::string>
+{
+ return {
+ {
+ // clang-format off
+
+ // Relational Operator Presensence Tests
+ KAWAII(an_instance_of_new__type_has_less_than_if_its_derivation_clause_contains_relational),
+ KAWAII(an_instance_of_new__type_does_not_have_less_than_if_its_derivation_clause_does_not_contain_relational),
+ KAWAII(an_instance_of_new__type_has_greater_than_if_its_derivation_clause_contains_relational),
+ KAWAII(an_instance_of_new__type_does_not_have_greater_than_if_its_derivation_clause_does_not_contain_relational),
+ KAWAII(an_instance_of_new__type_has_less_than_or_equal_to_if_its_derivation_clause_contains_relational),
+ KAWAII(an_instance_of_new__type_does_not_have_less_than_or_equal_to_if_its_derivation_clause_does_not_contain_relational),
+ KAWAII(an_instance_of_new__type_has_greater_than_or_equal_to_if_its_derivation_clause_contains_relational),
+ KAWAII(an_instance_of_new__type_does_not_have_greater_than_or_equal_to_if_its_derivation_clause_does_not_contain_relational),
+
+ // Less-than Comparison Tests
+ KAWAII(two_instances_of_the_same_new__type_compare_less_than_if_both_contained_values_would_compare_less_than),
+ KAWAII(two_instances_of_the_same_new__type_do_not_compare_less_than_if_both_contained_values_would_not_compare_less_than),
+ KAWAII(two_instances_of_the_same_new__type_compare_less_than_or_equal_if_both_contained_values_would_compare_less_than_or_equal),
+ KAWAII(two_instances_of_the_same_new__type_do_not_compare_less_than_or_equal_if_both_contained_values_would_not_compare_less_than_or_equal),
+
+ // Greater-than Comparison Tests
+ KAWAII(two_instances_of_the_same_new__type_compare_greater_than_if_both_contained_values_would_compare_greater_than),
+ KAWAII(two_instances_of_the_same_new__type_do_not_compare_greater_than_if_both_contained_values_would_not_compare_greater_than),
+ KAWAII(two_instances_of_the_same_new__type_compare_greater_than_or_equal_if_both_contained_values_would_compare_greater_than_or_equal),
+ KAWAII(two_instances_of_the_same_new__type_do_not_compare_greater_than_or_equal_if_both_contained_values_would_not_compare_greater_than_or_equal),
+
+ // Relational Operators noexcept Tests
+ KAWAII(less_than_comparison_of_two_intances_of_the_same_new__type_is_noexcept_if_comparison_of_the_base_type_is_noexcept),
+ KAWAII(less_than_comparison_of_two_intances_of_the_same_new__type_is_not_noexcept_if_comparison_of_the_base_type_is_not_noexcept),
+ KAWAII(less_than_equal_to_comparison_of_two_intances_of_the_same_new__type_is_noexcept_if_comparison_of_the_base_type_is_noexcept),
+ KAWAII(less_than_equal_to_comparison_of_two_intances_of_the_same_new__type_is_not_noexcept_if_comparison_of_the_base_type_is_not_noexcept),
+ KAWAII(greater_than_comparison_of_two_intances_of_the_same_new__type_is_noexcept_if_comparison_of_the_base_type_is_noexcept),
+ KAWAII(greater_than_comparison_of_two_intances_of_the_same_new__type_is_not_noexcept_if_comparison_of_the_base_type_is_not_noexcept),
+ KAWAII(greater_than_equal_to_comparison_of_two_intances_of_the_same_new__type_is_noexcept_if_comparison_of_the_base_type_is_noexcept),
+ KAWAII(greater_than_equal_to_comparison_of_two_intances_of_the_same_new__type_is_not_noexcept_if_comparison_of_the_base_type_is_not_noexcept),
+
+ // clang-format on
+ },
+ "Relational Operators Tests"};
+} \ No newline at end of file