From 440d47cae6431de3332ac934b6056a970cc1a0d7 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 26 Feb 2025 11:24:59 +0100 Subject: build: remove conan --- .conan/profiles/gnu | 4 - .conan/profiles/std | 4 - .github/FUNDING.yml | 1 - .vscode/c_cpp_properties.json | 16 - .vscode/settings.json | 8 +- CMakeLists.txt | 19 + CMakePresets.json | 30 + conanfile.py | 65 -- doc/.gitignore | 2 + doc/CMakeLists.txt | 81 ++ doc/requirements.txt | 1 + doc/src/conf.py | 48 + doc/src/index.rst | 1271 +++++++++++++++++++++++++ examples/CMakeLists.txt | 15 + examples/src/basic_usage.cpp | 39 + examples/src/basic_usage_with_read.cpp | 37 + examples/src/basic_usage_with_show.cpp | 39 + lib/CMakeLists.txt | 16 + lib/include/newtype/newtype.hpp | 753 +++++++++++++++ source/CMakeLists.txt | 19 - source/doc/.gitignore | 2 - source/doc/CMakeLists.txt | 81 -- source/doc/requirements.txt | 1 - source/doc/src/conf.py | 48 - source/doc/src/index.rst | 1271 ------------------------- source/examples/CMakeLists.txt | 15 - source/examples/src/basic_usage.cpp | 39 - source/examples/src/basic_usage_with_read.cpp | 37 - source/examples/src/basic_usage_with_show.cpp | 39 - source/lib/CMakeLists.txt | 16 - source/lib/include/newtype/newtype.hpp | 753 --------------- source/tests/CMakeLists.txt | 33 - source/tests/src/arithmetic.cpp | 297 ------ source/tests/src/constructors.cpp | 102 -- source/tests/src/conversion.cpp | 133 --- source/tests/src/derivation_clause.cpp | 63 -- source/tests/src/equality_comparison.cpp | 102 -- source/tests/src/hash.cpp | 59 -- source/tests/src/io_operators.cpp | 110 --- source/tests/src/iterable.cpp | 1060 --------------------- source/tests/src/relational_operators.cpp | 249 ----- source/tests/src/threeway_comparison.cpp | 52 - test_package/.gitignore | 1 - test_package/CMakeLists.txt | 13 - test_package/conanfile.py | 21 - test_package/main.cpp | 11 - tests/CMakeLists.txt | 33 + tests/src/arithmetic.cpp | 297 ++++++ tests/src/constructors.cpp | 102 ++ tests/src/conversion.cpp | 133 +++ tests/src/derivation_clause.cpp | 63 ++ tests/src/equality_comparison.cpp | 102 ++ tests/src/hash.cpp | 59 ++ tests/src/io_operators.cpp | 110 +++ tests/src/iterable.cpp | 1060 +++++++++++++++++++++ tests/src/relational_operators.cpp | 249 +++++ tests/src/threeway_comparison.cpp | 52 + 57 files changed, 4614 insertions(+), 4722 deletions(-) delete mode 100644 .conan/profiles/gnu delete mode 100644 .conan/profiles/std delete mode 100644 .github/FUNDING.yml delete mode 100644 .vscode/c_cpp_properties.json create mode 100644 CMakeLists.txt create mode 100644 CMakePresets.json delete mode 100644 conanfile.py create mode 100644 doc/.gitignore create mode 100644 doc/CMakeLists.txt create mode 100644 doc/requirements.txt create mode 100644 doc/src/conf.py create mode 100644 doc/src/index.rst create mode 100644 examples/CMakeLists.txt create mode 100644 examples/src/basic_usage.cpp create mode 100644 examples/src/basic_usage_with_read.cpp create mode 100644 examples/src/basic_usage_with_show.cpp create mode 100644 lib/CMakeLists.txt create mode 100644 lib/include/newtype/newtype.hpp delete mode 100644 source/CMakeLists.txt delete mode 100644 source/doc/.gitignore delete mode 100644 source/doc/CMakeLists.txt delete mode 100644 source/doc/requirements.txt delete mode 100644 source/doc/src/conf.py delete mode 100644 source/doc/src/index.rst delete mode 100644 source/examples/CMakeLists.txt delete mode 100644 source/examples/src/basic_usage.cpp delete mode 100644 source/examples/src/basic_usage_with_read.cpp delete mode 100644 source/examples/src/basic_usage_with_show.cpp delete mode 100644 source/lib/CMakeLists.txt delete mode 100644 source/lib/include/newtype/newtype.hpp delete mode 100644 source/tests/CMakeLists.txt delete mode 100644 source/tests/src/arithmetic.cpp delete mode 100644 source/tests/src/constructors.cpp delete mode 100644 source/tests/src/conversion.cpp delete mode 100644 source/tests/src/derivation_clause.cpp delete mode 100644 source/tests/src/equality_comparison.cpp delete mode 100644 source/tests/src/hash.cpp delete mode 100644 source/tests/src/io_operators.cpp delete mode 100644 source/tests/src/iterable.cpp delete mode 100644 source/tests/src/relational_operators.cpp delete mode 100644 source/tests/src/threeway_comparison.cpp delete mode 100644 test_package/.gitignore delete mode 100644 test_package/CMakeLists.txt delete mode 100644 test_package/conanfile.py delete mode 100644 test_package/main.cpp create mode 100644 tests/CMakeLists.txt create mode 100644 tests/src/arithmetic.cpp create mode 100644 tests/src/constructors.cpp create mode 100644 tests/src/conversion.cpp create mode 100644 tests/src/derivation_clause.cpp create mode 100644 tests/src/equality_comparison.cpp create mode 100644 tests/src/hash.cpp create mode 100644 tests/src/io_operators.cpp create mode 100644 tests/src/iterable.cpp create mode 100644 tests/src/relational_operators.cpp create mode 100644 tests/src/threeway_comparison.cpp diff --git a/.conan/profiles/gnu b/.conan/profiles/gnu deleted file mode 100644 index eb91651..0000000 --- a/.conan/profiles/gnu +++ /dev/null @@ -1,4 +0,0 @@ -include(default) - -[settings] -compiler.cppstd=gnu20 \ No newline at end of file diff --git a/.conan/profiles/std b/.conan/profiles/std deleted file mode 100644 index dedc2a6..0000000 --- a/.conan/profiles/std +++ /dev/null @@ -1,4 +0,0 @@ -include(default) - -[settings] -compiler.cppstd=20 \ No newline at end of file diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 77f52dd..0000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -github: fmorgner diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json deleted file mode 100644 index 2091cd3..0000000 --- a/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "configurations": [ - { - "name": "Linux", - "includePath": [ - "${workspaceFolder}/source/lib/include" - ], - "defines": [], - "compilerPath": "/usr/bin/g++", - "cppStandard": "c++20", - "intelliSenseMode": "gcc-x64", - "configurationProvider": "ms-vscode.cmake-tools" - } - ], - "version": 4 -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 72ff434..f9b4fe7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,20 +2,18 @@ // CMake Configuration "cmake.configureOnOpen": true, "cmake.useCMakePresets": "always", - "cmake.sourceDirectory": "${workspaceFolder}/source", // C++ Configuration "[cpp]": { "editor.formatOnSave": true }, "C_Cpp.autoAddFileAssociations": false, - "C_Cpp.intelliSenseEngine": "default", - "C_Cpp.errorSquiggles": "enabled", - "C_Cpp.autocomplete": "default", + "C_Cpp.intelliSenseEngine": "disabled", + "C_Cpp.errorSquiggles": "disabled", + "C_Cpp.autocomplete": "disabled", // RST Configuration "[python]": { "editor.defaultFormatter": "ms-python.black-formatter" }, - "esbonio.sphinx.confDir": "${workspaceFolder}/doc/src" } diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..36b5b62 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION "3.25.0") + +project("newtype" + LANGUAGES CXX + DESCRIPTION "A library of types and functions to create strong type aliases" +) + +enable_testing() + +# Project Options + +option(BUILD_EXAMPLES "Build the library examples" OFF) + +# Project Components + +add_subdirectory("doc") +add_subdirectory("examples") +add_subdirectory("lib") +#add_subdirectory("tests") \ No newline at end of file diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..da73733 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,30 @@ +{ + "version": 6, + "cmakeMinimumRequired": { + "major": 3, + "minor": 25, + "patch": 0 + }, + "configurePresets": [ + { + "name": "default", + "generator": "Ninja Multi-Config", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_CONFIGURATION_TYPES": "Debug;MinSizeRel" + } + } + ], + "buildPresets": [ + { + "name": "debug", + "configurePreset": "default", + "configuration": "Debug" + }, + { + "name": "release", + "configurePreset": "default", + "configuration": "MinSizeRel" + } + ] +} \ No newline at end of file diff --git a/conanfile.py b/conanfile.py deleted file mode 100644 index 222ac72..0000000 --- a/conanfile.py +++ /dev/null @@ -1,65 +0,0 @@ -from conan import ConanFile -from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout -from conan.tools.build import check_min_cppstd - - -class NewtypeConan(ConanFile): - name = "newtype" - version = "2.0.0" - license = "BSD-3-Clause" - description = "A library of types and functions to create strong type aliases" - url = "https://github.com/fmorgner/newtype" - - settings = ("os", "arch", "compiler", "build_type") - - scm = { - "type": "git", - "url": "auto", - "revision": "auto" - } - - generators = [ - "CMakeDeps" - ] - - exports_sources = [ - "source/*", - "test_package/*", - "LICENSE", - ] - - def build(self): - cmake = CMake(self) - cmake.configure() - if not self.conf.get("tools.build:skip_test", default=False): - cmake.build() - cmake.test() - - def build_requirements(self): - self.tool_requires("cmake/[>3.25]") - self.tool_requires("ninja/[>1.11]") - self.test_requires("catch2/[>3.3]") - - def generate(self): - toolchain = CMakeToolchain(self, generator="Ninja Multi-Config") - toolchain.variables["CMAKE_EXPORT_COMPILE_COMMANDS"] = True - toolchain.variables["PROJECT_VERSION"] = self.version - toolchain.variables["PROJECT_DESCRIPTION"] = self.description - toolchain.generate() - - def layout(self): - cmake_layout(self, generator="Ninja Multi-Config", src_folder="source") - - def package(self): - cmake = CMake(self) - cmake.install() - - def package_id(self): - self.info.clear() - - def package_info(self): - self.cpp_info.bindirs = [] - self.cpp_info.libdirs = [] - - def validate(self): - check_min_cppstd(self, 20) diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 0000000..ee826c1 --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,2 @@ +Pipfile +Pipfile.lock diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt new file mode 100644 index 0000000..7a9e27d --- /dev/null +++ b/doc/CMakeLists.txt @@ -0,0 +1,81 @@ +find_package("Python3" + REQUIRED + COMPONENTS "Interpreter" +) + +set(DOCENV_DIR "${PROJECT_BINARY_DIR}/docenv") + +if(NOT EXISTS "${DOCENV_DIR}") + message(STATUS "Creating Python virtual environment") + execute_process(COMMAND "${Python3_EXECUTABLE}" + "-m" + "venv" + "${DOCENV_DIR}" + OUTPUT_QUIET + ) + message(STATUS "Installing documentation requirements") + execute_process(COMMAND + "${DOCENV_DIR}/bin/python" + "-m" + "pip" + "install" + "-r" + "${CMAKE_CURRENT_SOURCE_DIR}/requirements.txt" + OUTPUT_QUIET + ) +else() + message(STATUS "Reusing existing Python virtual environment") +endif() + +file(GLOB SOURCES + CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/src/*" + "${PROJECT_SOURCE_DIR}/examples/src/*" +) + +add_custom_target("docs" + ALL + DEPENDS + "${CMAKE_CURRENT_BINARY_DIR}/html/index.html" + "${CMAKE_CURRENT_BINARY_DIR}/man/newtype.3" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "Building documentation" +) + +add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/html/index.html" + COMMAND "${DOCENV_DIR}/bin/sphinx-build" + "-q" + "-b" + "singlehtml" + "-E" + "${CMAKE_CURRENT_SOURCE_DIR}/src" + "${CMAKE_CURRENT_BINARY_DIR}/html" + DEPENDS ${SOURCES} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "Compiling HTML documentation" +) + +add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/man/newtype.3" + COMMAND "${DOCENV_DIR}/bin/sphinx-build" + "-q" + "-b" + "man" + "-E" + "${CMAKE_CURRENT_SOURCE_DIR}/src" + "${CMAKE_CURRENT_BINARY_DIR}/man" + DEPENDS ${SOURCES} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "Compiling man page" +) + +install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/singlehtml" + TYPE DOC + PATTERN ".doctrees" EXCLUDE + PATTERN ".buildinfo" EXCLUDE + PATTERN ".nojekyll" EXCLUDE +) + +install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/man/" + TYPE MAN + PATTERN ".doctrees" EXCLUDE +) diff --git a/doc/requirements.txt b/doc/requirements.txt new file mode 100644 index 0000000..47b0805 --- /dev/null +++ b/doc/requirements.txt @@ -0,0 +1 @@ +sphinx~=7.0 diff --git a/doc/src/conf.py b/doc/src/conf.py new file mode 100644 index 0000000..a6e65de --- /dev/null +++ b/doc/src/conf.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- + +import os + + +# -- Project information ----------------------------------------------------- + +project = 'newtype' +copyright = '2023, Felix Morgner' +author = 'Felix Morgner' +version = '2.0' +release = '2.0.0' + + +# -- General configuration --------------------------------------------------- + +master_doc = 'index' + +extensions = [ + 'sphinx.ext.todo', + 'sphinx.ext.githubpages', +] + +highlight_language = 'c++' + +pygments_style = 'tango' + +exclude_patterns = [] + +numfig = True + +# -- Options for HTML output ------------------------------------------------- + +html_theme = 'haiku' +html_theme_options = { +} + +# -- Options for manual page output ------------------------------------------------- + +man_pages = [ + ( + "index", + "newtype", + "A library of types and functions to create strong type aliases", + ["Felix Morgner "], + 3, + ), +] \ No newline at end of file diff --git a/doc/src/index.rst b/doc/src/index.rst new file mode 100644 index 0000000..88925aa --- /dev/null +++ b/doc/src/index.rst @@ -0,0 +1,1271 @@ +.. cpp:namespace-push:: nt + +.. |BaseTypeDoc| replace:: The type of the contained object +.. |TagTypeDoc| replace:: A tag to uniquely identify an instance of :cpp:class:`nt::new_type` +.. |DerivationClauseDoc| replace:: A (possibly empty) list of derivation tags as generated by :cpp:func:`nt::deriving` + +.. only:: html + + .. contents:: Table of Contents + :depth: 5 + +############# +Documentation +############# + +The ``newtype`` library provides types and functions to facilitate the creation of strong type aliases. + +Example Usage +############# + +.. note:: + + 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 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++ + :linenos: + :name: new-type-usage-basic + :caption: Basic usage of :cpp:class:`new_type` + +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 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: 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` 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: 5,6,29,30,32 + :language: c++ + :linenos: + :name: new-type-usage-basic-read + :caption: Deriving input operations using the :cpp:var:`Read` derivation tag + +API +### + +This section of the documentation describes the public API of the *new_type*. +It provides detailed descriptions of the types and functions designed to be used by applications. +All declarations described in this section are found in the namespace :cpp:any:`nt`, unless noted otherwise. + +Header :literal:`` +======================================== + +This header contains the definitions of the class template :cpp:class:`new_type` as well as a set of associated namespace-level functions. + +Class template :cpp:class:`new_type` +------------------------------------ + +.. cpp:class:: template \ + 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 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| + :tparam DerivationClause: |DerivationClauseDoc| + + .. versionadded:: 1.0.0 + + **Member Type Aliases** + + .. cpp:type:: base_type = BaseType + + .. cpp:type:: tag_type = TagType + + .. cpp:type:: derivation_clause_type = decltype(DerivationClause) + + .. cpp:type:: iterator = typename BaseType::iterator + + :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 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 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 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 + + **Static Data Members** + + .. cpp:var:: static derivation_clause_type constexpr derivation_clause = DerivationClause + + **Constructors** + + .. 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 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) 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 :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`. + This constructor shall be noexcept iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *nothrow copy-construtible*. + :enablement: This constructor shall be defined as :literal:`= default` iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *copy-construtible*. + Otherwise, this constructor shall be explicitely deleted. + + .. cpp:function:: constexpr new_type(new_type && other) + + Construct a new instance of this :cpp:class:`new_type` by move-constructing the contained object using the value contained by :literal:`other`. + + :param other: An existing instance of this :cpp:class:`new_type` + :throws: Any exception thrown by the move-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 move-construtible*. + :enablement: This constructor shall be defined as :literal:`= default` iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *move-construtible*. + Otherwise, this constructor shall be explicitely deleted. + + .. cpp:function:: constexpr new_type(BaseType const & value) + + Construct a new instance of this :cpp:class:`new_type` by copy-constructing the contained object using :literal:`value`. + + :param value: 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`. + This constructor shall be noexcept iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *nothrow copy-construtible*. + :enablement: This constructor shall be defined as :literal:`= default` iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *copy-construtible*. + Otherwise, this constructor shall be explicitely deleted. + + .. cpp:function:: constexpr new_type(BaseType && value) + + Construct a new instance of this :cpp:class:`new_type` by move-constructing the contained object using :literal:`value`. + + :param value: An existing instance of this :cpp:class:`new_type` + :throws: Any exception thrown by the move-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 move-construtible*. + :enablement: This constructor shall be defined as :literal:`= default` iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *move-construtible*. + Otherwise, this constructor shall be explicitely deleted. + + **Assignment Operators** + + .. cpp:function:: constexpr new_type & operator=(new_type const & other) + + Copy the value of an existing instance of this :cpp:class:`new_type` and replace this instance's value + + :param other: An existing instance of this :cpp:class:`new_type` + :throws: Any exception thrown by the copy-assignment operator of this :cpp:class:`new_type`'s :cpp:type:`base_type`. + This operator shall be noexcept iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *nothrow copy-assignable*. + :enablement: This operator shall be defined as :literal:`= default` iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *copy-assignable*. + Otherwise, this operator shall be explicitely deleted. + + .. cpp:function:: constexpr new_type & operator=(new_type && other) + + Move the value of an existing instance of this :cpp:class:`new_type` and replace this instance's value + + :param other: An existing instance of this :cpp:class:`new_type` + :throws: Any exception thrown by the move-assignment operator of this :cpp:class:`new_type`'s :cpp:type:`base_type`. + This operator shall be noexcept iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *nothrow move-assignable*. + :enablement: This operator shall be defined as :literal:`= default` iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *move-assignable*. + Otherwise, this operator shall be explicitely deleted. + + **Accessors** + + .. cpp:function:: constexpr BaseType decay() const + + Retrieve a copy of the object contained by this :cpp:class:`new_type` object + + :throws: Any exception thrown by the copy-constructor of this :cpp:class:`new_type`'s :cpp:type:`base_type`. + This operator shall be noexcept iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *nothrow copy-constructible*. + + .. cpp:function:: constexpr operator BaseType() const + + Retrieve a copy of the object contained by this :cpp:class:`new_type` object + + :throws: Any exception thrown by the copy-constructor of this :cpp:class:`new_type`'s :cpp:type:`base_type`. + This operator shall be noexcept iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *nothrow copy-constructible*. + :explicit: This conversion operator shall be explicit unless this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`ImplicitConversion`. + + **Member Access Through Pointer** + + .. cpp:function:: constexpr BaseType operator->() noexcept + + Perform "member access through pointer" via a pointer to object contained by this :cpp:class:`new_type` + + :enablement: This operator shall be available iff. this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Indirection` + + .. cpp:function:: constexpr BaseType const * operator->() const noexcept + + Perform "member access through pointer" via a pointer to object contained by this :cpp:class:`new_type` + + :enablement: This operator shall be available iff. this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Indirection` + + **Iterators** + + .. cpp:function:: constexpr iterator begin() + + Get an iterator to the beginning of the object contained by this :cpp:class:`new_type` + + :enablement: This function shall be available iff. + + a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`begin() ` that returns an instance of type :cpp:type:`iterator` + + .. versionadded:: 1.1.0 + + .. cpp:function:: constexpr iterator begin() const + + Get a constant iterator to the beginning of the object contained by this :cpp:class:`new_type` + + :enablement: This function shall be available iff. + + a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`begin() const ` that returns an instance of type :cpp:type:`const_iterator` + + .. versionadded:: 1.1.0 + + .. cpp:function:: constexpr iterator cbegin() const + + Get a constant iterator to the beginning of the object contained by this :cpp:class:`new_type` + + :enablement: This function shall be available iff. + + a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`cbegin() const ` that returns an instance of type :cpp:type:`const_iterator` + + .. versionadded:: 1.1.0 + + .. cpp:function:: constexpr iterator rbegin() + + Get a reverse iterator to the beginning of the object contained by this :cpp:class:`new_type` + + :enablement: This function shall be available iff. + + a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`rbegin() ` that returns an instance of type :cpp:type:`reverse_iterator` + + .. versionadded:: 1.1.0 + + .. cpp:function:: constexpr iterator rbegin() const + + Get a constant reverse iterator to the beginning of the object contained by this :cpp:class:`new_type` + + :enablement: This function shall be available iff. + + a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`rbegin() const ` that returns an instance of type :cpp:type:`const_reverse_iterator` + + .. versionadded:: 1.1.0 + + .. cpp:function:: constexpr iterator crbegin() const + + Get a constant reverse iterator to the beginning of the object contained by this :cpp:class:`new_type` + + :enablement: This function shall be available iff. + + a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`crbegin() const ` that returns an instance of type :cpp:type:`const_reverse_iterator` + + .. versionadded:: 1.1.0 + + .. cpp:function:: constexpr iterator end() + + Get an iterator beyond the end of the object contained by this :cpp:class:`new_type` + + :enablement: This function shall be available iff. + + a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`end() ` that returns an instance of type :cpp:type:`iterator` + + .. versionadded:: 1.1.0 + + .. cpp:function:: constexpr iterator end() const + + Get a constant iterator beyond the end of the object contained by this :cpp:class:`new_type` + + :enablement: This function shall be available iff. + + a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`end() const ` that returns an instance of type :cpp:type:`const_iterator` + + .. versionadded:: 1.1.0 + + .. cpp:function:: constexpr iterator cend() const + + Get a constant iterator beyond the end of the object contained by this :cpp:class:`new_type` + + :enablement: This function shall be available iff. + + a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`cend() const ` that returns an instance of type :cpp:type:`const_iterator` + + .. versionadded:: 1.1.0 + + .. cpp:function:: constexpr iterator rend() + + Get a reverse iterator beyond the end of the object contained by this :cpp:class:`new_type` + + :enablement: This function shall be available iff. + + a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`rend() ` that returns an instance of type :cpp:type:`reverse_iterator` + + .. versionadded:: 1.1.0 + + .. cpp:function:: constexpr iterator rend() const + + Get a constant reverse iterator beyond the end of the object contained by this :cpp:class:`new_type` + + :enablement: This function shall be available iff. + + a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`rend() const ` that returns an instance of type :cpp:type:`const_reverse_iterator` + + .. versionadded:: 1.1.0 + + .. cpp:function:: constexpr iterator crend() const + + Get a constant reverse iterator beyond the end of the object contained by this :cpp:class:`new_type` + + :enablement: This function shall be available iff. + + a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`crend() const ` that returns an instance of type :cpp:type:`const_reverse_iterator` + + .. versionadded:: 1.1.0 + +:literal:`namespace`-level functions and function templates +----------------------------------------------------------- + +The functions and functions templates described in this section provide additional functionality for the class template :cpp:class:`new_type` that is not part of the class itself. + +Equality Comparison Operators +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. cpp:function:: template \ + constexpr bool operator==(new_type const & lhs, new_type const & rhs) + + Check two instances of :cpp:class:`new_type\` for equality. + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param lhs: The left-hand side of the comparison + :param rhs: The right-hand side of the comparison + :returns: The value returned by the comparison of the contained objects. + :throws: Any exception thrown by the comparison operator of objects contained by :literal:`lhs` and :literal:`rhs`. + This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow equals-comparable*. + :enablement: This operator shall be available iff. :cpp:type:`new_type::base_type` supports comparison using :literal:`==` + + .. versionadded:: 1.0.0 + +.. cpp:function:: template \ + constexpr bool operator==(new_type const & lhs, BaseType const & rhs) + + Check an instance of :cpp:class:`new_type\` for equality with an instance of :cpp:type:`BaseType`. + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param lhs: The left-hand side of the comparison + :param rhs: The right-hand side of the comparison + :returns: The value returned by the comparison of object contained by :literal:`lhs` with an object of the :cpp:type:`base type `. + :throws: Any exception thrown by the comparison of object contained by :literal:`lhs` with an object of the :cpp:type:`base type `. This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow equals-comparable*. + :enablement: This operator shall be available iff. + + a. :cpp:type:`new_type::base_type` supports comparison using :literal:`==` and + b. the :cpp:var:`derivation clause ` contains :cpp:var:`EqBase` + + .. versionadded:: 1.0.0 + +.. cpp:function:: template \ + constexpr bool operator==(BaseType const & lhs, new_type const & rhs) + + Check an instance of :cpp:type:`BaseType` for equality with an instance of :cpp:class:`new_type\`. + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param lhs: The left-hand side of the comparison + :param rhs: The right-hand side of the comparison + :returns: The value returned by the comparison of an object of :cpp:type:`base type ` with the object contained by :literal:`rhs`. + :throws: Any exception thrown by the comparison of an object of :cpp:type:`base type ` with the object contained by :literal:`rhs`. This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow equals-comparable*. + :enablement: This operator shall be available iff. + + a. :cpp:type:`new_type::base_type` supports comparison using :literal:`==` and + b. the :cpp:var:`derivation clause ` contains :cpp:var:`EqBase` + + .. versionadded:: 1.0.0 + +.. cpp:function:: template \ + constexpr bool operator!=(new_type const & lhs, new_type const & rhs) + + Check two instances of :cpp:class:`new_type\` for in-equality. + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param lhs: The left-hand side of the comparison + :param rhs: The right-hand side of the comparison + :returns: The value returned by the comparison of the contained objects. + :throws: Any exception thrown by the comparison operator of theobjects contained by :literal:`lhs` and :literal:`rhs`. + This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow not-equals-comparable*. + :enablement: This operator shall be available iff. :cpp:type:`new_type::base_type` supports comparison using :literal:`!=` + + .. versionadded:: 1.0.0 + +.. cpp:function:: template \ + constexpr bool operator!=(new_type const & lhs, BaseType const & rhs) + + Check an instance of :cpp:class:`new_type\` for in-equality with an instance of :cpp:type:`BaseType`. + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param lhs: The left-hand side of the comparison + :param rhs: The right-hand side of the comparison + :returns: The value returned by the comparison of the object contained by :literal:`lhs` with an object of the :cpp:type:`base type `. + :throws: Any exception thrown by the comparison of the object contained by :literal:`lhs` with an object of the :cpp:type:`base type `. + This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow not-equals-comparable*. + :enablement: This operator shall be available iff. + + a) :cpp:type:`new_type::base_type` supports comparison using :literal:`!=` and + b) the :cpp:var:`derivation clause ` contains :cpp:var:`EqBase` + + .. versionadded:: 1.0.0 + +.. cpp:function:: template \ + constexpr bool operator!=(BaseType const & lhs, new_type const & rhs) + + Check an instance of :cpp:type:`BaseType` for in-equality with an instance of :cpp:class:`new_type\`. + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param lhs: The left-hand side of the comparison + :param rhs: The right-hand side of the comparison + :returns: The value returned by the comparison of an object of :cpp:type:`base type ` with the object contained by :literal:`rhs`. + :throws: Any exception thrown by the comparison of an object of :cpp:type:`base type ` with the object contained by :literal:`rhs`. This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow not-equals-comparable*. + :enablement: This operator shall be available iff. + + a. :cpp:type:`new_type::base_type` supports comparison using :literal:`!=` and + b. the :cpp:var:`derivation clause ` contains :cpp:var:`EqBase` + + .. versionadded:: 1.0.0 + +Relational Comparison Operators +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. cpp:function:: template \ + constexpr bool operator<(new_type const & lhs, new_type const & rhs) + + Compare two instances of the same :cpp:class:`new_type` using :literal:`<` (*less-than*). + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param lhs: The left-hand side of the comparison + :param rhs: The right-hand side of the comparison + :returns: The value returned by the comparison of the contained objects. + :throws: Any exception thrown by the comparison operator of the objects contained by :literal:`lhs` and :literal:`rhs`. + This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow less-than-comparable*. + :enablement: This operator shall be available iff. + + a. :cpp:type:`new_type::base_type` supports comparison using :literal:`<` and + b. the :cpp:var:`derivation clause ` contains :cpp:var:`Relational` + + .. versionadded:: 1.0.0 + +.. cpp:function:: template \ + constexpr bool operator>(new_type const & lhs, new_type const & rhs) + + Compare two instances of the same :cpp:class:`new_type` using :literal:`>` (*greater-than*). + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param lhs: The left-hand side of the comparison + :param rhs: The right-hand side of the comparison + :returns: The value returned by the comparison of the contained objects. + :throws: Any exception thrown by the comparison operator of the objects contained by :literal:`lhs` and :literal:`rhs`. + This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow greater-than-comparable*. + :enablement: This operator shall be available iff. + + a. :cpp:type:`new_type::base_type` supports comparison using :literal:`>` and + b. the :cpp:var:`derivation clause ` contains :cpp:var:`Relational` + + .. versionadded:: 1.0.0 + +.. cpp:function:: template \ + constexpr bool operator<=(new_type const & lhs, new_type const & rhs) + + Compare two instances of the same :cpp:class:`new_type` using :literal:`<=` (*less-than-equal*). + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param lhs: The left-hand side of the comparison + :param rhs: The right-hand side of the comparison + :returns: The value returned by the comparison of the contained objects. + :throws: Any exception thrown by the comparison operator of the objects contained by :literal:`lhs` and :literal:`rhs`. + This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow less-than-equal-comparable*. + :enablement: This operator shall be available iff. + + a. :cpp:type:`new_type::base_type` supports comparison using :literal:`<=` and + b. the :cpp:var:`derivation clause ` contains :cpp:var:`Relational` + + .. versionadded:: 1.0.0 + +.. cpp:function:: template \ + constexpr bool operator>=(new_type const & lhs, new_type const & rhs) + + Compare two instances of the same :cpp:class:`new_type` using :literal:`>=` (*greater-than-equal*). + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param lhs: The left-hand side of the comparison + :param rhs: The right-hand side of the comparison + :returns: The value returned by the comparison of the contained objects. + :throws: Any exception thrown by the comparison operator of the objects contained by :literal:`lhs` and :literal:`rhs`. + This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow greater-than-equal-comparable*. + :enablement: This operator shall be available iff. + + a. :cpp:type:`new_type::base_type` supports comparison using :literal:`>=` and + b. the :cpp:var:`derivation clause ` contains :cpp:var:`Relational` + + .. versionadded:: 1.0.0 + +Stream I/O Operators +~~~~~~~~~~~~~~~~~~~~ + +.. cpp:function:: template \ + std::basic_ostream & operator<<(std::basic_ostream & out, new_type const & value) + + Write an instance of :cpp:class:`new_type\` to a standard :cpp:type:`ostream `. + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :tparam CharType: The stream character type + :tparam StreamTraits: The traits of the output stream + :param out: The output stream + :param value: A :cpp:class:`new_type` value to write to the output stream + :returns: A reference to the output stream + :throws: Any exception thrown by the stream-output operator of the object contained by :literal:`value`. + This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow output-streamable*. + :enablement: This operator shall be available iff. + + a. :cpp:type:`new_type::base_type` supports being written to an output stream using :literal:`<<` and + b. the :cpp:var:`derivation clause ` contains :cpp:var:`Show` + + .. versionadded:: 1.0.0 + +.. cpp:function:: template \ + std::basic_istream & operator>>(std::basic_istream & in, new_type & value) + + Read an instance of :cpp:class:`new_type\` from a standard :cpp:type:`istream `. + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :tparam CharType: The stream character type + :tparam StreamTraits: The traits of the input stream + :param in: The input stream + :param value: A :cpp:class:`new_type` value to be read from the output stream + :returns: A reference to the input stream + :throws: Any exception thrown by the stream-input operator of the object contained by :literal:`value`. + This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow input-streamable*. + :enablement: This operator shall be available iff. + + a. :cpp:type:`new_type::base_type` supports being read from an input stream using :literal:`>>` and + b. the :cpp:var:`derivation clause ` contains :cpp:var:`Read` + + .. versionadded:: 1.0.0 + +Arithmetic Operators +~~~~~~~~~~~~~~~~~~~~ + +.. cpp:function:: template \ + constexpr new_type operator+(new_type const & lhs, new_type const & rhs) + + Add two instances of the same :cpp:class:`new_type`. + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param lhs: The left-hand side of the addition + :param rhs: The right-hand side of the addition + :returns: A new instance of :cpp:class:`new_type\` containing the result of applying :literal:`+` to the objects contained by :literal:`lhs` and :literal:`rhs`. + :throws: Any exception thrown by the addition operator of the objects contained by :literal:`lhs` and :literal:`rhs`. + This operator shall be noexcept iff. + + a. :cpp:type:`new_type::base_type` is *nothrow addable* and + b. :cpp:type:`new_type::base_type` is *nothrow copy-constructible* + + :enablement: This operator shall be available iff. + + a. :cpp:type:`new_type::base_type` supports addition using :literal:`+` and + b. the :cpp:var:`derivation clause ` contains :cpp:var:`Arithmetic` + + .. versionadded:: 1.0.0 + +.. cpp:function:: template \ + constexpr new_type & operator+=(new_type & lhs, new_type const & rhs) + + Add two instances of the same :cpp:class:`new_type` by overwriting the first one. + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param lhs: The left-hand side of the addition + :param rhs: The right-hand side of the addition + :returns: A reference to the first argument containing the value modified by applying :literal:`+=` to the objects contained by :literal:`lhs` and :literal:`rhs`. + :throws: Any exception thrown by the addition-assignment operator of the objects contained by :literal:`lhs` and :literal:`rhs`. + This operator shall be noexcept iff. + + a. :cpp:type:`new_type::base_type` is *nothrow add-assignable* and + b. :cpp:type:`new_type::base_type` is *nothrow copy-constructible* + + :enablement: This operator shall be available iff. + + a. :cpp:type:`new_type::base_type` supports addition using :literal:`+=` and + b. the :cpp:var:`derivation clause ` contains :cpp:var:`Arithmetic` + + .. versionadded:: 1.0.0 + +.. cpp:function:: template \ + constexpr new_type operator-(new_type const & lhs, new_type const & rhs) + + Subtract two instances of the same :cpp:class:`new_type`. + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param lhs: The left-hand side of the subtraction + :param rhs: The right-hand side of the subtraction + :returns: A new instance of :cpp:class:`new_type\` containing the result of applying :literal:`-` to the objects contained by :literal:`lhs` and :literal:`rhs`. + :throws: Any exception thrown by the subtraction operator of the objects contained by :literal:`lhs` and :literal:`rhs`. + This operator shall be noexcept iff. + + a. :cpp:type:`new_type::base_type` is *nothrow subtractable* and + b. :cpp:type:`new_type::base_type` is *nothrow copy-constructible* + + :enablement: This operator shall be available iff. + + a. :cpp:type:`new_type::base_type` supports subtraction using :literal:`-` and + b. the :cpp:var:`derivation clause ` contains :cpp:var:`Arithmetic` + + .. versionadded:: 1.0.0 + +.. cpp:function:: template \ + constexpr new_type & operator-=(new_type & lhs, new_type const & rhs) + + Subtract two instances of the same :cpp:class:`new_type` by overwriting the first one. + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param lhs: The left-hand side of the subtraction + :param rhs: The right-hand side of the subtraction + :returns: A reference to the first argument containing the value modified by applying :literal:`-=` to the objects contained by :literal:`lhs` and :literal:`rhs`. + :throws: Any exception thrown by the subtraction-assignment operator of the objects contained by :literal:`lhs` and :literal:`rhs`. + This operator shall be noexcept iff. + + a. :cpp:type:`new_type::base_type` is *nothrow subtract-assignable* and + b. :cpp:type:`new_type::base_type` is *nothrow copy-constructible* + + :enablement: This operator shall be available iff. + + a. :cpp:type:`new_type::base_type` supports subtraction using :literal:`-=` and + b. the :cpp:var:`derivation clause ` contains :cpp:var:`Arithmetic` + + .. versionadded:: 1.0.0 + +.. cpp:function:: template \ + constexpr new_type operator*(new_type const & lhs, new_type const & rhs) + + Multiply two instances of the same :cpp:class:`new_type`. + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param lhs: The left-hand side of the multiplication + :param rhs: The right-hand side of the multiplication + :returns: A new instance of :cpp:class:`new_type\` containing the result of applying :literal:`*` to the objects contained by :literal:`lhs` and :literal:`rhs`. + :throws: Any exception thrown by the multiplication operator of the objects contained by :literal:`lhs` and :literal:`rhs`. + This operator shall be noexcept iff. + + a. :cpp:type:`new_type::base_type` is *nothrow multipliable* and + b. :cpp:type:`new_type::base_type` is *nothrow copy-constructible* + + :enablement: This operator shall be available iff. + + a. :cpp:type:`new_type::base_type` supports multiplication using :literal:`*` and + b. the :cpp:var:`derivation clause ` contains :cpp:var:`Arithmetic` + + .. versionadded:: 1.0.0 + +.. cpp:function:: template \ + constexpr new_type & operator*=(new_type & lhs, new_type const & rhs) + + Multiply two instances of the same :cpp:class:`new_type` by overwriting the first one. + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param lhs: The left-hand side of the multiplication + :param rhs: The right-hand side of the multiplication + :returns: A reference to the first argument containing the value modified by applying :literal:`*=` to the objects contained by :literal:`lhs` and :literal:`rhs`. + :throws: Any exception thrown by the multiplication-assignment operator of the objects contained by :literal:`lhs` and :literal:`rhs`. + This operator shall be noexcept iff. + + a. :cpp:type:`new_type::base_type` is *nothrow multiply-assignable* and + b. :cpp:type:`new_type::base_type` is *nothrow copy-constructible* + + :enablement: This operator shall be available iff. + + a. :cpp:type:`new_type::base_type` supports multiplication using :literal:`*=` and + b. the :cpp:var:`derivation clause ` contains :cpp:var:`Arithmetic` + + .. versionadded:: 1.0.0 + +.. cpp:function:: template \ + constexpr new_type operator/(new_type const & lhs, new_type const & rhs) + + Divide two instances of the same :cpp:class:`new_type`. + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param lhs: The left-hand side of the division + :param rhs: The right-hand side of the division + :returns: A new instance of :cpp:class:`new_type\` containing the result of applying :literal:`/` to the objects contained by :literal:`lhs` and :literal:`rhs`. + :throws: Any exception thrown by the division operator of the objects contained by :literal:`lhs` and :literal:`rhs`. + This operator shall be noexcept iff. + + a. :cpp:type:`new_type::base_type` is *nothrow dividable* and + b. :cpp:type:`new_type::base_type` is *nothrow copy-constructible* + + :enablement: This operator shall be available iff. + + a. :cpp:type:`new_type::base_type` supports division using :literal:`/` and + b. the :cpp:var:`derivation clause ` contains :cpp:var:`Arithmetic` + + .. versionadded:: 1.0.0 +.. cpp:function:: template \ + constexpr new_type & operator/=(new_type & lhs, new_type const & rhs) + + Divide two instances of the same :cpp:class:`new_type` by overwriting the first one. + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param lhs: The left-hand side of the division + :param rhs: The right-hand side of the division + :returns: A reference to the first argument containing the value modified by applying :literal:`/=` to the objects contained by :literal:`lhs` and :literal:`rhs`. + :throws: Any exception thrown by the division-assignment operator of the objects contained by :literal:`lhs` and :literal:`rhs`. + This operator shall be noexcept iff. + + a. :cpp:type:`new_type::base_type` is *nothrow divide-assignable* and + b. :cpp:type:`new_type::base_type` is *nothrow copy-constructible* + + :enablement: This operator shall be available iff. + + a. :cpp:type:`new_type::base_type` supports division using :literal:`/=` and + b. the :cpp:var:`derivation clause ` contains :cpp:var:`Arithmetic` + + .. versionadded:: 1.0.0 + +Iterators +~~~~~~~~~ + +.. cpp:function:: template \ + constexpr new_type::iterator begin(new_type & obj) + + Get an iterator to the beginning of the object contained by an instance of :cpp:class:`new_type` + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param obj: The object to retrieve the iterator from + :returns: An iterator to the begining of the object of contained by :literal:`obj`. + :throws: Any exception + :enablement: This function shall be available iff. + + a) :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`begin(BaseType &)` that returns an instance of type :cpp:type:`new_type::iterator` + + .. versionadded:: 1.1.0 + +.. cpp:function:: template \ + constexpr new_type::const_iterator begin(new_type const & obj) + + Get a constant iterator to the beginning of the object contained by an instance of :cpp:class:`new_type` + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param obj: The object to retrieve the iterator from + :returns: An iterator to the begining of the object of contained by :cpp:var:`obj`. + :throws: Any exception + :enablement: This function shall be available iff. + + a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`begin(BaseType const &)` that returns an instance of type :cpp:type:`new_type::const_iterator` + + .. versionadded:: 1.1.0 + +.. cpp:function:: template \ + constexpr new_type::const_iterator cbegin(new_type const & obj) + + Get a constant iterator to the beginning of the object contained by an instance of :cpp:class:`new_type` + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param obj: The object to retrieve the iterator from + :returns: An iterator to the begining of the object of contained by :cpp:var:`obj`. + :throws: Any exception + :enablement: This function shall be available iff. + + a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`cbegin(BaseType const &)` that returns an instance of type :cpp:type:`new_type::const_iterator` + + .. versionadded:: 1.1.0 + +.. cpp:function:: template \ + constexpr new_type::reverse_iterator rbegin(new_type & obj) + + Get a reverse iterator to the beginning of the object contained by an instance of :cpp:class:`new_type` + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param obj: The object to retrieve the iterator from + :returns: An iterator to the begining of the object of contained by :literal:`obj`. + :throws: Any exception + :enablement: This function shall be available iff. + + a) :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`rbegin(BaseType &)` that returns an instance of type :cpp:type:`new_type::reverse_iterator` + + .. versionadded:: 1.1.0 + +.. cpp:function:: template \ + constexpr new_type::const_reverse_iterator rbegin(new_type const & obj) + + Get a constant reverse iterator to the beginning of the object contained by an instance of :cpp:class:`new_type` + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param obj: The object to retrieve the iterator from + :returns: An iterator to the begining of the object of contained by :cpp:var:`obj`. + :throws: Any exception + :enablement: This function shall be available iff. + + a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`rbegin(BaseType const &)` that returns an instance of type :cpp:type:`new_type::const_reverse_iterator` + + .. versionadded:: 1.1.0 + +.. cpp:function:: template \ + constexpr new_type::const_reverse_iterator crbegin(new_type const & obj) + + Get a constant reverse iterator to the beginning of the object contained by an instance of :cpp:class:`new_type` + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param obj: The object to retrieve the iterator from + :returns: An iterator to the begining of the object of contained by :cpp:var:`obj`. + :throws: Any exception + :enablement: This function shall be available iff. + + a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`crbegin(BaseType const &)` that returns an instance of type :cpp:type:`new_type::const_reverse_iterator` + + .. versionadded:: 1.1.0 + +.. cpp:function:: template \ + constexpr new_type::iterator end(new_type & obj) + + Get an iterator beyond the end of the object contained by an instance of :cpp:class:`new_type` + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param obj: The object to retrieve the iterator from + :returns: An iterator beyond the end of the object of contained by :literal:`obj`. + :throws: Any exception + :enablement: This function shall be available iff. + + a) :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`end(BaseType &)` that returns an instance of type :cpp:type:`new_type::iterator` + + .. versionadded:: 1.1.0 + +.. cpp:function:: template \ + constexpr new_type::const_iterator end(new_type const & obj) + + Get a constant iterator beyond the end of the object contained by an instance of :cpp:class:`new_type` + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param obj: The object to retrieve the iterator from + :returns: An iterator beyond the end of the object of contained by :cpp:var:`obj`. + :throws: Any exception + :enablement: This function shall be available iff. + + a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`end(BaseType const &)` that returns an instance of type :cpp:type:`new_type::const_iterator` + + .. versionadded:: 1.1.0 + +.. cpp:function:: template \ + constexpr new_type::const_iterator cend(new_type const & obj) + + Get a constant iterator beyond the end of the object contained by an instance of :cpp:class:`new_type` + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param obj: The object to retrieve the iterator from + :returns: An iterator beyond the end of the object of contained by :cpp:var:`obj`. + :throws: Any exception + :enablement: This function shall be available iff. + + a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`cend(BaseType const &)` that returns an instance of type :cpp:type:`new_type::const_iterator` + + .. versionadded:: 1.1.0 + +.. cpp:function:: template \ + constexpr new_type::reverse_iterator rend(new_type & obj) + + Get a reverse iterator beyond the end of the object contained by an instance of :cpp:class:`new_type` + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param obj: The object to retrieve the iterator from + :returns: An iterator beyond the end of the object of contained by :literal:`obj`. + :throws: Any exception + :enablement: This function shall be available iff. + + a) :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`rend(BaseType &)` that returns an instance of type :cpp:type:`new_type::reverse_iterator` + + .. versionadded:: 1.1.0 + +.. cpp:function:: template \ + constexpr new_type::const_reverse_iterator rend(new_type const & obj) + + Get a constant reverse iterator beyond the end of the object contained by an instance of :cpp:class:`new_type` + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param obj: The object to retrieve the iterator from + :returns: An iterator beyond the end of the object of contained by :cpp:var:`obj`. + :throws: Any exception + :enablement: This function shall be available iff. + + a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`rend(BaseType const &)` that returns an instance of type :cpp:type:`new_type::const_reverse_iterator` + + .. versionadded:: 1.1.0 + +.. cpp:function:: template \ + constexpr new_type::const_reverse_iterator crend(new_type const & obj) + + Get a constant reverse iterator beyond the end of the object contained by an instance of :cpp:class:`new_type` + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + :param obj: The object to retrieve the iterator from + :returns: An iterator beyond the end of the object of contained by :cpp:var:`obj`. + :throws: Any exception + :enablement: This function shall be available iff. + + a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and + b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`crend(BaseType const &)` that returns an instance of type :cpp:type:`new_type::const_reverse_iterator` + + .. versionadded:: 1.1.0 + +:cpp:class:`std::hash` Support +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. cpp:namespace-pop:: + +.. cpp:class:: template \ + std::hash> + + :tparam BaseType: |BaseTypeDoc| + :tparam TagType: |TagTypeDoc| + :tparam DerivationClause: |DerivationClauseDoc| + + Hash an instance of :cpp:class:`new_type` using the hash implementation of the :cpp:type:`base type `. + + .. cpp:function:: constexpr std::size operator()(nt::new_type const & value) const + + :param value: A :cpp:class:`nt::new_type` value to be hashed + :returns: The result of applying :cpp:class:`std::hash` to the object contained by :literal:`value` + :throws: Any exception thrown by the call operator of the specialization of :cpp:class`std::hash` for the type of the object contained by :literal:`value`. + :enablement: This operator shall be available iff. + + a. :cpp:type:`nt::new_type::base_type` is hashable and + b. the :cpp:var:`derivation clause ` contains :cpp:var:`Hash `. + + .. versionadded:: 1.0.0 + +.. cpp:namespace-push:: nt + +Header :literal:`` +========================================= + +This header defines the alias template :cpp:type:`derivable` as well as the set of standard derivation tags. + +Class template :cpp:type:`derivable` +------------------------------------ + +.. cpp:class:: template \ + derivable + + :tparam NameTag: A tag uniquely identifing a specific derivation tag + + .. versionadded:: 1.0.0 + +.. _sec-standard-derivation-tags: + +Standard derivation tags +------------------------ + +.. cpp:var:: auto constexpr Arithmetic = derivable{} + + This tag enables the derivation of the following arithmetic operators: + + * :cpp:func:`operator+(new_type const &, new_type const &) constexpr new_type operator+(new_type const & lhs, new_type const & rhs)>` + * :cpp:func:`operator-(new_type const &, new_type const &) constexpr new_type operator-(new_type const & lhs, new_type const & rhs)>` + * :cpp:func:`operator*(new_type const &, new_type const &) constexpr new_type operator*(new_type const & lhs, new_type const & rhs)>` + * :cpp:func:`operator/(new_type const &, new_type const &) constexpr new_type operator/(new_type const & lhs, new_type const & rhs)>` + * :cpp:func:`operator+=(new_type &, new_type const &) constexpr new_type & operator+=(new_type & lhs, new_type const & rhs)>` + * :cpp:func:`operator-=(new_type &, new_type const &) constexpr new_type & operator-=(new_type & lhs, new_type const & rhs)>` + * :cpp:func:`operator*=(new_type &, new_type const &) constexpr new_type & operator*=(new_type & lhs, new_type const & rhs)>` + * :cpp:func:`operator/=(new_type &, new_type const &) constexpr new_type & operator/=(new_type & lhs, new_type const & rhs)>` + + .. versionadded:: 1.0.0 + +.. cpp:var:: auto constexpr EqBase = derivable{} + + This tag enables the derivation of following "equality comparison with base type" operators: + + * :cpp:func:`operator==(BaseType const &, new_type const &) constexpr bool nt::operator==(BaseType const &, new_type const &)>` + * :cpp:func:`operator==(new_type const &, BaseType const &) constexpr bool nt::operator==(new_type const &, BaseType const &)>` + * :cpp:func:`operator!=(BaseType const &, new_type const &) constexpr bool nt::operator!=(BaseType const &, new_type const &)>` + * :cpp:func:`operator!=(new_type const &, BaseType const &) constexpr bool nt::operator!=(new_type const &, BaseType const &)>` + + By virtue of its nature, deriving this feature compromises the strength of the given :cpp:class:`new_type`. + + .. versionadded:: 1.0.0 + +.. cpp:var:: auto constexpr ImplicitConversion = derivable{} + + This tag enables the derivation of the implicit "conversion to base type" operator. + By virtue of its nature, deriving this feature compromises the strength of the given :cpp:class:`new_type`. + + .. versionadded:: 1.0.0 + +.. cpp:var:: auto constexpr Hash = derivable{} + + This tag enables the derivation of a specialization of :cpp:class:`std::hash` + + .. versionadded:: 1.0.0 + +.. cpp:var:: auto constexpr Indirection = derivable{} + + This tag enables the derivation of the "member access through pointer" operator :cpp:func:`operator->() ()()>` (both in :literal:`const` and non-:literal:`const` variants). + + .. versionadded:: 1.0.0 + +.. cpp:var:: auto constexpr Iterable = derivable{} + + This tag enables the derivation of the following "standard iterator functions": + + * :cpp:func:`begin() ` + * :cpp:func:`begin() const ` + + .. versionadded:: 1.1.0 + +.. cpp:var:: auto constexpr Read = derivable{} + + This tag enables the derivation of the "stream output" :cpp:func:`operator\<\<(std::basic_ostream &, new_type const &) ` + + .. versionadded:: 1.0.0 + +.. cpp:var:: auto constexpr Relational = derivable{} + + This tag enables the derivation of the following relational operators: + + * :cpp:func:`operator\<(new_type const &, new_type const &) constexpr bool operator<(new_type const &, new_type const &)>` + * :cpp:func:`operator>(new_type const &, new_type const &) constexpr bool operator>(new_type const &, new_type const &)>` + * :cpp:func:`operator\<=(new_type const &, new_type const &) constexpr bool operator<=(new_type const &, new_type const &)>` + * :cpp:func:`operator>=(new_type const &, new_type const &) constexpr bool operator>=(new_type const &, new_type const &)>` + + .. versionadded:: 1.0.0 + +.. cpp:var:: auto constexpr Show = derivable{} + + This tag enables the derivation of the "stream input" :cpp:func:`operator>>(std::basic_istream &, new_type &) >>` + + .. versionadded:: 1.0.0 + +Header :literal:`` +======================================== + +This header contains the definition of the function template :cpp:func:`deriving`. + +Function template :cpp:func:`deriving` +-------------------------------------- + +.. cpp:function:: template \ + constexpr derivation_clause deriving(derivable... features) noexcept + + This function can be used to create a new :cpp:class:`derivation_clause` for use in the definitions of instances of :cpp:class:`new_type`. + + .. versionadded:: 1.0.0 + + .. seealso:: :ref:`sec-standard-derivation-tags` for a list of standard derivation tags + +Header :literal:`` +================================================= + +This header contains the definition of the class template :cpp:class:`derivation_clause` + +Class template :cpp:class:`derivation_clause` +--------------------------------------------- + +.. cpp:class:: template \ + derivation_clause + + Derivation clauses are used by :cpp:class:`new_type` to allow users to specify a set of automatically derived support functions. + + :tparam DerivableTags: A (potentially empty) list of tag types identifying the contained derivations + + .. versionadded:: 1.0.0 + + **Constructors** + + .. cpp:function:: constexpr derivation_clause(derivable...) noexcept + + Construct a new derivations clause containing the given derivations + + **Evaluation Functions** + + .. cpp:function:: template \ + constexpr bool operator()(derivable) const noexcept + + Check if this :cpp:class:`derivation clause ` contains the given derivation + + :tparam DerivableTag: A tag uniquely identifying a derivation + + .. cpp:function:: template \ + constexpr bool operator()(derivable, derivable...) const noexcept + + Check if this :cpp:class:`derivation clause ` contains **all** of the given derivations + + :tparam DerivableTag: A tag uniquely identifying a derivation + :tparam RemainingDerivableTags: A list of tags uniquely identifying a list of derivations + + **Equality Comparison Operators** + + .. cpp:function:: template \ + constexpr bool operator==(derivation_clause other) const noexcept + + Check if this :cpp:class:`derivation clause ` is identical to the one represented by :cpp:any:`other`. + Two derivation clauses are considered equal iff. both contain the same derivations irrespective of their order. + + :tparam OtherDerivableTags: A (potentialy empty) list of tags uniquely identifying a list of derivations + :param other: An existing :cpp:class:`derivation clause ` + + .. cpp:function:: template \ + constexpr bool operator!=(derivation_clause other) const noexcept + + Check if this :cpp:class:`derivation clause ` is different from the one represented by :cpp:any:`other`. + Two derivation clauses are considered different iff. one contains at least one derivation not contained by the other. + + :tparam OtherDerivableTags: A (potentialy empty) list of tags uniquely identifying a list of derivations + :param other: An existing :cpp:class:`derivation clause ` + + **Relational Comparison Operators** + + .. cpp:function:: template \ + constexpr bool operator<(derivation_clause other) const noexcept + + Check if this :cpp:class:`derivation clause ` is a subset of the one represented by :cpp:any:`other`. + One :cpp:class:`derivation clause ` is considered to be a subset of another iff. the list of derivations of this instance forms a proper subset of the list of derivations of the other. + + :tparam OtherDerivableTags: A (potentialy empty) list of tags uniquely identifying a list of derivations + :param other: An existing :cpp:class:`derivation clause ` + + .. cpp:function:: template \ + constexpr bool operator>(derivation_clause other) const noexcept + + Check if this :cpp:class:`derivation clause ` is a superset of the one represented by :cpp:any:`other`. + One :cpp:class:`derivation clause ` is considered to be a superset of another iff. the list of derivations of this instance forms a proper superset of the list of derivations of the other. + + :tparam OtherDerivableTags: A (potentialy empty) list of tags uniquely identifying a list of derivations + :param other: An existing :cpp:class:`derivation clause ` + + .. cpp:function:: template \ + constexpr bool operator<=(derivation_clause other) const noexcept + + Check if this :cpp:class:`derivation clause ` is either identical to or a subset of the one represented by :cpp:any:`other`. + One :cpp:class:`derivation clause ` is considered to be identical to another iff. the list of derivations of this instance is identical to the list of derivations of the other. + One :cpp:class:`derivation clause ` is considered to be a subset of another iff. the list of derivations of this instance forms a proper subset of the list of derivations of the other. + + :tparam OtherDerivableTags: A (potentialy empty) list of tags uniquely identifying a list of derivations + :param other: An existing :cpp:class:`derivation clause ` + + .. cpp:function:: template \ + constexpr bool operator>=(derivation_clause other) const noexcept + + Check if this :cpp:class:`derivation clause ` is either identical to or a superset of the one represented by :cpp:any:`other`. + One :cpp:class:`derivation clause ` is considered to be identical to another iff. the list of derivations of this instance is identical to the list of derivations of the other. + One :cpp:class:`derivation clause ` is considered to be a superset of another iff. the list of derivations of this instance forms a proper superset of the list of derivations of the other. + + :tparam OtherDerivableTags: A (potentialy empty) list of tags uniquely identifying a list of derivations + :param other: An existing :cpp:class:`derivation clause ` diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..73d961e --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,15 @@ +file(GLOB SOURCES + RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" + CONFIGURE_DEPENDS + "src/*.cpp" +) + +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/examples/src/basic_usage.cpp b/examples/src/basic_usage.cpp new file mode 100644 index 0000000..35d1d2c --- /dev/null +++ b/examples/src/basic_usage.cpp @@ -0,0 +1,39 @@ +#include + +#include + +using Width = nt::new_type; +using Height = nt::new_type; +using Area = nt::new_type; + +struct Rectangle +{ + constexpr Rectangle(Width w, Height h) + : width{w} + , height{h} + { + } + + auto constexpr area() const noexcept -> Area + { + return {width.decay() * height.decay()}; + } + +private: + Width width; + Height height; +}; + +int main() +{ + auto w{0u}, h{0u}; + + std::cin >> w >> h; + + auto width = Width{w}; + auto height = Height{h}; + + auto rect = Rectangle{width, height}; + + std::cout << rect.area().decay() << '\n'; +} \ No newline at end of file diff --git a/examples/src/basic_usage_with_read.cpp b/examples/src/basic_usage_with_read.cpp new file mode 100644 index 0000000..531a8e3 --- /dev/null +++ b/examples/src/basic_usage_with_read.cpp @@ -0,0 +1,37 @@ +#include + +#include + +using Width = nt::new_type; +using Height = nt::new_type; +using Area = nt::new_type; + +struct Rectangle +{ + constexpr Rectangle(Width w, Height h) + : width{w} + , height{h} + { + } + + auto constexpr area() const noexcept -> Area + { + return {width.decay() * height.decay()}; + } + +private: + Width width; + Height height; +}; + +int main() +{ + auto width = Width{}; + auto height = Height{}; + + std::cin >> width >> height; + + auto rect = Rectangle{width, height}; + + std::cout << rect.area() << '\n'; +} \ No newline at end of file diff --git a/examples/src/basic_usage_with_show.cpp b/examples/src/basic_usage_with_show.cpp new file mode 100644 index 0000000..9e4d985 --- /dev/null +++ b/examples/src/basic_usage_with_show.cpp @@ -0,0 +1,39 @@ +#include + +#include + +using Width = nt::new_type; +using Height = nt::new_type; +using Area = nt::new_type; + +struct Rectangle +{ + constexpr Rectangle(Width w, Height h) + : width{w} + , height{h} + { + } + + auto constexpr area() const noexcept -> Area + { + return {width.decay() * height.decay()}; + } + +private: + Width width; + Height height; +}; + +int main() +{ + auto w{0u}, h{0u}; + + std::cin >> w >> h; + + auto width = Width{w}; + auto height = Height{h}; + + auto rect = Rectangle{width, height}; + + std::cout << rect.area() << '\n'; +} \ No newline at end of file diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100644 index 0000000..0219803 --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1,16 @@ +add_library("${PROJECT_NAME}" INTERFACE) + +target_include_directories("${PROJECT_NAME}" INTERFACE + "$" + "$" +) + +target_compile_features("${PROJECT_NAME}" INTERFACE + "cxx_std_20" +) + +install(DIRECTORY "include/" + TYPE INCLUDE +) + +add_library("${PROJECT_NAME}::${PROJECT_NAME}" ALIAS "${PROJECT_NAME}") diff --git a/lib/include/newtype/newtype.hpp b/lib/include/newtype/newtype.hpp new file mode 100644 index 0000000..fecaf2c --- /dev/null +++ b/lib/include/newtype/newtype.hpp @@ -0,0 +1,753 @@ +#ifndef NEWTYPE_NEWTYPE_HPP +#define NEWTYPE_NEWTYPE_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace nt +{ + inline namespace lib + { + constexpr struct + { + int const major; + int const minor; + int const patch; + + char const * const name; + } version{ + .major = 2, + .minor = 0, + .patch = 0, + .name = "Brynn", + }; + + } // namespace lib + + namespace impl + { + inline namespace storage + { + template + struct new_type_storage + { + T m_value; + }; + } // namespace storage + + inline namespace member_types + { + + template> + struct new_type_iterator : new_type_storage + { + }; + + template + struct new_type_iterator> : new_type_storage + { + using iterator = typename T::iterator; + }; + +#define NEWTYPE_MAKE_ITERATOR_BASE(NAME, BASE) \ + template> \ + struct new_type_##NAME : new_type_##BASE \ + { \ + }; \ + template \ + struct new_type_##NAME> : new_type_##BASE \ + { \ + using NAME = typename T::NAME; \ + }; + + NEWTYPE_MAKE_ITERATOR_BASE(const_iterator, iterator) + NEWTYPE_MAKE_ITERATOR_BASE(reverse_iterator, const_iterator) + NEWTYPE_MAKE_ITERATOR_BASE(const_reverse_iterator, reverse_iterator) +#undef NEWTYPE_MAKE_ITERATOR_BASE + + template + struct new_type_iterator_types : new_type_const_reverse_iterator + { + }; + + } // namespace member_types + + } // namespace impl + + namespace concepts + { + + inline namespace arithmetic + { +#define NEWTYPE_MAKE_ARITHMETIC_CONCEPT(NAME, OPERATOR, REF) \ + template \ + concept NAME = requires(SubjectType lhs, SubjectType rhs) { \ + { \ + lhs OPERATOR rhs \ + } -> std::same_as; \ + }; \ + template \ + concept nothrow_##NAME = requires(SubjectType lhs, SubjectType rhs) { \ + requires NAME; \ + { \ + lhs OPERATOR rhs \ + } noexcept; \ + }; + + NEWTYPE_MAKE_ARITHMETIC_CONCEPT(addable, +, /*no-ref*/) + NEWTYPE_MAKE_ARITHMETIC_CONCEPT(divisible, /, /*no-ref*/) + NEWTYPE_MAKE_ARITHMETIC_CONCEPT(multipliable, *, /*no-ref*/) + NEWTYPE_MAKE_ARITHMETIC_CONCEPT(subtractable, -, /*no-ref*/) + NEWTYPE_MAKE_ARITHMETIC_CONCEPT(compound_addable, +=, &) + NEWTYPE_MAKE_ARITHMETIC_CONCEPT(compound_divisible, /=, &) + NEWTYPE_MAKE_ARITHMETIC_CONCEPT(compound_multipliable, *=, &) + NEWTYPE_MAKE_ARITHMETIC_CONCEPT(compound_subtractable, -=, &) +#undef NEWTYPE_MAKE_ARITHMETIC_CONCEPT + } // namespace arithmetic + + 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) { + { + lhs == rhs + } -> std::convertible_to; + }; + + template + concept nothrow_equality_comparable = requires(SubjectType lhs, SubjectType rhs) { + requires equality_comparable; + { + lhs == rhs + } noexcept; + }; + + template + concept inequality_comparable = requires(SubjectType lhs, SubjectType rhs) { + { + lhs != rhs + } -> std::convertible_to; + }; + + template + concept nothrow_inequality_comparable = requires(SubjectType lhs, SubjectType rhs) { + requires inequality_comparable; + { + lhs != rhs + } noexcept; + }; + + template + concept less_than_comparable = requires(SubjectType lhs, SubjectType rhs) { + { + lhs < rhs + } -> std::convertible_to; + }; + + template + concept nothrow_less_than_comparable = requires(SubjectType lhs, SubjectType rhs) { + requires less_than_comparable; + { + lhs < rhs + } noexcept; + }; + + template + concept less_than_equal_comparable = requires(SubjectType lhs, SubjectType rhs) { + { + lhs <= rhs + } -> std::convertible_to; + }; + + template + concept nothrow_less_than_equal_comparable = requires(SubjectType lhs, SubjectType rhs) { + requires less_than_equal_comparable; + { + lhs <= rhs + } noexcept; + }; + + template + concept greater_than_comparable = requires(SubjectType lhs, SubjectType rhs) { + { + lhs > rhs + } -> std::convertible_to; + }; + + template + concept nothrow_greater_than_comparable = requires(SubjectType lhs, SubjectType rhs) { + requires greater_than_comparable; + { + lhs > rhs + } noexcept; + }; + + template + concept greater_than_equal_comparable = requires(SubjectType lhs, SubjectType rhs) { + { + lhs >= rhs + } -> std::convertible_to; + }; + + template + concept nothrow_greater_than_equal_comparable = requires(SubjectType lhs, SubjectType rhs) { + requires greater_than_equal_comparable; + { + lhs >= rhs + } noexcept; + }; + + } // namespace comparability + + inline namespace iostreamable + { + + template + concept input_streamable = requires(SubjectType subject) { + { + std::declval &>() >> subject + } -> std::same_as &>; + }; + + template + concept nothrow_input_streamable = requires(SubjectType subject) { + requires input_streamable; + { + std::declval &>() >> subject + } noexcept; + }; + + template + concept output_streamable = requires(SubjectType subject) { + { + std::declval &>() << subject + } -> std::same_as &>; + }; + + template + concept nothrow_output_streamable = requires(SubjectType subject) { + requires output_streamable; + { + std::declval &>() << subject + } noexcept; + }; + + } // namespace iostreamable + + inline namespace iterable + { + + template + concept free_begin = requires(SubjectType & subject) { + typename SubjectType::iterator; + { + begin(subject) + } -> std::same_as; + }; + + template + concept const_free_begin = requires(SubjectType const & subject) { + typename SubjectType::const_iterator; + { + begin(subject) + } -> std::same_as; + }; + + template + concept member_begin = requires(SubjectType & subject) { + typename SubjectType::iterator; + { + subject.begin() + } -> std::same_as; + }; + + template + concept const_member_begin = requires(SubjectType const & subject) { + typename SubjectType::const_iterator; + { + subject.begin() + } -> std::same_as; + }; + + template + concept beginnable = free_begin || member_begin; + + template + concept const_beginnable = const_free_begin || const_member_begin; + + template + concept free_cbegin = requires(SubjectType const & subject) { + typename SubjectType::const_iterator; + { + cbegin(subject) + } -> std::same_as; + }; + + template + concept member_cbegin = requires(SubjectType const & subject) { + typename SubjectType::const_iterator; + { + subject.cbegin() + } -> std::same_as; + }; + + template + concept cbeginnable = free_cbegin || member_cbegin; + + template + concept free_rbegin = requires(SubjectType & subject) { + typename SubjectType::reverse_iterator; + { + rbegin(subject) + } -> std::same_as; + }; + + template + concept const_free_rbegin = requires(SubjectType const & subject) { + typename SubjectType::const_reverse_iterator; + { + rbegin(subject) + } -> std::same_as; + }; + + template + concept member_rbegin = requires(SubjectType & subject) { + typename SubjectType::reverse_iterator; + { + subject.rbegin() + } -> std::same_as; + }; + + template + concept const_member_rbegin = requires(SubjectType const & subject) { + typename SubjectType::const_reverse_iterator; + { + subject.rbegin() + } -> std::same_as; + }; + + template + concept rbeginnable = free_rbegin || member_rbegin; + + template + concept const_rbeginnable = const_free_rbegin || const_member_rbegin; + + template + concept free_crbegin = requires(SubjectType const & subject) { + typename SubjectType::const_reverse_iterator; + { + crbegin(subject) + } -> std::same_as; + }; + + template + concept member_crbegin = requires(SubjectType const & subject) { + typename SubjectType::const_reverse_iterator; + { + subject.crbegin() + } -> std::same_as; + }; + + template + concept crbeginnable = free_crbegin || member_crbegin; + + template + concept free_end = requires(SubjectType & subject) { + typename SubjectType::iterator; + { + end(subject) + } -> std::same_as; + }; + + template + concept const_free_end = requires(SubjectType const & subject) { + typename SubjectType::const_iterator; + { + end(subject) + } -> std::same_as; + }; + + template + concept member_end = requires(SubjectType & subject) { + typename SubjectType::iterator; + { + subject.end() + } -> std::same_as; + }; + + template + concept const_member_end = requires(SubjectType const & subject) { + typename SubjectType::const_iterator; + { + subject.end() + } -> std::same_as; + }; + + template + concept endable = free_end || member_end; + + template + concept const_endable = const_free_end || const_member_end; + + template + concept free_cend = requires(SubjectType const & subject) { + typename SubjectType::const_iterator; + { + cend(subject) + } -> std::same_as; + }; + + template + concept member_cend = requires(SubjectType const & subject) { + typename SubjectType::const_iterator; + { + subject.cend() + } -> std::same_as; + }; + + template + concept cendable = free_cend || member_cend; + + template + concept free_rend = requires(SubjectType & subject) { + typename SubjectType::reverse_iterator; + { + rend(subject) + } -> std::same_as; + }; + + template + concept const_free_rend = requires(SubjectType const & subject) { + typename SubjectType::const_reverse_iterator; + { + rend(subject) + } -> std::same_as; + }; + + template + concept member_rend = requires(SubjectType & subject) { + typename SubjectType::reverse_iterator; + { + subject.rend() + } -> std::same_as; + }; + + template + concept const_member_rend = requires(SubjectType const & subject) { + typename SubjectType::const_reverse_iterator; + { + subject.rend() + } -> std::same_as; + }; + + template + concept rendable = free_rend || member_rend; + + template + concept const_rendable = const_free_rend || const_member_rend; + + template + concept free_crend = requires(SubjectType const & subject) { + typename SubjectType::const_reverse_iterator; + { + crend(subject) + } -> std::same_as; + }; + + template + concept member_crend = requires(SubjectType const & subject) { + typename SubjectType::const_reverse_iterator; + { + subject.crend() + } -> std::same_as; + }; + + template + concept crendable = free_crend || member_crend; + } // namespace iterable + + inline namespace standard_extensions + { + + template + concept hashable = requires(SubjectType subject) { + { + std::hash{}(subject) + } -> std::convertible_to; + }; + + } + + } // namespace concepts + + template + struct derivable final + { + using tag_type = DerivableTag; + }; + + inline namespace derivables + { + + auto constexpr Arithmetic = derivable{}; + auto constexpr EqBase = derivable{}; + auto constexpr Hash = derivable{}; + auto constexpr ImplicitConversion = derivable{}; + auto constexpr Indirection = derivable{}; + auto constexpr Iterable = derivable{}; + auto constexpr Read = derivable{}; + auto constexpr Relational = derivable{}; + auto constexpr Show = derivable{}; + auto constexpr ThreewayCompare = derivable{}; + + } // namespace derivables + + template + struct derivation_clause + { + template + using derives_one = std::disjunction...>; + + template + using derives = std::conjunction...>; + + constexpr derivation_clause(derivable...) noexcept + { + } + }; + + template + concept derives = requires(DerivationClause clause) { requires DerivationClause::template derives::value; }; + + template + concept doesnt_derive = !derives; + + template + auto constexpr deriving(derivable... features) noexcept -> derivation_clause + { + return {features...}; + } + + template + 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."); + + using super = impl::new_type_iterator_types>; + + public: + using base_type = BaseType; + using tag_type = TagType; + using derivation_clause_type = decltype(DerivationClause); + + auto constexpr static derivation_clause = DerivationClause; + + 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(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; + + auto constexpr decay() const noexcept(std::is_nothrow_copy_constructible_v) -> BaseType + { + return this->m_value; + } + + template + constexpr operator base_type() const noexcept(std::is_nothrow_copy_constructible_v) + requires(nt::derives) + { + return decay(); + } + + template + explicit constexpr operator base_type() const noexcept(std::is_nothrow_copy_constructible_v) + requires(nt::doesnt_derive) + { + return decay(); + } + + 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 in >> obj.m_value; + } + + 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 out << obj.m_value; + } + + // 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 decay() <=> rhs.decay(); + } + + auto constexpr operator==(new_type const & rhs) const -> bool + requires(nt::concepts::equality_comparable && nt::derives) + { + return decay() == rhs.decay(); + } + + // NOT THREE-WAY EQUAL + + auto constexpr operator==(new_type const & rhs) const noexcept(nt::concepts::nothrow_equality_comparable) -> bool + requires(nt::concepts::equality_comparable && nt::doesnt_derive) + { + return decay() == rhs.decay(); + } + + // BASE EQUAL + + auto constexpr operator==(BaseType const & rhs) const noexcept(nt::concepts::nothrow_equality_comparable) -> bool + requires(nt::concepts::equality_comparable && nt::derives) + { + return decay() == rhs; + } + + // 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 decay() != rhs.decay(); + } + +#define NEWTYPE_MAKE_LOGICAL_OPERATOR(OPERATOR, CONCEPT) \ + auto constexpr operator OPERATOR(new_type const & rhs) const noexcept(nt::concepts::nothrow_##CONCEPT##_comparable) -> bool \ + requires(nt::concepts::CONCEPT##_comparable && nt::doesnt_derive && \ + nt::derives) \ + { \ + return decay() OPERATOR rhs.decay(); \ + } + + NEWTYPE_MAKE_LOGICAL_OPERATOR(<, less_than) + NEWTYPE_MAKE_LOGICAL_OPERATOR(<=, less_than_equal) + NEWTYPE_MAKE_LOGICAL_OPERATOR(>, greater_than) + NEWTYPE_MAKE_LOGICAL_OPERATOR(>=, greater_than_equal) +#undef NEWTYPE_MAKE_LOGICAL_OPERATOR + +#define NEWTYPE_MAKE_ARITHMETIC_OPERATOR(OPERATOR, CONCEPT) \ + auto constexpr operator OPERATOR(new_type const & rhs) const noexcept(nt::concepts::nothrow_##CONCEPT) -> new_type \ + requires(nt::concepts::CONCEPT && nt::derives) \ + { \ + return {decay() OPERATOR rhs.decay()}; \ + } \ + auto constexpr operator OPERATOR##=(new_type const & rhs) const noexcept(nt::concepts::nothrow_compound_##CONCEPT) -> new_type & \ + requires(nt::concepts::compound_##CONCEPT && nt::derives) { \ + return this->m_value OPERATOR## = rhs.decay(), *this; \ + }; + + NEWTYPE_MAKE_ARITHMETIC_OPERATOR(+, addable) + NEWTYPE_MAKE_ARITHMETIC_OPERATOR(-, subtractable) + NEWTYPE_MAKE_ARITHMETIC_OPERATOR(*, multipliable) + NEWTYPE_MAKE_ARITHMETIC_OPERATOR(/, divisible) +#undef NEWTYPE_MAKE_ARITHMETIC_OPERATOR + +#define NEWTYPE_MAKE_ITERATOR_FACTORY_HELPER(NAME, QUALIFICATION, ITERATOR) \ + template \ + auto constexpr friend NAME(new_type QUALIFICATION & obj) -> new_type::ITERATOR \ + requires nt::derives \ + { \ + using std::NAME; \ + return NAME(obj.m_value); \ + } \ + template \ + auto constexpr NAME() QUALIFICATION->new_type::ITERATOR \ + requires nt::derives \ + { \ + return this->m_value.NAME(); \ + } + +#define NEWTYPE_MAKE_ITERATOR_FACTORY(NAME, ITERATOR) \ + NEWTYPE_MAKE_ITERATOR_FACTORY_HELPER(NAME, /*non-const*/, ITERATOR) \ + NEWTYPE_MAKE_ITERATOR_FACTORY_HELPER(NAME, const, const_##ITERATOR) \ + NEWTYPE_MAKE_ITERATOR_FACTORY_HELPER(c##NAME, const, const_##ITERATOR) + + NEWTYPE_MAKE_ITERATOR_FACTORY(begin, iterator) + NEWTYPE_MAKE_ITERATOR_FACTORY(rbegin, reverse_iterator) + NEWTYPE_MAKE_ITERATOR_FACTORY(end, iterator) + NEWTYPE_MAKE_ITERATOR_FACTORY(rend, reverse_iterator) +#undef NEWTYPE_MAKE_ITERATOR_FACTORY +#undef NEWTYPE_MAKE_ITERATOR_FACTORY_HELPER + + // INDIRECTION + + // template + // requires(nt::derives) + // auto constexpr operator->() noexcept -> BaseType * + // { + // return std::addressof(this->m_value); + // } + + // template + // requires(nt::derives) + // auto constexpr operator->() const noexcept -> BaseType const * + // { + // return std::addressof(this->m_value); + // } + }; + +} // namespace nt + +namespace std +{ + template auto DerivationClause> + struct hash> + { + auto constexpr operator()(nt::new_type const & object) const + { + return std::hash{}(object.decay()); + } + }; +} // namespace std + +#endif diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt deleted file mode 100644 index 984e085..0000000 --- a/source/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -cmake_minimum_required(VERSION "3.25.0") - -project("newtype" - LANGUAGES CXX - DESCRIPTION "A library of types and functions to create strong type aliases" -) - -enable_testing() - -# Project Options - -option(BUILD_EXAMPLES "Build the library examples" OFF) - -# Project Components - -add_subdirectory("doc") -add_subdirectory("examples") -add_subdirectory("lib") -add_subdirectory("tests") \ No newline at end of file diff --git a/source/doc/.gitignore b/source/doc/.gitignore deleted file mode 100644 index ee826c1..0000000 --- a/source/doc/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -Pipfile -Pipfile.lock diff --git a/source/doc/CMakeLists.txt b/source/doc/CMakeLists.txt deleted file mode 100644 index 7a9e27d..0000000 --- a/source/doc/CMakeLists.txt +++ /dev/null @@ -1,81 +0,0 @@ -find_package("Python3" - REQUIRED - COMPONENTS "Interpreter" -) - -set(DOCENV_DIR "${PROJECT_BINARY_DIR}/docenv") - -if(NOT EXISTS "${DOCENV_DIR}") - message(STATUS "Creating Python virtual environment") - execute_process(COMMAND "${Python3_EXECUTABLE}" - "-m" - "venv" - "${DOCENV_DIR}" - OUTPUT_QUIET - ) - message(STATUS "Installing documentation requirements") - execute_process(COMMAND - "${DOCENV_DIR}/bin/python" - "-m" - "pip" - "install" - "-r" - "${CMAKE_CURRENT_SOURCE_DIR}/requirements.txt" - OUTPUT_QUIET - ) -else() - message(STATUS "Reusing existing Python virtual environment") -endif() - -file(GLOB SOURCES - CONFIGURE_DEPENDS - "${CMAKE_CURRENT_SOURCE_DIR}/src/*" - "${PROJECT_SOURCE_DIR}/examples/src/*" -) - -add_custom_target("docs" - ALL - DEPENDS - "${CMAKE_CURRENT_BINARY_DIR}/html/index.html" - "${CMAKE_CURRENT_BINARY_DIR}/man/newtype.3" - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - COMMENT "Building documentation" -) - -add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/html/index.html" - COMMAND "${DOCENV_DIR}/bin/sphinx-build" - "-q" - "-b" - "singlehtml" - "-E" - "${CMAKE_CURRENT_SOURCE_DIR}/src" - "${CMAKE_CURRENT_BINARY_DIR}/html" - DEPENDS ${SOURCES} - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - COMMENT "Compiling HTML documentation" -) - -add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/man/newtype.3" - COMMAND "${DOCENV_DIR}/bin/sphinx-build" - "-q" - "-b" - "man" - "-E" - "${CMAKE_CURRENT_SOURCE_DIR}/src" - "${CMAKE_CURRENT_BINARY_DIR}/man" - DEPENDS ${SOURCES} - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - COMMENT "Compiling man page" -) - -install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/singlehtml" - TYPE DOC - PATTERN ".doctrees" EXCLUDE - PATTERN ".buildinfo" EXCLUDE - PATTERN ".nojekyll" EXCLUDE -) - -install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/man/" - TYPE MAN - PATTERN ".doctrees" EXCLUDE -) diff --git a/source/doc/requirements.txt b/source/doc/requirements.txt deleted file mode 100644 index 47b0805..0000000 --- a/source/doc/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -sphinx~=7.0 diff --git a/source/doc/src/conf.py b/source/doc/src/conf.py deleted file mode 100644 index a6e65de..0000000 --- a/source/doc/src/conf.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- - -import os - - -# -- Project information ----------------------------------------------------- - -project = 'newtype' -copyright = '2023, Felix Morgner' -author = 'Felix Morgner' -version = '2.0' -release = '2.0.0' - - -# -- General configuration --------------------------------------------------- - -master_doc = 'index' - -extensions = [ - 'sphinx.ext.todo', - 'sphinx.ext.githubpages', -] - -highlight_language = 'c++' - -pygments_style = 'tango' - -exclude_patterns = [] - -numfig = True - -# -- Options for HTML output ------------------------------------------------- - -html_theme = 'haiku' -html_theme_options = { -} - -# -- Options for manual page output ------------------------------------------------- - -man_pages = [ - ( - "index", - "newtype", - "A library of types and functions to create strong type aliases", - ["Felix Morgner "], - 3, - ), -] \ No newline at end of file diff --git a/source/doc/src/index.rst b/source/doc/src/index.rst deleted file mode 100644 index 88925aa..0000000 --- a/source/doc/src/index.rst +++ /dev/null @@ -1,1271 +0,0 @@ -.. cpp:namespace-push:: nt - -.. |BaseTypeDoc| replace:: The type of the contained object -.. |TagTypeDoc| replace:: A tag to uniquely identify an instance of :cpp:class:`nt::new_type` -.. |DerivationClauseDoc| replace:: A (possibly empty) list of derivation tags as generated by :cpp:func:`nt::deriving` - -.. only:: html - - .. contents:: Table of Contents - :depth: 5 - -############# -Documentation -############# - -The ``newtype`` library provides types and functions to facilitate the creation of strong type aliases. - -Example Usage -############# - -.. note:: - - 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 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++ - :linenos: - :name: new-type-usage-basic - :caption: Basic usage of :cpp:class:`new_type` - -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 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: 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` 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: 5,6,29,30,32 - :language: c++ - :linenos: - :name: new-type-usage-basic-read - :caption: Deriving input operations using the :cpp:var:`Read` derivation tag - -API -### - -This section of the documentation describes the public API of the *new_type*. -It provides detailed descriptions of the types and functions designed to be used by applications. -All declarations described in this section are found in the namespace :cpp:any:`nt`, unless noted otherwise. - -Header :literal:`` -======================================== - -This header contains the definitions of the class template :cpp:class:`new_type` as well as a set of associated namespace-level functions. - -Class template :cpp:class:`new_type` ------------------------------------- - -.. cpp:class:: template \ - 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 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| - :tparam DerivationClause: |DerivationClauseDoc| - - .. versionadded:: 1.0.0 - - **Member Type Aliases** - - .. cpp:type:: base_type = BaseType - - .. cpp:type:: tag_type = TagType - - .. cpp:type:: derivation_clause_type = decltype(DerivationClause) - - .. cpp:type:: iterator = typename BaseType::iterator - - :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 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 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 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 - - **Static Data Members** - - .. cpp:var:: static derivation_clause_type constexpr derivation_clause = DerivationClause - - **Constructors** - - .. 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 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) 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 :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`. - This constructor shall be noexcept iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *nothrow copy-construtible*. - :enablement: This constructor shall be defined as :literal:`= default` iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *copy-construtible*. - Otherwise, this constructor shall be explicitely deleted. - - .. cpp:function:: constexpr new_type(new_type && other) - - Construct a new instance of this :cpp:class:`new_type` by move-constructing the contained object using the value contained by :literal:`other`. - - :param other: An existing instance of this :cpp:class:`new_type` - :throws: Any exception thrown by the move-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 move-construtible*. - :enablement: This constructor shall be defined as :literal:`= default` iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *move-construtible*. - Otherwise, this constructor shall be explicitely deleted. - - .. cpp:function:: constexpr new_type(BaseType const & value) - - Construct a new instance of this :cpp:class:`new_type` by copy-constructing the contained object using :literal:`value`. - - :param value: 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`. - This constructor shall be noexcept iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *nothrow copy-construtible*. - :enablement: This constructor shall be defined as :literal:`= default` iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *copy-construtible*. - Otherwise, this constructor shall be explicitely deleted. - - .. cpp:function:: constexpr new_type(BaseType && value) - - Construct a new instance of this :cpp:class:`new_type` by move-constructing the contained object using :literal:`value`. - - :param value: An existing instance of this :cpp:class:`new_type` - :throws: Any exception thrown by the move-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 move-construtible*. - :enablement: This constructor shall be defined as :literal:`= default` iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *move-construtible*. - Otherwise, this constructor shall be explicitely deleted. - - **Assignment Operators** - - .. cpp:function:: constexpr new_type & operator=(new_type const & other) - - Copy the value of an existing instance of this :cpp:class:`new_type` and replace this instance's value - - :param other: An existing instance of this :cpp:class:`new_type` - :throws: Any exception thrown by the copy-assignment operator of this :cpp:class:`new_type`'s :cpp:type:`base_type`. - This operator shall be noexcept iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *nothrow copy-assignable*. - :enablement: This operator shall be defined as :literal:`= default` iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *copy-assignable*. - Otherwise, this operator shall be explicitely deleted. - - .. cpp:function:: constexpr new_type & operator=(new_type && other) - - Move the value of an existing instance of this :cpp:class:`new_type` and replace this instance's value - - :param other: An existing instance of this :cpp:class:`new_type` - :throws: Any exception thrown by the move-assignment operator of this :cpp:class:`new_type`'s :cpp:type:`base_type`. - This operator shall be noexcept iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *nothrow move-assignable*. - :enablement: This operator shall be defined as :literal:`= default` iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *move-assignable*. - Otherwise, this operator shall be explicitely deleted. - - **Accessors** - - .. cpp:function:: constexpr BaseType decay() const - - Retrieve a copy of the object contained by this :cpp:class:`new_type` object - - :throws: Any exception thrown by the copy-constructor of this :cpp:class:`new_type`'s :cpp:type:`base_type`. - This operator shall be noexcept iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *nothrow copy-constructible*. - - .. cpp:function:: constexpr operator BaseType() const - - Retrieve a copy of the object contained by this :cpp:class:`new_type` object - - :throws: Any exception thrown by the copy-constructor of this :cpp:class:`new_type`'s :cpp:type:`base_type`. - This operator shall be noexcept iff. this :cpp:class:`new_type`'s :cpp:type:`base_type` is *nothrow copy-constructible*. - :explicit: This conversion operator shall be explicit unless this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`ImplicitConversion`. - - **Member Access Through Pointer** - - .. cpp:function:: constexpr BaseType operator->() noexcept - - Perform "member access through pointer" via a pointer to object contained by this :cpp:class:`new_type` - - :enablement: This operator shall be available iff. this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Indirection` - - .. cpp:function:: constexpr BaseType const * operator->() const noexcept - - Perform "member access through pointer" via a pointer to object contained by this :cpp:class:`new_type` - - :enablement: This operator shall be available iff. this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Indirection` - - **Iterators** - - .. cpp:function:: constexpr iterator begin() - - Get an iterator to the beginning of the object contained by this :cpp:class:`new_type` - - :enablement: This function shall be available iff. - - a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`begin() ` that returns an instance of type :cpp:type:`iterator` - - .. versionadded:: 1.1.0 - - .. cpp:function:: constexpr iterator begin() const - - Get a constant iterator to the beginning of the object contained by this :cpp:class:`new_type` - - :enablement: This function shall be available iff. - - a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`begin() const ` that returns an instance of type :cpp:type:`const_iterator` - - .. versionadded:: 1.1.0 - - .. cpp:function:: constexpr iterator cbegin() const - - Get a constant iterator to the beginning of the object contained by this :cpp:class:`new_type` - - :enablement: This function shall be available iff. - - a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`cbegin() const ` that returns an instance of type :cpp:type:`const_iterator` - - .. versionadded:: 1.1.0 - - .. cpp:function:: constexpr iterator rbegin() - - Get a reverse iterator to the beginning of the object contained by this :cpp:class:`new_type` - - :enablement: This function shall be available iff. - - a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`rbegin() ` that returns an instance of type :cpp:type:`reverse_iterator` - - .. versionadded:: 1.1.0 - - .. cpp:function:: constexpr iterator rbegin() const - - Get a constant reverse iterator to the beginning of the object contained by this :cpp:class:`new_type` - - :enablement: This function shall be available iff. - - a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`rbegin() const ` that returns an instance of type :cpp:type:`const_reverse_iterator` - - .. versionadded:: 1.1.0 - - .. cpp:function:: constexpr iterator crbegin() const - - Get a constant reverse iterator to the beginning of the object contained by this :cpp:class:`new_type` - - :enablement: This function shall be available iff. - - a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`crbegin() const ` that returns an instance of type :cpp:type:`const_reverse_iterator` - - .. versionadded:: 1.1.0 - - .. cpp:function:: constexpr iterator end() - - Get an iterator beyond the end of the object contained by this :cpp:class:`new_type` - - :enablement: This function shall be available iff. - - a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`end() ` that returns an instance of type :cpp:type:`iterator` - - .. versionadded:: 1.1.0 - - .. cpp:function:: constexpr iterator end() const - - Get a constant iterator beyond the end of the object contained by this :cpp:class:`new_type` - - :enablement: This function shall be available iff. - - a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`end() const ` that returns an instance of type :cpp:type:`const_iterator` - - .. versionadded:: 1.1.0 - - .. cpp:function:: constexpr iterator cend() const - - Get a constant iterator beyond the end of the object contained by this :cpp:class:`new_type` - - :enablement: This function shall be available iff. - - a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`cend() const ` that returns an instance of type :cpp:type:`const_iterator` - - .. versionadded:: 1.1.0 - - .. cpp:function:: constexpr iterator rend() - - Get a reverse iterator beyond the end of the object contained by this :cpp:class:`new_type` - - :enablement: This function shall be available iff. - - a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`rend() ` that returns an instance of type :cpp:type:`reverse_iterator` - - .. versionadded:: 1.1.0 - - .. cpp:function:: constexpr iterator rend() const - - Get a constant reverse iterator beyond the end of the object contained by this :cpp:class:`new_type` - - :enablement: This function shall be available iff. - - a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`rend() const ` that returns an instance of type :cpp:type:`const_reverse_iterator` - - .. versionadded:: 1.1.0 - - .. cpp:function:: constexpr iterator crend() const - - Get a constant reverse iterator beyond the end of the object contained by this :cpp:class:`new_type` - - :enablement: This function shall be available iff. - - a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) this :cpp:class:`new_type`'s :cpp:type:`base type ` has a non-static member function :cpp:func:`crend() const ` that returns an instance of type :cpp:type:`const_reverse_iterator` - - .. versionadded:: 1.1.0 - -:literal:`namespace`-level functions and function templates ------------------------------------------------------------ - -The functions and functions templates described in this section provide additional functionality for the class template :cpp:class:`new_type` that is not part of the class itself. - -Equality Comparison Operators -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. cpp:function:: template \ - constexpr bool operator==(new_type const & lhs, new_type const & rhs) - - Check two instances of :cpp:class:`new_type\` for equality. - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param lhs: The left-hand side of the comparison - :param rhs: The right-hand side of the comparison - :returns: The value returned by the comparison of the contained objects. - :throws: Any exception thrown by the comparison operator of objects contained by :literal:`lhs` and :literal:`rhs`. - This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow equals-comparable*. - :enablement: This operator shall be available iff. :cpp:type:`new_type::base_type` supports comparison using :literal:`==` - - .. versionadded:: 1.0.0 - -.. cpp:function:: template \ - constexpr bool operator==(new_type const & lhs, BaseType const & rhs) - - Check an instance of :cpp:class:`new_type\` for equality with an instance of :cpp:type:`BaseType`. - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param lhs: The left-hand side of the comparison - :param rhs: The right-hand side of the comparison - :returns: The value returned by the comparison of object contained by :literal:`lhs` with an object of the :cpp:type:`base type `. - :throws: Any exception thrown by the comparison of object contained by :literal:`lhs` with an object of the :cpp:type:`base type `. This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow equals-comparable*. - :enablement: This operator shall be available iff. - - a. :cpp:type:`new_type::base_type` supports comparison using :literal:`==` and - b. the :cpp:var:`derivation clause ` contains :cpp:var:`EqBase` - - .. versionadded:: 1.0.0 - -.. cpp:function:: template \ - constexpr bool operator==(BaseType const & lhs, new_type const & rhs) - - Check an instance of :cpp:type:`BaseType` for equality with an instance of :cpp:class:`new_type\`. - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param lhs: The left-hand side of the comparison - :param rhs: The right-hand side of the comparison - :returns: The value returned by the comparison of an object of :cpp:type:`base type ` with the object contained by :literal:`rhs`. - :throws: Any exception thrown by the comparison of an object of :cpp:type:`base type ` with the object contained by :literal:`rhs`. This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow equals-comparable*. - :enablement: This operator shall be available iff. - - a. :cpp:type:`new_type::base_type` supports comparison using :literal:`==` and - b. the :cpp:var:`derivation clause ` contains :cpp:var:`EqBase` - - .. versionadded:: 1.0.0 - -.. cpp:function:: template \ - constexpr bool operator!=(new_type const & lhs, new_type const & rhs) - - Check two instances of :cpp:class:`new_type\` for in-equality. - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param lhs: The left-hand side of the comparison - :param rhs: The right-hand side of the comparison - :returns: The value returned by the comparison of the contained objects. - :throws: Any exception thrown by the comparison operator of theobjects contained by :literal:`lhs` and :literal:`rhs`. - This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow not-equals-comparable*. - :enablement: This operator shall be available iff. :cpp:type:`new_type::base_type` supports comparison using :literal:`!=` - - .. versionadded:: 1.0.0 - -.. cpp:function:: template \ - constexpr bool operator!=(new_type const & lhs, BaseType const & rhs) - - Check an instance of :cpp:class:`new_type\` for in-equality with an instance of :cpp:type:`BaseType`. - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param lhs: The left-hand side of the comparison - :param rhs: The right-hand side of the comparison - :returns: The value returned by the comparison of the object contained by :literal:`lhs` with an object of the :cpp:type:`base type `. - :throws: Any exception thrown by the comparison of the object contained by :literal:`lhs` with an object of the :cpp:type:`base type `. - This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow not-equals-comparable*. - :enablement: This operator shall be available iff. - - a) :cpp:type:`new_type::base_type` supports comparison using :literal:`!=` and - b) the :cpp:var:`derivation clause ` contains :cpp:var:`EqBase` - - .. versionadded:: 1.0.0 - -.. cpp:function:: template \ - constexpr bool operator!=(BaseType const & lhs, new_type const & rhs) - - Check an instance of :cpp:type:`BaseType` for in-equality with an instance of :cpp:class:`new_type\`. - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param lhs: The left-hand side of the comparison - :param rhs: The right-hand side of the comparison - :returns: The value returned by the comparison of an object of :cpp:type:`base type ` with the object contained by :literal:`rhs`. - :throws: Any exception thrown by the comparison of an object of :cpp:type:`base type ` with the object contained by :literal:`rhs`. This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow not-equals-comparable*. - :enablement: This operator shall be available iff. - - a. :cpp:type:`new_type::base_type` supports comparison using :literal:`!=` and - b. the :cpp:var:`derivation clause ` contains :cpp:var:`EqBase` - - .. versionadded:: 1.0.0 - -Relational Comparison Operators -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. cpp:function:: template \ - constexpr bool operator<(new_type const & lhs, new_type const & rhs) - - Compare two instances of the same :cpp:class:`new_type` using :literal:`<` (*less-than*). - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param lhs: The left-hand side of the comparison - :param rhs: The right-hand side of the comparison - :returns: The value returned by the comparison of the contained objects. - :throws: Any exception thrown by the comparison operator of the objects contained by :literal:`lhs` and :literal:`rhs`. - This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow less-than-comparable*. - :enablement: This operator shall be available iff. - - a. :cpp:type:`new_type::base_type` supports comparison using :literal:`<` and - b. the :cpp:var:`derivation clause ` contains :cpp:var:`Relational` - - .. versionadded:: 1.0.0 - -.. cpp:function:: template \ - constexpr bool operator>(new_type const & lhs, new_type const & rhs) - - Compare two instances of the same :cpp:class:`new_type` using :literal:`>` (*greater-than*). - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param lhs: The left-hand side of the comparison - :param rhs: The right-hand side of the comparison - :returns: The value returned by the comparison of the contained objects. - :throws: Any exception thrown by the comparison operator of the objects contained by :literal:`lhs` and :literal:`rhs`. - This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow greater-than-comparable*. - :enablement: This operator shall be available iff. - - a. :cpp:type:`new_type::base_type` supports comparison using :literal:`>` and - b. the :cpp:var:`derivation clause ` contains :cpp:var:`Relational` - - .. versionadded:: 1.0.0 - -.. cpp:function:: template \ - constexpr bool operator<=(new_type const & lhs, new_type const & rhs) - - Compare two instances of the same :cpp:class:`new_type` using :literal:`<=` (*less-than-equal*). - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param lhs: The left-hand side of the comparison - :param rhs: The right-hand side of the comparison - :returns: The value returned by the comparison of the contained objects. - :throws: Any exception thrown by the comparison operator of the objects contained by :literal:`lhs` and :literal:`rhs`. - This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow less-than-equal-comparable*. - :enablement: This operator shall be available iff. - - a. :cpp:type:`new_type::base_type` supports comparison using :literal:`<=` and - b. the :cpp:var:`derivation clause ` contains :cpp:var:`Relational` - - .. versionadded:: 1.0.0 - -.. cpp:function:: template \ - constexpr bool operator>=(new_type const & lhs, new_type const & rhs) - - Compare two instances of the same :cpp:class:`new_type` using :literal:`>=` (*greater-than-equal*). - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param lhs: The left-hand side of the comparison - :param rhs: The right-hand side of the comparison - :returns: The value returned by the comparison of the contained objects. - :throws: Any exception thrown by the comparison operator of the objects contained by :literal:`lhs` and :literal:`rhs`. - This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow greater-than-equal-comparable*. - :enablement: This operator shall be available iff. - - a. :cpp:type:`new_type::base_type` supports comparison using :literal:`>=` and - b. the :cpp:var:`derivation clause ` contains :cpp:var:`Relational` - - .. versionadded:: 1.0.0 - -Stream I/O Operators -~~~~~~~~~~~~~~~~~~~~ - -.. cpp:function:: template \ - std::basic_ostream & operator<<(std::basic_ostream & out, new_type const & value) - - Write an instance of :cpp:class:`new_type\` to a standard :cpp:type:`ostream `. - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :tparam CharType: The stream character type - :tparam StreamTraits: The traits of the output stream - :param out: The output stream - :param value: A :cpp:class:`new_type` value to write to the output stream - :returns: A reference to the output stream - :throws: Any exception thrown by the stream-output operator of the object contained by :literal:`value`. - This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow output-streamable*. - :enablement: This operator shall be available iff. - - a. :cpp:type:`new_type::base_type` supports being written to an output stream using :literal:`<<` and - b. the :cpp:var:`derivation clause ` contains :cpp:var:`Show` - - .. versionadded:: 1.0.0 - -.. cpp:function:: template \ - std::basic_istream & operator>>(std::basic_istream & in, new_type & value) - - Read an instance of :cpp:class:`new_type\` from a standard :cpp:type:`istream `. - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :tparam CharType: The stream character type - :tparam StreamTraits: The traits of the input stream - :param in: The input stream - :param value: A :cpp:class:`new_type` value to be read from the output stream - :returns: A reference to the input stream - :throws: Any exception thrown by the stream-input operator of the object contained by :literal:`value`. - This operator shall be noexcept iff. :cpp:type:`new_type::base_type` is *nothrow input-streamable*. - :enablement: This operator shall be available iff. - - a. :cpp:type:`new_type::base_type` supports being read from an input stream using :literal:`>>` and - b. the :cpp:var:`derivation clause ` contains :cpp:var:`Read` - - .. versionadded:: 1.0.0 - -Arithmetic Operators -~~~~~~~~~~~~~~~~~~~~ - -.. cpp:function:: template \ - constexpr new_type operator+(new_type const & lhs, new_type const & rhs) - - Add two instances of the same :cpp:class:`new_type`. - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param lhs: The left-hand side of the addition - :param rhs: The right-hand side of the addition - :returns: A new instance of :cpp:class:`new_type\` containing the result of applying :literal:`+` to the objects contained by :literal:`lhs` and :literal:`rhs`. - :throws: Any exception thrown by the addition operator of the objects contained by :literal:`lhs` and :literal:`rhs`. - This operator shall be noexcept iff. - - a. :cpp:type:`new_type::base_type` is *nothrow addable* and - b. :cpp:type:`new_type::base_type` is *nothrow copy-constructible* - - :enablement: This operator shall be available iff. - - a. :cpp:type:`new_type::base_type` supports addition using :literal:`+` and - b. the :cpp:var:`derivation clause ` contains :cpp:var:`Arithmetic` - - .. versionadded:: 1.0.0 - -.. cpp:function:: template \ - constexpr new_type & operator+=(new_type & lhs, new_type const & rhs) - - Add two instances of the same :cpp:class:`new_type` by overwriting the first one. - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param lhs: The left-hand side of the addition - :param rhs: The right-hand side of the addition - :returns: A reference to the first argument containing the value modified by applying :literal:`+=` to the objects contained by :literal:`lhs` and :literal:`rhs`. - :throws: Any exception thrown by the addition-assignment operator of the objects contained by :literal:`lhs` and :literal:`rhs`. - This operator shall be noexcept iff. - - a. :cpp:type:`new_type::base_type` is *nothrow add-assignable* and - b. :cpp:type:`new_type::base_type` is *nothrow copy-constructible* - - :enablement: This operator shall be available iff. - - a. :cpp:type:`new_type::base_type` supports addition using :literal:`+=` and - b. the :cpp:var:`derivation clause ` contains :cpp:var:`Arithmetic` - - .. versionadded:: 1.0.0 - -.. cpp:function:: template \ - constexpr new_type operator-(new_type const & lhs, new_type const & rhs) - - Subtract two instances of the same :cpp:class:`new_type`. - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param lhs: The left-hand side of the subtraction - :param rhs: The right-hand side of the subtraction - :returns: A new instance of :cpp:class:`new_type\` containing the result of applying :literal:`-` to the objects contained by :literal:`lhs` and :literal:`rhs`. - :throws: Any exception thrown by the subtraction operator of the objects contained by :literal:`lhs` and :literal:`rhs`. - This operator shall be noexcept iff. - - a. :cpp:type:`new_type::base_type` is *nothrow subtractable* and - b. :cpp:type:`new_type::base_type` is *nothrow copy-constructible* - - :enablement: This operator shall be available iff. - - a. :cpp:type:`new_type::base_type` supports subtraction using :literal:`-` and - b. the :cpp:var:`derivation clause ` contains :cpp:var:`Arithmetic` - - .. versionadded:: 1.0.0 - -.. cpp:function:: template \ - constexpr new_type & operator-=(new_type & lhs, new_type const & rhs) - - Subtract two instances of the same :cpp:class:`new_type` by overwriting the first one. - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param lhs: The left-hand side of the subtraction - :param rhs: The right-hand side of the subtraction - :returns: A reference to the first argument containing the value modified by applying :literal:`-=` to the objects contained by :literal:`lhs` and :literal:`rhs`. - :throws: Any exception thrown by the subtraction-assignment operator of the objects contained by :literal:`lhs` and :literal:`rhs`. - This operator shall be noexcept iff. - - a. :cpp:type:`new_type::base_type` is *nothrow subtract-assignable* and - b. :cpp:type:`new_type::base_type` is *nothrow copy-constructible* - - :enablement: This operator shall be available iff. - - a. :cpp:type:`new_type::base_type` supports subtraction using :literal:`-=` and - b. the :cpp:var:`derivation clause ` contains :cpp:var:`Arithmetic` - - .. versionadded:: 1.0.0 - -.. cpp:function:: template \ - constexpr new_type operator*(new_type const & lhs, new_type const & rhs) - - Multiply two instances of the same :cpp:class:`new_type`. - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param lhs: The left-hand side of the multiplication - :param rhs: The right-hand side of the multiplication - :returns: A new instance of :cpp:class:`new_type\` containing the result of applying :literal:`*` to the objects contained by :literal:`lhs` and :literal:`rhs`. - :throws: Any exception thrown by the multiplication operator of the objects contained by :literal:`lhs` and :literal:`rhs`. - This operator shall be noexcept iff. - - a. :cpp:type:`new_type::base_type` is *nothrow multipliable* and - b. :cpp:type:`new_type::base_type` is *nothrow copy-constructible* - - :enablement: This operator shall be available iff. - - a. :cpp:type:`new_type::base_type` supports multiplication using :literal:`*` and - b. the :cpp:var:`derivation clause ` contains :cpp:var:`Arithmetic` - - .. versionadded:: 1.0.0 - -.. cpp:function:: template \ - constexpr new_type & operator*=(new_type & lhs, new_type const & rhs) - - Multiply two instances of the same :cpp:class:`new_type` by overwriting the first one. - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param lhs: The left-hand side of the multiplication - :param rhs: The right-hand side of the multiplication - :returns: A reference to the first argument containing the value modified by applying :literal:`*=` to the objects contained by :literal:`lhs` and :literal:`rhs`. - :throws: Any exception thrown by the multiplication-assignment operator of the objects contained by :literal:`lhs` and :literal:`rhs`. - This operator shall be noexcept iff. - - a. :cpp:type:`new_type::base_type` is *nothrow multiply-assignable* and - b. :cpp:type:`new_type::base_type` is *nothrow copy-constructible* - - :enablement: This operator shall be available iff. - - a. :cpp:type:`new_type::base_type` supports multiplication using :literal:`*=` and - b. the :cpp:var:`derivation clause ` contains :cpp:var:`Arithmetic` - - .. versionadded:: 1.0.0 - -.. cpp:function:: template \ - constexpr new_type operator/(new_type const & lhs, new_type const & rhs) - - Divide two instances of the same :cpp:class:`new_type`. - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param lhs: The left-hand side of the division - :param rhs: The right-hand side of the division - :returns: A new instance of :cpp:class:`new_type\` containing the result of applying :literal:`/` to the objects contained by :literal:`lhs` and :literal:`rhs`. - :throws: Any exception thrown by the division operator of the objects contained by :literal:`lhs` and :literal:`rhs`. - This operator shall be noexcept iff. - - a. :cpp:type:`new_type::base_type` is *nothrow dividable* and - b. :cpp:type:`new_type::base_type` is *nothrow copy-constructible* - - :enablement: This operator shall be available iff. - - a. :cpp:type:`new_type::base_type` supports division using :literal:`/` and - b. the :cpp:var:`derivation clause ` contains :cpp:var:`Arithmetic` - - .. versionadded:: 1.0.0 -.. cpp:function:: template \ - constexpr new_type & operator/=(new_type & lhs, new_type const & rhs) - - Divide two instances of the same :cpp:class:`new_type` by overwriting the first one. - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param lhs: The left-hand side of the division - :param rhs: The right-hand side of the division - :returns: A reference to the first argument containing the value modified by applying :literal:`/=` to the objects contained by :literal:`lhs` and :literal:`rhs`. - :throws: Any exception thrown by the division-assignment operator of the objects contained by :literal:`lhs` and :literal:`rhs`. - This operator shall be noexcept iff. - - a. :cpp:type:`new_type::base_type` is *nothrow divide-assignable* and - b. :cpp:type:`new_type::base_type` is *nothrow copy-constructible* - - :enablement: This operator shall be available iff. - - a. :cpp:type:`new_type::base_type` supports division using :literal:`/=` and - b. the :cpp:var:`derivation clause ` contains :cpp:var:`Arithmetic` - - .. versionadded:: 1.0.0 - -Iterators -~~~~~~~~~ - -.. cpp:function:: template \ - constexpr new_type::iterator begin(new_type & obj) - - Get an iterator to the beginning of the object contained by an instance of :cpp:class:`new_type` - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param obj: The object to retrieve the iterator from - :returns: An iterator to the begining of the object of contained by :literal:`obj`. - :throws: Any exception - :enablement: This function shall be available iff. - - a) :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`begin(BaseType &)` that returns an instance of type :cpp:type:`new_type::iterator` - - .. versionadded:: 1.1.0 - -.. cpp:function:: template \ - constexpr new_type::const_iterator begin(new_type const & obj) - - Get a constant iterator to the beginning of the object contained by an instance of :cpp:class:`new_type` - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param obj: The object to retrieve the iterator from - :returns: An iterator to the begining of the object of contained by :cpp:var:`obj`. - :throws: Any exception - :enablement: This function shall be available iff. - - a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`begin(BaseType const &)` that returns an instance of type :cpp:type:`new_type::const_iterator` - - .. versionadded:: 1.1.0 - -.. cpp:function:: template \ - constexpr new_type::const_iterator cbegin(new_type const & obj) - - Get a constant iterator to the beginning of the object contained by an instance of :cpp:class:`new_type` - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param obj: The object to retrieve the iterator from - :returns: An iterator to the begining of the object of contained by :cpp:var:`obj`. - :throws: Any exception - :enablement: This function shall be available iff. - - a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`cbegin(BaseType const &)` that returns an instance of type :cpp:type:`new_type::const_iterator` - - .. versionadded:: 1.1.0 - -.. cpp:function:: template \ - constexpr new_type::reverse_iterator rbegin(new_type & obj) - - Get a reverse iterator to the beginning of the object contained by an instance of :cpp:class:`new_type` - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param obj: The object to retrieve the iterator from - :returns: An iterator to the begining of the object of contained by :literal:`obj`. - :throws: Any exception - :enablement: This function shall be available iff. - - a) :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`rbegin(BaseType &)` that returns an instance of type :cpp:type:`new_type::reverse_iterator` - - .. versionadded:: 1.1.0 - -.. cpp:function:: template \ - constexpr new_type::const_reverse_iterator rbegin(new_type const & obj) - - Get a constant reverse iterator to the beginning of the object contained by an instance of :cpp:class:`new_type` - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param obj: The object to retrieve the iterator from - :returns: An iterator to the begining of the object of contained by :cpp:var:`obj`. - :throws: Any exception - :enablement: This function shall be available iff. - - a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`rbegin(BaseType const &)` that returns an instance of type :cpp:type:`new_type::const_reverse_iterator` - - .. versionadded:: 1.1.0 - -.. cpp:function:: template \ - constexpr new_type::const_reverse_iterator crbegin(new_type const & obj) - - Get a constant reverse iterator to the beginning of the object contained by an instance of :cpp:class:`new_type` - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param obj: The object to retrieve the iterator from - :returns: An iterator to the begining of the object of contained by :cpp:var:`obj`. - :throws: Any exception - :enablement: This function shall be available iff. - - a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`crbegin(BaseType const &)` that returns an instance of type :cpp:type:`new_type::const_reverse_iterator` - - .. versionadded:: 1.1.0 - -.. cpp:function:: template \ - constexpr new_type::iterator end(new_type & obj) - - Get an iterator beyond the end of the object contained by an instance of :cpp:class:`new_type` - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param obj: The object to retrieve the iterator from - :returns: An iterator beyond the end of the object of contained by :literal:`obj`. - :throws: Any exception - :enablement: This function shall be available iff. - - a) :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`end(BaseType &)` that returns an instance of type :cpp:type:`new_type::iterator` - - .. versionadded:: 1.1.0 - -.. cpp:function:: template \ - constexpr new_type::const_iterator end(new_type const & obj) - - Get a constant iterator beyond the end of the object contained by an instance of :cpp:class:`new_type` - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param obj: The object to retrieve the iterator from - :returns: An iterator beyond the end of the object of contained by :cpp:var:`obj`. - :throws: Any exception - :enablement: This function shall be available iff. - - a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`end(BaseType const &)` that returns an instance of type :cpp:type:`new_type::const_iterator` - - .. versionadded:: 1.1.0 - -.. cpp:function:: template \ - constexpr new_type::const_iterator cend(new_type const & obj) - - Get a constant iterator beyond the end of the object contained by an instance of :cpp:class:`new_type` - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param obj: The object to retrieve the iterator from - :returns: An iterator beyond the end of the object of contained by :cpp:var:`obj`. - :throws: Any exception - :enablement: This function shall be available iff. - - a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`cend(BaseType const &)` that returns an instance of type :cpp:type:`new_type::const_iterator` - - .. versionadded:: 1.1.0 - -.. cpp:function:: template \ - constexpr new_type::reverse_iterator rend(new_type & obj) - - Get a reverse iterator beyond the end of the object contained by an instance of :cpp:class:`new_type` - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param obj: The object to retrieve the iterator from - :returns: An iterator beyond the end of the object of contained by :literal:`obj`. - :throws: Any exception - :enablement: This function shall be available iff. - - a) :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`rend(BaseType &)` that returns an instance of type :cpp:type:`new_type::reverse_iterator` - - .. versionadded:: 1.1.0 - -.. cpp:function:: template \ - constexpr new_type::const_reverse_iterator rend(new_type const & obj) - - Get a constant reverse iterator beyond the end of the object contained by an instance of :cpp:class:`new_type` - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param obj: The object to retrieve the iterator from - :returns: An iterator beyond the end of the object of contained by :cpp:var:`obj`. - :throws: Any exception - :enablement: This function shall be available iff. - - a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`rend(BaseType const &)` that returns an instance of type :cpp:type:`new_type::const_reverse_iterator` - - .. versionadded:: 1.1.0 - -.. cpp:function:: template \ - constexpr new_type::const_reverse_iterator crend(new_type const & obj) - - Get a constant reverse iterator beyond the end of the object contained by an instance of :cpp:class:`new_type` - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - :param obj: The object to retrieve the iterator from - :returns: An iterator beyond the end of the object of contained by :cpp:var:`obj`. - :throws: Any exception - :enablement: This function shall be available iff. - - a) this :cpp:class:`new_type`'s :cpp:var:`derivation clause ` contains :cpp:var:`Iterable` and - b) for the :cpp:class:`new_type`'s :cpp:type:`base type ` exists a namespace-level function :literal:`crend(BaseType const &)` that returns an instance of type :cpp:type:`new_type::const_reverse_iterator` - - .. versionadded:: 1.1.0 - -:cpp:class:`std::hash` Support -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. cpp:namespace-pop:: - -.. cpp:class:: template \ - std::hash> - - :tparam BaseType: |BaseTypeDoc| - :tparam TagType: |TagTypeDoc| - :tparam DerivationClause: |DerivationClauseDoc| - - Hash an instance of :cpp:class:`new_type` using the hash implementation of the :cpp:type:`base type `. - - .. cpp:function:: constexpr std::size operator()(nt::new_type const & value) const - - :param value: A :cpp:class:`nt::new_type` value to be hashed - :returns: The result of applying :cpp:class:`std::hash` to the object contained by :literal:`value` - :throws: Any exception thrown by the call operator of the specialization of :cpp:class`std::hash` for the type of the object contained by :literal:`value`. - :enablement: This operator shall be available iff. - - a. :cpp:type:`nt::new_type::base_type` is hashable and - b. the :cpp:var:`derivation clause ` contains :cpp:var:`Hash `. - - .. versionadded:: 1.0.0 - -.. cpp:namespace-push:: nt - -Header :literal:`` -========================================= - -This header defines the alias template :cpp:type:`derivable` as well as the set of standard derivation tags. - -Class template :cpp:type:`derivable` ------------------------------------- - -.. cpp:class:: template \ - derivable - - :tparam NameTag: A tag uniquely identifing a specific derivation tag - - .. versionadded:: 1.0.0 - -.. _sec-standard-derivation-tags: - -Standard derivation tags ------------------------- - -.. cpp:var:: auto constexpr Arithmetic = derivable{} - - This tag enables the derivation of the following arithmetic operators: - - * :cpp:func:`operator+(new_type const &, new_type const &) constexpr new_type operator+(new_type const & lhs, new_type const & rhs)>` - * :cpp:func:`operator-(new_type const &, new_type const &) constexpr new_type operator-(new_type const & lhs, new_type const & rhs)>` - * :cpp:func:`operator*(new_type const &, new_type const &) constexpr new_type operator*(new_type const & lhs, new_type const & rhs)>` - * :cpp:func:`operator/(new_type const &, new_type const &) constexpr new_type operator/(new_type const & lhs, new_type const & rhs)>` - * :cpp:func:`operator+=(new_type &, new_type const &) constexpr new_type & operator+=(new_type & lhs, new_type const & rhs)>` - * :cpp:func:`operator-=(new_type &, new_type const &) constexpr new_type & operator-=(new_type & lhs, new_type const & rhs)>` - * :cpp:func:`operator*=(new_type &, new_type const &) constexpr new_type & operator*=(new_type & lhs, new_type const & rhs)>` - * :cpp:func:`operator/=(new_type &, new_type const &) constexpr new_type & operator/=(new_type & lhs, new_type const & rhs)>` - - .. versionadded:: 1.0.0 - -.. cpp:var:: auto constexpr EqBase = derivable{} - - This tag enables the derivation of following "equality comparison with base type" operators: - - * :cpp:func:`operator==(BaseType const &, new_type const &) constexpr bool nt::operator==(BaseType const &, new_type const &)>` - * :cpp:func:`operator==(new_type const &, BaseType const &) constexpr bool nt::operator==(new_type const &, BaseType const &)>` - * :cpp:func:`operator!=(BaseType const &, new_type const &) constexpr bool nt::operator!=(BaseType const &, new_type const &)>` - * :cpp:func:`operator!=(new_type const &, BaseType const &) constexpr bool nt::operator!=(new_type const &, BaseType const &)>` - - By virtue of its nature, deriving this feature compromises the strength of the given :cpp:class:`new_type`. - - .. versionadded:: 1.0.0 - -.. cpp:var:: auto constexpr ImplicitConversion = derivable{} - - This tag enables the derivation of the implicit "conversion to base type" operator. - By virtue of its nature, deriving this feature compromises the strength of the given :cpp:class:`new_type`. - - .. versionadded:: 1.0.0 - -.. cpp:var:: auto constexpr Hash = derivable{} - - This tag enables the derivation of a specialization of :cpp:class:`std::hash` - - .. versionadded:: 1.0.0 - -.. cpp:var:: auto constexpr Indirection = derivable{} - - This tag enables the derivation of the "member access through pointer" operator :cpp:func:`operator->() ()()>` (both in :literal:`const` and non-:literal:`const` variants). - - .. versionadded:: 1.0.0 - -.. cpp:var:: auto constexpr Iterable = derivable{} - - This tag enables the derivation of the following "standard iterator functions": - - * :cpp:func:`begin() ` - * :cpp:func:`begin() const ` - - .. versionadded:: 1.1.0 - -.. cpp:var:: auto constexpr Read = derivable{} - - This tag enables the derivation of the "stream output" :cpp:func:`operator\<\<(std::basic_ostream &, new_type const &) ` - - .. versionadded:: 1.0.0 - -.. cpp:var:: auto constexpr Relational = derivable{} - - This tag enables the derivation of the following relational operators: - - * :cpp:func:`operator\<(new_type const &, new_type const &) constexpr bool operator<(new_type const &, new_type const &)>` - * :cpp:func:`operator>(new_type const &, new_type const &) constexpr bool operator>(new_type const &, new_type const &)>` - * :cpp:func:`operator\<=(new_type const &, new_type const &) constexpr bool operator<=(new_type const &, new_type const &)>` - * :cpp:func:`operator>=(new_type const &, new_type const &) constexpr bool operator>=(new_type const &, new_type const &)>` - - .. versionadded:: 1.0.0 - -.. cpp:var:: auto constexpr Show = derivable{} - - This tag enables the derivation of the "stream input" :cpp:func:`operator>>(std::basic_istream &, new_type &) >>` - - .. versionadded:: 1.0.0 - -Header :literal:`` -======================================== - -This header contains the definition of the function template :cpp:func:`deriving`. - -Function template :cpp:func:`deriving` --------------------------------------- - -.. cpp:function:: template \ - constexpr derivation_clause deriving(derivable... features) noexcept - - This function can be used to create a new :cpp:class:`derivation_clause` for use in the definitions of instances of :cpp:class:`new_type`. - - .. versionadded:: 1.0.0 - - .. seealso:: :ref:`sec-standard-derivation-tags` for a list of standard derivation tags - -Header :literal:`` -================================================= - -This header contains the definition of the class template :cpp:class:`derivation_clause` - -Class template :cpp:class:`derivation_clause` ---------------------------------------------- - -.. cpp:class:: template \ - derivation_clause - - Derivation clauses are used by :cpp:class:`new_type` to allow users to specify a set of automatically derived support functions. - - :tparam DerivableTags: A (potentially empty) list of tag types identifying the contained derivations - - .. versionadded:: 1.0.0 - - **Constructors** - - .. cpp:function:: constexpr derivation_clause(derivable...) noexcept - - Construct a new derivations clause containing the given derivations - - **Evaluation Functions** - - .. cpp:function:: template \ - constexpr bool operator()(derivable) const noexcept - - Check if this :cpp:class:`derivation clause ` contains the given derivation - - :tparam DerivableTag: A tag uniquely identifying a derivation - - .. cpp:function:: template \ - constexpr bool operator()(derivable, derivable...) const noexcept - - Check if this :cpp:class:`derivation clause ` contains **all** of the given derivations - - :tparam DerivableTag: A tag uniquely identifying a derivation - :tparam RemainingDerivableTags: A list of tags uniquely identifying a list of derivations - - **Equality Comparison Operators** - - .. cpp:function:: template \ - constexpr bool operator==(derivation_clause other) const noexcept - - Check if this :cpp:class:`derivation clause ` is identical to the one represented by :cpp:any:`other`. - Two derivation clauses are considered equal iff. both contain the same derivations irrespective of their order. - - :tparam OtherDerivableTags: A (potentialy empty) list of tags uniquely identifying a list of derivations - :param other: An existing :cpp:class:`derivation clause ` - - .. cpp:function:: template \ - constexpr bool operator!=(derivation_clause other) const noexcept - - Check if this :cpp:class:`derivation clause ` is different from the one represented by :cpp:any:`other`. - Two derivation clauses are considered different iff. one contains at least one derivation not contained by the other. - - :tparam OtherDerivableTags: A (potentialy empty) list of tags uniquely identifying a list of derivations - :param other: An existing :cpp:class:`derivation clause ` - - **Relational Comparison Operators** - - .. cpp:function:: template \ - constexpr bool operator<(derivation_clause other) const noexcept - - Check if this :cpp:class:`derivation clause ` is a subset of the one represented by :cpp:any:`other`. - One :cpp:class:`derivation clause ` is considered to be a subset of another iff. the list of derivations of this instance forms a proper subset of the list of derivations of the other. - - :tparam OtherDerivableTags: A (potentialy empty) list of tags uniquely identifying a list of derivations - :param other: An existing :cpp:class:`derivation clause ` - - .. cpp:function:: template \ - constexpr bool operator>(derivation_clause other) const noexcept - - Check if this :cpp:class:`derivation clause ` is a superset of the one represented by :cpp:any:`other`. - One :cpp:class:`derivation clause ` is considered to be a superset of another iff. the list of derivations of this instance forms a proper superset of the list of derivations of the other. - - :tparam OtherDerivableTags: A (potentialy empty) list of tags uniquely identifying a list of derivations - :param other: An existing :cpp:class:`derivation clause ` - - .. cpp:function:: template \ - constexpr bool operator<=(derivation_clause other) const noexcept - - Check if this :cpp:class:`derivation clause ` is either identical to or a subset of the one represented by :cpp:any:`other`. - One :cpp:class:`derivation clause ` is considered to be identical to another iff. the list of derivations of this instance is identical to the list of derivations of the other. - One :cpp:class:`derivation clause ` is considered to be a subset of another iff. the list of derivations of this instance forms a proper subset of the list of derivations of the other. - - :tparam OtherDerivableTags: A (potentialy empty) list of tags uniquely identifying a list of derivations - :param other: An existing :cpp:class:`derivation clause ` - - .. cpp:function:: template \ - constexpr bool operator>=(derivation_clause other) const noexcept - - Check if this :cpp:class:`derivation clause ` is either identical to or a superset of the one represented by :cpp:any:`other`. - One :cpp:class:`derivation clause ` is considered to be identical to another iff. the list of derivations of this instance is identical to the list of derivations of the other. - One :cpp:class:`derivation clause ` is considered to be a superset of another iff. the list of derivations of this instance forms a proper superset of the list of derivations of the other. - - :tparam OtherDerivableTags: A (potentialy empty) list of tags uniquely identifying a list of derivations - :param other: An existing :cpp:class:`derivation clause ` diff --git a/source/examples/CMakeLists.txt b/source/examples/CMakeLists.txt deleted file mode 100644 index 73d961e..0000000 --- a/source/examples/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -file(GLOB SOURCES - RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" - CONFIGURE_DEPENDS - "src/*.cpp" -) - -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/examples/src/basic_usage.cpp b/source/examples/src/basic_usage.cpp deleted file mode 100644 index 35d1d2c..0000000 --- a/source/examples/src/basic_usage.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include - -#include - -using Width = nt::new_type; -using Height = nt::new_type; -using Area = nt::new_type; - -struct Rectangle -{ - constexpr Rectangle(Width w, Height h) - : width{w} - , height{h} - { - } - - auto constexpr area() const noexcept -> Area - { - return {width.decay() * height.decay()}; - } - -private: - Width width; - Height height; -}; - -int main() -{ - auto w{0u}, h{0u}; - - std::cin >> w >> h; - - auto width = Width{w}; - auto height = Height{h}; - - auto rect = Rectangle{width, height}; - - std::cout << rect.area().decay() << '\n'; -} \ No newline at end of file diff --git a/source/examples/src/basic_usage_with_read.cpp b/source/examples/src/basic_usage_with_read.cpp deleted file mode 100644 index 531a8e3..0000000 --- a/source/examples/src/basic_usage_with_read.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include - -#include - -using Width = nt::new_type; -using Height = nt::new_type; -using Area = nt::new_type; - -struct Rectangle -{ - constexpr Rectangle(Width w, Height h) - : width{w} - , height{h} - { - } - - auto constexpr area() const noexcept -> Area - { - return {width.decay() * height.decay()}; - } - -private: - Width width; - Height height; -}; - -int main() -{ - auto width = Width{}; - auto height = Height{}; - - std::cin >> width >> height; - - auto rect = Rectangle{width, height}; - - std::cout << rect.area() << '\n'; -} \ No newline at end of file diff --git a/source/examples/src/basic_usage_with_show.cpp b/source/examples/src/basic_usage_with_show.cpp deleted file mode 100644 index 9e4d985..0000000 --- a/source/examples/src/basic_usage_with_show.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include - -#include - -using Width = nt::new_type; -using Height = nt::new_type; -using Area = nt::new_type; - -struct Rectangle -{ - constexpr Rectangle(Width w, Height h) - : width{w} - , height{h} - { - } - - auto constexpr area() const noexcept -> Area - { - return {width.decay() * height.decay()}; - } - -private: - Width width; - Height height; -}; - -int main() -{ - auto w{0u}, h{0u}; - - std::cin >> w >> h; - - auto width = Width{w}; - auto height = Height{h}; - - auto rect = Rectangle{width, height}; - - std::cout << rect.area() << '\n'; -} \ No newline at end of file diff --git a/source/lib/CMakeLists.txt b/source/lib/CMakeLists.txt deleted file mode 100644 index 0219803..0000000 --- a/source/lib/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -add_library("${PROJECT_NAME}" INTERFACE) - -target_include_directories("${PROJECT_NAME}" INTERFACE - "$" - "$" -) - -target_compile_features("${PROJECT_NAME}" INTERFACE - "cxx_std_20" -) - -install(DIRECTORY "include/" - TYPE INCLUDE -) - -add_library("${PROJECT_NAME}::${PROJECT_NAME}" ALIAS "${PROJECT_NAME}") diff --git a/source/lib/include/newtype/newtype.hpp b/source/lib/include/newtype/newtype.hpp deleted file mode 100644 index fecaf2c..0000000 --- a/source/lib/include/newtype/newtype.hpp +++ /dev/null @@ -1,753 +0,0 @@ -#ifndef NEWTYPE_NEWTYPE_HPP -#define NEWTYPE_NEWTYPE_HPP - -#include -#include -#include -#include -#include -#include -#include - -namespace nt -{ - inline namespace lib - { - constexpr struct - { - int const major; - int const minor; - int const patch; - - char const * const name; - } version{ - .major = 2, - .minor = 0, - .patch = 0, - .name = "Brynn", - }; - - } // namespace lib - - namespace impl - { - inline namespace storage - { - template - struct new_type_storage - { - T m_value; - }; - } // namespace storage - - inline namespace member_types - { - - template> - struct new_type_iterator : new_type_storage - { - }; - - template - struct new_type_iterator> : new_type_storage - { - using iterator = typename T::iterator; - }; - -#define NEWTYPE_MAKE_ITERATOR_BASE(NAME, BASE) \ - template> \ - struct new_type_##NAME : new_type_##BASE \ - { \ - }; \ - template \ - struct new_type_##NAME> : new_type_##BASE \ - { \ - using NAME = typename T::NAME; \ - }; - - NEWTYPE_MAKE_ITERATOR_BASE(const_iterator, iterator) - NEWTYPE_MAKE_ITERATOR_BASE(reverse_iterator, const_iterator) - NEWTYPE_MAKE_ITERATOR_BASE(const_reverse_iterator, reverse_iterator) -#undef NEWTYPE_MAKE_ITERATOR_BASE - - template - struct new_type_iterator_types : new_type_const_reverse_iterator - { - }; - - } // namespace member_types - - } // namespace impl - - namespace concepts - { - - inline namespace arithmetic - { -#define NEWTYPE_MAKE_ARITHMETIC_CONCEPT(NAME, OPERATOR, REF) \ - template \ - concept NAME = requires(SubjectType lhs, SubjectType rhs) { \ - { \ - lhs OPERATOR rhs \ - } -> std::same_as; \ - }; \ - template \ - concept nothrow_##NAME = requires(SubjectType lhs, SubjectType rhs) { \ - requires NAME; \ - { \ - lhs OPERATOR rhs \ - } noexcept; \ - }; - - NEWTYPE_MAKE_ARITHMETIC_CONCEPT(addable, +, /*no-ref*/) - NEWTYPE_MAKE_ARITHMETIC_CONCEPT(divisible, /, /*no-ref*/) - NEWTYPE_MAKE_ARITHMETIC_CONCEPT(multipliable, *, /*no-ref*/) - NEWTYPE_MAKE_ARITHMETIC_CONCEPT(subtractable, -, /*no-ref*/) - NEWTYPE_MAKE_ARITHMETIC_CONCEPT(compound_addable, +=, &) - NEWTYPE_MAKE_ARITHMETIC_CONCEPT(compound_divisible, /=, &) - NEWTYPE_MAKE_ARITHMETIC_CONCEPT(compound_multipliable, *=, &) - NEWTYPE_MAKE_ARITHMETIC_CONCEPT(compound_subtractable, -=, &) -#undef NEWTYPE_MAKE_ARITHMETIC_CONCEPT - } // namespace arithmetic - - 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) { - { - lhs == rhs - } -> std::convertible_to; - }; - - template - concept nothrow_equality_comparable = requires(SubjectType lhs, SubjectType rhs) { - requires equality_comparable; - { - lhs == rhs - } noexcept; - }; - - template - concept inequality_comparable = requires(SubjectType lhs, SubjectType rhs) { - { - lhs != rhs - } -> std::convertible_to; - }; - - template - concept nothrow_inequality_comparable = requires(SubjectType lhs, SubjectType rhs) { - requires inequality_comparable; - { - lhs != rhs - } noexcept; - }; - - template - concept less_than_comparable = requires(SubjectType lhs, SubjectType rhs) { - { - lhs < rhs - } -> std::convertible_to; - }; - - template - concept nothrow_less_than_comparable = requires(SubjectType lhs, SubjectType rhs) { - requires less_than_comparable; - { - lhs < rhs - } noexcept; - }; - - template - concept less_than_equal_comparable = requires(SubjectType lhs, SubjectType rhs) { - { - lhs <= rhs - } -> std::convertible_to; - }; - - template - concept nothrow_less_than_equal_comparable = requires(SubjectType lhs, SubjectType rhs) { - requires less_than_equal_comparable; - { - lhs <= rhs - } noexcept; - }; - - template - concept greater_than_comparable = requires(SubjectType lhs, SubjectType rhs) { - { - lhs > rhs - } -> std::convertible_to; - }; - - template - concept nothrow_greater_than_comparable = requires(SubjectType lhs, SubjectType rhs) { - requires greater_than_comparable; - { - lhs > rhs - } noexcept; - }; - - template - concept greater_than_equal_comparable = requires(SubjectType lhs, SubjectType rhs) { - { - lhs >= rhs - } -> std::convertible_to; - }; - - template - concept nothrow_greater_than_equal_comparable = requires(SubjectType lhs, SubjectType rhs) { - requires greater_than_equal_comparable; - { - lhs >= rhs - } noexcept; - }; - - } // namespace comparability - - inline namespace iostreamable - { - - template - concept input_streamable = requires(SubjectType subject) { - { - std::declval &>() >> subject - } -> std::same_as &>; - }; - - template - concept nothrow_input_streamable = requires(SubjectType subject) { - requires input_streamable; - { - std::declval &>() >> subject - } noexcept; - }; - - template - concept output_streamable = requires(SubjectType subject) { - { - std::declval &>() << subject - } -> std::same_as &>; - }; - - template - concept nothrow_output_streamable = requires(SubjectType subject) { - requires output_streamable; - { - std::declval &>() << subject - } noexcept; - }; - - } // namespace iostreamable - - inline namespace iterable - { - - template - concept free_begin = requires(SubjectType & subject) { - typename SubjectType::iterator; - { - begin(subject) - } -> std::same_as; - }; - - template - concept const_free_begin = requires(SubjectType const & subject) { - typename SubjectType::const_iterator; - { - begin(subject) - } -> std::same_as; - }; - - template - concept member_begin = requires(SubjectType & subject) { - typename SubjectType::iterator; - { - subject.begin() - } -> std::same_as; - }; - - template - concept const_member_begin = requires(SubjectType const & subject) { - typename SubjectType::const_iterator; - { - subject.begin() - } -> std::same_as; - }; - - template - concept beginnable = free_begin || member_begin; - - template - concept const_beginnable = const_free_begin || const_member_begin; - - template - concept free_cbegin = requires(SubjectType const & subject) { - typename SubjectType::const_iterator; - { - cbegin(subject) - } -> std::same_as; - }; - - template - concept member_cbegin = requires(SubjectType const & subject) { - typename SubjectType::const_iterator; - { - subject.cbegin() - } -> std::same_as; - }; - - template - concept cbeginnable = free_cbegin || member_cbegin; - - template - concept free_rbegin = requires(SubjectType & subject) { - typename SubjectType::reverse_iterator; - { - rbegin(subject) - } -> std::same_as; - }; - - template - concept const_free_rbegin = requires(SubjectType const & subject) { - typename SubjectType::const_reverse_iterator; - { - rbegin(subject) - } -> std::same_as; - }; - - template - concept member_rbegin = requires(SubjectType & subject) { - typename SubjectType::reverse_iterator; - { - subject.rbegin() - } -> std::same_as; - }; - - template - concept const_member_rbegin = requires(SubjectType const & subject) { - typename SubjectType::const_reverse_iterator; - { - subject.rbegin() - } -> std::same_as; - }; - - template - concept rbeginnable = free_rbegin || member_rbegin; - - template - concept const_rbeginnable = const_free_rbegin || const_member_rbegin; - - template - concept free_crbegin = requires(SubjectType const & subject) { - typename SubjectType::const_reverse_iterator; - { - crbegin(subject) - } -> std::same_as; - }; - - template - concept member_crbegin = requires(SubjectType const & subject) { - typename SubjectType::const_reverse_iterator; - { - subject.crbegin() - } -> std::same_as; - }; - - template - concept crbeginnable = free_crbegin || member_crbegin; - - template - concept free_end = requires(SubjectType & subject) { - typename SubjectType::iterator; - { - end(subject) - } -> std::same_as; - }; - - template - concept const_free_end = requires(SubjectType const & subject) { - typename SubjectType::const_iterator; - { - end(subject) - } -> std::same_as; - }; - - template - concept member_end = requires(SubjectType & subject) { - typename SubjectType::iterator; - { - subject.end() - } -> std::same_as; - }; - - template - concept const_member_end = requires(SubjectType const & subject) { - typename SubjectType::const_iterator; - { - subject.end() - } -> std::same_as; - }; - - template - concept endable = free_end || member_end; - - template - concept const_endable = const_free_end || const_member_end; - - template - concept free_cend = requires(SubjectType const & subject) { - typename SubjectType::const_iterator; - { - cend(subject) - } -> std::same_as; - }; - - template - concept member_cend = requires(SubjectType const & subject) { - typename SubjectType::const_iterator; - { - subject.cend() - } -> std::same_as; - }; - - template - concept cendable = free_cend || member_cend; - - template - concept free_rend = requires(SubjectType & subject) { - typename SubjectType::reverse_iterator; - { - rend(subject) - } -> std::same_as; - }; - - template - concept const_free_rend = requires(SubjectType const & subject) { - typename SubjectType::const_reverse_iterator; - { - rend(subject) - } -> std::same_as; - }; - - template - concept member_rend = requires(SubjectType & subject) { - typename SubjectType::reverse_iterator; - { - subject.rend() - } -> std::same_as; - }; - - template - concept const_member_rend = requires(SubjectType const & subject) { - typename SubjectType::const_reverse_iterator; - { - subject.rend() - } -> std::same_as; - }; - - template - concept rendable = free_rend || member_rend; - - template - concept const_rendable = const_free_rend || const_member_rend; - - template - concept free_crend = requires(SubjectType const & subject) { - typename SubjectType::const_reverse_iterator; - { - crend(subject) - } -> std::same_as; - }; - - template - concept member_crend = requires(SubjectType const & subject) { - typename SubjectType::const_reverse_iterator; - { - subject.crend() - } -> std::same_as; - }; - - template - concept crendable = free_crend || member_crend; - } // namespace iterable - - inline namespace standard_extensions - { - - template - concept hashable = requires(SubjectType subject) { - { - std::hash{}(subject) - } -> std::convertible_to; - }; - - } - - } // namespace concepts - - template - struct derivable final - { - using tag_type = DerivableTag; - }; - - inline namespace derivables - { - - auto constexpr Arithmetic = derivable{}; - auto constexpr EqBase = derivable{}; - auto constexpr Hash = derivable{}; - auto constexpr ImplicitConversion = derivable{}; - auto constexpr Indirection = derivable{}; - auto constexpr Iterable = derivable{}; - auto constexpr Read = derivable{}; - auto constexpr Relational = derivable{}; - auto constexpr Show = derivable{}; - auto constexpr ThreewayCompare = derivable{}; - - } // namespace derivables - - template - struct derivation_clause - { - template - using derives_one = std::disjunction...>; - - template - using derives = std::conjunction...>; - - constexpr derivation_clause(derivable...) noexcept - { - } - }; - - template - concept derives = requires(DerivationClause clause) { requires DerivationClause::template derives::value; }; - - template - concept doesnt_derive = !derives; - - template - auto constexpr deriving(derivable... features) noexcept -> derivation_clause - { - return {features...}; - } - - template - 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."); - - using super = impl::new_type_iterator_types>; - - public: - using base_type = BaseType; - using tag_type = TagType; - using derivation_clause_type = decltype(DerivationClause); - - auto constexpr static derivation_clause = DerivationClause; - - 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(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; - - auto constexpr decay() const noexcept(std::is_nothrow_copy_constructible_v) -> BaseType - { - return this->m_value; - } - - template - constexpr operator base_type() const noexcept(std::is_nothrow_copy_constructible_v) - requires(nt::derives) - { - return decay(); - } - - template - explicit constexpr operator base_type() const noexcept(std::is_nothrow_copy_constructible_v) - requires(nt::doesnt_derive) - { - return decay(); - } - - 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 in >> obj.m_value; - } - - 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 out << obj.m_value; - } - - // 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 decay() <=> rhs.decay(); - } - - auto constexpr operator==(new_type const & rhs) const -> bool - requires(nt::concepts::equality_comparable && nt::derives) - { - return decay() == rhs.decay(); - } - - // NOT THREE-WAY EQUAL - - auto constexpr operator==(new_type const & rhs) const noexcept(nt::concepts::nothrow_equality_comparable) -> bool - requires(nt::concepts::equality_comparable && nt::doesnt_derive) - { - return decay() == rhs.decay(); - } - - // BASE EQUAL - - auto constexpr operator==(BaseType const & rhs) const noexcept(nt::concepts::nothrow_equality_comparable) -> bool - requires(nt::concepts::equality_comparable && nt::derives) - { - return decay() == rhs; - } - - // 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 decay() != rhs.decay(); - } - -#define NEWTYPE_MAKE_LOGICAL_OPERATOR(OPERATOR, CONCEPT) \ - auto constexpr operator OPERATOR(new_type const & rhs) const noexcept(nt::concepts::nothrow_##CONCEPT##_comparable) -> bool \ - requires(nt::concepts::CONCEPT##_comparable && nt::doesnt_derive && \ - nt::derives) \ - { \ - return decay() OPERATOR rhs.decay(); \ - } - - NEWTYPE_MAKE_LOGICAL_OPERATOR(<, less_than) - NEWTYPE_MAKE_LOGICAL_OPERATOR(<=, less_than_equal) - NEWTYPE_MAKE_LOGICAL_OPERATOR(>, greater_than) - NEWTYPE_MAKE_LOGICAL_OPERATOR(>=, greater_than_equal) -#undef NEWTYPE_MAKE_LOGICAL_OPERATOR - -#define NEWTYPE_MAKE_ARITHMETIC_OPERATOR(OPERATOR, CONCEPT) \ - auto constexpr operator OPERATOR(new_type const & rhs) const noexcept(nt::concepts::nothrow_##CONCEPT) -> new_type \ - requires(nt::concepts::CONCEPT && nt::derives) \ - { \ - return {decay() OPERATOR rhs.decay()}; \ - } \ - auto constexpr operator OPERATOR##=(new_type const & rhs) const noexcept(nt::concepts::nothrow_compound_##CONCEPT) -> new_type & \ - requires(nt::concepts::compound_##CONCEPT && nt::derives) { \ - return this->m_value OPERATOR## = rhs.decay(), *this; \ - }; - - NEWTYPE_MAKE_ARITHMETIC_OPERATOR(+, addable) - NEWTYPE_MAKE_ARITHMETIC_OPERATOR(-, subtractable) - NEWTYPE_MAKE_ARITHMETIC_OPERATOR(*, multipliable) - NEWTYPE_MAKE_ARITHMETIC_OPERATOR(/, divisible) -#undef NEWTYPE_MAKE_ARITHMETIC_OPERATOR - -#define NEWTYPE_MAKE_ITERATOR_FACTORY_HELPER(NAME, QUALIFICATION, ITERATOR) \ - template \ - auto constexpr friend NAME(new_type QUALIFICATION & obj) -> new_type::ITERATOR \ - requires nt::derives \ - { \ - using std::NAME; \ - return NAME(obj.m_value); \ - } \ - template \ - auto constexpr NAME() QUALIFICATION->new_type::ITERATOR \ - requires nt::derives \ - { \ - return this->m_value.NAME(); \ - } - -#define NEWTYPE_MAKE_ITERATOR_FACTORY(NAME, ITERATOR) \ - NEWTYPE_MAKE_ITERATOR_FACTORY_HELPER(NAME, /*non-const*/, ITERATOR) \ - NEWTYPE_MAKE_ITERATOR_FACTORY_HELPER(NAME, const, const_##ITERATOR) \ - NEWTYPE_MAKE_ITERATOR_FACTORY_HELPER(c##NAME, const, const_##ITERATOR) - - NEWTYPE_MAKE_ITERATOR_FACTORY(begin, iterator) - NEWTYPE_MAKE_ITERATOR_FACTORY(rbegin, reverse_iterator) - NEWTYPE_MAKE_ITERATOR_FACTORY(end, iterator) - NEWTYPE_MAKE_ITERATOR_FACTORY(rend, reverse_iterator) -#undef NEWTYPE_MAKE_ITERATOR_FACTORY -#undef NEWTYPE_MAKE_ITERATOR_FACTORY_HELPER - - // INDIRECTION - - // template - // requires(nt::derives) - // auto constexpr operator->() noexcept -> BaseType * - // { - // return std::addressof(this->m_value); - // } - - // template - // requires(nt::derives) - // auto constexpr operator->() const noexcept -> BaseType const * - // { - // return std::addressof(this->m_value); - // } - }; - -} // namespace nt - -namespace std -{ - template auto DerivationClause> - struct hash> - { - auto constexpr operator()(nt::new_type const & object) const - { - return std::hash{}(object.decay()); - } - }; -} // namespace std - -#endif diff --git a/source/tests/CMakeLists.txt b/source/tests/CMakeLists.txt deleted file mode 100644 index 47b8331..0000000 --- a/source/tests/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -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 - "$<$:-Wall>" - "$<$:-Wextra>" - "$<$:-Werror>" - "$<$:-pedantic-errors>" - "$<$:-fconcepts-diagnostics-depth=5>" -) - -catch_discover_tests("${PROJECT_NAME}_tests") diff --git a/source/tests/src/arithmetic.cpp b/source/tests/src/arithmetic.cpp deleted file mode 100644 index 30c243f..0000000 --- a/source/tests/src/arithmetic.cpp +++ /dev/null @@ -1,297 +0,0 @@ -#include "newtype/newtype.hpp" - -#include - -#include - -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; - - THEN("it is not addable") - { - STATIC_REQUIRE(!nt::concepts::addable); - } - } - - GIVEN("A new_type deriving nt::Arithmetic") - { - using type_alias = nt::new_type; - - THEN("it is addable") - { - STATIC_REQUIRE(nt::concepts::addable); - } - } - - GIVEN("A new_type over a non-addable class type deriving nt::Arithmetic") - { - using type_alias = nt::new_type; - - THEN("it is not addable") - { - STATIC_REQUIRE(!nt::concepts::addable == nt::concepts::addable); - } - } - - GIVEN("A new_type over an addable class type deriving nt::Arithmetic") - { - using type_alias = nt::new_type; - - THEN("it is addable") - { - STATIC_REQUIRE(nt::concepts::addable == nt::concepts::addable); - } - } - - GIVEN("A new_type deriving nt::Arithmetic") - { - using type_alias = nt::new_type; - - THEN("addition produces the same type") - { - STATIC_REQUIRE(std::is_same_v() + std::declval())>); - } - } - - GIVEN("Two objects of a new_type deriving nt::Arithmetic") - { - using type_alias = nt::new_type; - 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; - - THEN("it is not subtractable") - { - STATIC_REQUIRE(!nt::concepts::subtractable); - } - } - - GIVEN("A new_type deriving nt::Arithmetic") - { - using type_alias = nt::new_type; - - THEN("it is subtractable") - { - STATIC_REQUIRE(nt::concepts::subtractable); - } - } - - GIVEN("A new_type over a non-subtractable class type deriving nt::Arithmetic") - { - using type_alias = nt::new_type; - - THEN("it is not addable") - { - STATIC_REQUIRE(!nt::concepts::subtractable); - } - } - - GIVEN("A new_type over a subtractable class type deriving nt::Arithmetic") - { - using type_alias = nt::new_type; - - THEN("it is subtractable") - { - STATIC_REQUIRE(nt::concepts::subtractable); - } - } - - GIVEN("A new_type deriving nt::Arithmetic") - { - using type_alias = nt::new_type; - - THEN("subtraction produces the same type") - { - STATIC_REQUIRE(std::is_same_v() - std::declval())>); - } - } - - GIVEN("Two objects of a new_type deriving nt::Arithmetic") - { - using type_alias = nt::new_type; - 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; - - THEN("it is not multipliable") - { - STATIC_REQUIRE(!nt::concepts::multipliable); - } - } - - GIVEN("A new_type deriving nt::Arithmetic") - { - using type_alias = nt::new_type; - - THEN("it is multipliable") - { - STATIC_REQUIRE(nt::concepts::multipliable); - } - } - - GIVEN("A new_type over a non-multipliable class type deriving nt::Arithmetic") - { - using type_alias = nt::new_type; - - THEN("it is not multipliable") - { - STATIC_REQUIRE(!nt::concepts::multipliable); - } - } - - GIVEN("A new_type over a multipliable class type deriving nt::Arithmetic") - { - using type_alias = nt::new_type; - - THEN("it is multipliable") - { - STATIC_REQUIRE(nt::concepts::multipliable); - } - } - - GIVEN("A new_type deriving nt::Arithmetic") - { - using type_alias = nt::new_type; - - THEN("multiplication produces the same type") - { - STATIC_REQUIRE(std::is_same_v() * std::declval())>); - } - } - - GIVEN("Two objects of a new_type deriving nt::Arithmetic") - { - using type_alias = nt::new_type; - 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; - - THEN("it is not divisible") - { - STATIC_REQUIRE(!nt::concepts::divisible); - } - } - - GIVEN("A new_type deriving nt::Arithmetic") - { - using type_alias = nt::new_type; - - THEN("it is divisible") - { - STATIC_REQUIRE(nt::concepts::divisible); - } - } - - GIVEN("A new_type over a non-divisible class type deriving nt::Arithmetic") - { - using type_alias = nt::new_type; - - THEN("it is not divisible") - { - STATIC_REQUIRE(!nt::concepts::divisible); - } - } - - GIVEN("A new_type over a divisible class type deriving nt::Arithmetic") - { - using type_alias = nt::new_type; - - THEN("it is divisible") - { - STATIC_REQUIRE(nt::concepts::divisible); - } - } - - GIVEN("A new_type deriving nt::Arithmetic") - { - using type_alias = nt::new_type; - - THEN("division produces the same type") - { - STATIC_REQUIRE(std::is_same_v() / std::declval())>); - } - } - - GIVEN("Two objects of a new_type deriving nt::Arithmetic") - { - using type_alias = nt::new_type; - auto lhs = type_alias{30}; - auto rhs = type_alias{15}; - - THEN("division produces the correct result with respect to the base type") - { - REQUIRE((lhs / rhs).decay() == 30 / 15); - } - } -} diff --git a/source/tests/src/constructors.cpp b/source/tests/src/constructors.cpp deleted file mode 100644 index b866f2e..0000000 --- a/source/tests/src/constructors.cpp +++ /dev/null @@ -1,102 +0,0 @@ -#include "newtype/newtype.hpp" - -#include -#include - -#include - -using fundamental_types = std::tuple; - -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; - - THEN("objects of it can be constructed from the fundamental type") - { - STATIC_REQUIRE(std::is_constructible_v); - } - } -} - -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; - static_assert(std::is_default_constructible_v); - - THEN("it is default-constructible") - { - STATIC_REQUIRE(std::is_default_constructible_v); - } - } - - GIVEN("A new_type over a type that is not default-constructible") - { - using type_alias = nt::new_type; - static_assert(!std::is_default_constructible_v); - - THEN("it is not default-constructible") - { - STATIC_REQUIRE_FALSE(std::is_default_constructible_v); - } - } -} - -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; - static_assert(std::is_copy_constructible_v); - - THEN("it is copy-constructible") - { - STATIC_REQUIRE(std::is_copy_constructible_v); - } - } - - GIVEN("A new_type over a type that is not copy-constructible") - { - using type_alias = nt::new_type; - static_assert(!std::is_copy_constructible_v); - - THEN("it is not copy-constructible") - { - STATIC_REQUIRE_FALSE(std::is_copy_constructible_v); - } - } -} diff --git a/source/tests/src/conversion.cpp b/source/tests/src/conversion.cpp deleted file mode 100644 index e7ce51c..0000000 --- a/source/tests/src/conversion.cpp +++ /dev/null @@ -1,133 +0,0 @@ -#include "newtype/newtype.hpp" - -#include -#include -#include - -#include -#include -#include - -using test_types = std::tuple; - -TEMPLATE_LIST_TEST_CASE("Scenario: Implicit Conversions", "[conversion]", test_types) -{ - GIVEN("A new_type not deriving nt::ImplicitConversion") - { - using type_alias = nt::new_type; - - THEN("it is not implicitly convertible to the base type") - { - STATIC_REQUIRE(!std::is_convertible_v); - } - } - - GIVEN("A new_type deriving nt::ImplicitConversion") - { - using type_alias = nt::new_type; - - THEN("it is implicitly convertible to the base type") - { - STATIC_REQUIRE(std::is_convertible_v); - } - } -} - -TEMPLATE_LIST_TEST_CASE("Scenario: Decay", "[conversion]", test_types) -{ - GIVEN("Any new_type") - { - using type_alias = nt::new_type; - - THEN("it's decay() member function returns a value of the base type") - { - STATIC_REQUIRE(std::is_same_v().decay())>); - } - } - - GIVEN("Any new_type") - { - using type_alias = nt::new_type; - - 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) - { - return std::to_string(integral_value); - } - else - { - return static_cast(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; - - THEN("the decay member function is nothrow-invokable") - { - STATIC_REQUIRE(noexcept(std::declval().decay())); - } - } - - GIVEN("A new_type over a non-nothrow-copyable type") - { - using type_alias = nt::new_type; - - THEN("the decay member function is not nothrow-invokable") - { - STATIC_REQUIRE(!noexcept(std::declval().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; - - THEN("the decay member function is nothrow-invokable") - { - STATIC_REQUIRE(noexcept(std::declval().operator int())); - } - } - - GIVEN("A new_type over a non-nothrow-copy-constructible type") - { - using type_alias = nt::new_type; - - THEN("the decay member function is not nothrow-invokable") - { - STATIC_REQUIRE(!noexcept(std::declval().operator strange_type())); - } - } -} diff --git a/source/tests/src/derivation_clause.cpp b/source/tests/src/derivation_clause.cpp deleted file mode 100644 index 78bd3d4..0000000 --- a/source/tests/src/derivation_clause.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "newtype/newtype.hpp" - -#include - -#include - -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); - } - } - - 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); - } - - THEN("it contains nt::Show") - { - STATIC_REQUIRE(nt::derives); - } - } - - 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); - } - - THEN("it contains nt::Show") - { - STATIC_REQUIRE(nt::derives); - } - - THEN("it contains both nt::Show and nt::EqBase") - { - STATIC_REQUIRE(nt::derives); - } - - THEN("it does not contain nt::Arithmetic") - { - STATIC_REQUIRE_FALSE(nt::derives); - } - - THEN("it does not contain both nt::Arithmetic and nt::Show") - { - STATIC_REQUIRE_FALSE(nt::derives); - } - } -} diff --git a/source/tests/src/equality_comparison.cpp b/source/tests/src/equality_comparison.cpp deleted file mode 100644 index e0be7f9..0000000 --- a/source/tests/src/equality_comparison.cpp +++ /dev/null @@ -1,102 +0,0 @@ -#include "newtype/newtype.hpp" - -#include - -#include -#include -#include - -SCENARIO("Equality Comparison", "[compare]") -{ - GIVEN("A new_type over an equality comparable type") - { - using type_alias = nt::new_type; - - 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() == std::declval())>); - } - - THEN("inequality comparison returns bool") - { - STATIC_REQUIRE(std::is_same_v() != std::declval())>); - } - } - - GIVEN("A new_type deriving nt::EqBase") - { - using type_alias = nt::new_type; - - 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; - - static_assert(nt::concepts::nothrow_equality_comparable); - static_assert(nt::concepts::nothrow_inequality_comparable); - - THEN("it is nothrow-equality-comparable") - { - STATIC_REQUIRE(nt::concepts::nothrow_equality_comparable); - } - - THEN("it is nothrow-inequality-comparable") - { - STATIC_REQUIRE(nt::concepts::nothrow_inequality_comparable); - } - } - - 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; - - static_assert(!nt::concepts::nothrow_equality_comparable); - static_assert(!nt::concepts::nothrow_inequality_comparable); - - THEN("it is not nothrow-equality-comparable") - { - STATIC_REQUIRE_FALSE(nt::concepts::nothrow_equality_comparable); - } - - THEN("it is not nothrow-inequality-comparable") - { - STATIC_REQUIRE_FALSE(noexcept(std::declval() != std::declval())); - } - } -} diff --git a/source/tests/src/hash.cpp b/source/tests/src/hash.cpp deleted file mode 100644 index 94f252f..0000000 --- a/source/tests/src/hash.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "newtype/newtype.hpp" - -#include -#include - -#include -#include - -TEMPLATE_TEST_CASE("Hash", "[hash]", std::string, int) -{ - GIVEN("A new_type not deriving nt::Hash") - { - using type_alias = nt::new_type; - static_assert(nt::concepts::hashable); - - THEN("it is not hashable") - { - STATIC_REQUIRE_FALSE(nt::concepts::hashable); - } - } - - GIVEN("A new_type over a hashable type deriving nt::Hash") - { - using type_alias = nt::new_type; - static_assert(nt::concepts::hashable); - - THEN("it is hashable") - { - STATIC_REQUIRE(nt::concepts::hashable); - } - } - - GIVEN("A new_type over a non-hashable type deriving nt::Hash") - { - struct non_hashable - { - }; - using type_alias = nt::new_type; - static_assert(!nt::concepts::hashable); - - THEN("it is not hashable") - { - STATIC_REQUIRE_FALSE(nt::concepts::hashable); - } - } - - GIVEN("A hashable new_type") - { - using type_alias = nt::new_type; - static_assert(nt::concepts::hashable); - - THEN("it can be used a the key in an unordered_map") - { - auto map = std::unordered_map{}; - map[type_alias{42}] = 43; - REQUIRE(map[type_alias{42}] == 43); - } - } -} diff --git a/source/tests/src/io_operators.cpp b/source/tests/src/io_operators.cpp deleted file mode 100644 index f7f8f29..0000000 --- a/source/tests/src/io_operators.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include "newtype/newtype.hpp" - -#include - -#include -#include -#include -#include -#include - -inline namespace traits_extensions -{ - - template - struct has_stream_input : std::false_type - { - }; - - template - struct has_stream_input() >> std::declval())>> : std::true_type - { - }; - - template - auto constexpr has_stream_input_v = has_stream_input::value; - - template - struct has_stream_output : std::false_type - { - }; - - template - struct has_stream_output() << std::declval())>> : std::true_type - { - }; - - template - auto constexpr has_stream_output_v = has_stream_output::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; - static_assert(has_stream_input_v); - - THEN("it has the stream input operator") - { - STATIC_REQUIRE(has_stream_input_v); - } - - 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; - static_assert(!has_stream_input_v); - - THEN("it does not have the input operator") - { - STATIC_REQUIRE(!has_stream_input_v); - } - } -} - -SCENARIO("Stream Output") -{ - GIVEN("A new_type over a stream-outputtable type deriving nt::Show") - { - using type_alias = nt::new_type; - static_assert(has_stream_output_v); - - THEN("it has the stream output operator") - { - STATIC_REQUIRE(has_stream_output_v); - } - - 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; - static_assert(!has_stream_output_v); - - THEN("it does not have the output operator") - { - STATIC_REQUIRE(!has_stream_output_v); - } - } -} diff --git a/source/tests/src/iterable.cpp b/source/tests/src/iterable.cpp deleted file mode 100644 index 85b7edc..0000000 --- a/source/tests/src/iterable.cpp +++ /dev/null @@ -1,1060 +0,0 @@ -#include "newtype/newtype.hpp" - -#include - -#include -#include -#include -#include - -namespace iterable_types -{ - - struct with_member - { - using iterator = char *; - using const_iterator = char const *; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_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; - using const_reverse_iterator = std::reverse_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; - static_assert(!nt::concepts::beginnable); - static_assert(!nt::concepts::beginnable); - static_assert(!nt::concepts::cbeginnable); - static_assert(!nt::concepts::rbeginnable); - static_assert(!nt::concepts::rbeginnable); - static_assert(!nt::concepts::crbeginnable); - static_assert(!nt::concepts::endable); - static_assert(!nt::concepts::endable); - static_assert(!nt::concepts::cendable); - static_assert(!nt::concepts::rendable); - static_assert(!nt::concepts::rendable); - static_assert(!nt::concepts::crendable); - - THEN("it has no begin") - { - STATIC_REQUIRE_FALSE(nt::concepts::beginnable); - } - - THEN("it has no constant begin") - { - STATIC_REQUIRE_FALSE(nt::concepts::beginnable); - } - - THEN("it has no cbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::cbeginnable); - } - - THEN("it has no rbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::rbeginnable); - } - - THEN("it has no constant rbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::rbeginnable); - } - - THEN("it has no crbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::crbeginnable); - } - - THEN("it has no end") - { - STATIC_REQUIRE_FALSE(nt::concepts::endable); - } - - THEN("it has no constant end") - { - STATIC_REQUIRE_FALSE(nt::concepts::endable); - } - - THEN("it has no cend") - { - STATIC_REQUIRE_FALSE(nt::concepts::cendable); - } - - THEN("it has no rend") - { - STATIC_REQUIRE_FALSE(nt::concepts::rendable); - } - - THEN("it has no constant rend") - { - STATIC_REQUIRE_FALSE(nt::concepts::rendable); - } - - THEN("it has no crend") - { - STATIC_REQUIRE_FALSE(nt::concepts::crendable); - } - } - - GIVEN("A new_type over a non-iterable base type deriving nt::Iterable") - { - using type_alias = nt::new_type; - static_assert(!nt::concepts::beginnable); - static_assert(!nt::concepts::beginnable); - static_assert(!nt::concepts::cbeginnable); - static_assert(!nt::concepts::rbeginnable); - static_assert(!nt::concepts::rbeginnable); - static_assert(!nt::concepts::crbeginnable); - static_assert(!nt::concepts::endable); - static_assert(!nt::concepts::endable); - static_assert(!nt::concepts::cendable); - static_assert(!nt::concepts::rendable); - static_assert(!nt::concepts::rendable); - static_assert(!nt::concepts::crendable); - - THEN("it has no begin") - { - STATIC_REQUIRE_FALSE(nt::concepts::beginnable); - } - - THEN("it has no constant begin") - { - STATIC_REQUIRE_FALSE(nt::concepts::beginnable); - } - - THEN("it has no cbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::cbeginnable); - } - - THEN("it has no rbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::rbeginnable); - } - - THEN("it has no constant rbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::rbeginnable); - } - - THEN("it has no crbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::crbeginnable); - } - - THEN("it has no end") - { - STATIC_REQUIRE_FALSE(nt::concepts::endable); - } - - THEN("it has no constant end") - { - STATIC_REQUIRE_FALSE(nt::concepts::endable); - } - - THEN("it has no cend") - { - STATIC_REQUIRE_FALSE(nt::concepts::cendable); - } - - THEN("it has no rend") - { - STATIC_REQUIRE_FALSE(nt::concepts::rendable); - } - - THEN("it has no constant rend") - { - STATIC_REQUIRE_FALSE(nt::concepts::rendable); - } - - THEN("it has no crend") - { - STATIC_REQUIRE_FALSE(nt::concepts::crendable); - } - } - - GIVEN("A new_type over an iterable base type not deriving nt::Iterable") - { - using type_alias = nt::new_type, struct tag>; - static_assert(nt::concepts::beginnable); - static_assert(nt::concepts::const_beginnable); - static_assert(nt::concepts::cbeginnable); - static_assert(nt::concepts::rbeginnable); - static_assert(nt::concepts::const_rbeginnable); - static_assert(nt::concepts::crbeginnable); - static_assert(nt::concepts::endable); - static_assert(nt::concepts::const_endable); - static_assert(nt::concepts::cendable); - static_assert(nt::concepts::rendable); - static_assert(nt::concepts::const_rendable); - static_assert(nt::concepts::crendable); - - THEN("it has no begin") - { - STATIC_REQUIRE_FALSE(nt::concepts::beginnable); - } - - THEN("it has no constant begin") - { - STATIC_REQUIRE_FALSE(nt::concepts::beginnable); - } - - THEN("it has no cbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::cbeginnable); - } - - THEN("it has no rbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::rbeginnable); - } - - THEN("it has no constant rbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::rbeginnable); - } - - THEN("it has no crbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::crbeginnable); - } - - THEN("it has no end") - { - STATIC_REQUIRE_FALSE(nt::concepts::endable); - } - - THEN("it has no constant end") - { - STATIC_REQUIRE_FALSE(nt::concepts::endable); - } - - THEN("it has no cend") - { - STATIC_REQUIRE_FALSE(nt::concepts::cendable); - } - - THEN("it has no rend") - { - STATIC_REQUIRE_FALSE(nt::concepts::rendable); - } - - THEN("it has no constant rend") - { - STATIC_REQUIRE_FALSE(nt::concepts::rendable); - } - - THEN("it has no crend") - { - STATIC_REQUIRE_FALSE(nt::concepts::crendable); - } - } - - GIVEN("A new_type over an iterable base type deriving nt::Iterable") - { - using type_alias = nt::new_type, struct tag, deriving(nt::Iterable)>; - static_assert(nt::concepts::beginnable); - static_assert(nt::concepts::const_beginnable); - static_assert(nt::concepts::cbeginnable); - static_assert(nt::concepts::rbeginnable); - static_assert(nt::concepts::const_rbeginnable); - static_assert(nt::concepts::crbeginnable); - static_assert(nt::concepts::endable); - static_assert(nt::concepts::const_endable); - static_assert(nt::concepts::cendable); - static_assert(nt::concepts::rendable); - static_assert(nt::concepts::const_rendable); - static_assert(nt::concepts::crendable); - - THEN("it has begin") - { - STATIC_REQUIRE(nt::concepts::beginnable); - } - - THEN("it has constant begin") - { - STATIC_REQUIRE(nt::concepts::const_beginnable); - } - - THEN("it has cbegin") - { - STATIC_REQUIRE(nt::concepts::cbeginnable); - } - - THEN("it has rbegin") - { - STATIC_REQUIRE(nt::concepts::rbeginnable); - } - - THEN("it has constant rbegin") - { - STATIC_REQUIRE(nt::concepts::const_rbeginnable); - } - - THEN("it has crbegin") - { - STATIC_REQUIRE(nt::concepts::crbeginnable); - } - - THEN("it has end") - { - STATIC_REQUIRE(nt::concepts::endable); - } - - THEN("it has constant end") - { - STATIC_REQUIRE(nt::concepts::const_endable); - } - - THEN("it has cend") - { - STATIC_REQUIRE(nt::concepts::cendable); - } - - THEN("it has rend") - { - STATIC_REQUIRE(nt::concepts::rendable); - } - - THEN("it has constant rend") - { - STATIC_REQUIRE(nt::concepts::const_rendable); - } - - THEN("it has crend") - { - STATIC_REQUIRE(nt::concepts::crendable); - } - } -} - -SCENARIO("Iterators (member)", "[iterators]") -{ - GIVEN("A new_type over a non-iterable base type not deriving nt::Iterable") - { - using type_alias = nt::new_type; - static_assert(!nt::concepts::member_begin); - static_assert(!nt::concepts::const_member_begin); - static_assert(!nt::concepts::member_cbegin); - static_assert(!nt::concepts::member_rbegin); - static_assert(!nt::concepts::const_member_rbegin); - static_assert(!nt::concepts::member_crbegin); - static_assert(!nt::concepts::member_end); - static_assert(!nt::concepts::const_member_end); - static_assert(!nt::concepts::member_cend); - static_assert(!nt::concepts::member_rend); - static_assert(!nt::concepts::const_member_rend); - static_assert(!nt::concepts::member_crend); - - THEN("it has no member begin") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_begin); - } - - THEN("it has no member constant begin") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_begin); - } - - THEN("it has no member cbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_cbegin); - } - - THEN("it has no member rbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_rbegin); - } - - THEN("it has no member constant rbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_rbegin); - } - - THEN("it has no member crbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_crbegin); - } - - THEN("it has no member end") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_end); - } - - THEN("it has no member constant end") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_end); - } - - THEN("it has no member cend") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_cend); - } - - THEN("it has no member rend") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_rend); - } - - THEN("it has no member constant rend") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_rend); - } - - THEN("it has no member crend") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_crend); - } - } - - GIVEN("A new_type over a non-iterable base type deriving nt::Iterable") - { - using type_alias = nt::new_type; - static_assert(!nt::concepts::member_begin); - static_assert(!nt::concepts::const_member_begin); - static_assert(!nt::concepts::member_cbegin); - static_assert(!nt::concepts::member_rbegin); - static_assert(!nt::concepts::const_member_rbegin); - static_assert(!nt::concepts::member_crbegin); - static_assert(!nt::concepts::member_end); - static_assert(!nt::concepts::const_member_end); - static_assert(!nt::concepts::member_cend); - static_assert(!nt::concepts::member_rend); - static_assert(!nt::concepts::const_member_rend); - static_assert(!nt::concepts::member_crend); - - THEN("it has no member begin") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_begin); - } - - THEN("it has no member constant begin") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_begin); - } - - THEN("it has no member cbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_cbegin); - } - - THEN("it has no member rbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_rbegin); - } - - THEN("it has no member constant rbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_rbegin); - } - - THEN("it has no member crbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_crbegin); - } - - THEN("it has no member end") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_end); - } - - THEN("it has no member constant end") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_end); - } - - THEN("it has no member cend") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_cend); - } - - THEN("it has no member rend") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_rend); - } - - THEN("it has no member constant rend") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_rend); - } - - THEN("it has no member crend") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_crend); - } - } - - GIVEN("A new_type over an iterable base type not deriving nt::Iterable") - { - using type_alias = nt::new_type, struct tag>; - static_assert(nt::concepts::member_begin); - static_assert(nt::concepts::const_member_begin); - static_assert(nt::concepts::member_cbegin); - static_assert(nt::concepts::member_rbegin); - static_assert(nt::concepts::const_member_rbegin); - static_assert(nt::concepts::member_crbegin); - static_assert(nt::concepts::member_end); - static_assert(nt::concepts::const_member_end); - static_assert(nt::concepts::member_cend); - static_assert(nt::concepts::member_rend); - static_assert(nt::concepts::const_member_rend); - static_assert(nt::concepts::member_crend); - - THEN("it has no member begin") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_begin); - } - - THEN("it has no member constant begin") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_begin); - } - - THEN("it has no member cbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_cbegin); - } - - THEN("it has no member rbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_rbegin); - } - - THEN("it has no member constant rbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_rbegin); - } - - THEN("it has no member crbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_crbegin); - } - - THEN("it has no member end") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_end); - } - - THEN("it has no member constant end") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_end); - } - - THEN("it has no member cend") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_cend); - } - - THEN("it has no member rend") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_rend); - } - - THEN("it has no member constant rend") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_rend); - } - - THEN("it has no member crend") - { - STATIC_REQUIRE_FALSE(nt::concepts::member_crend); - } - } - - GIVEN("A new_type over an iterable base type deriving nt::Iterable") - { - using type_alias = nt::new_type, struct tag, deriving(nt::Iterable)>; - static_assert(nt::concepts::member_begin); - static_assert(nt::concepts::const_member_begin); - static_assert(nt::concepts::member_cbegin); - static_assert(nt::concepts::member_rbegin); - static_assert(nt::concepts::const_member_rbegin); - static_assert(nt::concepts::member_crbegin); - static_assert(nt::concepts::member_end); - static_assert(nt::concepts::const_member_end); - static_assert(nt::concepts::member_cend); - static_assert(nt::concepts::member_rend); - static_assert(nt::concepts::const_member_rend); - static_assert(nt::concepts::member_crend); - - THEN("it has member begin") - { - STATIC_REQUIRE(nt::concepts::member_begin); - } - - THEN("it has member constant begin") - { - STATIC_REQUIRE(nt::concepts::const_member_begin); - } - - THEN("it has member cbegin") - { - STATIC_REQUIRE(nt::concepts::member_cbegin); - } - - THEN("it has member rbegin") - { - STATIC_REQUIRE(nt::concepts::member_rbegin); - } - - THEN("it has member constant rbegin") - { - STATIC_REQUIRE(nt::concepts::const_member_rbegin); - } - - THEN("it has member crbegin") - { - STATIC_REQUIRE(nt::concepts::member_crbegin); - } - - THEN("it has member end") - { - STATIC_REQUIRE(nt::concepts::member_end); - } - - THEN("it has member constant end") - { - STATIC_REQUIRE(nt::concepts::const_member_end); - } - - THEN("it has member cend") - { - STATIC_REQUIRE(nt::concepts::member_cend); - } - - THEN("it has member rend") - { - STATIC_REQUIRE(nt::concepts::member_rend); - } - - THEN("it has member constant rend") - { - STATIC_REQUIRE(nt::concepts::const_member_rend); - } - - THEN("it has member crend") - { - STATIC_REQUIRE(nt::concepts::member_crend); - } - } -} - -SCENARIO("Iterators (free)", "[iterators]") -{ - GIVEN("A new_type over a non-iterable base type not deriving nt::Iterable") - { - using type_alias = nt::new_type; - static_assert(!nt::concepts::free_begin); - static_assert(!nt::concepts::const_free_begin); - static_assert(!nt::concepts::free_cbegin); - static_assert(!nt::concepts::free_rbegin); - static_assert(!nt::concepts::const_free_rbegin); - static_assert(!nt::concepts::free_crbegin); - static_assert(!nt::concepts::free_end); - static_assert(!nt::concepts::const_free_end); - static_assert(!nt::concepts::free_cend); - static_assert(!nt::concepts::free_rend); - static_assert(!nt::concepts::const_free_rend); - static_assert(!nt::concepts::free_crend); - - THEN("it has no free begin") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_begin); - } - - THEN("it has no free constant begin") - { - STATIC_REQUIRE_FALSE(nt::concepts::const_free_begin); - } - - THEN("it has no free cbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_cbegin); - } - - THEN("it has no free rbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_rbegin); - } - - THEN("it has no free constant rbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::const_free_rbegin); - } - - THEN("it has no free crbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_crbegin); - } - - THEN("it has no free end") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_end); - } - - THEN("it has no free constant end") - { - STATIC_REQUIRE_FALSE(nt::concepts::const_free_end); - } - - THEN("it has no free cend") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_cend); - } - - THEN("it has no free rend") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_rend); - } - - THEN("it has no free constant rend") - { - STATIC_REQUIRE_FALSE(nt::concepts::const_free_rend); - } - - THEN("it has no free crend") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_crend); - } - } - - GIVEN("A new_type over a non-iterable base type deriving nt::Iterable") - { - using type_alias = nt::new_type; - static_assert(!nt::concepts::free_begin); - static_assert(!nt::concepts::const_free_begin); - static_assert(!nt::concepts::free_cbegin); - static_assert(!nt::concepts::free_rbegin); - static_assert(!nt::concepts::const_free_rbegin); - static_assert(!nt::concepts::free_crbegin); - static_assert(!nt::concepts::free_end); - static_assert(!nt::concepts::const_free_end); - static_assert(!nt::concepts::free_cend); - static_assert(!nt::concepts::free_rend); - static_assert(!nt::concepts::const_free_rend); - static_assert(!nt::concepts::free_crend); - - THEN("it has no free begin") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_begin); - } - - THEN("it has no free constant begin") - { - STATIC_REQUIRE_FALSE(nt::concepts::const_free_begin); - } - - THEN("it has no free cbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_cbegin); - } - - THEN("it has no free rbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_rbegin); - } - - THEN("it has no free constant rbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::const_free_rbegin); - } - - THEN("it has no free crbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_crbegin); - } - - THEN("it has no free end") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_end); - } - - THEN("it has no free constant end") - { - STATIC_REQUIRE_FALSE(nt::concepts::const_free_end); - } - - THEN("it has no free cend") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_cend); - } - - THEN("it has no free rend") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_rend); - } - - THEN("it has no free constant rend") - { - STATIC_REQUIRE_FALSE(nt::concepts::const_free_rend); - } - - THEN("it has no free crend") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_crend); - } - } - - GIVEN("A new_type over an iterable base type not deriving nt::Iterable") - { - using type_alias = nt::new_type, struct tag>; - static_assert(nt::concepts::free_begin); - static_assert(nt::concepts::const_free_begin); - static_assert(nt::concepts::free_cbegin); - static_assert(nt::concepts::free_rbegin); - static_assert(nt::concepts::const_free_rbegin); - static_assert(nt::concepts::free_crbegin); - static_assert(nt::concepts::free_end); - static_assert(nt::concepts::const_free_end); - static_assert(nt::concepts::free_cend); - static_assert(nt::concepts::free_rend); - static_assert(nt::concepts::const_free_rend); - static_assert(nt::concepts::free_crend); - - THEN("it has no free begin") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_begin); - } - - THEN("it has no free constant begin") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_begin); - } - - THEN("it has no free cbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_cbegin); - } - - THEN("it has no free rbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_rbegin); - } - - THEN("it has no free constant rbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_rbegin); - } - - THEN("it has no free crbegin") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_crbegin); - } - - THEN("it has no free end") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_end); - } - - THEN("it has no free constant end") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_end); - } - - THEN("it has no free cend") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_cend); - } - - THEN("it has no free rend") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_rend); - } - - THEN("it has no free constant rend") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_rend); - } - - THEN("it has no free crend") - { - STATIC_REQUIRE_FALSE(nt::concepts::free_crend); - } - } - - GIVEN("A new_type over an iterable base type deriving nt::Iterable") - { - using type_alias = nt::new_type, struct tag, deriving(nt::Iterable)>; - static_assert(nt::concepts::free_begin); - static_assert(nt::concepts::const_free_begin); - static_assert(nt::concepts::free_cbegin); - static_assert(nt::concepts::free_rbegin); - static_assert(nt::concepts::const_free_rbegin); - static_assert(nt::concepts::free_crbegin); - static_assert(nt::concepts::free_end); - static_assert(nt::concepts::const_free_end); - static_assert(nt::concepts::free_cend); - static_assert(nt::concepts::free_rend); - static_assert(nt::concepts::const_free_rend); - static_assert(nt::concepts::free_crend); - - THEN("it has free begin") - { - STATIC_REQUIRE(nt::concepts::free_begin); - } - - THEN("it has free constant begin") - { - STATIC_REQUIRE(nt::concepts::const_free_begin); - } - - THEN("it has free cbegin") - { - STATIC_REQUIRE(nt::concepts::free_cbegin); - } - - THEN("it has free rbegin") - { - STATIC_REQUIRE(nt::concepts::free_rbegin); - } - - THEN("it has free constant rbegin") - { - STATIC_REQUIRE(nt::concepts::const_free_rbegin); - } - - THEN("it has free crbegin") - { - STATIC_REQUIRE(nt::concepts::free_crbegin); - } - - THEN("it has free end") - { - STATIC_REQUIRE(nt::concepts::free_end); - } - - THEN("it has free constant end") - { - STATIC_REQUIRE(nt::concepts::const_free_end); - } - - THEN("it has free cend") - { - STATIC_REQUIRE(nt::concepts::free_cend); - } - - THEN("it has free rend") - { - STATIC_REQUIRE(nt::concepts::free_rend); - } - - THEN("it has free constant rend") - { - STATIC_REQUIRE(nt::concepts::const_free_rend); - } - - THEN("it has free crend") - { - STATIC_REQUIRE(nt::concepts::free_crend); - } - } -} - -SCENARIO("Iterator Semantics", "[iterators]") -{ - GIVEN("An iterable new_type") - { - using type_alias = nt::new_type, 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/source/tests/src/relational_operators.cpp b/source/tests/src/relational_operators.cpp deleted file mode 100644 index e852957..0000000 --- a/source/tests/src/relational_operators.cpp +++ /dev/null @@ -1,249 +0,0 @@ -#include "newtype/newtype.hpp" - -#include - -#include -#include -#include -#include - -SCENARIO("Relational Operator Availability") -{ - GIVEN("A new_type over a relationally comparable type not deriving nt::Relational") - { - using type_alias = nt::new_type; - static_assert(nt::concepts::less_than_comparable); - static_assert(nt::concepts::less_than_equal_comparable); - static_assert(nt::concepts::greater_than_comparable); - static_assert(nt::concepts::greater_than_equal_comparable); - - THEN("it does not have <") - { - STATIC_REQUIRE_FALSE(nt::concepts::less_than_comparable); - } - - THEN("it does not have <=") - { - STATIC_REQUIRE_FALSE(nt::concepts::less_than_equal_comparable); - } - - THEN("it does not have >") - { - STATIC_REQUIRE_FALSE(nt::concepts::greater_than_comparable); - } - - THEN("it does not have >=") - { - STATIC_REQUIRE_FALSE(nt::concepts::greater_than_equal_comparable); - } - } - - GIVEN("A new_type over a relationally comparable type deriving nt::Relational") - { - using type_alias = nt::new_type; - static_assert(nt::concepts::less_than_comparable); - static_assert(nt::concepts::less_than_equal_comparable); - static_assert(nt::concepts::greater_than_comparable); - static_assert(nt::concepts::greater_than_equal_comparable); - - THEN("it does have <") - { - STATIC_REQUIRE(nt::concepts::less_than_comparable); - } - - THEN("it does have <=") - { - STATIC_REQUIRE(nt::concepts::less_than_equal_comparable); - } - - THEN("it does have >") - { - STATIC_REQUIRE(nt::concepts::greater_than_comparable); - } - - THEN("it does have >=") - { - STATIC_REQUIRE(nt::concepts::greater_than_equal_comparable); - } - } - - GIVEN("A new_type over a type that is not relationally comparable not deriving nt::Relational") - { - using type_alias = nt::new_type; - static_assert(!nt::concepts::less_than_comparable); - static_assert(!nt::concepts::less_than_equal_comparable); - static_assert(!nt::concepts::greater_than_comparable); - static_assert(!nt::concepts::greater_than_equal_comparable); - - THEN("it does not have <") - { - STATIC_REQUIRE_FALSE(nt::concepts::less_than_comparable); - } - - THEN("it does not have <=") - { - STATIC_REQUIRE_FALSE(nt::concepts::less_than_equal_comparable); - } - - THEN("it does not have >") - { - STATIC_REQUIRE_FALSE(nt::concepts::greater_than_comparable); - } - - THEN("it does not have >=") - { - STATIC_REQUIRE_FALSE(nt::concepts::greater_than_equal_comparable); - } - } - - GIVEN("A new_type over a type that is not relationally comparable deriving nt::Relational") - { - using type_alias = nt::new_type; - static_assert(!nt::concepts::less_than_comparable); - static_assert(!nt::concepts::less_than_equal_comparable); - static_assert(!nt::concepts::greater_than_comparable); - static_assert(!nt::concepts::greater_than_equal_comparable); - - THEN("it does not have <") - { - STATIC_REQUIRE_FALSE(nt::concepts::less_than_comparable); - } - - THEN("it does not have <=") - { - STATIC_REQUIRE_FALSE(nt::concepts::less_than_equal_comparable); - } - - THEN("it does not have >") - { - STATIC_REQUIRE_FALSE(nt::concepts::greater_than_comparable); - } - - THEN("it does not have >=") - { - STATIC_REQUIRE_FALSE(nt::concepts::greater_than_equal_comparable); - } - } -} - -SCENARIO("Relational Comparisons") -{ - GIVEN("A new_type over a relationally comparable type deriving nt::Relational") - { - using type_alias = nt::new_type; - static_assert(nt::concepts::less_than_comparable); - static_assert(nt::concepts::less_than_equal_comparable); - static_assert(nt::concepts::greater_than_comparable); - static_assert(nt::concepts::greater_than_equal_comparable); - - 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; - static_assert(nt::concepts::nothrow_less_than_comparable); - static_assert(nt::concepts::nothrow_less_than_equal_comparable); - static_assert(nt::concepts::nothrow_greater_than_comparable); - static_assert(nt::concepts::nothrow_greater_than_equal_comparable); - - THEN("it is nothrow-comparable using < ") - { - STATIC_REQUIRE(nt::concepts::nothrow_less_than_comparable); - } - - THEN("it is nothrow-comparable using <= ") - { - STATIC_REQUIRE(nt::concepts::nothrow_less_than_equal_comparable); - } - - THEN("it is nothrow-comparable using > ") - { - STATIC_REQUIRE(nt::concepts::nothrow_greater_than_comparable); - } - - THEN("it is nothrow-comparable using >= ") - { - STATIC_REQUIRE(nt::concepts::nothrow_greater_than_equal_comparable); - } - } - - 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; - static_assert(nt::concepts::less_than_comparable && - !nt::concepts::nothrow_less_than_comparable); - static_assert(nt::concepts::less_than_equal_comparable && - !nt::concepts::nothrow_less_than_equal_comparable); - static_assert(nt::concepts::greater_than_comparable && - !nt::concepts::nothrow_greater_than_comparable); - static_assert(nt::concepts::greater_than_equal_comparable && - !nt::concepts::nothrow_greater_than_equal_comparable); - - THEN("it is not nothrow-comparable using < ") - { - STATIC_REQUIRE(nt::concepts::less_than_comparable && !nt::concepts::nothrow_less_than_comparable); - } - - THEN("it is not nothrow-comparable using <= ") - { - STATIC_REQUIRE(nt::concepts::less_than_equal_comparable && !nt::concepts::nothrow_less_than_equal_comparable); - } - - THEN("it is not nothrow-comparable using > ") - { - STATIC_REQUIRE(nt::concepts::greater_than_comparable && !nt::concepts::nothrow_greater_than_comparable); - } - - THEN("it is not nothrow-comparable using >= ") - { - STATIC_REQUIRE(nt::concepts::greater_than_equal_comparable && - !nt::concepts::nothrow_greater_than_equal_comparable); - } - } -} diff --git a/source/tests/src/threeway_comparison.cpp b/source/tests/src/threeway_comparison.cpp deleted file mode 100644 index 45a36af..0000000 --- a/source/tests/src/threeway_comparison.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "newtype/newtype.hpp" - -#include -#include - -#include -#include -#include - -TEMPLATE_TEST_CASE("Scenario: Three-way Comparison", "[comparison]", int, std::string, std::vector) -{ - GIVEN("A new_type over a <=> comparable base type deriving nt::ThreewayCompare") - { - using type_alias = nt::new_type; - static_assert(std::three_way_comparable); - - THEN("it is equality-comparable") - { - STATIC_REQUIRE(nt::concepts::equality_comparable); - } - - THEN("it is inequality-comparable") - { - STATIC_REQUIRE(nt::concepts::inequality_comparable); - } - - THEN("it is three-way comparable") - { - STATIC_REQUIRE(std::three_way_comparable); - } - - THEN("it is less-than comparable") - { - STATIC_REQUIRE(nt::concepts::less_than_comparable); - } - - THEN("it is less-than-equal comparable") - { - STATIC_REQUIRE(nt::concepts::greater_than_equal_comparable); - } - - THEN("it is greater-than comparable") - { - STATIC_REQUIRE(nt::concepts::less_than_comparable); - } - - THEN("it is greater-than-equal comparable") - { - STATIC_REQUIRE(nt::concepts::greater_than_equal_comparable); - } - } -} \ No newline at end of file diff --git a/test_package/.gitignore b/test_package/.gitignore deleted file mode 100644 index 42afabf..0000000 --- a/test_package/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/test_package/CMakeLists.txt b/test_package/CMakeLists.txt deleted file mode 100644 index f511e49..0000000 --- a/test_package/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -cmake_minimum_required(VERSION "3.25") - -project("TestPackage" CXX) - -find_package("newtype") - -add_executable("${PROJECT_NAME}" - "main.cpp" -) - -target_link_libraries("${PROJECT_NAME}" PRIVATE - "newtype::newtype" -) diff --git a/test_package/conanfile.py b/test_package/conanfile.py deleted file mode 100644 index ae8c241..0000000 --- a/test_package/conanfile.py +++ /dev/null @@ -1,21 +0,0 @@ -import os -from conan import ConanFile -from conan.tools.cmake import CMake, cmake_layout - - -class NewtypeTestConan(ConanFile): - settings = ("os", "arch", "compiler", "build_type") - requires = "newtype/[~2.0]" - generators = "CMakeDeps", "CMakeToolchain" - - def build(self): - cmake = CMake(self) - cmake.configure() - cmake.build() - - def test(self): - os.chdir(self.build_folder) - self.run(".%sTestPackage" % os.sep) - - def layout(self): - cmake_layout(self, generator="Ninja Multi-Config") diff --git a/test_package/main.cpp b/test_package/main.cpp deleted file mode 100644 index f0c2bea..0000000 --- a/test_package/main.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include - -#include - -using Integer = nt::new_type; - -auto main() -> int -{ - auto n = Integer{42}; - std::cout << "n == " << n.decay() << '\n'; -} \ No newline at end of file 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 + "$<$:-Wall>" + "$<$:-Wextra>" + "$<$:-Werror>" + "$<$:-pedantic-errors>" + "$<$:-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 + +#include + +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; + + THEN("it is not addable") + { + STATIC_REQUIRE(!nt::concepts::addable); + } + } + + GIVEN("A new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type; + + THEN("it is addable") + { + STATIC_REQUIRE(nt::concepts::addable); + } + } + + GIVEN("A new_type over a non-addable class type deriving nt::Arithmetic") + { + using type_alias = nt::new_type; + + THEN("it is not addable") + { + STATIC_REQUIRE(!nt::concepts::addable == nt::concepts::addable); + } + } + + GIVEN("A new_type over an addable class type deriving nt::Arithmetic") + { + using type_alias = nt::new_type; + + THEN("it is addable") + { + STATIC_REQUIRE(nt::concepts::addable == nt::concepts::addable); + } + } + + GIVEN("A new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type; + + THEN("addition produces the same type") + { + STATIC_REQUIRE(std::is_same_v() + std::declval())>); + } + } + + GIVEN("Two objects of a new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type; + 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; + + THEN("it is not subtractable") + { + STATIC_REQUIRE(!nt::concepts::subtractable); + } + } + + GIVEN("A new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type; + + THEN("it is subtractable") + { + STATIC_REQUIRE(nt::concepts::subtractable); + } + } + + GIVEN("A new_type over a non-subtractable class type deriving nt::Arithmetic") + { + using type_alias = nt::new_type; + + THEN("it is not addable") + { + STATIC_REQUIRE(!nt::concepts::subtractable); + } + } + + GIVEN("A new_type over a subtractable class type deriving nt::Arithmetic") + { + using type_alias = nt::new_type; + + THEN("it is subtractable") + { + STATIC_REQUIRE(nt::concepts::subtractable); + } + } + + GIVEN("A new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type; + + THEN("subtraction produces the same type") + { + STATIC_REQUIRE(std::is_same_v() - std::declval())>); + } + } + + GIVEN("Two objects of a new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type; + 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; + + THEN("it is not multipliable") + { + STATIC_REQUIRE(!nt::concepts::multipliable); + } + } + + GIVEN("A new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type; + + THEN("it is multipliable") + { + STATIC_REQUIRE(nt::concepts::multipliable); + } + } + + GIVEN("A new_type over a non-multipliable class type deriving nt::Arithmetic") + { + using type_alias = nt::new_type; + + THEN("it is not multipliable") + { + STATIC_REQUIRE(!nt::concepts::multipliable); + } + } + + GIVEN("A new_type over a multipliable class type deriving nt::Arithmetic") + { + using type_alias = nt::new_type; + + THEN("it is multipliable") + { + STATIC_REQUIRE(nt::concepts::multipliable); + } + } + + GIVEN("A new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type; + + THEN("multiplication produces the same type") + { + STATIC_REQUIRE(std::is_same_v() * std::declval())>); + } + } + + GIVEN("Two objects of a new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type; + 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; + + THEN("it is not divisible") + { + STATIC_REQUIRE(!nt::concepts::divisible); + } + } + + GIVEN("A new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type; + + THEN("it is divisible") + { + STATIC_REQUIRE(nt::concepts::divisible); + } + } + + GIVEN("A new_type over a non-divisible class type deriving nt::Arithmetic") + { + using type_alias = nt::new_type; + + THEN("it is not divisible") + { + STATIC_REQUIRE(!nt::concepts::divisible); + } + } + + GIVEN("A new_type over a divisible class type deriving nt::Arithmetic") + { + using type_alias = nt::new_type; + + THEN("it is divisible") + { + STATIC_REQUIRE(nt::concepts::divisible); + } + } + + GIVEN("A new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type; + + THEN("division produces the same type") + { + STATIC_REQUIRE(std::is_same_v() / std::declval())>); + } + } + + GIVEN("Two objects of a new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type; + 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 +#include + +#include + +using fundamental_types = std::tuple; + +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; + + THEN("objects of it can be constructed from the fundamental type") + { + STATIC_REQUIRE(std::is_constructible_v); + } + } +} + +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; + static_assert(std::is_default_constructible_v); + + THEN("it is default-constructible") + { + STATIC_REQUIRE(std::is_default_constructible_v); + } + } + + GIVEN("A new_type over a type that is not default-constructible") + { + using type_alias = nt::new_type; + static_assert(!std::is_default_constructible_v); + + THEN("it is not default-constructible") + { + STATIC_REQUIRE_FALSE(std::is_default_constructible_v); + } + } +} + +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; + static_assert(std::is_copy_constructible_v); + + THEN("it is copy-constructible") + { + STATIC_REQUIRE(std::is_copy_constructible_v); + } + } + + GIVEN("A new_type over a type that is not copy-constructible") + { + using type_alias = nt::new_type; + static_assert(!std::is_copy_constructible_v); + + THEN("it is not copy-constructible") + { + STATIC_REQUIRE_FALSE(std::is_copy_constructible_v); + } + } +} 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 +#include +#include + +#include +#include +#include + +using test_types = std::tuple; + +TEMPLATE_LIST_TEST_CASE("Scenario: Implicit Conversions", "[conversion]", test_types) +{ + GIVEN("A new_type not deriving nt::ImplicitConversion") + { + using type_alias = nt::new_type; + + THEN("it is not implicitly convertible to the base type") + { + STATIC_REQUIRE(!std::is_convertible_v); + } + } + + GIVEN("A new_type deriving nt::ImplicitConversion") + { + using type_alias = nt::new_type; + + THEN("it is implicitly convertible to the base type") + { + STATIC_REQUIRE(std::is_convertible_v); + } + } +} + +TEMPLATE_LIST_TEST_CASE("Scenario: Decay", "[conversion]", test_types) +{ + GIVEN("Any new_type") + { + using type_alias = nt::new_type; + + THEN("it's decay() member function returns a value of the base type") + { + STATIC_REQUIRE(std::is_same_v().decay())>); + } + } + + GIVEN("Any new_type") + { + using type_alias = nt::new_type; + + 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) + { + return std::to_string(integral_value); + } + else + { + return static_cast(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; + + THEN("the decay member function is nothrow-invokable") + { + STATIC_REQUIRE(noexcept(std::declval().decay())); + } + } + + GIVEN("A new_type over a non-nothrow-copyable type") + { + using type_alias = nt::new_type; + + THEN("the decay member function is not nothrow-invokable") + { + STATIC_REQUIRE(!noexcept(std::declval().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; + + THEN("the decay member function is nothrow-invokable") + { + STATIC_REQUIRE(noexcept(std::declval().operator int())); + } + } + + GIVEN("A new_type over a non-nothrow-copy-constructible type") + { + using type_alias = nt::new_type; + + THEN("the decay member function is not nothrow-invokable") + { + STATIC_REQUIRE(!noexcept(std::declval().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 + +#include + +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); + } + } + + 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); + } + + THEN("it contains nt::Show") + { + STATIC_REQUIRE(nt::derives); + } + } + + 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); + } + + THEN("it contains nt::Show") + { + STATIC_REQUIRE(nt::derives); + } + + THEN("it contains both nt::Show and nt::EqBase") + { + STATIC_REQUIRE(nt::derives); + } + + THEN("it does not contain nt::Arithmetic") + { + STATIC_REQUIRE_FALSE(nt::derives); + } + + THEN("it does not contain both nt::Arithmetic and nt::Show") + { + STATIC_REQUIRE_FALSE(nt::derives); + } + } +} 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 + +#include +#include +#include + +SCENARIO("Equality Comparison", "[compare]") +{ + GIVEN("A new_type over an equality comparable type") + { + using type_alias = nt::new_type; + + 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() == std::declval())>); + } + + THEN("inequality comparison returns bool") + { + STATIC_REQUIRE(std::is_same_v() != std::declval())>); + } + } + + GIVEN("A new_type deriving nt::EqBase") + { + using type_alias = nt::new_type; + + 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; + + static_assert(nt::concepts::nothrow_equality_comparable); + static_assert(nt::concepts::nothrow_inequality_comparable); + + THEN("it is nothrow-equality-comparable") + { + STATIC_REQUIRE(nt::concepts::nothrow_equality_comparable); + } + + THEN("it is nothrow-inequality-comparable") + { + STATIC_REQUIRE(nt::concepts::nothrow_inequality_comparable); + } + } + + 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; + + static_assert(!nt::concepts::nothrow_equality_comparable); + static_assert(!nt::concepts::nothrow_inequality_comparable); + + THEN("it is not nothrow-equality-comparable") + { + STATIC_REQUIRE_FALSE(nt::concepts::nothrow_equality_comparable); + } + + THEN("it is not nothrow-inequality-comparable") + { + STATIC_REQUIRE_FALSE(noexcept(std::declval() != std::declval())); + } + } +} 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 +#include + +#include +#include + +TEMPLATE_TEST_CASE("Hash", "[hash]", std::string, int) +{ + GIVEN("A new_type not deriving nt::Hash") + { + using type_alias = nt::new_type; + static_assert(nt::concepts::hashable); + + THEN("it is not hashable") + { + STATIC_REQUIRE_FALSE(nt::concepts::hashable); + } + } + + GIVEN("A new_type over a hashable type deriving nt::Hash") + { + using type_alias = nt::new_type; + static_assert(nt::concepts::hashable); + + THEN("it is hashable") + { + STATIC_REQUIRE(nt::concepts::hashable); + } + } + + GIVEN("A new_type over a non-hashable type deriving nt::Hash") + { + struct non_hashable + { + }; + using type_alias = nt::new_type; + static_assert(!nt::concepts::hashable); + + THEN("it is not hashable") + { + STATIC_REQUIRE_FALSE(nt::concepts::hashable); + } + } + + GIVEN("A hashable new_type") + { + using type_alias = nt::new_type; + static_assert(nt::concepts::hashable); + + THEN("it can be used a the key in an unordered_map") + { + auto map = std::unordered_map{}; + 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 + +#include +#include +#include +#include +#include + +inline namespace traits_extensions +{ + + template + struct has_stream_input : std::false_type + { + }; + + template + struct has_stream_input() >> std::declval())>> : std::true_type + { + }; + + template + auto constexpr has_stream_input_v = has_stream_input::value; + + template + struct has_stream_output : std::false_type + { + }; + + template + struct has_stream_output() << std::declval())>> : std::true_type + { + }; + + template + auto constexpr has_stream_output_v = has_stream_output::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; + static_assert(has_stream_input_v); + + THEN("it has the stream input operator") + { + STATIC_REQUIRE(has_stream_input_v); + } + + 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; + static_assert(!has_stream_input_v); + + THEN("it does not have the input operator") + { + STATIC_REQUIRE(!has_stream_input_v); + } + } +} + +SCENARIO("Stream Output") +{ + GIVEN("A new_type over a stream-outputtable type deriving nt::Show") + { + using type_alias = nt::new_type; + static_assert(has_stream_output_v); + + THEN("it has the stream output operator") + { + STATIC_REQUIRE(has_stream_output_v); + } + + 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; + static_assert(!has_stream_output_v); + + THEN("it does not have the output operator") + { + STATIC_REQUIRE(!has_stream_output_v); + } + } +} 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 + +#include +#include +#include +#include + +namespace iterable_types +{ + + struct with_member + { + using iterator = char *; + using const_iterator = char const *; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_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; + using const_reverse_iterator = std::reverse_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; + static_assert(!nt::concepts::beginnable); + static_assert(!nt::concepts::beginnable); + static_assert(!nt::concepts::cbeginnable); + static_assert(!nt::concepts::rbeginnable); + static_assert(!nt::concepts::rbeginnable); + static_assert(!nt::concepts::crbeginnable); + static_assert(!nt::concepts::endable); + static_assert(!nt::concepts::endable); + static_assert(!nt::concepts::cendable); + static_assert(!nt::concepts::rendable); + static_assert(!nt::concepts::rendable); + static_assert(!nt::concepts::crendable); + + THEN("it has no begin") + { + STATIC_REQUIRE_FALSE(nt::concepts::beginnable); + } + + THEN("it has no constant begin") + { + STATIC_REQUIRE_FALSE(nt::concepts::beginnable); + } + + THEN("it has no cbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::cbeginnable); + } + + THEN("it has no rbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::rbeginnable); + } + + THEN("it has no constant rbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::rbeginnable); + } + + THEN("it has no crbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::crbeginnable); + } + + THEN("it has no end") + { + STATIC_REQUIRE_FALSE(nt::concepts::endable); + } + + THEN("it has no constant end") + { + STATIC_REQUIRE_FALSE(nt::concepts::endable); + } + + THEN("it has no cend") + { + STATIC_REQUIRE_FALSE(nt::concepts::cendable); + } + + THEN("it has no rend") + { + STATIC_REQUIRE_FALSE(nt::concepts::rendable); + } + + THEN("it has no constant rend") + { + STATIC_REQUIRE_FALSE(nt::concepts::rendable); + } + + THEN("it has no crend") + { + STATIC_REQUIRE_FALSE(nt::concepts::crendable); + } + } + + GIVEN("A new_type over a non-iterable base type deriving nt::Iterable") + { + using type_alias = nt::new_type; + static_assert(!nt::concepts::beginnable); + static_assert(!nt::concepts::beginnable); + static_assert(!nt::concepts::cbeginnable); + static_assert(!nt::concepts::rbeginnable); + static_assert(!nt::concepts::rbeginnable); + static_assert(!nt::concepts::crbeginnable); + static_assert(!nt::concepts::endable); + static_assert(!nt::concepts::endable); + static_assert(!nt::concepts::cendable); + static_assert(!nt::concepts::rendable); + static_assert(!nt::concepts::rendable); + static_assert(!nt::concepts::crendable); + + THEN("it has no begin") + { + STATIC_REQUIRE_FALSE(nt::concepts::beginnable); + } + + THEN("it has no constant begin") + { + STATIC_REQUIRE_FALSE(nt::concepts::beginnable); + } + + THEN("it has no cbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::cbeginnable); + } + + THEN("it has no rbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::rbeginnable); + } + + THEN("it has no constant rbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::rbeginnable); + } + + THEN("it has no crbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::crbeginnable); + } + + THEN("it has no end") + { + STATIC_REQUIRE_FALSE(nt::concepts::endable); + } + + THEN("it has no constant end") + { + STATIC_REQUIRE_FALSE(nt::concepts::endable); + } + + THEN("it has no cend") + { + STATIC_REQUIRE_FALSE(nt::concepts::cendable); + } + + THEN("it has no rend") + { + STATIC_REQUIRE_FALSE(nt::concepts::rendable); + } + + THEN("it has no constant rend") + { + STATIC_REQUIRE_FALSE(nt::concepts::rendable); + } + + THEN("it has no crend") + { + STATIC_REQUIRE_FALSE(nt::concepts::crendable); + } + } + + GIVEN("A new_type over an iterable base type not deriving nt::Iterable") + { + using type_alias = nt::new_type, struct tag>; + static_assert(nt::concepts::beginnable); + static_assert(nt::concepts::const_beginnable); + static_assert(nt::concepts::cbeginnable); + static_assert(nt::concepts::rbeginnable); + static_assert(nt::concepts::const_rbeginnable); + static_assert(nt::concepts::crbeginnable); + static_assert(nt::concepts::endable); + static_assert(nt::concepts::const_endable); + static_assert(nt::concepts::cendable); + static_assert(nt::concepts::rendable); + static_assert(nt::concepts::const_rendable); + static_assert(nt::concepts::crendable); + + THEN("it has no begin") + { + STATIC_REQUIRE_FALSE(nt::concepts::beginnable); + } + + THEN("it has no constant begin") + { + STATIC_REQUIRE_FALSE(nt::concepts::beginnable); + } + + THEN("it has no cbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::cbeginnable); + } + + THEN("it has no rbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::rbeginnable); + } + + THEN("it has no constant rbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::rbeginnable); + } + + THEN("it has no crbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::crbeginnable); + } + + THEN("it has no end") + { + STATIC_REQUIRE_FALSE(nt::concepts::endable); + } + + THEN("it has no constant end") + { + STATIC_REQUIRE_FALSE(nt::concepts::endable); + } + + THEN("it has no cend") + { + STATIC_REQUIRE_FALSE(nt::concepts::cendable); + } + + THEN("it has no rend") + { + STATIC_REQUIRE_FALSE(nt::concepts::rendable); + } + + THEN("it has no constant rend") + { + STATIC_REQUIRE_FALSE(nt::concepts::rendable); + } + + THEN("it has no crend") + { + STATIC_REQUIRE_FALSE(nt::concepts::crendable); + } + } + + GIVEN("A new_type over an iterable base type deriving nt::Iterable") + { + using type_alias = nt::new_type, struct tag, deriving(nt::Iterable)>; + static_assert(nt::concepts::beginnable); + static_assert(nt::concepts::const_beginnable); + static_assert(nt::concepts::cbeginnable); + static_assert(nt::concepts::rbeginnable); + static_assert(nt::concepts::const_rbeginnable); + static_assert(nt::concepts::crbeginnable); + static_assert(nt::concepts::endable); + static_assert(nt::concepts::const_endable); + static_assert(nt::concepts::cendable); + static_assert(nt::concepts::rendable); + static_assert(nt::concepts::const_rendable); + static_assert(nt::concepts::crendable); + + THEN("it has begin") + { + STATIC_REQUIRE(nt::concepts::beginnable); + } + + THEN("it has constant begin") + { + STATIC_REQUIRE(nt::concepts::const_beginnable); + } + + THEN("it has cbegin") + { + STATIC_REQUIRE(nt::concepts::cbeginnable); + } + + THEN("it has rbegin") + { + STATIC_REQUIRE(nt::concepts::rbeginnable); + } + + THEN("it has constant rbegin") + { + STATIC_REQUIRE(nt::concepts::const_rbeginnable); + } + + THEN("it has crbegin") + { + STATIC_REQUIRE(nt::concepts::crbeginnable); + } + + THEN("it has end") + { + STATIC_REQUIRE(nt::concepts::endable); + } + + THEN("it has constant end") + { + STATIC_REQUIRE(nt::concepts::const_endable); + } + + THEN("it has cend") + { + STATIC_REQUIRE(nt::concepts::cendable); + } + + THEN("it has rend") + { + STATIC_REQUIRE(nt::concepts::rendable); + } + + THEN("it has constant rend") + { + STATIC_REQUIRE(nt::concepts::const_rendable); + } + + THEN("it has crend") + { + STATIC_REQUIRE(nt::concepts::crendable); + } + } +} + +SCENARIO("Iterators (member)", "[iterators]") +{ + GIVEN("A new_type over a non-iterable base type not deriving nt::Iterable") + { + using type_alias = nt::new_type; + static_assert(!nt::concepts::member_begin); + static_assert(!nt::concepts::const_member_begin); + static_assert(!nt::concepts::member_cbegin); + static_assert(!nt::concepts::member_rbegin); + static_assert(!nt::concepts::const_member_rbegin); + static_assert(!nt::concepts::member_crbegin); + static_assert(!nt::concepts::member_end); + static_assert(!nt::concepts::const_member_end); + static_assert(!nt::concepts::member_cend); + static_assert(!nt::concepts::member_rend); + static_assert(!nt::concepts::const_member_rend); + static_assert(!nt::concepts::member_crend); + + THEN("it has no member begin") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_begin); + } + + THEN("it has no member constant begin") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_begin); + } + + THEN("it has no member cbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_cbegin); + } + + THEN("it has no member rbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_rbegin); + } + + THEN("it has no member constant rbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_rbegin); + } + + THEN("it has no member crbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_crbegin); + } + + THEN("it has no member end") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_end); + } + + THEN("it has no member constant end") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_end); + } + + THEN("it has no member cend") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_cend); + } + + THEN("it has no member rend") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_rend); + } + + THEN("it has no member constant rend") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_rend); + } + + THEN("it has no member crend") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_crend); + } + } + + GIVEN("A new_type over a non-iterable base type deriving nt::Iterable") + { + using type_alias = nt::new_type; + static_assert(!nt::concepts::member_begin); + static_assert(!nt::concepts::const_member_begin); + static_assert(!nt::concepts::member_cbegin); + static_assert(!nt::concepts::member_rbegin); + static_assert(!nt::concepts::const_member_rbegin); + static_assert(!nt::concepts::member_crbegin); + static_assert(!nt::concepts::member_end); + static_assert(!nt::concepts::const_member_end); + static_assert(!nt::concepts::member_cend); + static_assert(!nt::concepts::member_rend); + static_assert(!nt::concepts::const_member_rend); + static_assert(!nt::concepts::member_crend); + + THEN("it has no member begin") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_begin); + } + + THEN("it has no member constant begin") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_begin); + } + + THEN("it has no member cbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_cbegin); + } + + THEN("it has no member rbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_rbegin); + } + + THEN("it has no member constant rbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_rbegin); + } + + THEN("it has no member crbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_crbegin); + } + + THEN("it has no member end") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_end); + } + + THEN("it has no member constant end") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_end); + } + + THEN("it has no member cend") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_cend); + } + + THEN("it has no member rend") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_rend); + } + + THEN("it has no member constant rend") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_rend); + } + + THEN("it has no member crend") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_crend); + } + } + + GIVEN("A new_type over an iterable base type not deriving nt::Iterable") + { + using type_alias = nt::new_type, struct tag>; + static_assert(nt::concepts::member_begin); + static_assert(nt::concepts::const_member_begin); + static_assert(nt::concepts::member_cbegin); + static_assert(nt::concepts::member_rbegin); + static_assert(nt::concepts::const_member_rbegin); + static_assert(nt::concepts::member_crbegin); + static_assert(nt::concepts::member_end); + static_assert(nt::concepts::const_member_end); + static_assert(nt::concepts::member_cend); + static_assert(nt::concepts::member_rend); + static_assert(nt::concepts::const_member_rend); + static_assert(nt::concepts::member_crend); + + THEN("it has no member begin") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_begin); + } + + THEN("it has no member constant begin") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_begin); + } + + THEN("it has no member cbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_cbegin); + } + + THEN("it has no member rbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_rbegin); + } + + THEN("it has no member constant rbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_rbegin); + } + + THEN("it has no member crbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_crbegin); + } + + THEN("it has no member end") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_end); + } + + THEN("it has no member constant end") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_end); + } + + THEN("it has no member cend") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_cend); + } + + THEN("it has no member rend") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_rend); + } + + THEN("it has no member constant rend") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_rend); + } + + THEN("it has no member crend") + { + STATIC_REQUIRE_FALSE(nt::concepts::member_crend); + } + } + + GIVEN("A new_type over an iterable base type deriving nt::Iterable") + { + using type_alias = nt::new_type, struct tag, deriving(nt::Iterable)>; + static_assert(nt::concepts::member_begin); + static_assert(nt::concepts::const_member_begin); + static_assert(nt::concepts::member_cbegin); + static_assert(nt::concepts::member_rbegin); + static_assert(nt::concepts::const_member_rbegin); + static_assert(nt::concepts::member_crbegin); + static_assert(nt::concepts::member_end); + static_assert(nt::concepts::const_member_end); + static_assert(nt::concepts::member_cend); + static_assert(nt::concepts::member_rend); + static_assert(nt::concepts::const_member_rend); + static_assert(nt::concepts::member_crend); + + THEN("it has member begin") + { + STATIC_REQUIRE(nt::concepts::member_begin); + } + + THEN("it has member constant begin") + { + STATIC_REQUIRE(nt::concepts::const_member_begin); + } + + THEN("it has member cbegin") + { + STATIC_REQUIRE(nt::concepts::member_cbegin); + } + + THEN("it has member rbegin") + { + STATIC_REQUIRE(nt::concepts::member_rbegin); + } + + THEN("it has member constant rbegin") + { + STATIC_REQUIRE(nt::concepts::const_member_rbegin); + } + + THEN("it has member crbegin") + { + STATIC_REQUIRE(nt::concepts::member_crbegin); + } + + THEN("it has member end") + { + STATIC_REQUIRE(nt::concepts::member_end); + } + + THEN("it has member constant end") + { + STATIC_REQUIRE(nt::concepts::const_member_end); + } + + THEN("it has member cend") + { + STATIC_REQUIRE(nt::concepts::member_cend); + } + + THEN("it has member rend") + { + STATIC_REQUIRE(nt::concepts::member_rend); + } + + THEN("it has member constant rend") + { + STATIC_REQUIRE(nt::concepts::const_member_rend); + } + + THEN("it has member crend") + { + STATIC_REQUIRE(nt::concepts::member_crend); + } + } +} + +SCENARIO("Iterators (free)", "[iterators]") +{ + GIVEN("A new_type over a non-iterable base type not deriving nt::Iterable") + { + using type_alias = nt::new_type; + static_assert(!nt::concepts::free_begin); + static_assert(!nt::concepts::const_free_begin); + static_assert(!nt::concepts::free_cbegin); + static_assert(!nt::concepts::free_rbegin); + static_assert(!nt::concepts::const_free_rbegin); + static_assert(!nt::concepts::free_crbegin); + static_assert(!nt::concepts::free_end); + static_assert(!nt::concepts::const_free_end); + static_assert(!nt::concepts::free_cend); + static_assert(!nt::concepts::free_rend); + static_assert(!nt::concepts::const_free_rend); + static_assert(!nt::concepts::free_crend); + + THEN("it has no free begin") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_begin); + } + + THEN("it has no free constant begin") + { + STATIC_REQUIRE_FALSE(nt::concepts::const_free_begin); + } + + THEN("it has no free cbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_cbegin); + } + + THEN("it has no free rbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_rbegin); + } + + THEN("it has no free constant rbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::const_free_rbegin); + } + + THEN("it has no free crbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_crbegin); + } + + THEN("it has no free end") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_end); + } + + THEN("it has no free constant end") + { + STATIC_REQUIRE_FALSE(nt::concepts::const_free_end); + } + + THEN("it has no free cend") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_cend); + } + + THEN("it has no free rend") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_rend); + } + + THEN("it has no free constant rend") + { + STATIC_REQUIRE_FALSE(nt::concepts::const_free_rend); + } + + THEN("it has no free crend") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_crend); + } + } + + GIVEN("A new_type over a non-iterable base type deriving nt::Iterable") + { + using type_alias = nt::new_type; + static_assert(!nt::concepts::free_begin); + static_assert(!nt::concepts::const_free_begin); + static_assert(!nt::concepts::free_cbegin); + static_assert(!nt::concepts::free_rbegin); + static_assert(!nt::concepts::const_free_rbegin); + static_assert(!nt::concepts::free_crbegin); + static_assert(!nt::concepts::free_end); + static_assert(!nt::concepts::const_free_end); + static_assert(!nt::concepts::free_cend); + static_assert(!nt::concepts::free_rend); + static_assert(!nt::concepts::const_free_rend); + static_assert(!nt::concepts::free_crend); + + THEN("it has no free begin") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_begin); + } + + THEN("it has no free constant begin") + { + STATIC_REQUIRE_FALSE(nt::concepts::const_free_begin); + } + + THEN("it has no free cbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_cbegin); + } + + THEN("it has no free rbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_rbegin); + } + + THEN("it has no free constant rbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::const_free_rbegin); + } + + THEN("it has no free crbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_crbegin); + } + + THEN("it has no free end") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_end); + } + + THEN("it has no free constant end") + { + STATIC_REQUIRE_FALSE(nt::concepts::const_free_end); + } + + THEN("it has no free cend") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_cend); + } + + THEN("it has no free rend") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_rend); + } + + THEN("it has no free constant rend") + { + STATIC_REQUIRE_FALSE(nt::concepts::const_free_rend); + } + + THEN("it has no free crend") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_crend); + } + } + + GIVEN("A new_type over an iterable base type not deriving nt::Iterable") + { + using type_alias = nt::new_type, struct tag>; + static_assert(nt::concepts::free_begin); + static_assert(nt::concepts::const_free_begin); + static_assert(nt::concepts::free_cbegin); + static_assert(nt::concepts::free_rbegin); + static_assert(nt::concepts::const_free_rbegin); + static_assert(nt::concepts::free_crbegin); + static_assert(nt::concepts::free_end); + static_assert(nt::concepts::const_free_end); + static_assert(nt::concepts::free_cend); + static_assert(nt::concepts::free_rend); + static_assert(nt::concepts::const_free_rend); + static_assert(nt::concepts::free_crend); + + THEN("it has no free begin") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_begin); + } + + THEN("it has no free constant begin") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_begin); + } + + THEN("it has no free cbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_cbegin); + } + + THEN("it has no free rbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_rbegin); + } + + THEN("it has no free constant rbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_rbegin); + } + + THEN("it has no free crbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_crbegin); + } + + THEN("it has no free end") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_end); + } + + THEN("it has no free constant end") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_end); + } + + THEN("it has no free cend") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_cend); + } + + THEN("it has no free rend") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_rend); + } + + THEN("it has no free constant rend") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_rend); + } + + THEN("it has no free crend") + { + STATIC_REQUIRE_FALSE(nt::concepts::free_crend); + } + } + + GIVEN("A new_type over an iterable base type deriving nt::Iterable") + { + using type_alias = nt::new_type, struct tag, deriving(nt::Iterable)>; + static_assert(nt::concepts::free_begin); + static_assert(nt::concepts::const_free_begin); + static_assert(nt::concepts::free_cbegin); + static_assert(nt::concepts::free_rbegin); + static_assert(nt::concepts::const_free_rbegin); + static_assert(nt::concepts::free_crbegin); + static_assert(nt::concepts::free_end); + static_assert(nt::concepts::const_free_end); + static_assert(nt::concepts::free_cend); + static_assert(nt::concepts::free_rend); + static_assert(nt::concepts::const_free_rend); + static_assert(nt::concepts::free_crend); + + THEN("it has free begin") + { + STATIC_REQUIRE(nt::concepts::free_begin); + } + + THEN("it has free constant begin") + { + STATIC_REQUIRE(nt::concepts::const_free_begin); + } + + THEN("it has free cbegin") + { + STATIC_REQUIRE(nt::concepts::free_cbegin); + } + + THEN("it has free rbegin") + { + STATIC_REQUIRE(nt::concepts::free_rbegin); + } + + THEN("it has free constant rbegin") + { + STATIC_REQUIRE(nt::concepts::const_free_rbegin); + } + + THEN("it has free crbegin") + { + STATIC_REQUIRE(nt::concepts::free_crbegin); + } + + THEN("it has free end") + { + STATIC_REQUIRE(nt::concepts::free_end); + } + + THEN("it has free constant end") + { + STATIC_REQUIRE(nt::concepts::const_free_end); + } + + THEN("it has free cend") + { + STATIC_REQUIRE(nt::concepts::free_cend); + } + + THEN("it has free rend") + { + STATIC_REQUIRE(nt::concepts::free_rend); + } + + THEN("it has free constant rend") + { + STATIC_REQUIRE(nt::concepts::const_free_rend); + } + + THEN("it has free crend") + { + STATIC_REQUIRE(nt::concepts::free_crend); + } + } +} + +SCENARIO("Iterator Semantics", "[iterators]") +{ + GIVEN("An iterable new_type") + { + using type_alias = nt::new_type, 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 + +#include +#include +#include +#include + +SCENARIO("Relational Operator Availability") +{ + GIVEN("A new_type over a relationally comparable type not deriving nt::Relational") + { + using type_alias = nt::new_type; + static_assert(nt::concepts::less_than_comparable); + static_assert(nt::concepts::less_than_equal_comparable); + static_assert(nt::concepts::greater_than_comparable); + static_assert(nt::concepts::greater_than_equal_comparable); + + THEN("it does not have <") + { + STATIC_REQUIRE_FALSE(nt::concepts::less_than_comparable); + } + + THEN("it does not have <=") + { + STATIC_REQUIRE_FALSE(nt::concepts::less_than_equal_comparable); + } + + THEN("it does not have >") + { + STATIC_REQUIRE_FALSE(nt::concepts::greater_than_comparable); + } + + THEN("it does not have >=") + { + STATIC_REQUIRE_FALSE(nt::concepts::greater_than_equal_comparable); + } + } + + GIVEN("A new_type over a relationally comparable type deriving nt::Relational") + { + using type_alias = nt::new_type; + static_assert(nt::concepts::less_than_comparable); + static_assert(nt::concepts::less_than_equal_comparable); + static_assert(nt::concepts::greater_than_comparable); + static_assert(nt::concepts::greater_than_equal_comparable); + + THEN("it does have <") + { + STATIC_REQUIRE(nt::concepts::less_than_comparable); + } + + THEN("it does have <=") + { + STATIC_REQUIRE(nt::concepts::less_than_equal_comparable); + } + + THEN("it does have >") + { + STATIC_REQUIRE(nt::concepts::greater_than_comparable); + } + + THEN("it does have >=") + { + STATIC_REQUIRE(nt::concepts::greater_than_equal_comparable); + } + } + + GIVEN("A new_type over a type that is not relationally comparable not deriving nt::Relational") + { + using type_alias = nt::new_type; + static_assert(!nt::concepts::less_than_comparable); + static_assert(!nt::concepts::less_than_equal_comparable); + static_assert(!nt::concepts::greater_than_comparable); + static_assert(!nt::concepts::greater_than_equal_comparable); + + THEN("it does not have <") + { + STATIC_REQUIRE_FALSE(nt::concepts::less_than_comparable); + } + + THEN("it does not have <=") + { + STATIC_REQUIRE_FALSE(nt::concepts::less_than_equal_comparable); + } + + THEN("it does not have >") + { + STATIC_REQUIRE_FALSE(nt::concepts::greater_than_comparable); + } + + THEN("it does not have >=") + { + STATIC_REQUIRE_FALSE(nt::concepts::greater_than_equal_comparable); + } + } + + GIVEN("A new_type over a type that is not relationally comparable deriving nt::Relational") + { + using type_alias = nt::new_type; + static_assert(!nt::concepts::less_than_comparable); + static_assert(!nt::concepts::less_than_equal_comparable); + static_assert(!nt::concepts::greater_than_comparable); + static_assert(!nt::concepts::greater_than_equal_comparable); + + THEN("it does not have <") + { + STATIC_REQUIRE_FALSE(nt::concepts::less_than_comparable); + } + + THEN("it does not have <=") + { + STATIC_REQUIRE_FALSE(nt::concepts::less_than_equal_comparable); + } + + THEN("it does not have >") + { + STATIC_REQUIRE_FALSE(nt::concepts::greater_than_comparable); + } + + THEN("it does not have >=") + { + STATIC_REQUIRE_FALSE(nt::concepts::greater_than_equal_comparable); + } + } +} + +SCENARIO("Relational Comparisons") +{ + GIVEN("A new_type over a relationally comparable type deriving nt::Relational") + { + using type_alias = nt::new_type; + static_assert(nt::concepts::less_than_comparable); + static_assert(nt::concepts::less_than_equal_comparable); + static_assert(nt::concepts::greater_than_comparable); + static_assert(nt::concepts::greater_than_equal_comparable); + + 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; + static_assert(nt::concepts::nothrow_less_than_comparable); + static_assert(nt::concepts::nothrow_less_than_equal_comparable); + static_assert(nt::concepts::nothrow_greater_than_comparable); + static_assert(nt::concepts::nothrow_greater_than_equal_comparable); + + THEN("it is nothrow-comparable using < ") + { + STATIC_REQUIRE(nt::concepts::nothrow_less_than_comparable); + } + + THEN("it is nothrow-comparable using <= ") + { + STATIC_REQUIRE(nt::concepts::nothrow_less_than_equal_comparable); + } + + THEN("it is nothrow-comparable using > ") + { + STATIC_REQUIRE(nt::concepts::nothrow_greater_than_comparable); + } + + THEN("it is nothrow-comparable using >= ") + { + STATIC_REQUIRE(nt::concepts::nothrow_greater_than_equal_comparable); + } + } + + 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; + static_assert(nt::concepts::less_than_comparable && + !nt::concepts::nothrow_less_than_comparable); + static_assert(nt::concepts::less_than_equal_comparable && + !nt::concepts::nothrow_less_than_equal_comparable); + static_assert(nt::concepts::greater_than_comparable && + !nt::concepts::nothrow_greater_than_comparable); + static_assert(nt::concepts::greater_than_equal_comparable && + !nt::concepts::nothrow_greater_than_equal_comparable); + + THEN("it is not nothrow-comparable using < ") + { + STATIC_REQUIRE(nt::concepts::less_than_comparable && !nt::concepts::nothrow_less_than_comparable); + } + + THEN("it is not nothrow-comparable using <= ") + { + STATIC_REQUIRE(nt::concepts::less_than_equal_comparable && !nt::concepts::nothrow_less_than_equal_comparable); + } + + THEN("it is not nothrow-comparable using > ") + { + STATIC_REQUIRE(nt::concepts::greater_than_comparable && !nt::concepts::nothrow_greater_than_comparable); + } + + THEN("it is not nothrow-comparable using >= ") + { + STATIC_REQUIRE(nt::concepts::greater_than_equal_comparable && + !nt::concepts::nothrow_greater_than_equal_comparable); + } + } +} 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 +#include + +#include +#include +#include + +TEMPLATE_TEST_CASE("Scenario: Three-way Comparison", "[comparison]", int, std::string, std::vector) +{ + GIVEN("A new_type over a <=> comparable base type deriving nt::ThreewayCompare") + { + using type_alias = nt::new_type; + static_assert(std::three_way_comparable); + + THEN("it is equality-comparable") + { + STATIC_REQUIRE(nt::concepts::equality_comparable); + } + + THEN("it is inequality-comparable") + { + STATIC_REQUIRE(nt::concepts::inequality_comparable); + } + + THEN("it is three-way comparable") + { + STATIC_REQUIRE(std::three_way_comparable); + } + + THEN("it is less-than comparable") + { + STATIC_REQUIRE(nt::concepts::less_than_comparable); + } + + THEN("it is less-than-equal comparable") + { + STATIC_REQUIRE(nt::concepts::greater_than_equal_comparable); + } + + THEN("it is greater-than comparable") + { + STATIC_REQUIRE(nt::concepts::less_than_comparable); + } + + THEN("it is greater-than-equal comparable") + { + STATIC_REQUIRE(nt::concepts::greater_than_equal_comparable); + } + } +} \ No newline at end of file -- cgit v1.2.3