From d3987d09b3e89316fc39e1681de0432eebec35df Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 21 Dec 2023 16:14:04 +0100 Subject: new_type: simplify implementation --- .vscode/c_cpp_properties.json | 3 +- .vscode/settings.json | 5 +- conanfile.py | 4 +- source/doc/CMakeLists.txt | 8 +- source/doc/src/index.rst | 39 +- source/examples/CMakeLists.txt | 17 +- source/lib/include/newtype/newtype.hpp | 845 ++++++++++++------------------- source/tests/src/threeway_comparison.cpp | 2 +- 8 files changed, 372 insertions(+), 551 deletions(-) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index eeb9f71..2091cd3 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -9,8 +9,7 @@ "compilerPath": "/usr/bin/g++", "cppStandard": "c++20", "intelliSenseMode": "gcc-x64", - "configurationProvider": "ms-vscode.cmake-tools", - "compileCommands": "${workspaceFolder}/build/compile_commands.json" + "configurationProvider": "ms-vscode.cmake-tools" } ], "version": 4 diff --git a/.vscode/settings.json b/.vscode/settings.json index 8db3e08..72ff434 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,8 @@ { // CMake Configuration - "cmake.configureOnOpen": false, + "cmake.configureOnOpen": true, "cmake.useCMakePresets": "always", "cmake.sourceDirectory": "${workspaceFolder}/source", - "cmake.skipConfigureIfCachePresent": true, // C++ Configuration "[cpp]": { @@ -11,7 +10,7 @@ }, "C_Cpp.autoAddFileAssociations": false, "C_Cpp.intelliSenseEngine": "default", - "C_Cpp.errorSquiggles": "enabledIfIncludesResolve", + "C_Cpp.errorSquiggles": "enabled", "C_Cpp.autocomplete": "default", // RST Configuration diff --git a/conanfile.py b/conanfile.py index 4616c8a..222ac72 100644 --- a/conanfile.py +++ b/conanfile.py @@ -37,8 +37,8 @@ class NewtypeConan(ConanFile): def build_requirements(self): self.tool_requires("cmake/[>3.25]") - self.tool_requires("ninja/[~1.11]") - self.test_requires("catch2/[~3.3]") + self.tool_requires("ninja/[>1.11]") + self.test_requires("catch2/[>3.3]") def generate(self): toolchain = CMakeToolchain(self, generator="Ninja Multi-Config") diff --git a/source/doc/CMakeLists.txt b/source/doc/CMakeLists.txt index 616b040..7a9e27d 100644 --- a/source/doc/CMakeLists.txt +++ b/source/doc/CMakeLists.txt @@ -44,9 +44,10 @@ add_custom_target("docs" add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/html/index.html" COMMAND "${DOCENV_DIR}/bin/sphinx-build" + "-q" "-b" "singlehtml" - "-q" + "-E" "${CMAKE_CURRENT_SOURCE_DIR}/src" "${CMAKE_CURRENT_BINARY_DIR}/html" DEPENDS ${SOURCES} @@ -56,9 +57,10 @@ add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/html/index.html" add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/man/newtype.3" COMMAND "${DOCENV_DIR}/bin/sphinx-build" + "-q" "-b" "man" - "-q" + "-E" "${CMAKE_CURRENT_SOURCE_DIR}/src" "${CMAKE_CURRENT_BINARY_DIR}/man" DEPENDS ${SOURCES} @@ -66,7 +68,7 @@ add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/man/newtype.3" COMMENT "Compiling man page" ) -install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/html" +install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/singlehtml" TYPE DOC PATTERN ".doctrees" EXCLUDE PATTERN ".buildinfo" EXCLUDE diff --git a/source/doc/src/index.rst b/source/doc/src/index.rst index 290f17e..88925aa 100644 --- a/source/doc/src/index.rst +++ b/source/doc/src/index.rst @@ -22,8 +22,8 @@ Example Usage All examples shown in this section can be found in the directory :literal:`examples/src` within the source root. -:ref:`new-type-usage-basic` below demonstrates the basic usage of :cpp:class:`new_type`. -In it, :cpp:class:`new_type` is used to create thre new strong aliases :literal:`Width`, :literal:`Height`, and :literal:`Area` that all alias :literal:`unsigned int`. +:ref:`new-type-usage-basic` below illustrates the basic usage of :cpp:class:`new_type`. +In it, :cpp:class:`new_type` is used to create three new strong aliases :literal:`Width`, :literal:`Height`, and :literal:`Area`, all aliasing :literal:`unsigned int`. .. literalinclude:: ../../examples/src/basic_usage.cpp :language: c++ @@ -31,22 +31,22 @@ In it, :cpp:class:`new_type` is used to create thre new strong aliases :literal: :name: new-type-usage-basic :caption: Basic usage of :cpp:class:`new_type` -However, using :cpp:class:`new_type` in this fashion seem quite cumbersome. +However, using :cpp:class:`new_type` in this fashion is quite cumbersome. Starting from the bottom, :literal:`unsigned int` can normally be shifted on to any :cpp:class:`std::basic_ostream`, like :cpp:var:`std::cout` in this example. -Since printing values, among other things, is a common scenario, ``newtype`` provides facilities to support automatic derivation of supporting functions. +Since printing values, among others, is a common use case, ``newtype`` provides facilities to support automatic derivation of supporting functions. .. literalinclude:: ../../examples/src/basic_usage_with_show.cpp - :emphasize-lines: 1,2,9 + :emphasize-lines: 7,38 :language: c++ :linenos: :name: new-type-usage-basic-show :caption: Improved usability using the :cpp:var:`Show` derivation tag -:ref:`new-type-usage-basic-show` demonstrates how the function template :cpp:func:`deriving` can be used to enable automatic derivation of the stream output operator for :literal:`Area`. +:ref:`new-type-usage-basic-show` illustrates how the function template :cpp:func:`deriving` can be used to enable automatic derivation of the stream output operator for :literal:`Area`. Similarly, it is possible to derive the stream input operators of :literal:`Width` and :literal:`Height`, as shown in :ref:`new-type-usage-basic-read` below. .. literalinclude:: ../../examples/src/basic_usage_with_read.cpp - :emphasize-lines: 7,8,31,32,34 + :emphasize-lines: 5,6,29,30,32 :language: c++ :linenos: :name: new-type-usage-basic-read @@ -71,7 +71,10 @@ Class template :cpp:class:`new_type` new_type The class template :cpp:class:`new_type` is designed to allow the creation of new types based on existing types. - Similarly to the Haskell newtype, this class template creates a new type that is layout equivalent to the underlying type. + Similarly to the newtype keyword in Haskell, this class template creates a new type that is layout equivalent to the underlying type. + During creation of the of new strong type, features can be derived using :cpp:any:`nt::deriving`. + Actual feature availability depends on the :cpp:any:`BaseType` chosen for :cpp:class:`new_type` instance. + For example, deriving :cpp:any:`nt::Show` for a :cpp:class:`new_type` instance over a type not supporting output on a standard output stream, will not cause the instance to be output-streamable. :tparam BaseType: |BaseTypeDoc| :tparam TagType: |TagTypeDoc| @@ -89,25 +92,25 @@ Class template :cpp:class:`new_type` .. cpp:type:: iterator = typename BaseType::iterator - :enablement: This type alias shall be defined iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` has a member type :cpp:type:`iterator ` and the :cpp:var:`derivation clause ` contains :cpp:var:`Iterable`. + :enablement: This type alias is defined iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` has a member type :cpp:type:`iterator ` and the :cpp:var:`derivation clause ` contains :cpp:var:`Iterable`. .. versionadded:: 1.1.0 .. cpp:type:: const_iterator = typename BaseType::const_iterator - :enablement: This type alias shall be defined iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` has a member type :cpp:type:`const_iterator ` and the :cpp:var:`derivation clause ` contains :cpp:var:`Iterable`. + :enablement: This type alias is defined iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` has a member type :cpp:type:`const_iterator ` and the :cpp:var:`derivation clause ` contains :cpp:var:`Iterable`. .. versionadded:: 1.1.0 .. cpp:type:: reverse_iterator = typename BaseType::reverse_iterator - :enablement: This type alias shall be defined iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` has a member type :cpp:type:`reverse_iterator ` and the :cpp:var:`derivation clause ` contains :cpp:var:`Iterable`. + :enablement: This type alias is defined iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` has a member type :cpp:type:`reverse_iterator ` and the :cpp:var:`derivation clause ` contains :cpp:var:`Iterable`. .. versionadded:: 1.1.0 .. cpp:type:: const_reverse_iterator = typename BaseType::const_reverse_iterator - :enablement: This type alias shall be defined iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` has a member type :cpp:type:`const_reverse_iterator ` and the :cpp:var:`derivation clause ` contains :cpp:var:`Iterable`. + :enablement: This type alias is defined iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` has a member type :cpp:type:`const_reverse_iterator ` and the :cpp:var:`derivation clause ` contains :cpp:var:`Iterable`. .. versionadded:: 1.1.0 @@ -117,18 +120,18 @@ Class template :cpp:class:`new_type` **Constructors** - .. cpp:function:: constexpr new_type() + .. cpp:function:: constexpr new_type() noexcept(std::is_nothrow_default_constructible_v) Construct a new instance of this :cpp:class:`new_type` by default constructing the contained object. :throws: Any exception thrown by the default constructor of this :cpp:class:`new_type`'s :cpp:type:`base_type`. - This constructor shall be noexcept iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *nothrow default-construtible*. - :enablement: This constructor shall be defined as :literal:`= default` iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *default-construtible*. - Otherwise, this constructor shall be explicitely deleted. + This constructor is noexcept iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *nothrow default-construtible*. + :enablement: This constructor is defined as :cpp:expr:`= default` iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *default-construtible*. + Otherwise, this constructor is declared as explicitely deleted. - .. cpp:function:: constexpr new_type(new_type const & other) + .. cpp:function:: constexpr new_type(new_type const & other) noexcept(std::is_nothrow_copy_constructible_v) - Construct a new instance of this :cpp:class:`new_type` by copy-constructing the contained object using the value contained by :literal:`other`. + Construct a new instance of this :cpp:class:`new_type` by copy-constructing the contained object using the value contained by :cpp:any:`other`. :param other: An existing instance of this :cpp:class:`new_type` :throws: Any exception thrown by the copy-constructor of this :cpp:class:`new_type`'s :cpp:type:`base_type`. diff --git a/source/examples/CMakeLists.txt b/source/examples/CMakeLists.txt index 45d1638..73d961e 100644 --- a/source/examples/CMakeLists.txt +++ b/source/examples/CMakeLists.txt @@ -1,11 +1,14 @@ -function(add_example NAME) - add_executable("ex_${NAME}" "src/${NAME}.cpp") - target_link_libraries("ex_${NAME}" "${PROJECT_NAME}") -endfunction() +file(GLOB SOURCES + RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" + CONFIGURE_DEPENDS + "src/*.cpp" +) -add_example("basic_usage") -add_example("basic_usage_with_show") -add_example("basic_usage_with_read") +foreach(EXAMPLE IN LISTS SOURCES) + get_filename_component(NAME "${EXAMPLE}" NAME_WE) + add_executable("ex_${NAME}" "${EXAMPLE}") + target_link_libraries("ex_${NAME}" "newtype::newtype") +endforeach() install(DIRECTORY "src/" TYPE DOC diff --git a/source/lib/include/newtype/newtype.hpp b/source/lib/include/newtype/newtype.hpp index 3654afd..25c7313 100644 --- a/source/lib/include/newtype/newtype.hpp +++ b/source/lib/include/newtype/newtype.hpp @@ -2,6 +2,7 @@ #define NEWTYPE_NEWTYPE_HPP #include +#include #include #include #include @@ -13,196 +14,70 @@ namespace nt namespace impl { - inline namespace storage { - - template + template struct new_type_storage { - constexpr new_type_storage() noexcept(std::is_nothrow_default_constructible_v) - : 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> - struct new_type_constructor : new_type_storage - { - using new_type_storage::new_type_storage; + T m_value; }; - - template - struct new_type_constructor : new_type_storage - { - using new_type_storage::new_type_storage; - - constexpr new_type_constructor() = delete; - }; - - template> - struct new_type_copy_constructor : new_type_constructor - { - using new_type_constructor::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 - struct new_type_copy_constructor : new_type_constructor - { - using new_type_constructor::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> - struct new_type_move_constructor : new_type_copy_constructor - { - using new_type_copy_constructor::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 - struct new_type_move_constructor : new_type_copy_constructor - { - using new_type_copy_constructor::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> - struct new_type_copy_assignment : new_type_move_constructor - { - using new_type_move_constructor::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 - struct new_type_copy_assignment : new_type_move_constructor - { - using new_type_move_constructor::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> - struct new_type_move_assignment : new_type_copy_assignment - { - using new_type_copy_assignment::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 - struct new_type_move_assignment : new_type_copy_assignment - { - using new_type_copy_assignment::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 storage inline namespace member_types { template> - struct new_type_iterator + struct new_type_iterator : new_type_storage { }; template - struct new_type_iterator> + struct new_type_iterator> : new_type_storage { using iterator = typename T::iterator; }; - template> - struct new_type_const_iterator + template> + struct new_type_const_iterator : new_type_iterator { }; template - struct new_type_const_iterator> + struct new_type_const_iterator> : new_type_iterator { using const_iterator = typename T::const_iterator; }; - template> - struct new_type_reverse_iterator + template> + struct new_type_reverse_iterator : new_type_const_iterator { }; template - struct new_type_reverse_iterator> + struct new_type_reverse_iterator> : new_type_const_iterator { using reverse_iterator = typename T::reverse_iterator; }; - template> - struct new_type_const_reverse_iterator + template> + struct new_type_const_reverse_iterator : new_type_reverse_iterator { }; template - struct new_type_const_reverse_iterator> + struct new_type_const_reverse_iterator> : new_type_reverse_iterator { using const_reverse_iterator = typename T::const_reverse_iterator; }; template - struct new_type_iterator_types - : new_type_iterator - , new_type_const_iterator - , new_type_reverse_iterator - , new_type_const_reverse_iterator + struct new_type_iterator_types : new_type_const_reverse_iterator { }; } // namespace member_types - } // namespace impl + } // namespace impl inline namespace lib { @@ -293,6 +168,14 @@ namespace nt inline namespace comparability { + template + concept nothrow_three_way_comparable = requires(SubjectType lhs, SubjectType rhs) { + requires std::three_way_comparable; + { + lhs <=> rhs + } noexcept; + }; + template concept equality_comparable = requires(SubjectType lhs, SubjectType rhs) { { @@ -778,96 +661,14 @@ namespace nt } template - class new_type - : impl::new_type_move_assignment - , public impl::new_type_iterator_types> + class new_type : public impl::new_type_iterator_types> { static_assert(!std::is_reference_v, "The base type must not be a reference type"); static_assert(!std::is_void_v>, "The base type must not be possibly cv-qualified void"); static_assert(!derives, "Cannot derive both nt::Relational and nt::ThreewayCompare at the same time."); - template BaseTypeT, - typename TagTypeT, - nt::derives auto DerivationClauseV> - auto friend operator>>(std::basic_istream &, new_type &) noexcept( - nt::concepts::nothrow_input_streamable) -> std::basic_istream &; - - template auto DerivationClauseV> - auto constexpr friend - operator+=(new_type & lhs, - new_type const & rhs) noexcept(nt::concepts::nothrow_compound_addable) - -> new_type &; - - template auto DerivationClauseV> - auto constexpr friend - operator-=(new_type & lhs, - new_type const & rhs) noexcept(nt::concepts::nothrow_compound_subtractable) - -> new_type &; - - template auto DerivationClauseV> - auto constexpr friend - operator*=(new_type & lhs, - new_type const & rhs) noexcept(nt::concepts::nothrow_compound_multipliable) - -> new_type &; - - template auto DerivationClauseV> - auto constexpr friend - operator/=(new_type & lhs, - new_type const & rhs) noexcept(nt::concepts::nothrow_compound_divisible) - -> new_type &; - - template auto DerivationClauseV> - auto constexpr friend begin(new_type & obj) -> - typename new_type::iterator; - - template auto DerivationClauseV> - auto constexpr friend begin(new_type const & obj) -> - typename new_type::const_iterator; - - template auto DerivationClauseV> - auto constexpr friend cbegin(new_type const & obj) -> - typename new_type::const_iterator; - - template auto DerivationClauseV> - auto constexpr friend rbegin(new_type & obj) -> - typename new_type::reverse_iterator; - - template auto DerivationClauseV> - auto constexpr friend rbegin(new_type const & obj) -> - typename new_type::const_reverse_iterator; - - template auto DerivationClauseV> - auto constexpr friend crbegin(new_type const & obj) -> - typename new_type::const_reverse_iterator; - - template auto DerivationClauseV> - auto constexpr friend end(new_type & obj) -> - typename new_type::iterator; - - template auto DerivationClauseV> - auto constexpr friend end(new_type const & obj) -> - typename new_type::const_iterator; - - template auto DerivationClauseV> - auto constexpr friend cend(new_type const & obj) -> - typename new_type::const_iterator; - - template auto DerivationClauseV> - auto constexpr friend rend(new_type & obj) -> - typename new_type::reverse_iterator; - - template auto DerivationClauseV> - auto constexpr friend rend(new_type const & obj) -> - typename new_type::const_reverse_iterator; - - template auto DerivationClauseV> - auto constexpr friend crend(new_type const & obj) -> - typename new_type::const_reverse_iterator; - - using super = impl::new_type_move_assignment; + using super = impl::new_type_iterator_types>; public: using base_type = BaseType; @@ -876,11 +677,29 @@ namespace nt auto constexpr static derivation_clause = DerivationClause; - using super::super; + constexpr new_type() noexcept(std::is_nothrow_default_constructible_v) + requires std::constructible_from + = default; + + constexpr new_type(new_type const &) noexcept(std::is_nothrow_copy_constructible_v) + requires std::copy_constructible + = default; + + constexpr new_type(new_type &&) noexcept(std::is_nothrow_move_constructible_v) + requires std::move_constructible + = default; + + constexpr new_type(BaseType const & base) noexcept(std::is_nothrow_copy_constructible_v) + requires std::copy_constructible + : super{base} + { + } - constexpr new_type() noexcept(std::is_nothrow_default_constructible_v) = default; - constexpr new_type(new_type const &) noexcept(std::is_nothrow_copy_constructible_v) = default; - constexpr new_type(new_type &&) noexcept(std::is_nothrow_move_constructible_v) = default; + constexpr new_type(BaseType && base) noexcept(std::is_nothrow_move_constructible_v) + requires std::move_constructible + : super{std::move(base)} + { + } auto constexpr operator=(new_type const &) noexcept(std::is_nothrow_copy_assignable_v) -> new_type & = default; auto constexpr operator=(new_type &&) noexcept(std::is_nothrow_move_assignable_v) -> new_type & = default; @@ -899,369 +718,365 @@ namespace nt template explicit constexpr operator base_type() const noexcept(std::is_nothrow_copy_constructible_v) - requires(!nt::derives) + requires(nt::doesnt_derive) { return decay(); } - template - requires(nt::derives) - auto constexpr operator->() noexcept -> BaseType * + template BaseTypeT = BaseType> + auto friend operator>>(std::basic_istream & in, + new_type & obj) noexcept(nt::concepts::nothrow_input_streamable) + -> std::basic_istream & + requires nt::derives { - return std::addressof(this->m_value); + return in >> obj.m_value; } - template - requires(nt::derives) - auto constexpr operator->() const noexcept -> BaseType const * + template BaseTypeT = BaseType> + auto friend operator<<(std::basic_ostream & out, + new_type const & obj) noexcept(nt::concepts::nothrow_output_streamable) + -> std::basic_ostream & + requires nt::derives { - return std::addressof(this->m_value); + return out << obj.m_value; } - template auto DerivationClauseV = DerivationClause> - auto constexpr begin() -> typename new_type::iterator + /** + * @brief Check two instances of nt::new_type for equality. + * + * @param lhs The left-hand side of the comparison. + * @param rhs The right-hand side of the comparison. + * + * @note This overload is available iff. the underlying type is equality-comparable and this nt::new_type does not derive + * nt::ThreewayCompare. + * + * @return The value returned by the comparison of the underlying objects. + * + * @throws Any exception thrown by the comparison operator of the underlying objects of @ref lhs and @ref rhs. `noexcept` iff. + * new_type::base_type is nothrow equality-comparable. + */ + + // THREE-WAY ORDER AND EQUAL + + auto constexpr operator<=>(new_type const & rhs) const noexcept(nt::concepts::nothrow_three_way_comparable) + requires(std::three_way_comparable && nt::derives) { - return this->m_value.begin(); + return decay() <=> rhs.decay(); } - template auto DerivationClauseV = DerivationClause> - auto constexpr begin() const -> typename new_type::const_iterator + auto constexpr operator==(new_type const & rhs) const -> bool + requires(nt::concepts::equality_comparable && nt::derives) { - return this->m_value.begin(); + return decay() == rhs.decay(); } - template auto DerivationClauseV = DerivationClause> - auto constexpr cbegin() const -> typename new_type::const_iterator - { - return this->m_value.cbegin(); - } + // NOT THREE-WAY EQUAL - template auto DerivationClauseV = DerivationClause> - auto constexpr rbegin() -> typename new_type::reverse_iterator + auto constexpr operator==(new_type const & rhs) const noexcept(nt::concepts::nothrow_equality_comparable) -> bool + requires(nt::concepts::equality_comparable && nt::doesnt_derive) { - return this->m_value.rbegin(); + return decay() == rhs.decay(); } - template auto DerivationClauseV = DerivationClause> - auto constexpr rbegin() const -> typename new_type::const_reverse_iterator + // BASE EQUAL + + auto constexpr operator==(BaseType const & rhs) const noexcept(nt::concepts::nothrow_equality_comparable) -> bool + requires(nt::concepts::equality_comparable && nt::derives) { - return this->m_value.rbegin(); + return decay() == rhs; } - template auto DerivationClauseV = DerivationClause> - auto constexpr crbegin() const -> typename new_type::const_reverse_iterator + // NOT THREE-WAY EQUAL + + auto constexpr operator!=(new_type const & rhs) const noexcept(nt::concepts::nothrow_inequality_comparable) -> bool + requires(nt::concepts::inequality_comparable && nt::doesnt_derive) { - return this->m_value.crbegin(); + return decay() != rhs.decay(); } - template auto DerivationClauseV = DerivationClause> - auto constexpr end() -> typename new_type::iterator + // NOT THREE-WAY ORDER + + auto constexpr operator<(new_type const & rhs) const noexcept(nt::concepts::nothrow_less_than_comparable) -> bool + requires(nt::concepts::less_than_comparable && // + nt::doesnt_derive && // + nt::derives) { - return this->m_value.end(); + return decay() < rhs.decay(); } - template auto DerivationClauseV = DerivationClause> - auto constexpr end() const -> typename new_type::const_iterator + auto constexpr operator<=(new_type const & rhs) const noexcept(nt::concepts::nothrow_less_than_equal_comparable) -> bool + requires(nt::concepts::less_than_equal_comparable && // + nt::doesnt_derive && // + nt::derives) { - return this->m_value.end(); + return decay() <= rhs.decay(); } - template auto DerivationClauseV = DerivationClause> - auto constexpr cend() const -> typename new_type::const_iterator + auto constexpr operator>(new_type const & rhs) const noexcept(nt::concepts::nothrow_greater_than_comparable) -> bool + requires(nt::concepts::greater_than_comparable && // + nt::doesnt_derive && // + nt::derives) { - return this->m_value.cend(); + return decay() > rhs.decay(); } - template auto DerivationClauseV = DerivationClause> - auto constexpr rend() -> typename new_type::reverse_iterator + auto constexpr operator>=(new_type const & rhs) const noexcept(nt::concepts::nothrow_greater_than_equal_comparable) -> bool + requires(nt::concepts::greater_than_equal_comparable && // + nt::doesnt_derive && // + nt::derives) { - return this->m_value.rend(); + return decay() >= rhs.decay(); } - template auto DerivationClauseV = DerivationClause> - auto constexpr rend() const -> typename new_type::const_reverse_iterator + // ARITHMETIC + + auto constexpr operator+(new_type const & rhs) const noexcept(nt::concepts::nothrow_addable) -> new_type + requires(nt::concepts::addable && nt::derives) { - return this->m_value.rend(); + return {decay() + rhs.decay()}; } - template auto DerivationClauseV = DerivationClause> - auto constexpr crend() const -> typename new_type::const_reverse_iterator + auto constexpr operator+=(new_type const & rhs) const noexcept(nt::concepts::nothrow_compound_addable) + -> new_type & requires(nt::concepts::compound_addable && nt::derives) { + return this->m_value += rhs.decay(); + } + + auto constexpr + operator-(new_type const & rhs) const noexcept(nt::concepts::nothrow_subtractable) -> new_type + requires(nt::concepts::subtractable && nt::derives) { - return this->m_value.crend(); + return {decay() - rhs.decay()}; } - }; - template auto DerivationClause> - auto constexpr - operator==(new_type const & lhs, - new_type const & rhs) noexcept(nt::concepts::nothrow_equality_comparable) -> bool - { - return lhs.decay() == rhs.decay(); - } + auto constexpr operator-=(new_type const & rhs) const noexcept(nt::concepts::nothrow_compound_subtractable) + -> new_type & requires(nt::concepts::compound_subtractable && nt::derives) { + return this->m_value -= rhs.decay(); + } - template auto DerivationClause> - auto constexpr operator==(new_type const & lhs, - new_type const & rhs) -> bool - { - return lhs.decay() == rhs.decay(); - } + auto constexpr + operator*(new_type const & rhs) const noexcept(nt::concepts::nothrow_multipliable) -> new_type + requires(nt::concepts::multipliable && nt::derives) + { + return {decay() * rhs.decay()}; + } - template auto DerivationClause> - auto constexpr operator==(new_type const & lhs, - BaseType const & rhs) noexcept(nt::concepts::nothrow_equality_comparable) -> bool - { - return lhs.decay() == rhs; - } + auto constexpr operator*=(new_type const & rhs) const noexcept(nt::concepts::nothrow_compound_multipliable) + -> new_type & requires(nt::concepts::compound_multipliable && nt::derives) { + return this->m_value *= rhs.decay(); + } - template auto DerivationClause> - auto constexpr - operator==(BaseType const & lhs, - new_type const & rhs) noexcept(nt::concepts::nothrow_equality_comparable) -> bool - { - return lhs == rhs.decay(); - } + auto constexpr + operator/(new_type const & rhs) const noexcept(nt::concepts::nothrow_divisible) -> new_type + requires(nt::concepts::divisible && nt::derives) + { + return {decay() / rhs.decay()}; + } - template auto DerivationClause> - auto constexpr operator<=>(new_type const & lhs, - new_type const & rhs) - { - return lhs.decay() <=> rhs.decay(); - } + auto constexpr operator/=(new_type const & rhs) const noexcept(nt::concepts::nothrow_compound_divisible) + -> new_type & requires(nt::concepts::compound_divisible && nt::derives) { + return this->m_value /= rhs.decay(); + } - template - auto constexpr - operator!=(new_type const & lhs, - new_type const & rhs) noexcept(nt::concepts::nothrow_inequality_comparable) -> bool - { - return lhs.decay() != rhs.decay(); - } + // INDIRECTION - template auto DerivationClause> - auto constexpr operator!=(new_type const & lhs, - BaseType const & rhs) noexcept(nt::concepts::nothrow_inequality_comparable) -> bool - { - return lhs.decay() != rhs; - } + // template + // requires(nt::derives) + // auto constexpr operator->() noexcept -> BaseType * + // { + // return std::addressof(this->m_value); + // } - template auto DerivationClause> - auto constexpr - operator!=(BaseType const & lhs, - new_type const & rhs) noexcept(nt::concepts::nothrow_inequality_comparable) -> bool - { - return lhs != rhs.decay(); - } + // template + // requires(nt::derives) + // auto constexpr operator->() const noexcept -> BaseType const * + // { + // return std::addressof(this->m_value); + // } - template auto DerivationClause> - auto constexpr - operator<(new_type const & lhs, - new_type const & rhs) noexcept(nt::concepts::nothrow_less_than_comparable) - { - return lhs.decay() < rhs.decay(); - } + // FREE ITERATORS - template auto DerivationClause> - auto constexpr - operator>(new_type const & lhs, - new_type const & rhs) noexcept(nt::concepts::nothrow_greater_than_comparable) - { - return lhs.decay() > rhs.decay(); - } + template + auto constexpr friend begin(new_type & obj) -> new_type::iterator + requires nt::derives + { + using std::begin; + return begin(obj.m_value); + } - template auto DerivationClause> - auto constexpr - operator<=(new_type const & lhs, - new_type const & rhs) noexcept(nt::concepts::nothrow_less_than_equal_comparable) - { - return lhs.decay() <= rhs.decay(); - } + template + auto constexpr friend begin(new_type const & obj) -> new_type::const_iterator + requires nt::derives + { + using std::begin; + return begin(obj.m_value); + } - template auto DerivationClause> - auto constexpr - operator>=(new_type const & lhs, - new_type const & rhs) noexcept(nt::concepts::nothrow_greater_than_equal_comparable) - { - return lhs.decay() >= rhs.decay(); - } + template + auto constexpr friend cbegin(new_type const & obj) -> new_type::const_iterator + requires nt::derives + { + using std::cbegin; + return cbegin(obj.m_value); + } - template BaseType, - typename TagType, - nt::derives auto DerivationClause> - auto operator<<(std::basic_ostream & output, new_type const & source) noexcept( - nt::concepts::nothrow_output_streamable) -> std::basic_ostream & - { - return output << source.decay(); - } + template + auto constexpr friend rbegin(new_type & obj) -> new_type::reverse_iterator + requires nt::derives + { + using std::rbegin; + return rbegin(obj.m_value); + } - template BaseType, - typename TagType, - nt::derives auto DerivationClause> - auto operator>>(std::basic_istream & input, new_type & target) noexcept( - nt::concepts::nothrow_input_streamable) -> std::basic_istream & - { - return input >> target.m_value; - } + template + auto constexpr friend rbegin(new_type const & obj) -> new_type::const_reverse_iterator + requires nt::derives + { + using std::rbegin; + return rbegin(obj.m_value); + } - template auto DerivationClause> - auto constexpr - operator+(new_type const & lhs, new_type const & rhs) noexcept( - nt::concepts::nothrow_addable && std::is_nothrow_copy_constructible_v) - -> new_type - { - return {lhs.decay() + rhs.decay()}; - } + template + auto constexpr friend crbegin(new_type const & obj) -> new_type::const_reverse_iterator + requires nt::derives + { + using std::crbegin; + return crbegin(obj.m_value); + } - template auto DerivationClause> - auto constexpr - operator+=(new_type & lhs, - new_type const & rhs) noexcept(nt::concepts::nothrow_compound_addable) - -> new_type & - { - lhs.m_value += rhs.m_value; - return lhs; - } + template + auto constexpr friend end(new_type & obj) -> new_type::iterator + requires nt::derives + { + using std::end; + return end(obj.m_value); + } - template auto DerivationClause> - auto constexpr - operator-(new_type const & lhs, new_type const & rhs) noexcept( - nt::concepts::nothrow_subtractable && std::is_nothrow_copy_constructible_v) - -> new_type - { - return {lhs.decay() - rhs.decay()}; - } + template + auto constexpr friend end(new_type const & obj) -> new_type::const_iterator + requires nt::derives + { + using std::end; + return end(obj.m_value); + } - template auto DerivationClause> - auto constexpr - operator-=(new_type & lhs, - new_type const & rhs) noexcept(nt::concepts::nothrow_compound_subtractable) - -> new_type & - { - lhs.m_value -= rhs.m_value; - return lhs; - } + template + auto constexpr friend cend(new_type const & obj) -> new_type::const_iterator + requires nt::derives + { + using std::cend; + return cend(obj.m_value); + } - template auto DerivationClause> - auto constexpr - operator*(new_type const & lhs, new_type const & rhs) noexcept( - nt::concepts::nothrow_multipliable && std::is_nothrow_copy_constructible_v) - -> new_type - { - return {lhs.decay() * rhs.decay()}; - } + template + auto constexpr friend rend(new_type & obj) -> new_type::reverse_iterator + requires nt::derives + { + using std::rend; + return rend(obj.m_value); + } - template auto DerivationClause> - auto constexpr - operator*=(new_type & lhs, - new_type const & rhs) noexcept(nt::concepts::nothrow_compound_multipliable) - -> new_type & - { - lhs.m_value *= rhs.m_value; - return lhs; - } + template + auto constexpr friend rend(new_type const & obj) -> new_type::const_reverse_iterator + requires nt::derives + { + using std::rend; + return rend(obj.m_value); + } - template auto DerivationClause> - auto constexpr - operator/(new_type const & lhs, new_type const & rhs) noexcept( - nt::concepts::nothrow_divisible && std::is_nothrow_copy_constructible_v) - -> new_type - { - return {lhs.decay() / rhs.decay()}; - } + template + auto constexpr friend crend(new_type const & obj) -> new_type::const_reverse_iterator + requires nt::derives + { + using std::crend; + return crend(obj.m_value); + } - template auto DerivationClause> - auto constexpr - operator/=(new_type & lhs, - new_type const & rhs) noexcept(nt::concepts::nothrow_compound_divisible) - -> new_type & - { - lhs.m_value /= rhs.m_value; - return lhs; - } + // MEMBER ITERATORS - template auto DerivationClause> - auto constexpr begin(new_type & obj) -> typename new_type::iterator - { - return begin(obj.m_value); - } + template + auto constexpr begin() -> new_type::iterator + requires nt::derives + { + return this->m_value.begin(); + } - template auto DerivationClause> - auto constexpr begin(new_type const & obj) -> - typename new_type::const_iterator - { - return begin(obj.m_value); - } + template + auto constexpr begin() const -> new_type::const_iterator + requires nt::derives + { + return this->m_value.begin(); + } - template auto DerivationClause> - auto constexpr cbegin(new_type const & obj) -> - typename new_type::const_iterator - { - return cbegin(obj.m_value); - } + template + auto constexpr cbegin() const -> new_type::const_iterator + requires nt::derives + { + return this->m_value.cbegin(); + } - template auto DerivationClause> - auto constexpr rbegin(new_type & obj) -> - typename new_type::reverse_iterator - { - return rbegin(obj.m_value); - } + template + auto constexpr rbegin() -> new_type::reverse_iterator + requires nt::derives + { + return this->m_value.rbegin(); + } - template auto DerivationClause> - auto constexpr rbegin(new_type const & obj) -> - typename new_type::const_reverse_iterator - { - return rbegin(obj.m_value); - } + template + auto constexpr rbegin() const -> new_type::const_reverse_iterator + requires nt::derives + { + return this->m_value.rbegin(); + } - template auto DerivationClause> - auto constexpr crbegin(new_type const & obj) -> - typename new_type::const_reverse_iterator - { - return crbegin(obj.m_value); - } + template + auto constexpr crbegin() const -> new_type::const_reverse_iterator + requires nt::derives + { + return this->m_value.crbegin(); + } - template auto DerivationClause> - auto constexpr end(new_type & obj) -> typename new_type::iterator - { - return end(obj.m_value); - } + template + auto constexpr end() -> new_type::iterator + requires nt::derives + { + return this->m_value.end(); + } - template auto DerivationClause> - auto constexpr end(new_type const & obj) -> - typename new_type::const_iterator - { - return end(obj.m_value); - } + template + auto constexpr end() const -> new_type::const_iterator + requires nt::derives + { + return this->m_value.end(); + } - template auto DerivationClause> - auto constexpr cend(new_type const & obj) -> - typename new_type::const_iterator - { - return cend(obj.m_value); - } + template + auto constexpr cend() const -> new_type::const_iterator + requires nt::derives + { + return this->m_value.cend(); + } - template auto DerivationClause> - auto constexpr rend(new_type & obj) -> - typename new_type::reverse_iterator - { - return rend(obj.m_value); - } + template + auto constexpr rend() -> new_type::reverse_iterator + requires nt::derives + { + return this->m_value.rend(); + } - template auto DerivationClause> - auto constexpr rend(new_type const & obj) -> - typename new_type::const_reverse_iterator - { - return rend(obj.m_value); - } + template + auto constexpr rend() const -> new_type::const_reverse_iterator + requires nt::derives + { + return this->m_value.rend(); + } - template auto DerivationClause> - auto constexpr crend(new_type const & obj) -> - typename new_type::const_reverse_iterator - { - return crend(obj.m_value); - } + template + auto constexpr crend() const -> new_type::const_reverse_iterator + requires nt::derives + { + return this->m_value.crend(); + } + }; } // namespace nt diff --git a/source/tests/src/threeway_comparison.cpp b/source/tests/src/threeway_comparison.cpp index ddca7f3..45a36af 100644 --- a/source/tests/src/threeway_comparison.cpp +++ b/source/tests/src/threeway_comparison.cpp @@ -16,7 +16,7 @@ TEMPLATE_TEST_CASE("Scenario: Three-way Comparison", "[comparison]", int, std::s THEN("it is equality-comparable") { - STA