aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/CMakeLists.txt33
-rw-r--r--tests/src/arithmetic.cpp297
-rw-r--r--tests/src/constructors.cpp102
-rw-r--r--tests/src/conversion.cpp133
-rw-r--r--tests/src/derivation_clause.cpp63
-rw-r--r--tests/src/equality_comparison.cpp102
-rw-r--r--tests/src/hash.cpp59
-rw-r--r--tests/src/io_operators.cpp110
-rw-r--r--tests/src/iterable.cpp1060
-rw-r--r--tests/src/relational_operators.cpp249
-rw-r--r--tests/src/threeway_comparison.cpp52
11 files changed, 2260 insertions, 0 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000..47b8331
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,33 @@
+find_package("Catch2" "3.1"
+ COMPONENTS "Catch2WithMain"
+ REQUIRED
+)
+
+include("CTest")
+include("Catch")
+
+file(GLOB SOURCES
+ RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
+ CONFIGURE_DEPENDS
+ "src/*.cpp"
+)
+
+
+add_executable("${PROJECT_NAME}_tests"
+ ${SOURCES}
+)
+
+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>"
+ "$<$<CXX_COMPILER_ID:GNU>:-fconcepts-diagnostics-depth=5>"
+)
+
+catch_discover_tests("${PROJECT_NAME}_tests")
diff --git a/tests/src/arithmetic.cpp b/tests/src/arithmetic.cpp
new file mode 100644
index 0000000..30c243f
--- /dev/null
+++ b/tests/src/arithmetic.cpp
@@ -0,0 +1,297 @@
+#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")
+ {
+ STATIC_REQUIRE(!nt::concepts::addable<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")
+ {
+ STATIC_REQUIRE(nt::concepts::addable<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")
+ {
+ STATIC_REQUIRE(!nt::concepts::addable<addable_type> == nt::concepts::addable<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")
+ {
+ STATIC_REQUIRE(nt::concepts::addable<addable_type> == nt::concepts::addable<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")
+ {
+ STATIC_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")
+ {
+ STATIC_REQUIRE(!nt::concepts::subtractable<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")
+ {
+ STATIC_REQUIRE(nt::concepts::subtractable<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")
+ {
+ STATIC_REQUIRE(!nt::concepts::subtractable<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")
+ {
+ STATIC_REQUIRE(nt::concepts::subtractable<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")
+ {
+ STATIC_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")
+ {
+ STATIC_REQUIRE(!nt::concepts::multipliable<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")
+ {
+ STATIC_REQUIRE(nt::concepts::multipliable<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")
+ {
+ STATIC_REQUIRE(!nt::concepts::multipliable<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")
+ {
+ STATIC_REQUIRE(nt::concepts::multipliable<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")
+ {
+ STATIC_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")
+ {
+ STATIC_REQUIRE(!nt::concepts::divisible<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")
+ {
+ STATIC_REQUIRE(nt::concepts::divisible<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")
+ {
+ STATIC_REQUIRE(!nt::concepts::divisible<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")
+ {
+ STATIC_REQUIRE(nt::concepts::divisible<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")
+ {
+ STATIC_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/tests/src/constructors.cpp b/tests/src/constructors.cpp
new file mode 100644
index 0000000..b866f2e
--- /dev/null
+++ b/tests/src/constructors.cpp
@@ -0,0 +1,102 @@
+#include "newtype/newtype.hpp"
+
+#include <catch2/catch_template_test_macros.hpp>
+#include <catch2/catch_test_macros.hpp>
+
+#include <type_traits>
+
+using fundamental_types = std::tuple<bool,
+ char,
+ unsigned char,
+ signed char,
+ wchar_t,
+ char16_t,
+ char32_t,
+ short,
+ unsigned short,
+ int,
+ unsigned int,
+ long,
+ unsigned long,
+ long long,
+ unsigned long long,
+ float,
+ double,
+ long double>;
+
+TEMPLATE_LIST_TEST_CASE("Scenario: Construction from Fundamental Types", "[construction]", fundamental_types)
+{
+ GIVEN("A new_type over a fundamental type")
+ {
+ using type_alias = nt::new_type<TestType, struct tag>;
+
+ THEN("objects of it can be constructed from the fundamental type")
+ {
+ STATIC_REQUIRE(std::is_constructible_v<type_alias, TestType>);
+ }
+ }
+}
+
+SCENARIO("Default Construction", "[construction]")
+{
+ struct not_default_constructible
+ {
+ not_default_constructible() = delete;
+ };
+
+ GIVEN("A new_type over a default-constructible type")
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ static_assert(std::is_default_constructible_v<type_alias::base_type>);
+
+ THEN("it is default-constructible")
+ {
+ STATIC_REQUIRE(std::is_default_constructible_v<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over a type that is not default-constructible")
+ {
+ using type_alias = nt::new_type<not_default_constructible, struct tag>;
+ static_assert(!std::is_default_constructible_v<type_alias::base_type>);
+
+ THEN("it is not default-constructible")
+ {
+ STATIC_REQUIRE_FALSE(std::is_default_constructible_v<type_alias>);
+ }
+ }
+}
+
+SCENARIO("Copy Construction", "[construction]")
+{
+ 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;
+ };
+
+ GIVEN("A new_type over a copy-constructible type")
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ static_assert(std::is_copy_constructible_v<type_alias::base_type>);
+
+ THEN("it is copy-constructible")
+ {
+ STATIC_REQUIRE(std::is_copy_constructible_v<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over a type that is not copy-constructible")
+ {
+ using type_alias = nt::new_type<not_copy_constructible, struct tag>;
+ static_assert(!std::is_copy_constructible_v<type_alias::base_type>);
+
+ THEN("it is not copy-constructible")
+ {
+ STATIC_REQUIRE_FALSE(std::is_copy_constructible_v<type_alias>);
+ }
+ }
+}
diff --git a/tests/src/conversion.cpp b/tests/src/conversion.cpp
new file mode 100644
index 0000000..e7ce51c
--- /dev/null
+++ b/tests/src/conversion.cpp
@@ -0,0 +1,133 @@
+#include "newtype/newtype.hpp"
+
+#include <catch2/catch_template_test_macros.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/generators/catch_generators_all.hpp>
+
+#include <string>
+#include <type_traits>
+#include <vector>
+
+using test_types = std::tuple<bool, char, int, double, std::string>;
+
+TEMPLATE_LIST_TEST_CASE("Scenario: Implicit Conversions", "[conversion]", test_types)
+{
+ GIVEN("A new_type not deriving nt::ImplicitConversion")
+ {
+ using type_alias = nt::new_type<TestType, struct conversion_test>;
+
+ THEN("it is not implicitly convertible to the base type")
+ {
+ STATIC_REQUIRE(!std::is_convertible_v<type_alias, TestType>);
+ }
+ }
+
+ GIVEN("A new_type deriving nt::ImplicitConversion")
+ {
+ using type_alias = nt::new_type<TestType, struct conversion_test, deriving(nt::ImplicitConversion)>;
+
+ THEN("it is implicitly convertible to the base type")
+ {
+ STATIC_REQUIRE(std::is_convertible_v<type_alias, TestType>);
+ }
+ }
+}
+
+TEMPLATE_LIST_TEST_CASE("Scenario: Decay", "[conversion]", test_types)
+{
+ GIVEN("Any new_type")
+ {
+ using type_alias = nt::new_type<TestType, struct conversion_test>;
+
+ THEN("it's decay() member function returns a value of the base type")
+ {
+ STATIC_REQUIRE(std::is_same_v<TestType, decltype(std::declval<type_alias>().decay())>);
+ }
+ }
+
+ GIVEN("Any new_type")
+ {
+ using type_alias = nt::new_type<TestType, struct conversion_test>;
+
+ WHEN("an object of that type is constructed")
+ {
+ auto integral_value = GENERATE(take(64, random(0, 127)));
+ auto value = [integral_value] {
+ if constexpr (std::is_same_v<std::string, TestType>)
+ {
+ return std::to_string(integral_value);
+ }
+ else
+ {
+ return static_cast<TestType>(integral_value);
+ }
+ }();
+ auto obj = type_alias{value};
+
+ THEN("it's decay() member function return the underlying value")
+ {
+ REQUIRE(obj.decay() == value);
+ }
+ }
+ }
+}
+
+SCENARIO("Nothrow Decay")
+{
+ struct strange_type
+ {
+ strange_type(strange_type const &) noexcept(false)
+ {
+ }
+ };
+
+ GIVEN("A new_type over a nothrow-copyable type")
+ {
+ using type_alias = nt::new_type<int, struct conversion_test>;
+
+ THEN("the decay member function is nothrow-invokable")
+ {
+ STATIC_REQUIRE(noexcept(std::declval<type_alias>().decay()));
+ }
+ }
+
+ GIVEN("A new_type over a non-nothrow-copyable type")
+ {
+ using type_alias = nt::new_type<strange_type, struct conversion_test>;
+
+ THEN("the decay member function is not nothrow-invokable")
+ {
+ STATIC_REQUIRE(!noexcept(std::declval<type_alias>().decay()));
+ }
+ }
+}
+
+SCENARIO("Nothrow Conversion")
+{
+ struct strange_type
+ {
+ strange_type(strange_type const &) noexcept(false)
+ {
+ }
+ };
+
+ GIVEN("A new_type over a nothrow-copy-constructible type")
+ {
+ using type_alias = nt::new_type<int, struct conversion_test>;
+
+ THEN("the decay member function is nothrow-invokable")
+ {
+ STATIC_REQUIRE(noexcept(std::declval<type_alias>().operator int()));
+ }
+ }
+
+ GIVEN("A new_type over a non-nothrow-copy-constructible type")
+ {
+ using type_alias = nt::new_type<strange_type, struct conversion_test>;
+
+ THEN("the decay member function is not nothrow-invokable")
+ {
+ STATIC_REQUIRE(!noexcept(std::declval<type_alias>().operator strange_type()));
+ }
+ }
+}
diff --git a/tests/src/derivation_clause.cpp b/tests/src/derivation_clause.cpp
new file mode 100644
index 0000000..78bd3d4
--- /dev/null
+++ b/tests/src/derivation_clause.cpp
@@ -0,0 +1,63 @@
+#include "newtype/newtype.hpp"
+
+#include <catch2/catch_test_macros.hpp>
+
+#include <string>
+
+SCENARIO("Derivation Clause", "[infrastructure]")
+{
+ GIVEN("An empty derivation clause")
+ {
+ auto clause = nt::deriving();
+
+ THEN("it doesn't contain any derivable")
+ {
+ STATIC_REQUIRE_FALSE(nt::derives<decltype(clause), nt::Show>);
+ }
+ }
+
+ GIVEN("A derivation clause containing only nt::Show")
+ {
+ auto clause = deriving(nt::Show);
+
+ THEN("it doesn't contain nt::EqBase")
+ {
+ STATIC_REQUIRE_FALSE(nt::derives<decltype(clause), nt::EqBase>);
+ }
+
+ THEN("it contains nt::Show")
+ {
+ STATIC_REQUIRE(nt::derives<decltype(clause), nt::Show>);
+ }
+ }
+
+ GIVEN("A derivation clause containing only nt::Show and nt::EqBase")
+ {
+ auto clause = deriving(nt::Show, nt::EqBase);
+
+ THEN("it contains nt::EqBase")
+ {
+ STATIC_REQUIRE(nt::derives<decltype(clause), nt::EqBase>);
+ }
+
+ THEN("it contains nt::Show")
+ {
+ STATIC_REQUIRE(nt::derives<decltype(clause), nt::Show>);
+ }
+
+ THEN("it contains both nt::Show and nt::EqBase")
+ {
+ STATIC_REQUIRE(nt::derives<decltype(clause), nt::Show, nt::EqBase>);
+ }
+
+ THEN("it does not contain nt::Arithmetic")
+ {
+ STATIC_REQUIRE_FALSE(nt::derives<decltype(clause), nt::Arithmetic>);
+ }
+
+ THEN("it does not contain both nt::Arithmetic and nt::Show")
+ {
+ STATIC_REQUIRE_FALSE(nt::derives<decltype(clause), nt::Arithmetic, nt::Show>);
+ }
+ }
+}
diff --git a/tests/src/equality_comparison.cpp b/tests/src/equality_comparison.cpp
new file mode 100644
index 0000000..e0be7f9
--- /dev/null
+++ b/tests/src/equality_comparison.cpp
@@ -0,0 +1,102 @@
+#include "newtype/newtype.hpp"
+
+#include <catch2/catch_test_macros.hpp>
+
+#include <string>
+#include <type_traits>
+#include <utility>
+
+SCENARIO("Equality Comparison", "[compare]")
+{
+ GIVEN("A new_type over an equality comparable type")
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+
+ THEN("two objects of it with the same value compare equal")
+ {
+ REQUIRE(type_alias{42} == type_alias{42});
+ }
+
+ THEN("two objects of it with the same value do not compare not-equal")
+ {
+ REQUIRE_FALSE(type_alias{42} != type_alias{42});
+ }
+
+ THEN("two object of it with different values do not compare equal")
+ {
+ REQUIRE_FALSE(type_alias{42} == type_alias{43});
+ }
+
+ THEN("two object of it with different values compare not-equal")
+ {
+ REQUIRE(type_alias{42} != type_alias{43});
+ }
+
+ THEN("equality comparison returns bool")
+ {
+ STATIC_REQUIRE(std::is_same_v<bool, decltype(std::declval<type_alias>() == std::declval<type_alias>())>);
+ }
+
+ THEN("inequality comparison returns bool")
+ {
+ STATIC_REQUIRE(std::is_same_v<bool, decltype(std::declval<type_alias>() != std::declval<type_alias>())>);
+ }
+ }
+
+ GIVEN("A new_type deriving nt::EqBase")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::EqBase)>;
+
+ THEN("an instance of it compares equal to the equivalent base type value")
+ {
+ REQUIRE(type_alias{42} == 42);
+ }
+
+ THEN("an instance of it comapres not-equal to a different base type value")
+ {
+ REQUIRE(type_alias{42} != 43);
+ }
+ }
+
+ GIVEN("A new_type over a nothrow-comparable type")
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+
+ static_assert(nt::concepts::nothrow_equality_comparable<type_alias::base_type>);
+ static_assert(nt::concepts::nothrow_inequality_comparable<type_alias::base_type>);
+
+ THEN("it is nothrow-equality-comparable")
+ {
+ STATIC_REQUIRE(nt::concepts::nothrow_equality_comparable<type_alias>);
+ }
+
+ THEN("it is nothrow-inequality-comparable")
+ {
+ STATIC_REQUIRE(nt::concepts::nothrow_inequality_comparable<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over a non-nothrow-comparable type")
+ {
+ struct not_nothrow_comparable
+ {
+ auto operator==(not_nothrow_comparable) const noexcept(false) -> bool;
+ auto operator!=(not_nothrow_comparable) const noexcept(false) -> bool;
+ };
+
+ using type_alias = nt::new_type<not_nothrow_comparable, struct tag>;
+
+ static_assert(!nt::concepts::nothrow_equality_comparable<type_alias::base_type>);
+ static_assert(!nt::concepts::nothrow_inequality_comparable<type_alias::base_type>);
+
+ THEN("it is not nothrow-equality-comparable")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::nothrow_equality_comparable<type_alias>);
+ }
+
+ THEN("it is not nothrow-inequality-comparable")
+ {
+ STATIC_REQUIRE_FALSE(noexcept(std::declval<type_alias>() != std::declval<type_alias>()));
+ }
+ }
+}
diff --git a/tests/src/hash.cpp b/tests/src/hash.cpp
new file mode 100644
index 0000000..94f252f
--- /dev/null
+++ b/tests/src/hash.cpp
@@ -0,0 +1,59 @@
+#include "newtype/newtype.hpp"
+
+#include <catch2/catch_template_test_macros.hpp>
+#include <catch2/catch_test_macros.hpp>
+
+#include <string>
+#include <unordered_map>
+
+TEMPLATE_TEST_CASE("Hash", "[hash]", std::string, int)
+{
+ GIVEN("A new_type not deriving nt::Hash")
+ {
+ using type_alias = nt::new_type<TestType, struct tag>;
+ static_assert(nt::concepts::hashable<typename type_alias::base_type>);
+
+ THEN("it is not hashable")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::hashable<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over a hashable type deriving nt::Hash")
+ {
+ using type_alias = nt::new_type<TestType, struct tag, deriving(nt::Hash)>;
+ static_assert(nt::concepts::hashable<typename type_alias::base_type>);
+
+ THEN("it is hashable")
+ {
+ STATIC_REQUIRE(nt::concepts::hashable<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over a non-hashable type deriving nt::Hash")
+ {
+ struct non_hashable
+ {
+ };
+ using type_alias = nt::new_type<non_hashable, struct tag, deriving(nt::Hash)>;
+ static_assert(!nt::concepts::hashable<typename type_alias::base_type>);
+
+ THEN("it is not hashable")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::hashable<type_alias>);
+ }
+ }
+
+ GIVEN("A hashable new_type")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Hash)>;
+ static_assert(nt::concepts::hashable<typename type_alias::base_type>);
+
+ THEN("it can be used a the key in an unordered_map")
+ {
+ auto map = std::unordered_map<type_alias, int>{};
+ map[type_alias{42}] = 43;
+ REQUIRE(map[type_alias{42}] == 43);
+ }
+ }
+}
diff --git a/tests/src/io_operators.cpp b/tests/src/io_operators.cpp
new file mode 100644
index 0000000..f7f8f29
--- /dev/null
+++ b/tests/src/io_operators.cpp
@@ -0,0 +1,110 @@
+#include "newtype/newtype.hpp"
+
+#include <catch2/catch_test_macros.hpp>
+
+#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
+
+SCENARIO("Stream Input")
+{
+ GIVEN("A new_type over a stream-inputtable type deriving nt::Read")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Read)>;
+ static_assert(has_stream_input_v<type_alias::base_type>);
+
+ THEN("it has the stream input operator")
+ {
+ STATIC_REQUIRE(has_stream_input_v<type_alias>);
+ }
+
+ THEN("an instance of it can be read from an std::istream")
+ {
+ auto obj = type_alias{};
+ auto str = std::istringstream{"42"};
+
+ str >> obj;
+
+ REQUIRE(obj.decay() == 42);
+ }
+ }
+
+ GIVEN("A new_type over a non-stream-inputtable type deriving nt::Read")
+ {
+ using type_alias = nt::new_type<std::istream, struct tag, deriving(nt::Read)>;
+ static_assert(!has_stream_input_v<type_alias::base_type>);
+
+ THEN("it does not have the input operator")
+ {
+ STATIC_REQUIRE(!has_stream_input_v<type_alias>);
+ }
+ }
+}
+
+SCENARIO("Stream Output")
+{
+ GIVEN("A new_type over a stream-outputtable type deriving nt::Show")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Show)>;
+ static_assert(has_stream_output_v<type_alias::base_type>);
+
+ THEN("it has the stream output operator")
+ {
+ STATIC_REQUIRE(has_stream_output_v<type_alias>);
+ }
+
+ THEN("an instance of it can be written to an std::ostream")
+ {
+ auto obj = type_alias{42};
+ auto str = std::ostringstream{};
+
+ str << obj;
+
+ REQUIRE(str.str() == "42");
+ }
+ }
+
+ GIVEN("A new_type over a non-stream-outputtable type deriving nt::Show")
+ {
+ using type_alias = nt::new_type<std::istream, struct tag, deriving(nt::Show)>;
+ static_assert(!has_stream_output_v<type_alias::base_type>);
+
+ THEN("it does not have the output operator")
+ {
+ STATIC_REQUIRE(!has_stream_output_v<type_alias>);
+ }
+ }
+}
diff --git a/tests/src/iterable.cpp b/tests/src/iterable.cpp
new file mode 100644
index 0000000..85b7edc
--- /dev/null
+++ b/tests/src/iterable.cpp
@@ -0,0 +1,1060 @@
+#include "newtype/newtype.hpp"
+
+#include <catch2/catch_test_macros.hpp>
+
+#include <algorithm>
+#include <array>
+#include <iterator>
+#include <numeric>
+
+namespace iterable_types
+{
+
+ 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 constexpr begin() -> iterator;
+ auto constexpr begin() const -> const_iterator;
+ auto constexpr cbegin() const -> const_iterator;
+ auto constexpr rbegin() -> reverse_iterator;
+ auto constexpr rbegin() const -> const_reverse_iterator;
+ auto constexpr crbegin() const -> const_reverse_iterator;
+
+ auto constexpr end() -> iterator;
+ auto constexpr end() const -> const_iterator;
+ auto constexpr cend() const -> const_iterator;
+ auto constexpr rend() -> reverse_iterator;
+ auto constexpr rend() const -> const_reverse_iterator;
+ auto constexpr 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 constexpr begin(with_free &) -> with_free::iterator;
+ auto constexpr begin(with_free const &) -> with_free::const_iterator;
+ auto constexpr cbegin(with_free const &) -> with_free::const_iterator;
+ auto constexpr rbegin(with_free &) -> with_free::reverse_iterator;
+ auto constexpr rbegin(with_free const &) -> with_free::const_reverse_iterator;
+ auto constexpr crbegin(with_free const &) -> with_free::const_reverse_iterator;
+ auto constexpr end(with_free &) -> with_free::iterator;
+ auto constexpr end(with_free const &) -> with_free::const_iterator;
+ auto constexpr cend(with_free const &) -> with_free::const_iterator;
+ auto constexpr rend(with_free &) -> with_free::reverse_iterator;
+ auto constexpr rend(with_free const &) -> with_free::const_reverse_iterator;
+ auto constexpr crend(with_free const &) -> with_free::const_reverse_iterator;
+
+} // namespace iterable_types
+
+SCENARIO("Iterators", "[iterators]")
+{
+ GIVEN("A new_type over a non-iterable base type not deriving nt::Iterable")
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ static_assert(!nt::concepts::beginnable<type_alias::base_type>);
+ static_assert(!nt::concepts::beginnable<type_alias::base_type const>);
+ static_assert(!nt::concepts::cbeginnable<type_alias::base_type>);
+ static_assert(!nt::concepts::rbeginnable<type_alias::base_type>);
+ static_assert(!nt::concepts::rbeginnable<type_alias::base_type const>);
+ static_assert(!nt::concepts::crbeginnable<type_alias::base_type>);
+ static_assert(!nt::concepts::endable<type_alias::base_type>);
+ static_assert(!nt::concepts::endable<type_alias::base_type const>);
+ static_assert(!nt::concepts::cendable<type_alias::base_type>);
+ static_assert(!nt::concepts::rendable<type_alias::base_type>);
+ static_assert(!nt::concepts::rendable<type_alias::base_type const>);
+ static_assert(!nt::concepts::crendable<type_alias::base_type>);
+
+ THEN("it has no begin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::beginnable<type_alias>);
+ }
+
+ THEN("it has no constant begin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::beginnable<type_alias const>);
+ }
+
+ THEN("it has no cbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::cbeginnable<type_alias>);
+ }
+
+ THEN("it has no rbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::rbeginnable<type_alias>);
+ }
+
+ THEN("it has no constant rbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::rbeginnable<type_alias const>);
+ }
+
+ THEN("it has no crbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::crbeginnable<type_alias>);
+ }
+
+ THEN("it has no end")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::endable<type_alias>);
+ }
+
+ THEN("it has no constant end")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::endable<type_alias const>);
+ }
+
+ THEN("it has no cend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::cendable<type_alias>);
+ }
+
+ THEN("it has no rend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::rendable<type_alias>);
+ }
+
+ THEN("it has no constant rend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::rendable<type_alias const>);
+ }
+
+ THEN("it has no crend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::crendable<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over a non-iterable base type deriving nt::Iterable")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Iterable)>;
+ static_assert(!nt::concepts::beginnable<type_alias::base_type>);
+ static_assert(!nt::concepts::beginnable<type_alias::base_type const>);
+ static_assert(!nt::concepts::cbeginnable<type_alias::base_type>);
+ static_assert(!nt::concepts::rbeginnable<type_alias::base_type>);
+ static_assert(!nt::concepts::rbeginnable<type_alias::base_type const>);
+ static_assert(!nt::concepts::crbeginnable<type_alias::base_type>);
+ static_assert(!nt::concepts::endable<type_alias::base_type>);
+ static_assert(!nt::concepts::endable<type_alias::base_type const>);
+ static_assert(!nt::concepts::cendable<type_alias::base_type>);
+ static_assert(!nt::concepts::rendable<type_alias::base_type>);
+ static_assert(!nt::concepts::rendable<type_alias::base_type const>);
+ static_assert(!nt::concepts::crendable<type_alias::base_type>);
+
+ THEN("it has no begin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::beginnable<type_alias>);
+ }
+
+ THEN("it has no constant begin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::beginnable<type_alias const>);
+ }
+
+ THEN("it has no cbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::cbeginnable<type_alias>);
+ }
+
+ THEN("it has no rbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::rbeginnable<type_alias>);
+ }
+
+ THEN("it has no constant rbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::rbeginnable<type_alias const>);
+ }
+
+ THEN("it has no crbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::crbeginnable<type_alias>);
+ }
+
+ THEN("it has no end")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::endable<type_alias>);
+ }
+
+ THEN("it has no constant end")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::endable<type_alias const>);
+ }
+
+ THEN("it has no cend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::cendable<type_alias>);
+ }
+
+ THEN("it has no rend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::rendable<type_alias>);
+ }
+
+ THEN("it has no constant rend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::rendable<type_alias const>);
+ }
+
+ THEN("it has no crend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::crendable<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over an iterable base type not deriving nt::Iterable")
+ {
+ using type_alias = nt::new_type<std::array<int, 1>, struct tag>;
+ static_assert(nt::concepts::beginnable<type_alias::base_type>);
+ static_assert(nt::concepts::const_beginnable<type_alias::base_type>);
+ static_assert(nt::concepts::cbeginnable<type_alias::base_type>);
+ static_assert(nt::concepts::rbeginnable<type_alias::base_type>);
+ static_assert(nt::concepts::const_rbeginnable<type_alias::base_type>);
+ static_assert(nt::concepts::crbeginnable<type_alias::base_type>);
+ static_assert(nt::concepts::endable<type_alias::base_type>);
+ static_assert(nt::concepts::const_endable<type_alias::base_type>);
+ static_assert(nt::concepts::cendable<type_alias::base_type>);
+ static_assert(nt::concepts::rendable<type_alias::base_type>);
+ static_assert(nt::concepts::const_rendable<type_alias::base_type>);
+ static_assert(nt::concepts::crendable<type_alias::base_type>);
+
+ THEN("it has no begin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::beginnable<type_alias>);
+ }
+
+ THEN("it has no constant begin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::beginnable<type_alias const>);
+ }
+
+ THEN("it has no cbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::cbeginnable<type_alias>);
+ }
+
+ THEN("it has no rbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::rbeginnable<type_alias>);
+ }
+
+ THEN("it has no constant rbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::rbeginnable<type_alias const>);
+ }
+
+ THEN("it has no crbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::crbeginnable<type_alias>);
+ }
+
+ THEN("it has no end")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::endable<type_alias>);
+ }
+
+ THEN("it has no constant end")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::endable<type_alias const>);
+ }
+
+ THEN("it has no cend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::cendable<type_alias>);
+ }
+
+ THEN("it has no rend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::rendable<type_alias>);
+ }
+
+ THEN("it has no constant rend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::rendable<type_alias const>);
+ }
+
+ THEN("it has no crend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::crendable<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over an iterable base type deriving nt::Iterable")
+ {
+ using type_alias = nt::new_type<std::array<int, 1>, struct tag, deriving(nt::Iterable)>;
+ static_assert(nt::concepts::beginnable<type_alias::base_type>);
+ static_assert(nt::concepts::const_beginnable<type_alias::base_type>);
+ static_assert(nt::concepts::cbeginnable<type_alias::base_type>);
+ static_assert(nt::concepts::rbeginnable<type_alias::base_type>);
+ static_assert(nt::concepts::const_rbeginnable<type_alias::base_type>);
+ static_assert(nt::concepts::crbeginnable<type_alias::base_type>);
+ static_assert(nt::concepts::endable<type_alias::base_type>);
+ static_assert(nt::concepts::const_endable<type_alias::base_type>);
+ static_assert(nt::concepts::cendable<type_alias::base_type>);
+ static_assert(nt::concepts::rendable<type_alias::base_type>);
+ static_assert(nt::concepts::const_rendable<type_alias::base_type>);
+ static_assert(nt::concepts::crendable<type_alias::base_type>);
+
+ THEN("it has begin")
+ {
+ STATIC_REQUIRE(nt::concepts::beginnable<type_alias>);
+ }
+
+ THEN("it has constant begin")
+ {
+ STATIC_REQUIRE(nt::concepts::const_beginnable<type_alias>);
+ }
+
+ THEN("it has cbegin")
+ {
+ STATIC_REQUIRE(nt::concepts::cbeginnable<type_alias>);
+ }
+
+ THEN("it has rbegin")
+ {
+ STATIC_REQUIRE(nt::concepts::rbeginnable<type_alias>);
+ }
+
+ THEN("it has constant rbegin")
+ {
+ STATIC_REQUIRE(nt::concepts::const_rbeginnable<type_alias>);
+ }
+
+ THEN("it has crbegin")
+ {
+ STATIC_REQUIRE(nt::concepts::crbeginnable<type_alias>);
+ }
+
+ THEN("it has end")
+ {
+ STATIC_REQUIRE(nt::concepts::endable<type_alias>);
+ }
+
+ THEN("it has constant end")
+ {
+ STATIC_REQUIRE(nt::concepts::const_endable<type_alias>);
+ }
+
+ THEN("it has cend")
+ {
+ STATIC_REQUIRE(nt::concepts::cendable<type_alias>);
+ }
+
+ THEN("it has rend")
+ {
+ STATIC_REQUIRE(nt::concepts::rendable<type_alias>);
+ }
+
+ THEN("it has constant rend")
+ {
+ STATIC_REQUIRE(nt::concepts::const_rendable<type_alias>);
+ }
+
+ THEN("it has crend")
+ {
+ STATIC_REQUIRE(nt::concepts::crendable<type_alias>);
+ }
+ }
+}
+
+SCENARIO("Iterators (member)", "[iterators]")
+{
+ GIVEN("A new_type over a non-iterable base type not deriving nt::Iterable")
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ static_assert(!nt::concepts::member_begin<type_alias::base_type>);
+ static_assert(!nt::concepts::const_member_begin<type_alias::base_type>);
+ static_assert(!nt::concepts::member_cbegin<type_alias::base_type>);
+ static_assert(!nt::concepts::member_rbegin<type_alias::base_type>);
+ static_assert(!nt::concepts::const_member_rbegin<type_alias::base_type>);
+ static_assert(!nt::concepts::member_crbegin<type_alias::base_type>);
+ static_assert(!nt::concepts::member_end<type_alias::base_type>);
+ static_assert(!nt::concepts::const_member_end<type_alias::base_type>);
+ static_assert(!nt::concepts::member_cend<type_alias::base_type>);
+ static_assert(!nt::concepts::member_rend<type_alias::base_type>);
+ static_assert(!nt::concepts::const_member_rend<type_alias::base_type>);
+ static_assert(!nt::concepts::member_crend<type_alias::base_type>);
+
+ THEN("it has no member begin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_begin<type_alias>);
+ }
+
+ THEN("it has no member constant begin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_begin<type_alias const>);
+ }
+
+ THEN("it has no member cbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_cbegin<type_alias>);
+ }
+
+ THEN("it has no member rbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_rbegin<type_alias>);
+ }
+
+ THEN("it has no member constant rbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_rbegin<type_alias const>);
+ }
+
+ THEN("it has no member crbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_crbegin<type_alias>);
+ }
+
+ THEN("it has no member end")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_end<type_alias>);
+ }
+
+ THEN("it has no member constant end")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_end<type_alias const>);
+ }
+
+ THEN("it has no member cend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_cend<type_alias>);
+ }
+
+ THEN("it has no member rend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_rend<type_alias>);
+ }
+
+ THEN("it has no member constant rend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_rend<type_alias const>);
+ }
+
+ THEN("it has no member crend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_crend<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over a non-iterable base type deriving nt::Iterable")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Iterable)>;
+ static_assert(!nt::concepts::member_begin<type_alias::base_type>);
+ static_assert(!nt::concepts::const_member_begin<type_alias::base_type>);
+ static_assert(!nt::concepts::member_cbegin<type_alias::base_type>);
+ static_assert(!nt::concepts::member_rbegin<type_alias::base_type>);
+ static_assert(!nt::concepts::const_member_rbegin<type_alias::base_type>);
+ static_assert(!nt::concepts::member_crbegin<type_alias::base_type>);
+ static_assert(!nt::concepts::member_end<type_alias::base_type>);
+ static_assert(!nt::concepts::const_member_end<type_alias::base_type>);
+ static_assert(!nt::concepts::member_cend<type_alias::base_type>);
+ static_assert(!nt::concepts::member_rend<type_alias::base_type>);
+ static_assert(!nt::concepts::const_member_rend<type_alias::base_type>);
+ static_assert(!nt::concepts::member_crend<type_alias::base_type>);
+
+ THEN("it has no member begin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_begin<type_alias>);
+ }
+
+ THEN("it has no member constant begin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_begin<type_alias const>);
+ }
+
+ THEN("it has no member cbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_cbegin<type_alias>);
+ }
+
+ THEN("it has no member rbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_rbegin<type_alias>);
+ }
+
+ THEN("it has no member constant rbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_rbegin<type_alias const>);
+ }
+
+ THEN("it has no member crbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_crbegin<type_alias>);
+ }
+
+ THEN("it has no member end")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_end<type_alias>);
+ }
+
+ THEN("it has no member constant end")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_end<type_alias const>);
+ }
+
+ THEN("it has no member cend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_cend<type_alias>);
+ }
+
+ THEN("it has no member rend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_rend<type_alias>);
+ }
+
+ THEN("it has no member constant rend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_rend<type_alias const>);
+ }
+
+ THEN("it has no member crend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_crend<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over an iterable base type not deriving nt::Iterable")
+ {
+ using type_alias = nt::new_type<std::array<int, 1>, struct tag>;
+ static_assert(nt::concepts::member_begin<type_alias::base_type>);
+ static_assert(nt::concepts::const_member_begin<type_alias::base_type>);
+ static_assert(nt::concepts::member_cbegin<type_alias::base_type>);
+ static_assert(nt::concepts::member_rbegin<type_alias::base_type>);
+ static_assert(nt::concepts::const_member_rbegin<type_alias::base_type>);
+ static_assert(nt::concepts::member_crbegin<type_alias::base_type>);
+ static_assert(nt::concepts::member_end<type_alias::base_type>);
+ static_assert(nt::concepts::const_member_end<type_alias::base_type>);
+ static_assert(nt::concepts::member_cend<type_alias::base_type>);
+ static_assert(nt::concepts::member_rend<type_alias::base_type>);
+ static_assert(nt::concepts::const_member_rend<type_alias::base_type>);
+ static_assert(nt::concepts::member_crend<type_alias::base_type>);
+
+ THEN("it has no member begin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_begin<type_alias>);
+ }
+
+ THEN("it has no member constant begin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_begin<type_alias const>);
+ }
+
+ THEN("it has no member cbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_cbegin<type_alias>);
+ }
+
+ THEN("it has no member rbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_rbegin<type_alias>);
+ }
+
+ THEN("it has no member constant rbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_rbegin<type_alias const>);
+ }
+
+ THEN("it has no member crbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_crbegin<type_alias>);
+ }
+
+ THEN("it has no member end")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_end<type_alias>);
+ }
+
+ THEN("it has no member constant end")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_end<type_alias const>);
+ }
+
+ THEN("it has no member cend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_cend<type_alias>);
+ }
+
+ THEN("it has no member rend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_rend<type_alias>);
+ }
+
+ THEN("it has no member constant rend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_rend<type_alias const>);
+ }
+
+ THEN("it has no member crend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::member_crend<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over an iterable base type deriving nt::Iterable")
+ {
+ using type_alias = nt::new_type<std::array<int, 1>, struct tag, deriving(nt::Iterable)>;
+ static_assert(nt::concepts::member_begin<type_alias::base_type>);
+ static_assert(nt::concepts::const_member_begin<type_alias::base_type>);
+ static_assert(nt::concepts::member_cbegin<type_alias::base_type>);
+ static_assert(nt::concepts::member_rbegin<type_alias::base_type>);
+ static_assert(nt::concepts::const_member_rbegin<type_alias::base_type>);
+ static_assert(nt::concepts::member_crbegin<type_alias::base_type>);
+ static_assert(nt::concepts::member_end<type_alias::base_type>);
+ static_assert(nt::concepts::const_member_end<type_alias::base_type>);
+ static_assert(nt::concepts::member_cend<type_alias::base_type>);
+ static_assert(nt::concepts::member_rend<type_alias::base_type>);
+ static_assert(nt::concepts::const_member_rend<type_alias::base_type>);
+ static_assert(nt::concepts::member_crend<type_alias::base_type>);
+
+ THEN("it has member begin")
+ {
+ STATIC_REQUIRE(nt::concepts::member_begin<type_alias>);
+ }
+
+ THEN("it has member constant begin")
+ {
+ STATIC_REQUIRE(nt::concepts::const_member_begin<type_alias>);
+ }
+
+ THEN("it has member cbegin")
+ {
+ STATIC_REQUIRE(nt::concepts::member_cbegin<type_alias>);
+ }
+
+ THEN("it has member rbegin")
+ {
+ STATIC_REQUIRE(nt::concepts::member_rbegin<type_alias>);
+ }
+
+ THEN("it has member constant rbegin")
+ {
+ STATIC_REQUIRE(nt::concepts::const_member_rbegin<type_alias>);
+ }
+
+ THEN("it has member crbegin")
+ {
+ STATIC_REQUIRE(nt::concepts::member_crbegin<type_alias>);
+ }
+
+ THEN("it has member end")
+ {
+ STATIC_REQUIRE(nt::concepts::member_end<type_alias>);
+ }
+
+ THEN("it has member constant end")
+ {
+ STATIC_REQUIRE(nt::concepts::const_member_end<type_alias>);
+ }
+
+ THEN("it has member cend")
+ {
+ STATIC_REQUIRE(nt::concepts::member_cend<type_alias>);
+ }
+
+ THEN("it has member rend")
+ {
+ STATIC_REQUIRE(nt::concepts::member_rend<type_alias>);
+ }
+
+ THEN("it has member constant rend")
+ {
+ STATIC_REQUIRE(nt::concepts::const_member_rend<type_alias>);
+ }
+
+ THEN("it has member crend")
+ {
+ STATIC_REQUIRE(nt::concepts::member_crend<type_alias>);
+ }
+ }
+}
+
+SCENARIO("Iterators (free)", "[iterators]")
+{
+ GIVEN("A new_type over a non-iterable base type not deriving nt::Iterable")
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ static_assert(!nt::concepts::free_begin<type_alias::base_type>);
+ static_assert(!nt::concepts::const_free_begin<type_alias::base_type>);
+ static_assert(!nt::concepts::free_cbegin<type_alias::base_type>);
+ static_assert(!nt::concepts::free_rbegin<type_alias::base_type>);
+ static_assert(!nt::concepts::const_free_rbegin<type_alias::base_type>);
+ static_assert(!nt::concepts::free_crbegin<type_alias::base_type>);
+ static_assert(!nt::concepts::free_end<type_alias::base_type>);
+ static_assert(!nt::concepts::const_free_end<type_alias::base_type>);
+ static_assert(!nt::concepts::free_cend<type_alias::base_type>);
+ static_assert(!nt::concepts::free_rend<type_alias::base_type>);
+ static_assert(!nt::concepts::const_free_rend<type_alias::base_type>);
+ static_assert(!nt::concepts::free_crend<type_alias::base_type>);
+
+ THEN("it has no free begin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_begin<type_alias>);
+ }
+
+ THEN("it has no free constant begin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::const_free_begin<type_alias>);
+ }
+
+ THEN("it has no free cbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_cbegin<type_alias>);
+ }
+
+ THEN("it has no free rbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_rbegin<type_alias>);
+ }
+
+ THEN("it has no free constant rbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::const_free_rbegin<type_alias>);
+ }
+
+ THEN("it has no free crbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_crbegin<type_alias>);
+ }
+
+ THEN("it has no free end")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_end<type_alias>);
+ }
+
+ THEN("it has no free constant end")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::const_free_end<type_alias>);
+ }
+
+ THEN("it has no free cend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_cend<type_alias>);
+ }
+
+ THEN("it has no free rend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_rend<type_alias>);
+ }
+
+ THEN("it has no free constant rend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::const_free_rend<type_alias>);
+ }
+
+ THEN("it has no free crend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_crend<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over a non-iterable base type deriving nt::Iterable")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Iterable)>;
+ static_assert(!nt::concepts::free_begin<type_alias::base_type>);
+ static_assert(!nt::concepts::const_free_begin<type_alias::base_type>);
+ static_assert(!nt::concepts::free_cbegin<type_alias::base_type>);
+ static_assert(!nt::concepts::free_rbegin<type_alias::base_type>);
+ static_assert(!nt::concepts::const_free_rbegin<type_alias::base_type>);
+ static_assert(!nt::concepts::free_crbegin<type_alias::base_type>);
+ static_assert(!nt::concepts::free_end<type_alias::base_type>);
+ static_assert(!nt::concepts::const_free_end<type_alias::base_type>);
+ static_assert(!nt::concepts::free_cend<type_alias::base_type>);
+ static_assert(!nt::concepts::free_rend<type_alias::base_type>);
+ static_assert(!nt::concepts::const_free_rend<type_alias::base_type>);
+ static_assert(!nt::concepts::free_crend<type_alias::base_type>);
+
+ THEN("it has no free begin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_begin<type_alias>);
+ }
+
+ THEN("it has no free constant begin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::const_free_begin<type_alias>);
+ }
+
+ THEN("it has no free cbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_cbegin<type_alias>);
+ }
+
+ THEN("it has no free rbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_rbegin<type_alias>);
+ }
+
+ THEN("it has no free constant rbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::const_free_rbegin<type_alias>);
+ }
+
+ THEN("it has no free crbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_crbegin<type_alias>);
+ }
+
+ THEN("it has no free end")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_end<type_alias>);
+ }
+
+ THEN("it has no free constant end")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::const_free_end<type_alias>);
+ }
+
+ THEN("it has no free cend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_cend<type_alias>);
+ }
+
+ THEN("it has no free rend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_rend<type_alias>);
+ }
+
+ THEN("it has no free constant rend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::const_free_rend<type_alias>);
+ }
+
+ THEN("it has no free crend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_crend<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over an iterable base type not deriving nt::Iterable")
+ {
+ using type_alias = nt::new_type<std::array<int, 1>, struct tag>;
+ static_assert(nt::concepts::free_begin<type_alias::base_type>);
+ static_assert(nt::concepts::const_free_begin<type_alias::base_type>);
+ static_assert(nt::concepts::free_cbegin<type_alias::base_type>);
+ static_assert(nt::concepts::free_rbegin<type_alias::base_type>);
+ static_assert(nt::concepts::const_free_rbegin<type_alias::base_type>);
+ static_assert(nt::concepts::free_crbegin<type_alias::base_type>);
+ static_assert(nt::concepts::free_end<type_alias::base_type>);
+ static_assert(nt::concepts::const_free_end<type_alias::base_type>);
+ static_assert(nt::concepts::free_cend<type_alias::base_type>);
+ static_assert(nt::concepts::free_rend<type_alias::base_type>);
+ static_assert(nt::concepts::const_free_rend<type_alias::base_type>);
+ static_assert(nt::concepts::free_crend<type_alias::base_type>);
+
+ THEN("it has no free begin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_begin<type_alias>);
+ }
+
+ THEN("it has no free constant begin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_begin<type_alias const>);
+ }
+
+ THEN("it has no free cbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_cbegin<type_alias>);
+ }
+
+ THEN("it has no free rbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_rbegin<type_alias>);
+ }
+
+ THEN("it has no free constant rbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_rbegin<type_alias const>);
+ }
+
+ THEN("it has no free crbegin")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_crbegin<type_alias>);
+ }
+
+ THEN("it has no free end")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_end<type_alias>);
+ }
+
+ THEN("it has no free constant end")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_end<type_alias const>);
+ }
+
+ THEN("it has no free cend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_cend<type_alias>);
+ }
+
+ THEN("it has no free rend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_rend<type_alias>);
+ }
+
+ THEN("it has no free constant rend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_rend<type_alias const>);
+ }
+
+ THEN("it has no free crend")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::free_crend<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over an iterable base type deriving nt::Iterable")
+ {
+ using type_alias = nt::new_type<std::array<int, 1>, struct tag, deriving(nt::Iterable)>;
+ static_assert(nt::concepts::free_begin<type_alias::base_type>);
+ static_assert(nt::concepts::const_free_begin<type_alias::base_type>);
+ static_assert(nt::concepts::free_cbegin<type_alias::base_type>);
+ static_assert(nt::concepts::free_rbegin<type_alias::base_type>);
+ static_assert(nt::concepts::const_free_rbegin<type_alias::base_type>);
+ static_assert(nt::concepts::free_crbegin<type_alias::base_type>);
+ static_assert(nt::concepts::free_end<type_alias::base_type>);
+ static_assert(nt::concepts::const_free_end<type_alias::base_type>);
+ static_assert(nt::concepts::free_cend<type_alias::base_type>);
+ static_assert(nt::concepts::free_rend<type_alias::base_type>);
+ static_assert(nt::concepts::const_free_rend<type_alias::base_type>);
+ static_assert(nt::concepts::free_crend<type_alias::base_type>);
+
+ THEN("it has free begin")
+ {
+ STATIC_REQUIRE(nt::concepts::free_begin<type_alias>);
+ }
+
+ THEN("it has free constant begin")
+ {
+ STATIC_REQUIRE(nt::concepts::const_free_begin<type_alias>);
+ }
+
+ THEN("it has free cbegin")
+ {
+ STATIC_REQUIRE(nt::concepts::free_cbegin<type_alias>);
+ }
+
+ THEN("it has free rbegin")
+ {
+ STATIC_REQUIRE(nt::concepts::free_rbegin<type_alias>);
+ }
+
+ THEN("it has free constant rbegin")
+ {
+ STATIC_REQUIRE(nt::concepts::const_free_rbegin<type_alias>);
+ }
+
+ THEN("it has free crbegin")
+ {
+ STATIC_REQUIRE(nt::concepts::free_crbegin<type_alias>);
+ }
+
+ THEN("it has free end")
+ {
+ STATIC_REQUIRE(nt::concepts::free_end<type_alias>);
+ }
+
+ THEN("it has free constant end")
+ {
+ STATIC_REQUIRE(nt::concepts::const_free_end<type_alias>);
+ }
+
+ THEN("it has free cend")
+ {
+ STATIC_REQUIRE(nt::concepts::free_cend<type_alias>);
+ }
+
+ THEN("it has free rend")
+ {
+ STATIC_REQUIRE(nt::concepts::free_rend<type_alias>);
+ }
+
+ THEN("it has free constant rend")
+ {
+ STATIC_REQUIRE(nt::concepts::const_free_rend<type_alias>);
+ }
+
+ THEN("it has free crend")
+ {
+ STATIC_REQUIRE(nt::concepts::free_crend<type_alias>);
+ }
+ }
+}
+
+SCENARIO("Iterator Semantics", "[iterators]")
+{
+ GIVEN("An iterable new_type")
+ {
+ using type_alias = nt::new_type<std::array<int, 3>, struct tag, deriving(nt::Iterable)>;
+
+ THEN("a non-const object can be used in value range-based for")
+ {
+ auto obj = type_alias{{1, 2, 3}};
+ auto res = 0;
+ for (auto e : obj)
+ {
+ res += e;
+ }
+ REQUIRE(res == 6);
+ }
+
+ THEN("a const object can be used in value range-based for")
+ {
+ auto const obj = type_alias{{1, 2, 3}};
+ auto res = 0;
+ for (auto e : obj)
+ {
+ res += e;
+ }
+ REQUIRE(res == 6);
+ }
+
+ THEN("a non-const object can be used in reference range-based for")
+ {
+ auto obj = type_alias{{1, 2, 3}};
+ auto res = 0;
+ for (auto & e : obj)
+ {
+ res += e;
+ }
+ REQUIRE(res == 6);
+ }
+
+ THEN("a const object can be used in const-reference range-based for")
+ {
+ auto const obj = type_alias{{1, 2, 3}};
+ auto res = 0;
+ for (auto const & e : obj)
+ {
+ res += e;
+ }
+ REQUIRE(res == 6);
+ }
+
+ THEN("using an instance of it in an STL algorithm yields the same results as using an instance of the base type")
+ {
+ auto const base_obj = type_alias::base_type{{1, 2, 3}};
+ auto const nt_obj = type_alias{base_obj};
+
+ auto base_res = std::accumulate(begin(base_obj), end(base_obj), 0);
+ auto nt_res = std::accumulate(begin(nt_obj), end(nt_obj), 0);
+
+ REQUIRE(nt_res == base_res);
+ }
+
+ THEN("iterating over an instance yields the same elements in the same order as iterating over an instance of the base type")
+ {
+ auto const base_obj = type_alias::base_type{{1, 2, 3}};
+ auto const nt_obj = type_alias{base_obj};
+
+ REQUIRE(std::equal(cbegin(nt_obj), cend(nt_obj), cbegin(base_obj), cend(base_obj)));
+ }
+ }
+}
diff --git a/tests/src/relational_operators.cpp b/tests/src/relational_operators.cpp
new file mode 100644
index 0000000..e852957
--- /dev/null
+++ b/tests/src/relational_operators.cpp
@@ -0,0 +1,249 @@
+#include "newtype/newtype.hpp"
+
+#include <catch2/catch_test_macros.hpp>
+
+#include <istream>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+SCENARIO("Relational Operator Availability")
+{
+ GIVEN("A new_type over a relationally comparable type not deriving nt::Relational")
+ {
+ using type_alias = nt::new_type<int, struct tag>;
+ static_assert(nt::concepts::less_than_comparable<type_alias::base_type>);
+ static_assert(nt::concepts::less_than_equal_comparable<type_alias::base_type>);
+ static_assert(nt::concepts::greater_than_comparable<type_alias::base_type>);
+ static_assert(nt::concepts::greater_than_equal_comparable<type_alias::base_type>);
+
+ THEN("it does not have <")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::less_than_comparable<type_alias>);
+ }
+
+ THEN("it does not have <=")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::less_than_equal_comparable<type_alias>);
+ }
+
+ THEN("it does not have >")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::greater_than_comparable<type_alias>);
+ }
+
+ THEN("it does not have >=")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::greater_than_equal_comparable<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over a relationally comparable type deriving nt::Relational")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Relational)>;
+ static_assert(nt::concepts::less_than_comparable<type_alias::base_type>);
+ static_assert(nt::concepts::less_than_equal_comparable<type_alias::base_type>);
+ static_assert(nt::concepts::greater_than_comparable<type_alias::base_type>);
+ static_assert(nt::concepts::greater_than_equal_comparable<type_alias::base_type>);
+
+ THEN("it does have <")
+ {
+ STATIC_REQUIRE(nt::concepts::less_than_comparable<type_alias>);
+ }
+
+ THEN("it does have <=")
+ {
+ STATIC_REQUIRE(nt::concepts::less_than_equal_comparable<type_alias>);
+ }
+
+ THEN("it does have >")
+ {
+ STATIC_REQUIRE(nt::concepts::greater_than_comparable<type_alias>);
+ }
+
+ THEN("it does have >=")
+ {
+ STATIC_REQUIRE(nt::concepts::greater_than_equal_comparable<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over a type that is not relationally comparable not deriving nt::Relational")
+ {
+ using type_alias = nt::new_type<std::istream, struct tag>;
+ static_assert(!nt::concepts::less_than_comparable<type_alias::base_type>);
+ static_assert(!nt::concepts::less_than_equal_comparable<type_alias::base_type>);
+ static_assert(!nt::concepts::greater_than_comparable<type_alias::base_type>);
+ static_assert(!nt::concepts::greater_than_equal_comparable<type_alias::base_type>);
+
+ THEN("it does not have <")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::less_than_comparable<type_alias>);
+ }
+
+ THEN("it does not have <=")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::less_than_equal_comparable<type_alias>);
+ }
+
+ THEN("it does not have >")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::greater_than_comparable<type_alias>);
+ }
+
+ THEN("it does not have >=")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::greater_than_equal_comparable<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over a type that is not relationally comparable deriving nt::Relational")
+ {
+ using type_alias = nt::new_type<std::istream, struct tag, deriving(nt::Relational)>;
+ static_assert(!nt::concepts::less_than_comparable<type_alias::base_type>);
+ static_assert(!nt::concepts::less_than_equal_comparable<type_alias::base_type>);
+ static_assert(!nt::concepts::greater_than_comparable<type_alias::base_type>);
+ static_assert(!nt::concepts::greater_than_equal_comparable<type_alias::base_type>);
+
+ THEN("it does not have <")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::less_than_comparable<type_alias>);
+ }
+
+ THEN("it does not have <=")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::less_than_equal_comparable<type_alias>);
+ }
+
+ THEN("it does not have >")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::greater_than_comparable<type_alias>);
+ }
+
+ THEN("it does not have >=")
+ {
+ STATIC_REQUIRE_FALSE(nt::concepts::greater_than_equal_comparable<type_alias>);
+ }
+ }
+}
+
+SCENARIO("Relational Comparisons")
+{
+ GIVEN("A new_type over a relationally comparable type deriving nt::Relational")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Relational)>;
+ static_assert(nt::concepts::less_than_comparable<type_alias::base_type>);
+ static_assert(nt::concepts::less_than_equal_comparable<type_alias::base_type>);
+ static_assert(nt::concepts::greater_than_comparable<type_alias::base_type>);
+ static_assert(nt::concepts::greater_than_equal_comparable<type_alias::base_type>);
+
+ THEN("comparing two instances using < yields the same result as it does for the base type")
+ {
+ REQUIRE((type_alias{1} < type_alias{2}) == (1 < 2));
+ REQUIRE((type_alias{2} < type_alias{1}) == (2 < 1));
+ }
+
+ THEN("comparing two instances using <= yields the same result as it does for the base type")
+ {
+ REQUIRE((type_alias{1} <= type_alias{2}) == (1 <= 2));
+ REQUIRE((type_alias{2} <= type_alias{1}) == (2 <= 1));
+ }
+
+ THEN("comparing two instances using > yields the same result as it does for the base type")
+ {
+ REQUIRE((type_alias{1} > type_alias{2}) == (1 > 2));
+ REQUIRE((type_alias{2} > type_alias{1}) == (2 > 1));
+ }
+
+ THEN("comparing two instances using >= yields the same result as it does for the base type")
+ {
+ REQUIRE((type_alias{1} >= type_alias{2}) == (1 >= 2));
+ REQUIRE((type_alias{2} >= type_alias{1}) == (2 >= 1));
+ }
+ }
+}
+
+SCENARIO("Nothrow Relational Comparison")
+{
+ GIVEN("A new_type over a nothrow relationally comparable type deriving nt::Relational")
+ {
+ using type_alias = nt::new_type<int, struct tag, deriving(nt::Relational)>;
+ static_assert(nt::concepts::nothrow_less_than_comparable<type_alias::base_type>);
+ static_assert(nt::concepts::nothrow_less_than_equal_comparable<type_alias::base_type>);
+ static_assert(nt::concepts::nothrow_greater_than_comparable<type_alias::base_type>);
+ static_assert(nt::concepts::nothrow_greater_than_equal_comparable<type_alias::base_type>);
+
+ THEN("it is nothrow-comparable using < ")
+ {
+ STATIC_REQUIRE(nt::concepts::nothrow_less_than_comparable<type_alias>);
+ }
+
+ THEN("it is nothrow-comparable using <= ")
+ {
+ STATIC_REQUIRE(nt::concepts::nothrow_less_than_equal_comparable<type_alias>);
+ }
+
+ THEN("it is nothrow-comparable using > ")
+ {
+ STATIC_REQUIRE(nt::concepts::nothrow_greater_than_comparable<type_alias>);
+ }
+
+ THEN("it is nothrow-comparable using >= ")
+ {
+ STATIC_REQUIRE(nt::concepts::nothrow_greater_than_equal_comparable<type_alias>);
+ }
+ }
+
+ GIVEN("A new_type over a non-nothrow relationally comparable type deriving nt::Relational")
+ {
+ struct strange_type
+ {
+ auto constexpr operator<(strange_type const &) const noexcept(false) -> bool
+ {
+ return false;
+ }
+ auto constexpr operator>(strange_type const &) const noexcept(false) -> bool
+ {
+ return false;
+ }
+ auto constexpr operator<=(strange_type const &) const noexcept(false) -> bool
+ {
+ return false;
+ }
+ auto constexpr operator>=(strange_type const &) const noexcept(false) -> bool
+ {
+ return false;
+ }
+ };
+
+ using type_alias = nt::new_type<strange_type, struct tag, deriving(nt::Relational)>;
+ static_assert(nt::concepts::less_than_comparable<type_alias::base_type> &&
+ !nt::concepts::nothrow_less_than_comparable<type_alias::base_type>);
+ static_assert(nt::concepts::less_than_equal_comparable<type_alias::base_type> &&
+ !nt::concepts::nothrow_less_than_equal_comparable<type_alias::base_type>);
+ static_assert(nt::concepts::greater_than_comparable<type_alias::base_type> &&
+ !nt::concepts::nothrow_greater_than_comparable<type_alias::base_type>);
+ static_assert(nt::concepts::greater_than_equal_comparable<type_alias::base_type> &&
+ !nt::concepts::nothrow_greater_than_equal_comparable<type_alias::base_type>);
+
+ THEN("it is not nothrow-comparable using < ")
+ {
+ STATIC_REQUIRE(nt::concepts::less_than_comparable<type_alias> && !nt::concepts::nothrow_less_than_comparable<type_alias>);
+ }
+
+ THEN("it is not nothrow-comparable using <= ")
+ {
+ STATIC_REQUIRE(nt::concepts::less_than_equal_comparable<type_alias> && !nt::concepts::nothrow_less_than_equal_comparable<type_alias>);
+ }
+
+ THEN("it is not nothrow-comparable using > ")
+ {
+ STATIC_REQUIRE(nt::concepts::greater_than_comparable<type_alias> && !nt::concepts::nothrow_greater_than_comparable<type_alias>);
+ }
+
+ THEN("it is not nothrow-comparable using >= ")
+ {
+ STATIC_REQUIRE(nt::concepts::greater_than_equal_comparable<type_alias> &&
+ !nt::concepts::nothrow_greater_than_equal_comparable<type_alias>);
+ }
+ }
+}
diff --git a/tests/src/threeway_comparison.cpp b/tests/src/threeway_comparison.cpp
new file mode 100644
index 0000000..45a36af
--- /dev/null
+++ b/tests/src/threeway_comparison.cpp
@@ -0,0 +1,52 @@
+#include "newtype/newtype.hpp"
+
+#include <catch2/catch_template_test_macros.hpp>
+#include <catch2/catch_test_macros.hpp>
+
+#include <compare>
+#include <string>
+#include <vector>
+
+TEMPLATE_TEST_CASE("Scenario: Three-way Comparison", "[comparison]", int, std::string, std::vector<double>)
+{
+ GIVEN("A new_type over a <=> comparable base type deriving nt::ThreewayCompare")
+ {
+ using type_alias = nt::new_type<TestType, struct tag, deriving(nt::ThreewayCompare)>;
+ static_assert(std::three_way_comparable<TestType>);
+
+ THEN("it is equality-comparable")
+ {
+ STATIC_REQUIRE(nt::concepts::equality_comparable<type_alias>);
+ }
+
+ THEN("it is inequality-comparable")
+ {
+ STATIC_REQUIRE(nt::concepts::inequality_comparable<type_alias>);
+ }
+
+ THEN("it is three-way comparable")
+ {
+ STATIC_REQUIRE(std::three_way_comparable<type_alias>);
+ }
+
+ THEN("it is less-than comparable")
+ {
+ STATIC_REQUIRE(nt::concepts::less_than_comparable<type_alias>);
+ }
+
+ THEN("it is less-than-equal comparable")
+ {
+ STATIC_REQUIRE(nt::concepts::greater_than_equal_comparable<type_alias>);
+ }
+
+ THEN("it is greater-than comparable")
+ {
+ STATIC_REQUIRE(nt::concepts::less_than_comparable<type_alias>);
+ }
+
+ THEN("it is greater-than-equal comparable")
+ {
+ STATIC_REQUIRE(nt::concepts::greater_than_equal_comparable<type_alias>);
+ }
+ }
+} \ No newline at end of file