aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@gmail.com>2019-12-22 19:47:21 +0100
committerFelix Morgner <felix.morgner@gmail.com>2019-12-22 21:12:58 +0100
commit6370b3fc6ffb973cc272f18d18db521c02fea0f1 (patch)
treeda965d0a1bb37d523d5e840d74f9a07676a818a0
downloadnewtype-6370b3fc6ffb973cc272f18d18db521c02fea0f1.tar.xz
newtype-6370b3fc6ffb973cc272f18d18db521c02fea0f1.zip
newtype: initial commit
-rw-r--r--.clang-format111
-rw-r--r--.gitignore1
-rw-r--r--.vscode/c_cpp_properties.json16
-rw-r--r--.vscode/settings.json72
-rw-r--r--CMakeLists.txt145
-rw-r--r--LICENSE26
-rw-r--r--cmake/Modules/Conan.cmake554
-rw-r--r--cmake/Modules/DiscoverTests.cmake55
-rw-r--r--cmake/Modules/DiscoverTestsImpl.cmake39
-rw-r--r--cmake/config.cmake.in4
-rw-r--r--conanfile.py33
-rw-r--r--doc/Pipfile14
-rw-r--r--doc/Pipfile.lock232
-rw-r--r--doc/src/conf.py44
-rw-r--r--doc/src/index.rst154
-rw-r--r--include/newtype/derivable.hpp63
-rw-r--r--include/newtype/deriving.hpp123
-rw-r--r--include/newtype/new_type.hpp124
-rw-r--r--include/newtype/type.hpp29
-rw-r--r--test/include/derivation_clause_suite.hpp11
-rw-r--r--test/include/kawaii.hpp142
-rw-r--r--test/include/new_type_constructor_suite.hpp11
-rw-r--r--test/src/derivation_clause_suite.cpp312
-rw-r--r--test/src/driver.cpp89
-rw-r--r--test/src/new_type_constructor_suite.cpp68
25 files changed, 2472 insertions, 0 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..e834805
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,111 @@
+---
+Language: Cpp
+
+AccessModifierOffset: -2
+
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlines: Right
+AlignOperands: true
+AlignTrailingComments: true
+
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowAllArgumentsOnNextLine: false
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLambdasOnASingleLine: Inline
+AllowShortLoopsOnASingleLine: false
+
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakTemplateDeclarations: true
+
+BinPackArguments: false
+BinPackParameters: false
+
+BraceWrapping:
+ AfterClass: true
+ AfterControlStatement: true
+ AfterEnum: true
+ AfterFunction: true
+ AfterNamespace: true
+ AfterStruct: true
+ AfterUnion: true
+ AfterExternBlock: true
+ BeforeCatch: true
+ BeforeElse: true
+ IndentBraces: false
+
+BreakBeforeBraces: Custom
+BreakBeforeInheritanceComma: true
+BreakConstructorInitializers: BeforeComma
+BreakStringLiterals: true
+
+ColumnLimit: 144
+
+CompactNamespaces: false
+
+Cpp11BracedListStyle: true
+
+DerivePointerAlignment: false
+
+FixNamespaceComments: true
+
+IncludeBlocks: Regroup
+IncludeCategories:
+ # CUTE Headers
+ - Regex: '<cute/[[:alnum:]._]+\.(h|hpp)>'
+ Priority: 200
+ # STL Headers
+ - Regex: '<[[:alnum:]._]+(?!\.(h|hpp))>'
+ Priority: 400
+ # General System Headers
+ - Regex: '<([[:alnum:]._]/*)+\.(h|hpp)>'
+ Priority: 300
+ # newtype Headers
+ - Regex: '"newtype/.+\.hpp"'
+ Priority: 100
+ # Local Headers
+ - Regex: '".+\.hpp"'
+ Priority: 100
+
+
+IndentCaseLabels: false
+IndentPPDirectives: None
+IndentWidth: 2
+
+KeepEmptyLinesAtTheStartOfBlocks: true
+
+MaxEmptyLinesToKeep: 1
+
+NamespaceIndentation: All
+
+PointerAlignment: Middle
+
+ReflowComments: true
+
+SortIncludes: true
+SortUsingDeclarations: true
+
+SpaceAfterCStyleCast: false
+SpaceAfterTemplateKeyword: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 2
+SpacesInAngles: false
+SpacesInContainerLiterals: false
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+
+Standard: Cpp11
+
+TabWidth: 2
+
+UseTab: Never
+
+...
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/build \ No newline at end of file
diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json
new file mode 100644
index 0000000..179e475
--- /dev/null
+++ b/.vscode/c_cpp_properties.json
@@ -0,0 +1,16 @@
+{
+ "configurations": [
+ {
+ "name": "Linux",
+ "includePath": [
+ "${workspaceFolder}/**"
+ ],
+ "defines": [],
+ "compilerPath": "/usr/bin/clang",
+ "cppStandard": "c++20",
+ "intelliSenseMode": "clang-x64",
+ "configurationProvider": "go2sh.cmake-integration"
+ }
+ ],
+ "version": 4
+} \ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..fd771a0
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,72 @@
+{
+ "files.associations": {
+ ".clang-format": "yaml",
+ "array": "cpp",
+ "chrono": "cpp",
+ "functional": "cpp",
+ "istream": "cpp",
+ "ostream": "cpp",
+ "ratio": "cpp",
+ "tuple": "cpp",
+ "type_traits": "cpp",
+ "utility": "cpp",
+ "variant": "cpp",
+ "atomic": "cpp",
+ "hash_map": "cpp",
+ "bit": "cpp",
+ "*.tcc": "cpp",
+ "cctype": "cpp",
+ "clocale": "cpp",
+ "cmath": "cpp",
+ "condition_variable": "cpp",
+ "cstdarg": "cpp",
+ "cstddef": "cpp",
+ "cstdint": "cpp",
+ "cstdio": "cpp",
+ "cstdlib": "cpp",
+ "cstring": "cpp",
+ "ctime": "cpp",
+ "cwchar": "cpp",
+ "cwctype": "cpp",
+ "deque": "cpp",
+ "list": "cpp",
+ "map": "cpp",
+ "set": "cpp",
+ "unordered_map": "cpp",
+ "vector": "cpp",
+ "exception": "cpp",
+ "algorithm": "cpp",
+ "iterator": "cpp",
+ "memory": "cpp",
+ "memory_resource": "cpp",
+ "numeric": "cpp",
+ "optional": "cpp",
+ "random": "cpp",
+ "string": "cpp",
+ "string_view": "cpp",
+ "system_error": "cpp",
+ "fstream": "cpp",
+ "initializer_list": "cpp",
+ "iomanip": "cpp",
+ "iosfwd": "cpp",
+ "iostream": "cpp",
+ "limits": "cpp",
+ "mutex": "cpp",
+ "new": "cpp",
+ "sstream": "cpp",
+ "stdexcept": "cpp",
+ "streambuf": "cpp",
+ "thread": "cpp",
+ "cfenv": "cpp",
+ "cinttypes": "cpp",
+ "typeinfo": "cpp",
+ "valarray": "cpp"
+ },
+ "cmake.configureArguments": "-DCMAKE_EXPORT_COMPILE_COMMANDS=YES",
+ "cmake.cpptools.intelliSenseMode": "gcc-x64",
+ "cmake.cpptools.guessSourceFileConfigurations": true,
+ "[cpp]": {
+ "editor.formatOnSave": true
+ },
+ "restructuredtext.confPath": "${workspaceFolder}/doc/src"
+} \ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..845903b
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,145 @@
+cmake_minimum_required(VERSION "3.9.0")
+
+project("newtype"
+ VERSION "0.0.1"
+ LANGUAGES CXX
+ DESCRIPTION "A library to create new types based on existing ones."
+)
+
+set(CMAKE_CXX_STANDARD "20")
+set(CMAKE_CXX_STANDARD_REQUIRED YES)
+set(CMAKE_CXX_EXTENSIONS OFF)
+
+include("CTest")
+include("CMakePackageConfigHelpers")
+
+# 'newtype' library
+
+add_library("${PROJECT_NAME}" INTERFACE)
+
+target_include_directories("${PROJECT_NAME}" INTERFACE
+ $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
+ $<INSTALL_INTERFACE:include>
+)
+
+install(TARGETS "${PROJECT_NAME}"
+ EXPORT "${PROJECT_NAME}Targets"
+ PUBLIC_HEADER DESTINATION "include"
+)
+
+install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/"
+ DESTINATION "include"
+)
+
+# 'newtype' tests
+
+if(BUILD_TESTING)
+ include("${PROJECT_SOURCE_DIR}/cmake/Modules/DiscoverTests.cmake")
+ include("${PROJECT_SOURCE_DIR}/cmake/Modules/Conan.cmake")
+
+ conan_check(REQUIRED)
+ conan_add_remote(NAME "fmorgner-public" URL "https://api.bintray.com/conan/fmorgner/conan-public")
+ conan_cmake_run(CONANFILE "conanfile.py"
+ BASIC_SETUP
+ CMAKE_TARGETS
+ BUILD "missing"
+ NO_OUTPUT_DIRS
+ )
+
+ add_executable("${PROJECT_NAME}_tests"
+ "${PROJECT_SOURCE_DIR}/test/src/driver.cpp"
+
+ "${PROJECT_SOURCE_DIR}/test/src/derivation_clause_suite.cpp"
+ "${PROJECT_SOURCE_DIR}/test/src/new_type_constructor_suite.cpp"
+ )
+
+ target_include_directories("${PROJECT_NAME}_tests" PRIVATE
+ "${PROJECT_SOURCE_DIR}/test/include"
+ )
+
+ target_link_libraries("${PROJECT_NAME}_tests"
+ "newtype"
+ "CONAN_PKG::CUTE"
+ "CONAN_PKG::lyra"
+ )
+
+ target_compile_options("${PROJECT_NAME}_tests" PRIVATE
+ "-Wall"
+ "-Wextra"
+ "-Werror"
+ "-pedantic-errors"
+ )
+
+ discover_tests(TARGET "${PROJECT_NAME}_tests")
+
+ add_custom_target("run_all_tests"
+ COMMAND ${CMAKE_CTEST_COMMAND} "--output-on-failure"
+ WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
+ DEPENDS "newtype_tests"
+ COMMENT "Running unit tests"
+ )
+endif()
+
+# 'newtype' docs
+
+option(BUILD_DOCS "Build the library documentation" OFF)
+
+if(BUILD_DOCS)
+ find_program(PIPENV_EXE NAMES "pipenv3" "pipenv")
+ if(NOT PIPENV_EXE)
+ message(FATAL_ERROR "Could not find pipenv")
+ endif()
+
+ add_custom_target("docs" ALL
+ DEPENDS "${PROJECT_BINARY_DIR}/doc/html/index.html" "${PROJECT_BINARY_DIR}/doc/man/newtype.3"
+ )
+
+ add_custom_command(OUTPUT "${PROJECT_BINARY_DIR}/doc/html/index.html"
+ COMMAND "${PIPENV_EXE}" "run" "sphinx-build" "-b" "singlehtml" "${PROJECT_SOURCE_DIR}/doc/src" "${PROJECT_BINARY_DIR}/doc/html" ">/dev/null"
+ DEPENDS "${PROJECT_SOURCE_DIR}/doc/src/index.rst" "${PROJECT_SOURCE_DIR}/doc/src/conf.py"
+ WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/doc"
+ COMMENT "Compiling HTML documentation"
+ )
+
+ add_custom_command(OUTPUT "${PROJECT_BINARY_DIR}/doc/man/newtype.3"
+ COMMAND "${PIPENV_EXE}" "run" "sphinx-build" "-b" "man" "${PROJECT_SOURCE_DIR}/doc/src" "${PROJECT_BINARY_DIR}/doc/man" ">/dev/null"
+ DEPENDS "${PROJECT_SOURCE_DIR}/doc/src/index.rst" "${PROJECT_SOURCE_DIR}/doc/src/conf.py"
+ WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/doc"
+ COMMENT "Compiling man pages"
+ )
+
+ install(DIRECTORY "${PROJECT_BINARY_DIR}/doc/html"
+ DESTINATION "share/doc/${PROJECT_NAME}"
+ PATTERN ".nojekyll" EXCLUDE
+ PATTERN ".buildinfo" EXCLUDE
+ PATTERN ".doctrees" EXCLUDE
+ PATTERN "objects.inv" EXCLUDE
+ )
+
+ install(FILES "${PROJECT_BINARY_DIR}/doc/man/${PROJECT_NAME}.3"
+ DESTINATION "share/man/man3"
+ )
+ endif()
+
+# CMake support files
+
+write_basic_package_version_file(
+ "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
+ COMPATIBILITY "AnyNewerVersion"
+)
+
+configure_package_config_file(
+ "${PROJECT_SOURCE_DIR}/cmake/config.cmake.in"
+ "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
+ INSTALL_DESTINATION "lib/cmake/${PROJECT_NAME}"
+)
+
+install(FILES
+ "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
+ "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
+ DESTINATION "lib/cmake/${PROJECT_NAME}"
+)
+
+install(EXPORT "${PROJECT_NAME}Targets"
+ DESTINATION "lib/cmake/${PROJECT_NAME}"
+)
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..3959b78
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,26 @@
+Copyright (c) 2019, Felix Morgner <felix.morgner@gmail.com>, all rights reserved
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ - Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ - Neither the name of the copyright holders nor the names of its contributors may
+ be used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/cmake/Modules/Conan.cmake b/cmake/Modules/Conan.cmake
new file mode 100644
index 0000000..aac3b9c
--- /dev/null
+++ b/cmake/Modules/Conan.cmake
@@ -0,0 +1,554 @@
+# The MIT License (MIT)
+
+# Copyright (c) 2018 JFrog
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+
+
+# This file comes from: https://github.com/conan-io/cmake-conan. Please refer
+# to this repository for issues and documentation.
+
+# Its purpose is to wrap and launch Conan C/C++ Package Manager when cmake is called.
+# It will take CMake current settings (os, compiler, compiler version, architecture)
+# and translate them to conan settings for installing and retrieving dependencies.
+
+# It is intended to facilitate developers building projects that have conan dependencies,
+# but it is only necessary on the end-user side. It is not necessary to create conan
+# packages, in fact it shouldn't be use for that. Check the project documentation.
+
+
+include(CMakeParseArguments)
+
+function(_get_msvc_ide_version result)
+ set(${result} "" PARENT_SCOPE)
+ if(NOT MSVC_VERSION VERSION_LESS 1400 AND MSVC_VERSION VERSION_LESS 1500)
+ set(${result} 8 PARENT_SCOPE)
+ elseif(NOT MSVC_VERSION VERSION_LESS 1500 AND MSVC_VERSION VERSION_LESS 1600)
+ set(${result} 9 PARENT_SCOPE)
+ elseif(NOT MSVC_VERSION VERSION_LESS 1600 AND MSVC_VERSION VERSION_LESS 1700)
+ set(${result} 10 PARENT_SCOPE)
+ elseif(NOT MSVC_VERSION VERSION_LESS 1700 AND MSVC_VERSION VERSION_LESS 1800)
+ set(${result} 11 PARENT_SCOPE)
+ elseif(NOT MSVC_VERSION VERSION_LESS 1800 AND MSVC_VERSION VERSION_LESS 1900)
+ set(${result} 12 PARENT_SCOPE)
+ elseif(NOT MSVC_VERSION VERSION_LESS 1900 AND MSVC_VERSION VERSION_LESS 1910)
+ set(${result} 14 PARENT_SCOPE)
+ elseif(NOT MSVC_VERSION VERSION_LESS 1910 AND MSVC_VERSION VERSION_LESS 1920)
+ set(${result} 15 PARENT_SCOPE)
+ elseif(NOT MSVC_VERSION VERSION_LESS 1920 AND MSVC_VERSION VERSION_LESS 1930)
+ set(${result} 16 PARENT_SCOPE)
+ else()
+ message(FATAL_ERROR "Conan: Unknown MSVC compiler version [${MSVC_VERSION}]")
+ endif()
+endfunction()
+
+function(conan_cmake_settings result)
+ #message(STATUS "COMPILER " ${CMAKE_CXX_COMPILER})
+ #message(STATUS "COMPILER " ${CMAKE_CXX_COMPILER_ID})
+ #message(STATUS "VERSION " ${CMAKE_CXX_COMPILER_VERSION})
+ #message(STATUS "FLAGS " ${CMAKE_LANG_FLAGS})
+ #message(STATUS "LIB ARCH " ${CMAKE_CXX_LIBRARY_ARCHITECTURE})
+ #message(STATUS "BUILD TYPE " ${CMAKE_BUILD_TYPE})
+ #message(STATUS "GENERATOR " ${CMAKE_GENERATOR})
+ #message(STATUS "GENERATOR WIN64 " ${CMAKE_CL_64})
+
+ message(STATUS "Conan: Automatic detection of conan settings from cmake")
+
+ parse_arguments(${ARGV})
+
+ if(ARGUMENTS_BUILD_TYPE)
+ set(_CONAN_SETTING_BUILD_TYPE ${ARGUMENTS_BUILD_TYPE})
+ elseif(CMAKE_BUILD_TYPE)
+ set(_CONAN_SETTING_BUILD_TYPE ${CMAKE_BUILD_TYPE})
+ else()
+ message(FATAL_ERROR "Please specify in command line CMAKE_BUILD_TYPE (-DCMAKE_BUILD_TYPE=Release)")
+ endif()
+
+ string(TOUPPER ${_CONAN_SETTING_BUILD_TYPE} _CONAN_SETTING_BUILD_TYPE_UPPER)
+ if (_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "DEBUG")
+ set(_CONAN_SETTING_BUILD_TYPE "Debug")
+ elseif(_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "RELEASE")
+ set(_CONAN_SETTING_BUILD_TYPE "Release")
+ elseif(_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "RELWITHDEBINFO")
+ set(_CONAN_SETTING_BUILD_TYPE "RelWithDebInfo")
+ elseif(_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "MINSIZEREL")
+ set(_CONAN_SETTING_BUILD_TYPE "MinSizeRel")
+ endif()
+
+ if(ARGUMENTS_ARCH)
+ set(_CONAN_SETTING_ARCH ${ARGUMENTS_ARCH})
+ endif()
+ #handle -s os setting
+ if(CMAKE_SYSTEM_NAME)
+ #use default conan os setting if CMAKE_SYSTEM_NAME is not defined
+ set(CONAN_SYSTEM_NAME ${CMAKE_SYSTEM_NAME})
+ if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
+ set(CONAN_SYSTEM_NAME Macos)
+ endif()
+ set(CONAN_SUPPORTED_PLATFORMS Windows Linux Macos Android iOS FreeBSD WindowsStore)
+ list (FIND CONAN_SUPPORTED_PLATFORMS "${CONAN_SYSTEM_NAME}" _index)
+ if (${_index} GREATER -1)
+ #check if the cmake system is a conan supported one
+ set(_CONAN_SETTING_OS ${CONAN_SYSTEM_NAME})
+ else()
+ message(FATAL_ERROR "cmake system ${CONAN_SYSTEM_NAME} is not supported by conan. Use one of ${CONAN_SUPPORTED_PLATFORMS}")
+ endif()
+ endif()
+
+ get_property(_languages GLOBAL PROPERTY ENABLED_LANGUAGES)
+ if (";${_languages};" MATCHES ";CXX;")
+ set(LANGUAGE CXX)
+ set(USING_CXX 1)
+ elseif (";${_languages};" MATCHES ";C;")
+ set(LANGUAGE C)
+ set(USING_CXX 0)
+ else ()
+ message(FATAL_ERROR "Conan: Neither C or C++ was detected as a language for the project. Unabled to detect compiler version.")
+ endif()
+
+ if (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL GNU)
+ # using GCC
+ # TODO: Handle other params
+ string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION})
+ list(GET VERSION_LIST 0 MAJOR)
+ list(GET VERSION_LIST 1 MINOR)
+ set(COMPILER_VERSION ${MAJOR}.${MINOR})
+ if(${MAJOR} GREATER 4)
+ set(COMPILER_VERSION ${MAJOR})
+ endif()
+ set(_CONAN_SETTING_COMPILER gcc)
+ set(_CONAN_SETTING_COMPILER_VERSION ${COMPILER_VERSION})
+ if (USING_CXX)
+ conan_cmake_detect_unix_libcxx(_LIBCXX)
+ set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX})
+ endif ()
+ elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL AppleClang)
+ # using AppleClang
+ string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION})
+ list(GET VERSION_LIST 0 MAJOR)
+ list(GET VERSION_LIST 1 MINOR)
+ set(_CONAN_SETTING_COMPILER apple-clang)
+ set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR}.${MINOR})
+ if (USING_CXX)
+ conan_cmake_detect_unix_libcxx(_LIBCXX)
+ set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX})
+ endif ()
+ elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL Clang)
+ string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION})
+ list(GET VERSION_LIST 0 MAJOR)
+ list(GET VERSION_LIST 1 MINOR)
+ set(_CONAN_SETTING_COMPILER clang)
+ set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR}.${MINOR})
+ if(APPLE)
+ cmake_policy(GET CMP0025 APPLE_CLANG_POLICY_ENABLED)
+ if(NOT APPLE_CLANG_POLICY_ENABLED)
+ message(STATUS "Conan: APPLE and Clang detected. Assuming apple-clang compiler. Set CMP0025 to avoid it")
+ set(_CONAN_SETTING_COMPILER apple-clang)
+ endif()
+ endif()
+ if(${_CONAN_SETTING_COMPILER} STREQUAL clang AND ${MAJOR} GREATER 7)
+ set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR})
+ endif()
+ if (USING_CXX)
+ conan_cmake_detect_unix_libcxx(_LIBCXX)
+ set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX})
+ endif ()
+ elseif(${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL MSVC)
+ set(_VISUAL "Visual Studio")
+ _get_msvc_ide_version(_VISUAL_VERSION)
+ if("${_VISUAL_VERSION}" STREQUAL "")
+ message(FATAL_ERROR "Conan: Visual Studio not recognized")
+ else()
+ set(_CONAN_SETTING_COMPILER ${_VISUAL})
+ set(_CONAN_SETTING_COMPILER_VERSION ${_VISUAL_VERSION})
+ endif()
+
+ if(NOT _CONAN_SETTING_ARCH)
+ if (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "64")
+ set(_CONAN_SETTING_ARCH x86_64)
+ elseif (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "^ARM")
+ message(STATUS "Conan: Using default ARM architecture from MSVC")
+ set(_CONAN_SETTING_ARCH armv6)
+ elseif (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "86")
+ set(_CONAN_SETTING_ARCH x86)
+ else ()
+ message(FATAL_ERROR "Conan: Unknown MSVC architecture [${MSVC_${LANGUAGE}_ARCHITECTURE_ID}]")
+ endif()
+ endif()
+
+ conan_cmake_detect_vs_runtime(_vs_runtime)
+ message(STATUS "Conan: Detected VS runtime: ${_vs_runtime}")
+ set(_CONAN_SETTING_COMPILER_RUNTIME ${_vs_runtime})
+
+ if (CMAKE_GENERATOR_TOOLSET)
+ set(_CONAN_SETTING_COMPILER_TOOLSET ${CMAKE_VS_PLATFORM_TOOLSET})
+ elseif(CMAKE_VS_PLATFORM_TOOLSET AND (CMAKE_GENERATOR STREQUAL "Ninja"))
+ set(_CONAN_SETTING_COMPILER_TOOLSET ${CMAKE_VS_PLATFORM_TOOLSET})
+ endif()
+ else()
+ message(FATAL_ERROR "Conan: compiler setup not recognized")
+ endif()
+
+ # If profile is defined it is used
+ if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND ARGUMENTS_DEBUG_PROFILE)
+ set(_APPLIED_PROFILES ${ARGUMENTS_DEBUG_PROFILE})
+ elseif(CMAKE_BUILD_TYPE STREQUAL "Release" AND ARGUMENTS_RELEASE_PROFILE)
+ set(_APPLIED_PROFILES ${ARGUMENTS_RELEASE_PROFILE})
+ elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" AND ARGUMENTS_RELWITHDEBINFO_PROFILE)
+ set(_APPLIED_PROFILES ${ARGUMENTS_RELWITHDEBINFO_PROFILE})
+ elseif(CMAKE_BUILD_TYPE STREQUAL "MinSizeRel" AND ARGUMENTS_MINSIZEREL_PROFILE)
+ set(_APPLIED_PROFILES ${ARGUMENTS_MINSIZEREL_PROFILE})
+ elseif(ARGUMENTS_PROFILE)
+ set(_APPLIED_PROFILES ${ARGUMENTS_PROFILE})
+ endif()
+
+ foreach(ARG ${_APPLIED_PROFILES})
+ set(_SETTINGS ${_SETTINGS} -pr ${ARG})
+ endforeach()
+
+ if(NOT _SETTINGS OR ARGUMENTS_PROFILE_AUTO STREQUAL "ALL")
+ set(ARGUMENTS_PROFILE_AUTO arch build_type compiler compiler.version
+ compiler.runtime compiler.libcxx compiler.toolset)
+ endif()
+
+ # Automatic from CMake
+ foreach(ARG ${ARGUMENTS_PROFILE_AUTO})
+ string(TOUPPER ${ARG} _arg_name)
+ string(REPLACE "." "_" _arg_name ${_arg_name})
+ if(_CONAN_SETTING_${_arg_name})
+ set(_SETTINGS ${_SETTINGS} -s ${ARG}=${_CONAN_SETTING_${_arg_name}})
+ endif()
+ endforeach()
+
+ foreach(ARG ${ARGUMENTS_SETTINGS})
+ set(_SETTINGS ${_SETTINGS} -s ${ARG})
+ endforeach()
+
+ message(STATUS "Conan: Settings= ${_SETTINGS}")
+
+ set(${result} ${_SETTINGS} PARENT_SCOPE)
+endfunction()
+
+
+function(conan_cmake_detect_unix_libcxx result)
+ # Take into account any -stdlib in compile options
+ get_directory_property(compile_options DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_OPTIONS)
+
+ # Take into account any _GLIBCXX_USE_CXX11_ABI in compile definitions
+ get_directory_property(defines DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_DEFINITIONS)
+ foreach(define ${defines})
+ if(define MATCHES "_GLIBCXX_USE_CXX11_ABI")
+ if(define MATCHES "^-D")
+ set(compile_options ${compile_options} "${define}")
+ else()
+ set(compile_options ${compile_options} "-D${define}")
+ endif()
+ endif()
+ endforeach()
+
+ execute_process(
+ COMMAND ${CMAKE_COMMAND} -E echo "#include <string>"
+ COMMAND ${CMAKE_CXX_COMPILER} -x c++ ${compile_options} -E -dM -
+ OUTPUT_VARIABLE string_defines
+ )
+
+ if(string_defines MATCHES "#define __GLIBCXX__")
+ # Allow -D_GLIBCXX_USE_CXX11_ABI=ON/OFF as argument to cmake
+ if(DEFINED _GLIBCXX_USE_CXX11_ABI)
+ if(_GLIBCXX_USE_CXX11_ABI)
+ set(${result} libstdc++11 PARENT_SCOPE)
+ return()
+ else()
+ set(${result} libstdc++ PARENT_SCOPE)
+ return()
+ endif()
+ endif()
+
+ if(string_defines MATCHES "#define _GLIBCXX_USE_CXX11_ABI 1\n")
+ set(${result} libstdc++11 PARENT_SCOPE)
+ else()
+ # Either the compiler is missing the define because it is old, and so
+ # it can't use the new abi, or the compiler was configured to use the
+ # old abi by the user or distro (e.g. devtoolset on RHEL/CentOS)
+ set(${result} libstdc++ PARENT_SCOPE)
+ endif()
+ else()
+ set(${result} libc++ PARENT_SCOPE)
+ endif()
+endfunction()
+
+function(conan_cmake_detect_vs_runtime result)
+ string(TOUPPER ${CMAKE_BUILD_TYPE} build_type)
+ set(variables CMAKE_CXX_FLAGS_${build_type} CMAKE_C_FLAGS_${build_type} CMAKE_CXX_FLAGS CMAKE_C_FLAGS)
+ foreach(variable ${variables})
+ string(REPLACE " " ";" flags ${${variable}})
+ foreach (flag ${flags})
+ if(${flag} STREQUAL "/MD" OR ${flag} STREQUAL "/MDd" OR ${flag} STREQUAL "/MT" OR ${flag} STREQUAL "/MTd")
+ string(SUBSTRING ${flag} 1 -1 runtime)
+ set(${result} ${runtime} PARENT_SCOPE)
+ return()
+ endif()
+ endforeach()
+ endforeach()
+ if(${build_type} STREQUAL "DEBUG")
+ set(${result} "MDd" PARENT_SCOPE)
+ else()
+ set(${result} "MD" PARENT_SCOPE)
+ endif()
+endfunction()
+
+
+macro(parse_arguments)
+ set(options BASIC_SETUP CMAKE_TARGETS UPDATE KEEP_RPATHS NO_LOAD NO_OUTPUT_DIRS OUTPUT_QUIET NO_IMPORTS)
+ set(oneValueArgs CONANFILE ARCH BUILD_TYPE INSTALL_FOLDER CONAN_COMMAND)
+ set(multiValueArgs DEBUG_PROFILE RELEASE_PROFILE RELWITHDEBINFO_PROFILE MINSIZEREL_PROFILE
+ PROFILE REQUIRES OPTIONS IMPORTS SETTINGS BUILD ENV GENERATORS PROFILE_AUTO
+ INSTALL_ARGS)
+ cmake_parse_arguments(ARGUMENTS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+endmacro()
+
+function(conan_cmake_install)
+ # Calls "conan install"
+ # Argument BUILD is equivalant to --build={missing, PkgName,...} or
+ # --build when argument is 'BUILD all' (which builds all packages from source)
+ # Argument CONAN_COMMAND, to specify the conan path, e.g. in case of running from source
+ # cmake does not identify conan as command, even if it is +x and it is in the path
+ parse_arguments(${ARGV})
+
+ if(CONAN_CMAKE_MULTI)
+ set(ARGUMENTS_GENERATORS ${ARGUMENTS_GENERATORS} cmake_multi)
+ else()
+ set(ARGUMENTS_GENERATORS ${ARGUMENTS_GENERATORS} cmake)
+ endif()
+
+ set(CONAN_BUILD_POLICY "")
+ foreach(ARG ${ARGUMENTS_BUILD})
+ if(${ARG} STREQUAL "all")
+ set(CONAN_BUILD_POLICY ${CONAN_BUILD_POLICY} --build)
+ break()
+ else()
+ set(CONAN_BUILD_POLICY ${CONAN_BUILD_POLICY} --build=${ARG})
+ endif()
+ endforeach()
+ if(ARGUMENTS_CONAN_COMMAND)
+ set(conan_command ${ARGUMENTS_CONAN_COMMAND})
+ else()
+ set(conan_command conan)
+ endif()
+ set(CONAN_OPTIONS "")
+ if(ARGUMENTS_CONANFILE)
+ set(CONANFILE ${CMAKE_CURRENT_SOURCE_DIR}/${ARGUMENTS_CONANFILE})
+ # A conan file has been specified - apply specified options as well if provided
+ foreach(ARG ${ARGUMENTS_OPTIONS})
+ set(CONAN_OPTIONS ${CONAN_OPTIONS} -o=${ARG})
+ endforeach()
+ else()
+ set(CONANFILE ".")
+ endif()
+ if(ARGUMENTS_UPDATE)
+ set(CONAN_INSTALL_UPDATE --update)
+ endif()
+ if(ARGUMENTS_NO_IMPORTS)
+ set(CONAN_INSTALL_NO_IMPORTS --no-imports)
+ endif()
+ set(CONAN_INSTALL_FOLDER "")
+ if(ARGUMENTS_INSTALL_FOLDER)
+ set(CONAN_INSTALL_FOLDER -if=${ARGUMENTS_INSTALL_FOLDER})
+ endif()
+ foreach(ARG ${ARGUMENTS_GENERATORS})
+ set(CONAN_GENERATORS ${CONAN_GENERATORS} -g=${ARG})
+ endforeach()
+ foreach(ARG ${ARGUMENTS_ENV})
+ set(CONAN_ENV_VARS ${CONAN_ENV_VARS} -e=${ARG})
+ endforeach()
+ set(conan_args install ${CONANFILE} ${settings} ${CONAN_ENV_VARS} ${CONAN_GENERATORS} ${CONAN_BUILD_POLICY} ${CONAN_INSTALL_UPDATE} ${CONAN_INSTALL_NO_IMPORTS} ${CONAN_OPTIONS} ${CONAN_INSTALL_FOLDER} ${ARGUMENTS_INSTALL_ARGS})
+
+ string (REPLACE ";" " " _conan_args "${conan_args}")
+ message(STATUS "Conan executing: ${conan_command} ${_conan_args}")
+
+ if(ARGUMENTS_OUTPUT_QUIET)
+ execute_process(COMMAND ${conan_command} ${conan_args}
+ RESULT_VARIABLE return_code
+ OUTPUT_VARIABLE conan_output
+ ERROR_VARIABLE conan_output
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+ else()
+ execute_process(COMMAND ${conan_command} ${conan_args}
+ RESULT_VARIABLE return_code
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+ endif()
+
+ if(NOT "${return_code}" STREQUAL "0")
+ message(FATAL_ERROR "Conan install failed='${return_code}'")
+ endif()
+
+endfunction()
+
+
+function(conan_cmake_setup_conanfile)
+ parse_arguments(${ARGV})
+ if(ARGUMENTS_CONANFILE)
+ get_filename_component(_CONANFILE_NAME ${ARGUMENTS_CONANFILE} NAME)
+ # configure_file will make sure cmake re-runs when conanfile is updated
+ configure_file(${ARGUMENTS_CONANFILE} ${CMAKE_CURRENT_BINARY_DIR}/${_CONANFILE_NAME}.junk)
+ file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/${_CONANFILE_NAME}.junk)
+ else()
+ conan_cmake_generate_conanfile(${ARGV})
+ endif()
+endfunction()
+
+function(conan_cmake_generate_conanfile)
+ # Generate, writing in disk a conanfile.txt with the requires, options, and imports
+ # specified as arguments
+ # This will be considered as temporary file, generated in CMAKE_CURRENT_BINARY_DIR)
+ parse_arguments(${ARGV})
+ set(_FN "${CMAKE_CURRENT_BINARY_DIR}/conanfile.txt")
+
+ file(WRITE ${_FN} "[generators]\ncmake\n\n[requires]\n")
+ foreach(ARG ${ARGUMENTS_REQUIRES})
+ file(APPEND ${_FN} ${ARG} "\n")
+ endforeach()
+
+ file(APPEND ${_FN} ${ARG} "\n[options]\n")
+ foreach(ARG ${ARGUMENTS_OPTIONS})
+ file(APPEND ${_FN} ${ARG} "\n")
+ endforeach()
+
+ file(APPEND ${_FN} ${ARG} "\n[imports]\n")
+ foreach(ARG ${ARGUMENTS_IMPORTS})
+ file(APPEND ${_FN} ${ARG} "\n")
+ endforeach()
+endfunction()
+
+
+macro(conan_load_buildinfo)
+ if(CONAN_CMAKE_MULTI)
+ set(_CONANBUILDINFO conanbuildinfo_multi.cmake)
+ else()
+ set(_CONANBUILDINFO conanbuildinfo.cmake)
+ endif()
+ if(ARGUMENTS_INSTALL_FOLDER)
+ set(_CONANBUILDINFOFOLDER ${ARGUMENTS_INSTALL_FOLDER})
+ else()
+ set(_CONANBUILDINFOFOLDER ${CMAKE_CURRENT_BINARY_DIR})
+ endif()
+ # Checks for the existence of conanbuildinfo.cmake, and loads it
+ # important that it is macro, so variables defined at parent scope
+ if(EXISTS "${_CONANBUILDINFOFOLDER}/${_CONANBUILDINFO}")
+ message(STATUS "Conan: Loading ${_CONANBUILDINFO}")
+ include(${_CONANBUILDINFOFOLDER}/${_CONANBUILDINFO})
+ else()
+ message(FATAL_ERROR "${_CONANBUILDINFO} doesn't exist in ${CMAKE_CURRENT_BINARY_DIR}")
+ endif()
+endmacro()
+
+
+macro(conan_cmake_run)
+ parse_arguments(${ARGV})
+
+ if(CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE AND NOT CONAN_EXPORTED
+ AND NOT ARGUMENTS_BUILD_TYPE)
+ set(CONAN_CMAKE_MULTI ON)
+ message(STATUS "Conan: Using cmake-multi generator")
+ else()
+ set(CONAN_CMAKE_MULTI OFF)
+ endif()
+
+ if(NOT CONAN_EXPORTED)
+ conan_cmake_setup_conanfile(${ARGV})
+ if(CONAN_CMAKE_MULTI)
+ foreach(CMAKE_BUILD_TYPE "Release" "Debug")
+ set(ENV{CONAN_IMPORT_PATH} ${CMAKE_BUILD_TYPE})
+ conan_cmake_settings(settings ${ARGV})
+ conan_cmake_install(SETTINGS ${settings} ${ARGV})
+ endforeach()
+ set(CMAKE_BUILD_TYPE)
+ else()
+ conan_cmake_settings(settings ${ARGV})
+ conan_cmake_install(SETTINGS ${settings} ${ARGV})
+ endif()
+ endif()
+
+ if (NOT ARGUMENTS_NO_LOAD)
+ conan_load_buildinfo()
+ endif()
+
+ if(ARGUMENTS_BASIC_SETUP)
+ foreach(_option CMAKE_TARGETS KEEP_RPATHS NO_OUTPUT_DIRS)
+ if(ARGUMENTS_${_option})
+ if(${_option} STREQUAL "CMAKE_TARGETS")
+ list(APPEND _setup_options "TARGETS")
+ else()
+ list(APPEND _setup_options ${_option})
+ endif()
+ endif()
+ endforeach()
+ conan_basic_setup(${_setup_options})
+ endif()
+endmacro()
+
+macro(conan_check)
+ # Checks conan availability in PATH
+ # Arguments REQUIRED and VERSION are optional
+ # Example usage:
+ # conan_check(VERSION 1.0.0 REQUIRED)
+ message(STATUS "Conan: checking conan executable in path")
+ set(options REQUIRED)
+ set(oneValueArgs VERSION)
+ cmake_parse_arguments(CONAN "${options}" "${oneValueArgs}" "" ${ARGN})
+
+ find_program(CONAN_CMD conan)
+ if(NOT CONAN_CMD AND CONAN_REQUIRED)
+ message(FATAL_ERROR "Conan executable not found!")
+ endif()
+ message(STATUS "Conan: Found program ${CONAN_CMD}")
+ execute_process(COMMAND ${CONAN_CMD} --version
+ OUTPUT_VARIABLE CONAN_VERSION_OUTPUT
+ ERROR_VARIABLE CONAN_VERSION_OUTPUT)
+ message(STATUS "Conan: Version found ${CONAN_VERSION_OUTPUT}")
+
+ if(DEFINED CONAN_VERSION)
+ string(REGEX MATCH ".*Conan version ([0-9]+\.[0-9]+\.[0-9]+)" FOO
+ "${CONAN_VERSION_OUTPUT}")
+ if(${CMAKE_MATCH_1} VERSION_LESS ${CONAN_VERSION})
+ message(FATAL_ERROR "Conan outdated. Installed: ${CMAKE_MATCH_1}, \
+ required: ${CONAN_VERSION}. Consider updating via 'pip \
+ install conan==${CONAN_VERSION}'.")
+ endif()
+ endif()
+endmacro()
+
+macro(conan_add_remote)
+ # Adds a remote
+ # Arguments URL and NAME are required, INDEX is optional
+ # Example usage:
+ # conan_add_remote(NAME bincrafters INDEX 1
+ # URL https://api.bintray.com/conan/bincrafters/public-conan)
+ set(oneValueArgs URL NAME INDEX)
+ cmake_parse_arguments(CONAN "" "${oneValueArgs}" "" ${ARGN})
+
+ if(DEFINED CONAN_INDEX)
+ set(CONAN_INDEX_ARG "-i ${CONAN_INDEX}")
+ endif()
+
+ message(STATUS "Conan: Adding ${CONAN_NAME} remote repositoy (${CONAN_URL})")
+ execute_process(COMMAND ${CONAN_CMD} remote add ${CONAN_NAME} ${CONAN_URL}
+ ${CONAN_INDEX_ARG} -f)
+endmacro()
diff --git a/cmake/Modules/DiscoverTests.cmake b/cmake/Modules/DiscoverTests.cmake
new file mode 100644
index 0000000..882fbf8
--- /dev/null
+++ b/cmake/Modules/DiscoverTests.cmake
@@ -0,0 +1,55 @@
+#[=[
+Discover all tests in our test suites.
+
+This implementation is adapted vom GoogleTest.cmake
+#]=]
+
+function(discover_tests)
+ set(OPTIONS "")
+ set(SINGLE_VALUE_KEYWORDS "TARGET")
+ set(MULTI_VALUE_KEYWORDS "")
+
+ cmake_parse_arguments(DISCOVER_TESTS
+ "${OPTIONS}"
+ "${SINGLE_VALUE_KEYWORDS}"
+ "${MULTI_VALUE_KEYWORDS}"
+ ${ARGN}
+ )
+
+ if(NOT DISCOVER_TESTS_TARGET)
+ message(FATAL_ERROR "Missing argument TARGET")
+ endif()
+
+ if(NOT TARGET "${DISCOVER_TESTS_TARGET}")
+ message(FATAL_ERROR "${DISCOVER_TESTS_TARGET} does not name a target known to CMake")
+ endif()
+
+ set(CTEST_FILE_BASE "${CMAKE_CURRENT_BINARY_DIR}/${DISCOVER_TESTS_TARGET}")
+ set(CTEST_INCLUDE_FILE "${CTEST_FILE_BASE}_include.cmake")
+ set(CTEST_TESTS_FILE "${CTEST_FILE_BASE}_tests.cmake")
+
+ add_custom_command(TARGET "${DISCOVER_TESTS_TARGET}"
+ POST_BUILD
+ BYPRODUCTS "${CTEST_TESTS_FILE}"
+ COMMAND "${CMAKE_COMMAND}"
+ "-D" "TEST_TARGET=${DISCOVER_TESTS_TARGET}"
+ "-D" "TEST_EXECUTABLE=$<TARGET_FILE:${DISCOVER_TESTS_TARGET}>"
+ "-D" "TEST_WORKING_DIR=${CMAKE_CURRENT_BINARY_DIR}"
+ "-D" "CTEST_FILE=${CTEST_TESTS_FILE}"
+ "-P" "${PROJECT_SOURCE_DIR}/cmake/Modules/DiscoverTestsImpl.cmake"
+ VERBATIM
+ )
+
+ file(WRITE "${CTEST_INCLUDE_FILE}"
+ "if(EXISTS \"${CTEST_TESTS_FILE}\")\n"
+ " include(\"${CTEST_TESTS_FILE}\")\n"
+ "else()\n"
+ " add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)\n"
+ "endif()\n"
+ )
+
+ set_property(DIRECTORY
+ APPEND
+ PROPERTY TEST_INCLUDE_FILES "${CTEST_INCLUDE_FILE}"
+ )
+endfunction() \ No newline at end of file
diff --git a/cmake/Modules/DiscoverTestsImpl.cmake b/cmake/Modules/DiscoverTestsImpl.cmake
new file mode 100644
index 0000000..893d998
--- /dev/null
+++ b/cmake/Modules/DiscoverTestsImpl.cmake
@@ -0,0 +1,39 @@
+#[=[
+Discover all tests in our test suites.
+
+This implementation is adapted vom GoogleTestAddTests.cmake
+#]=]
+
+set(SCRIPT)
+
+function(add_command CMD EXE TEST)
+endfunction()
+
+if(NOT EXISTS "${TEST_EXECUTABLE}")
+ message(FATAL_ERROR "The test driver ${TEST_EXECUTABLE} does not exist.")
+endif()
+
+execute_process(COMMAND ${TEST_EXECUTABLE} "--tests"
+ WORKING_DIRECTORY "${TEST_WORKING_DIR}"
+ TIMEOUT "5"
+ OUTPUT_VARIABLE "TEST_LIST_OUTPUT"
+ RESULT_VARIABLE "TEST_LIST_RESULT"
+)
+
+if(NOT ${TEST_LIST_RESULT} EQUAL 0)
+ message(FATAL_ERROR "Error while discovering tests:\n"
+ "Executable: ${TEST_EXECUTABLE}\n"
+ "Result: ${TEST_LIST_RESULT}\n"
+ "Output:\n"
+ " ${TEST_LIST_OUTPUT}\n"
+ )
+endif()
+
+string(REPLACE "\n" ";" TEST_LIST_OUTPUT "${TEST_LIST_OUTPUT}")
+
+foreach(LINE ${TEST_LIST_OUTPUT})
+ string(REPLACE "#" ": " TEST_NAME "${LINE}")
+ set(SCRIPT "${SCRIPT}add_test(\"${TEST_NAME}\" \"${TEST_EXECUTABLE}\" \"${LINE}\")\n")
+endforeach()
+
+file(WRITE "${CTEST_FILE}" "${SCRIPT}") \ No newline at end of file
diff --git a/cmake/config.cmake.in b/cmake/config.cmake.in
new file mode 100644
index 0000000..f07fcfd
--- /dev/null
+++ b/cmake/config.cmake.in
@@ -0,0 +1,4 @@
+@PACKAGE_INIT@
+
+include("${CMAKE_CURRENT_LIST_DIR}/newtypeTargets.cmake")
+check_required_components("@PROJECT_NAME@") \ No newline at end of file
diff --git a/conanfile.py b/conanfile.py
new file mode 100644
index 0000000..bebb254
--- /dev/null
+++ b/conanfile.py
@@ -0,0 +1,33 @@
+from conans import ConanFile, CMake
+
+class NewtypeConan(ConanFile):
+ name = "newtype"
+ scm = {
+ "type": "git",
+ "url": "https://github.com/fmorgner/newtype.git",
+ "revision": "auto",
+ }
+ version = "0.0.1"
+ license = "BSD-3-Clause"
+ url = "https://github.com/fmorgner/newtype"
+ description = "A library of types and functions to create strong type aliases"
+ generators = "cmake"
+ requires = (
+ "CUTE/2.2.6@fmorgner/stable",
+ "lyra/1.2.0"
+ )
+
+ def _configure_cmake(self):
+ cmake = CMake(self)
+ cmake.definitions["BUILD_TESTING"] = True
+ cmake.configure()
+ return cmake
+
+ def build(self):
+ cmake = self._configure_cmake()
+ cmake.build()
+ cmake.test()
+
+ def package(self):
+ cmake = self._configure_cmake()
+ cmake.install()
diff --git a/doc/Pipfile b/doc/Pipfile
new file mode 100644
index 0000000..92d66ea
--- /dev/null
+++ b/doc/Pipfile
@@ -0,0 +1,14 @@
+[[source]]
+name = "pypi"
+url = "https://pypi.org/simple"
+verify_ssl = true
+
+[dev-packages]
+
+[packages]
+sphinx = "*"
+breathe = "*"
+sphinx-better-theme = "*"
+
+[requires]
+python_version = "3"
diff --git a/doc/Pipfile.lock b/doc/Pipfile.lock
new file mode 100644
index 0000000..fca92c1
--- /dev/null
+++ b/doc/Pipfile.lock
@@ -0,0 +1,232 @@
+{
+ "_meta": {
+ "hash": {
+ "sha256": "867ebbc446f1c3064607a63aa5b4a7f0b53e2878ee60ccb51b6d33e320622f25"
+ },
+ "pipfile-spec": 6,
+ "requires": {
+ "python_version": "3"
+ },
+ "sources": [
+ {
+ "name": "pypi",
+ "url": "https://pypi.org/simple",
+ "verify_ssl": true
+ }
+ ]
+ },
+ "default": {
+ "alabaster": {
+ "hashes": [
+ "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359",
+ "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"
+ ],
+ "version": "==0.7.12"
+ },
+ "babel": {
+ "hashes": [
+ "sha256:af92e6106cb7c55286b25b38ad7695f8b4efb36a90ba483d7f7a6628c46158ab",
+ "sha256:e86135ae101e31e2c8ec20a4e0c5220f4eed12487d5cf3f78be7e98d3a57fc28"
+ ],
+ "version": "==2.7.0"
+ },
+ "breathe": {
+ "hashes": [
+ "sha256:178848e4088faf8c2c60f000379fcabfb3411b260e0fbddc08fb57e0e5caea08",
+ "sha256:94b4267355571f729038eaad076dab9a851016c5d8b9ba156e2e2360dbbf930d"
+ ],
+ "index": "pypi",
+ "version": "==4.14.0"
+ },
+ "certifi": {
+ "hashes": [
+ "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3",
+ "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"
+ ],
+ "version": "==2019.11.28"
+ },
+ "chardet": {
+ "hashes": [
+ "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
+ "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
+ ],
+ "version": "==3.0.4"
+ },
+ "docutils": {
+ "hashes": [
+ "sha256:6c4f696463b79f1fb8ba0c594b63840ebd41f059e92b31957c46b74a4599b6d0",
+ "sha256:9e4d7ecfc600058e07ba661411a2b7de2fd0fafa17d1a7f7361cd47b1175c827",
+ "sha256:a2aeea129088da402665e92e0b25b04b073c04b2dce4ab65caaa38b7ce2e1a99"
+ ],
+ "version": "==0.15.2"
+ },
+ "idna": {
+ "hashes": [
+ "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
+ "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
+ ],
+ "version": "==2.8"
+ },
+ "imagesize": {
+ "hashes": [
+ "sha256:3f349de3eb99145973fefb7dbe38554414e5c30abd0c8e4b970a7c9d09f3a1d8",
+ "sha256:f3832918bc3c66617f92e35f5d70729187676313caa60c187eb0f28b8fe5e3b5"
+ ],
+ "version": "==1.1.0"
+ },
+ "jinja2": {
+ "hashes": [
+ "sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f",
+ "sha256:9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de"
+ ],
+ "version": "==2.10.3"
+ },
+ "markupsafe": {
+ "hashes": [
+ "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
+ "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
+ "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
+ "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
+ "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
+ "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
+ "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
+ "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
+ "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
+ "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
+ "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
+ "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
+ "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
+ "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
+ "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
+ "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
+ "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
+ "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
+ "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
+ "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
+ "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
+ "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
+ "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
+ "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
+ "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
+ "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
+ "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
+ "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"
+ ],
+ "version": "==1.1.1"
+ },
+ "packaging": {
+ "hashes": [
+ "sha256:28b924174df7a2fa32c1953825ff29c61e2f5e082343165438812f00d3a7fc47",
+ "sha256:d9551545c6d761f3def1677baf08ab2a3ca17c56879e70fecba2fc4dde4ed108"
+ ],
+ "version": "==19.2"
+ },
+ "pygments": {
+ "hashes": [
+ "sha256:2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b",
+ "sha256:98c8aa5a9f778fcd1026a17361ddaf7330d1b7c62ae97c3bb0ae73e0b9b6b0fe"
+ ],
+ "version": "==2.5.2"
+ },
+ "pyparsing": {
+ "hashes": [
+ "sha256:20f995ecd72f2a1f4bf6b072b63b22e2eb457836601e76d6e5dfcd75436acc1f",
+ "sha256:4ca62001be367f01bd3e92ecbb79070272a9d4964dce6a48a82ff0b8bc7e683a"
+ ],
+ "version": "==2.4.5"
+ },
+ "pytz": {
+ "hashes": [
+ "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d",
+ "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"
+ ],
+ "version": "==2019.3"
+ },
+ "requests": {
+ "hashes": [
+ "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
+ "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
+ ],
+ "version": "==2.22.0"
+ },
+ "six": {
+ "hashes": [
+ "sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd",
+ "sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"
+ ],
+ "version": "==1.13.0"
+ },
+ "snowballstemmer": {
+ "hashes": [
+ "sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0",
+ "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"
+ ],
+ "version": "==2.0.0"
+ },
+ "sphinx": {
+ "hashes": [
+ "sha256:298537cb3234578b2d954ff18c5608468229e116a9757af3b831c2b2b4819159",
+ "sha256:e6e766b74f85f37a5f3e0773a1e1be8db3fcb799deb58ca6d18b70b0b44542a5"
+ ],
+ "index": "pypi",
+ "version": "==2.3.1"
+ },
+ "sphinx-better-theme": {
+ "hashes": [
+ "sha256:420570d80d8cab25b35cef2b79619104675777dff5a23deebe9ba2191d48c1ce"
+ ],
+ "index": "pypi",
+ "version": "==0.1.5"
+ },
+ "sphinxcontrib-applehelp": {
+ "hashes": [
+ "sha256:edaa0ab2b2bc74403149cb0209d6775c96de797dfd5b5e2a71981309efab3897",
+ "sha256:fb8dee85af95e5c30c91f10e7eb3c8967308518e0f7488a2828ef7bc191d0d5d"
+ ],
+ "version": "==1.0.1"
+ },
+ "sphinxcontrib-devhelp": {
+ "hashes": [
+ "sha256:6c64b077937330a9128a4da74586e8c2130262f014689b4b89e2d08ee7294a34",
+ "sha256:9512ecb00a2b0821a146736b39f7aeb90759834b07e81e8cc23a9c70bacb9981"
+ ],
+ "version": "==1.0.1"
+ },
+ "sphinxcontrib-htmlhelp": {
+ "hashes": [
+ "sha256:4670f99f8951bd78cd4ad2ab962f798f5618b17675c35c5ac3b2132a14ea8422",
+ "sha256:d4fd39a65a625c9df86d7fa8a2d9f3cd8299a3a4b15db63b50aac9e161d8eff7"
+ ],
+ "version": "==1.0.2"
+ },
+ "sphinxcontrib-jsmath": {
+ "hashes": [
+ "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178",
+ "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"
+ ],
+ "version": "==1.0.1"
+ },
+ "sphinxcontrib-qthelp": {
+ "hashes": [
+ "sha256:513049b93031beb1f57d4daea74068a4feb77aa5630f856fcff2e50de14e9a20",
+ "sha256:79465ce11ae5694ff165becda529a600c754f4bc459778778c7017374d4d406f"
+ ],
+ "version": "==1.0.2"
+ },
+ "sphinxcontrib-serializinghtml": {
+ "hashes": [
+ "sha256:c0efb33f8052c04fd7a26c0a07f1678e8512e0faec19f4aa8f2473a8b81d5227",
+ "sha256:db6615af393650bf1151a6cd39120c29abaf93cc60db8c48eb2dddbfdc3a9768"
+ ],
+ "version": "==1.1.3"
+ },
+ "urllib3": {
+ "hashes": [
+ "sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293",
+ "sha256:f3c5fd51747d450d4dcf6f923c81f78f811aab8205fda64b0aba34a4e48b0745"
+ ],
+ "version": "==1.25.7"
+ }
+ },
+ "develop": {}
+}
diff --git a/doc/src/conf.py b/doc/src/conf.py
new file mode 100644
index 0000000..305ef6b
--- /dev/null
+++ b/doc/src/conf.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+
+import os
+
+
+# -- Project information -----------------------------------------------------
+
+project = 'newtype'
+copyright = '2019, Felix Morgner'
+author = 'Felix Morgner'
+version = '1.0'
+release = '1.0.0'
+
+
+# -- General configuration ---------------------------------------------------
+
+extensions = [
+ 'sphinx.ext.todo',
+ 'sphinx.ext.githubpages',
+]
+
+highlight_language = 'c++'
+
+pygments_style = 'tango'
+
+exclude_patterns = []
+
+# -- 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 <felix.morgner@gmail.com>"],
+ 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..3a324e1
--- /dev/null
+++ b/doc/src/index.rst
@@ -0,0 +1,154 @@
+#############
+Documentation
+#############
+
+The ``newtype`` library provides types and functions to facilitate the creation of strong type aliases.
+
+API
+===
+
+.. cpp:namespace-push:: nt
+
+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.
+Additionally, this section provides usage examples that demonstrate the use and properties of the public API.
+
+Class template :cpp:class:`new_type`
+----------------------------------------
+
+.. cpp:class:: template<typename BaseType, typename TagType, auto DerivationClause = deriving()> \
+ new_type
+
+ The class template :cpp:class:`new_type` is designed to allow the creation of new types based on existing types.
+ Similarly to the Haskell :literal:`newtype`, this class template creates a new type that is layout equivalent to the underlying type.
+
+ :tparam BaseType: The underlying type of the new strong alias
+ :tparam TagType: A type uniquely identifying this string alias
+ :tparam DerivationClause: A :cpp:struct:`derivation_clause` listing all features that shall be automatically derived.
+
+
+Usage
+~~~~~
+
+:ref:`new-type-usage-simple` below demonstrate the most basic usage of :cpp:class:`new_type`.
+In it, :cpp:class:`new_type` is used to create a new strong alias :literal:`width` for :literal:`unsigned int`.
+
+.. code-block:: c++
+ :linenos:
+ :name: new-type-usage-simple
+ :caption: Creating a new strong alias for :literal:`unsigned int`
+
+ #include <newtype/new_type.hpp>
+
+ using width = nt::new_type<unsigned int, struct width_tag>;
+
+The class template :cpp:class:`new_type` expects the desired underlying type as its first template argument, :literal:`unsigned int` in the example above.
+As a second template argument, :cpp:class:`new_type` expects a tag- or phantom-type.
+Neither the underlying type, nor the tag-type are is required to be complete.
+
+The class template :cpp:class:`new_type` takes as a third template argument an instance of :cpp:class:`derivation_clause`.
+Derivation clauses make it possible to let the implementation derive certain operations automatically.
+For example, the derivation tag :cpp:var:`Arithmetic` enables automatic derivation of arithmetic operations for a given instance of :cpp:class:`new_type` (see :ref:`new-type-usage-deriving-arithmetic` below).
+
+.. code-block:: c++
+ :linenos:
+ :name: new-type-usage-deriving-arithmetic
+ :caption: Automatically deriving arithmetic operations
+
+ #include <newtype/new_type.hpp>
+
+ using width = nt::new_type<unsigned int, struct width_tag, deriving(nt::Arithmetic)>;
+
+Synopsis
+~~~~~~~~
+
+.. code-block::
+
+ namespace nt
+ {
+ template<typename BaseType, typename TagType, auto DerivationClause = deriving()>
+ class new_type
+ {
+ public:
+
+ // Constructors
+
+ constexpr explicit new_type() noexcept(std::is_nothrow_default_constructible_v<BaseType>) = /*see below*/;
+
+ constexpr explicit new_type(BaseType const &) noexcept(std::is_nothrow_copy_constructible_v<BaseType>);
+
+ constexpr explicit new_type(BaseType &&) noexcept(std::is_nothrow_move_constructible_v<BaseType>);
+
+ // Assignment operators
+
+ auto constexpr operator=(new_type const &) noexcept(std::is_nothrow_copy_assignable_v<BaseType>) -> new_type & = /*see below*/
+
+ auto constexpr operator=(new_type &&) noexcept(std::is_nothrow_move_assignable_v<BaseType>) -> new_type & = /*see below*/
+
+ // Accessors
+
+ auto constexpr decay() const noexcept -> BaseType;
+
+ /* EXPLICIT: see below */ constexpr operator base_type() const noexcept(/*see below*/)
+
+ // Indirection operators
+
+ auto constexpr operator->() noexcept -> std::enable_if_t<DerivationClause(nt::Indirection), BaseType *>;
+
+ auto constexpr operator->() const noexcept -> std::enable_if_t<DerivationClause(nt::Indirection), BaseType const *>;
+
+ private:
+ BaseType m_value;
+ };
+
+ // Equality comparison operators
+
+ template<typename BaseType,
+ typename TagType,
+ auto DerivationClause>
+ auto constexpr operator==(new_type<BaseType, TagType, DerivationClause> const &,
+ new_type<BaseType, TagType, DerivationClause> const &) noexcept(/*see below*/)
+ -> bool;
+
+ template<typename BaseType,
+ typename TagType,
+ auto DerivationClause>
+ auto constexpr operator!=(new_type<BaseType, TagType, DerivationClause> const &,
+ new_type<BaseType, TagType, DerivationClause> const &) noexcept(/*see below*/)
+ -> bool;
+
+ // Relational operators
+
+ template<typename BaseType,
+ typename TagType,
+ auto DerivationClause,
+ typename = std::enable_if_t<DerivationClause(nt::Relational)>
+ auto constexpr operator<(new_type<BaseType, TagType, DerivationClause> const &,
+ new_type<BaseType, TagType, DerivationClause> const &) noexcept(/*see below*/)
+ -> bool;
+
+ template<typename BaseType,
+ typename TagType,
+ auto DerivationClause,
+ typename = std::enable_if_t<DerivationClause(nt::Relational)>
+ auto constexpr operator>(new_type<BaseType, TagType, DerivationClause> const &,
+ new_type<BaseType, TagType, DerivationClause> const &) noexcept(/*see below*/)
+ -> bool;
+
+ template<typename BaseType,
+ typename TagType,
+ auto DerivationClause,
+ typename = std::enable_if_t<DerivationClause(nt::Relational)>
+ auto constexpr operator<=(new_type<BaseType, TagType, DerivationClause> const &,
+ new_type<BaseType, TagType, DerivationClause> const &) noexcept(/*see below*/)
+ -> bool;
+
+ template<typename BaseType,
+ typename TagType,
+ auto DerivationClause,
+ typename = std::enable_if_t<DerivationClause(nt::Relational)>
+ auto constexpr operator>=(new_type<BaseType, TagType, DerivationClause> const &,
+ new_type<BaseType, TagType, DerivationClause> const &) noexcept(/*see below*/)
+ -> bool;
+
+ }
diff --git a/include/newtype/derivable.hpp b/include/newtype/derivable.hpp
new file mode 100644
index 0000000..4fec2b7
--- /dev/null
+++ b/include/newtype/derivable.hpp
@@ -0,0 +1,63 @@
+#ifndef NEWTYPE_DERIVABLE_HPP
+#define NEWTYPE_DERIVABLE_HPP
+
+#include "newtype/type.hpp"
+
+namespace nt
+{
+
+ template<typename NameTag>
+ using derivable = type<NameTag>;
+
+ /**
+ * A set of standard derivation tags
+ *
+ * This convenience namespace contains all standard derivation tags.
+ *
+ * @since 1.0.0
+ */
+ inline namespace derivables
+ {
+
+ /**
+ * A tag to enable derivation of arithmetic operators
+ *
+ * @since 1.0.0
+ */
+ auto constexpr Arithmetic = derivable<struct arithmetic_tag>{};
+
+ /**
+ * A tag to enable derivation of equality comparison operators
+ *
+ * @since 1.0.0
+ */
+ auto constexpr EqBase = derivable<struct eq_tag>{};
+
+ /**
+ * A tag to enable derivation of the implicit "conversion to base type" operator
+ *
+ * @note If this tag is not present in the derivation clause of any given nt::new_type, the type instance only supports explicit
+ * "conversion to base type"
+ * @since 1.0.0
+ */
+ auto constexpr ImplicitConversion = derivable<struct implicit_conversion_tag>{};
+
+ /**
+ * A tag to enable derivation of the stream input operator
+ *
+ * @since 1.0.0
+ */
+ auto constexpr Read = derivable<struct read_tag>{};
+
+ /**
+ * A tag to enable derivation of the stream output operator
+ *
+ * @since 1.0.0
+ */
+ auto constexpr Show = derivable<struct show_tag>{};
+
+ } // namespace derivables
+
+} // namespace nt
+
+#endif \ No newline at end of file
diff --git a/include/newtype/deriving.hpp b/include/newtype/deriving.hpp
new file mode 100644
index 0000000..431742f
--- /dev/null
+++ b/include/newtype/deriving.hpp
@@ -0,0 +1,123 @@
+#ifndef NEWTYPE_DERIVING_HPP
+#define NEWTYPE_DERIVING_HPP
+
+#include "newtype/derivable.hpp"
+
+#include <type_traits>
+
+namespace nt
+{
+
+ /**
+ * A @p deriving clause type
+ *
+ * @tparam DerivableTags A list of tag types defining a set of derivable features
+ */
+ template<typename... DerivableTags>
+ struct derivation_clause
+ {
+ constexpr derivation_clause(derivable<DerivableTags>...) noexcept
+ {
+ }
+
+ /**
+ * Check whether the derivation clause contains a given derivable
+ */
+ template<typename DerivableTag>
+ auto constexpr operator()(derivable<DerivableTag>) const noexcept -> bool
+ {
+ return (std::is_same_v<DerivableTags, DerivableTag> || ...);
+ }
+
+ /**
+ * Check whether the derivation clause contains all derivables in a given lists
+ */
+ template<typename DerivableTag, typename... RemainingDerivableTags>
+ auto constexpr operator()(derivable<DerivableTag>, derivable<RemainingDerivableTags>...) const noexcept -> bool
+ {
+ return (*this)(derivable<DerivableTag>{}) && (*this)(derivable<RemainingDerivableTags>{}...);
+ }
+
+ /**
+ * Check whether this derivation clause is less than an other derivation clause
+ *
+ * A derivation clause is considered less than an other derivation clause iff. its set of derivables is a strict subset of
+ * the set derivables of the other.
+ */
+ template<typename... OtherDerivableTags>
+ auto constexpr operator<(derivation_clause<OtherDerivableTags...> other) const noexcept -> bool
+ {
+ return (sizeof...(DerivableTags) < sizeof...(OtherDerivableTags)) && other(derivable<DerivableTags>{}...);
+ }
+
+ /**
+ * Check whether this derivation clause is greater than an other derivation clause
+ *
+ * A derivation clause is considered greater than an other derivation clause iff. its set of derivables is a strict superset
+ * of the set derivables of the other.
+ */
+ template<typename... OtherDerivableTags>
+ auto constexpr operator>(derivation_clause<OtherDerivableTags...> other) const noexcept -> bool
+ {
+ return other < *this;
+ }
+
+ /**
+ * Check whether this derivation clause is equal to an other derivation clause
+ *
+ * Two derivation clauses are considered equal if both have the same set of derivables
+ */
+ template<typename... OtherDerivableTags>
+ auto constexpr operator==(derivation_clause<OtherDerivableTags...> other) const noexcept -> bool
+ {
+ return sizeof...(DerivableTags) == sizeof...(OtherDerivableTags) && other(derivable<DerivableTags>{}...);
+ }
+
+ /**
+ * Check whether this derivation clause is not equal to an other derivation clause
+ *
+ * Two derivation clauses are considered not equal if neither has the same set of derivables as the other
+ */
+ template<typename... OtherDerivableTags>
+ auto constexpr operator!=(derivation_clause<OtherDerivableTags...> other) const noexcept -> bool
+ {
+ return !(*this == other);
+ }
+
+ /**
+ * Check whether this derivation clause is less-than or equal to an other derivation clause
+ *
+ * @see nt::distinct::operator==
+ * @see nt::distinct::operator<
+ */
+ template<typename... OtherDerivableTags>
+ auto constexpr operator<=(derivation_clause<OtherDerivableTags...> other) const noexcept -> bool
+ {
+ return *this < other || *this == other;
+ }
+
+ /**
+ * Check whether this derivation clause is greater-than or equal to an other derivation clause
+ *
+ * @see nt::distinct::operator==
+ * @see nt::distinct::operator<
+ */
+ template<typename... OtherDerivableTags>
+ auto constexpr operator>=(derivation_clause<OtherDerivableTags...> other) const noexcept -> bool
+ {
+ return *this > other || *this == other;
+ }
+ };
+
+ /**
+ * Create a new derivation clause with the given derivables
+ */
+ template<typename... DerivableTags>
+ auto constexpr deriving(derivable<DerivableTags>... request) -> derivation_clause<DerivableTags...>
+ {
+ return {request...};
+ }
+
+} // namespace nt
+
+#endif \ No newline at end of file
diff --git a/include/newtype/new_type.hpp b/include/newtype/new_type.hpp
new file mode 100644
index 0000000..9c1074c
--- /dev/null
+++ b/include/newtype/new_type.hpp
@@ -0,0 +1,124 @@
+#ifndef NEWTYPE_NEW_TYPE_HPP
+#define NEWTYPE_NEW_TYPE_HPP
+
+#include "newtype/derivable.hpp"
+#include "newtype/deriving.hpp"
+#include "newtype/type.hpp"
+
+#include <type_traits>
+
+namespace nt
+{
+
+ namespace impl
+ {
+
+ /**
+ * This class forms the a base type of nt::new_type and provides its storage
+ *
+ * This specialization enables the default constructor for cases in which @p BaseType is default constructible
+ *
+ * @tparam BaseType An existing type that shall aliased
+ * @tparam TagType A unique type to identify this nt::new_type
+ */
+ template<typename BaseType, typename TagType, bool = std::is_default_constructible_v<BaseType>>
+ struct new_type_storage
+ {
+ constexpr new_type_storage() noexcept(std::is_nothrow_default_constructible_v<BaseType>) = default;
+
+ constexpr explicit new_type_storage(BaseType const & value) noexcept(std::is_nothrow_copy_constructible_v<BaseType>)
+ : m_value{value}
+ {
+ }
+
+ constexpr explicit new_type_storage(BaseType && value) noexcept(std::is_nothrow_move_constructible_v<BaseType>)
+ : m_value{std::move(value)}
+ {
+ }
+
+ protected:
+ BaseType m_value{};
+ };
+
+ /**
+ * This class forms the a base type of nt::new_type and provides its storage
+ *
+ * This specialization explicitly deletes the default constructor for cases in which @p BaseType is not default
+ * constructible
+ *
+ * @tparam BaseType An existing type that shall aliased
+ * @tparam TagType A unique type to identify this nt::new_type
+ */
+ template<typename BaseType, typename TagType>
+ struct new_type_storage<BaseType, TagType, false>
+ {
+ constexpr new_type_storage() = delete;
+
+ constexpr explicit new_type_storage(BaseType const & value) noexcept(std::is_nothrow_copy_constructible_v<BaseType>)
+ : m_value{value}
+ {
+ }
+
+ constexpr explicit new_type_storage(BaseType && value) noexcept(std::is_nothrow_move_constructible_v<BaseType>)
+ : m_value{std::move(value)}
+ {
+ }
+
+ protected:
+ BaseType m_value;
+ };
+
+ } // namespace impl
+
+ /**
+ * @tparam BaseType An existing type that shall aliased
+ * @tparam TagType A unique type to identify this nt::new_type
+ * @tparam DervivationClause An nt::derivation_clause describing which features shall be automatically derived for the new type alias
+ */
+ template<typename BaseType, typename TagType, auto DerivationClause = deriving()>
+ class new_type : public impl::new_type_storage<BaseType, TagType>
+ {
+ using impl::new_type_storage<BaseType, TagType>::new_type_storage;
+
+ using base_type = BaseType;
+ using tag_type = TagType;
+
+ auto constexpr static derivations = DerivationClause;
+
+ public:
+ /**
+ * Retrieve the base type value contained in this @p new_type
+ */
+ auto constexpr decay() const noexcept -> BaseType
+ {
+ return this->m_value;
+ }
+
+ /**
+ * Convert this instance into the equivalent base type value
+ *
+ * @note This overload participates only in overload resolution if the derication clause of this @p new_type contains
+ * nt::ImplicitConversion
+ */
+ template<typename NewType = new_type, std::enable_if_t<NewType::derivations(nt::ImplicitConversion)> * = nullptr>
+ constexpr operator base_type() const noexcept(std::is_nothrow_copy_constructible_v<base_type>)
+ {
+ return decay();
+ }
+
+ /**
+ * Convert this instance into the equivalent base type value
+ *
+ * @note This overload participates only in overload resolution if the derication clause of this @p new_type does not contain
+ * nt::ImplicitConversion
+ */
+ template<typename NewType = new_type, std::enable_if_t<!NewType::derivations(nt::ImplicitConversion)> * = nullptr>
+ explicit constexpr operator base_type() const noexcept(std::is_nothrow_copy_constructible_v<base_type>)
+ {
+ return decay();
+ }
+ };
+
+} // namespace nt
+
+#endif \ No newline at end of file
diff --git a/include/newtype/type.hpp b/include/newtype/type.hpp
new file mode 100644
index 0000000..b1f8787
--- /dev/null
+++ b/include/newtype/type.hpp
@@ -0,0 +1,29 @@
+#ifndef NEWTYPE_TYPE_HPP
+#define NEWTYPE_TYPE_HPP
+
+namespace nt
+{
+
+ /**
+ * A convenience type to make values out of types
+ *
+ * @tparam Type The type to wrap
+ * @since 1.0.0
+ */
+ template<typename Type>
+ struct type final
+ {
+ };
+
+ /**
+ * A value to represent a type
+ *
+ * @tparam The type to represent
+ * @since 1.0.0
+ */
+ template<typename Type>
+ auto constexpr type_v = type<Type>{};
+
+} // namespace nt
+
+#endif \ No newline at end of file
diff --git a/test/include/derivation_clause_suite.hpp b/test/include/derivation_clause_suite.hpp
new file mode 100644
index 0000000..b5ef5c2
--- /dev/null
+++ b/test/include/derivation_clause_suite.hpp
@@ -0,0 +1,11 @@
+#ifndef NEWTYPE_TEST_DERIVATION_CLAUSE_SUITE_HPP
+#define NEWTYPE_TEST_DERIVATION_CLAUSE_SUITE_HPP
+
+#include <cute/cute_suite.h>
+
+#include <string>
+#include <utility>
+
+auto derivation_clause_suite() -> std::pair<cute::suite, std::string>;
+
+#endif \ No newline at end of file
diff --git a/test/include/kawaii.hpp b/test/include/kawaii.hpp
new file mode 100644
index 0000000..787f38a
--- /dev/null
+++ b/test/include/kawaii.hpp
@@ -0,0 +1,142 @@
+#ifndef NEWTYPE_TEST_KAWAII_HPP
+#define NEWTYPE_TEST_KAWAII_HPP
+
+#include <cute/cute_test.h>
+
+#include <algorithm>
+#include <array>
+#include <cctype>
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <utility>
+
+namespace nt::test
+{
+
+ namespace impl
+ {
+ auto constexpr prepositions = std::array{"a", "an", "and", "as", "at", "by", "for", "in", "of", "on", "or", "the", "to"};
+
+ auto constexpr type_names = std::array{"new_type", "derivation_clause"};
+
+ auto inline replace_template_argument_syntax(std::string const & name) -> std::string
+ {
+ using namespace std::string_literals;
+
+ auto template_argument_start = find(cbegin(name), cend(name), '<');
+
+ if (template_argument_start == cend(name))
+ {
+ return name;
+ }
+
+ auto replaced{""s};
+
+ copy(cbegin(name), template_argument_start, back_inserter(replaced));
+
+ replaced += " [ T = ";
+
+ auto template_argument_end = find(template_argument_start, cend(name), '>');
+
+ copy(template_argument_start + 1, template_argument_end, back_inserter(replaced));
+
+ return replaced + " ]";
+ }
+
+ auto inline is_prefix(std::string const & suspect, std::string const & of) -> bool
+ {
+ if (suspect.size() > of.size())
+ {
+ return false;
+ }
+ return equal(cbegin(suspect), cend(suspect), cbegin(of));
+ }
+
+ auto inline is_type_name_prefix(std::string const & suspect) -> bool
+ {
+ return std::any_of(cbegin(type_names), cend(type_names), [&](auto type_name) { return is_prefix(suspect, type_name); });
+ }
+
+ auto inline wordify(std::string const & name)
+ {
+ using namespace std::string_literals;
+ using namespace impl;
+
+ auto stream = std::stringstream{name};
+ auto output{""s};
+
+ while (stream)
+ {
+ auto current_char = static_cast<char>(stream.get());
+
+ if (current_char == '_')
+ {
+ if (stream.peek() != '_')
+ {
+ output += ' ';
+ }
+ else
+ {
+ output += current_char;
+ stream.ignore();
+ }
+ }
+ else if (current_char != EOF)
+ {
+ output += current_char;
+ }
+ }
+
+ return output;
+ }
+
+ auto inline titelize(std::string const & name) -> std::string
+ {
+ using namespace std::string_literals;
+
+ auto stream = std::istringstream{name};
+ auto buffer{""s};
+ auto word{""s};
+ auto first{false};
+
+ while (stream >> word && word != "[")
+ {
+ auto is_preposition = std::find(cbegin(prepositions), cend(prepositions), word) != cend(prepositions);
+ auto is_type_name = std::find(cbegin(type_names), cend(type_names), word) != cend(type_names);
+ if ((!is_preposition || buffer.empty()) && !is_type_name)
+ {
+ word.front() = std::toupper(word.front());
+ }
+ buffer += (first ? "" : " ") + word;
+ first = false;
+ }
+
+ auto rest{""s};
+
+ if (stream)
+ {
+ buffer += " " + word;
+ std::getline(stream, rest);
+ }
+
+ return buffer + rest;
+ }
+
+ } // namespace impl
+
+ auto inline go_full_kawaii(std::string kowai) -> std::string
+ {
+ using namespace impl;
+
+ auto template_free = replace_template_argument_syntax(kowai);
+ auto wordified = wordify(template_free);
+
+ return titelize(wordified);
+ }
+
+} // namespace nt::test
+
+#define KAWAII(name) cute::test((&name), nt::test::go_full_kawaii(#name))
+
+#endif \ No newline at end of file
diff --git a/test/include/new_type_constructor_suite.hpp b/test/include/new_type_constructor_suite.hpp
new file mode 100644
index 0000000..38c3cba
--- /dev/null
+++ b/test/include/new_type_constructor_suite.hpp
@@ -0,0 +1,11 @@
+#ifndef NEWTYPE_TEST_NEW_TYPE_SUITE_HPP
+#define NEWTYPE_TEST_NEW_TYPE_SUITE_HPP
+
+#include <cute/cute_suite.h>
+
+#include <string>
+#include <utility>
+
+auto new_type_constructor_suite() -> std::pair<cute::suite, std::string>;
+
+#endif \ No newline at end of file
diff --git a/test/src/derivation_clause_suite.cpp b/test/src/derivation_clause_suite.cpp
new file mode 100644
index 0000000..86a34ab
--- /dev/null
+++ b/test/src/derivation_clause_suite.cpp
@@ -0,0 +1,312 @@
+#include "derivation_clause_suite.hpp"
+
+#include "kawaii.hpp"
+#include "newtype/derivable.hpp"
+#include "newtype/deriving.hpp"
+
+#include <cute/cute.h>
+
+#include <string>
+
+inline namespace subset_tests
+{
+
+ auto an_empty_derivation_clause_does_not_contain_any_derivable() -> void
+ {
+ auto derivation_clause = nt::deriving();
+ ASSERT(!derivation_clause(nt::Show));
+ }
+
+ auto a_derivation_clause_containing_only_show_does_not_contain_eqbase() -> void
+ {
+ auto derivation_clause = deriving(nt::Show);
+ ASSERT(!derivation_clause(nt::EqBase));
+ }
+
+ auto a_derivation_clause_containing_only_show_does_contain_show() -> void
+ {
+ auto derivation_clause = deriving(nt::Show);
+ ASSERT(derivation_clause(nt::Show));
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_does_contain_show() -> void
+ {
+ auto derivation_clause = deriving(nt::Show, nt::EqBase);
+ ASSERT(derivation_clause(nt::Show));
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_does_contain_both_show_and_eqbase() -> void
+ {
+ auto derivation_clause = deriving(nt::Show, nt::EqBase);
+ ASSERT(derivation_clause(nt::Show, nt::EqBase));
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_does_not_contain_arithmetic() -> void
+ {
+ auto derivation_clause = deriving(nt::Show, nt::EqBase);
+ ASSERT(!derivation_clause(nt::Arithmetic));
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_does_not_contain_both_show_and_arithmetic() -> void
+ {
+ auto derivation_clause = deriving(nt::Show, nt::EqBase);
+ ASSERT(!derivation_clause(nt::Show, nt::Arithmetic));
+ }
+
+} // namespace subset_tests
+
+inline namespace less_than_tests
+{
+
+ auto a_derivation_clause_containing_only_show_compares_less_than_one_containing_show_and_eqbase() -> void
+ {
+ auto only_show = deriving(nt::Show);
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT(only_show < show_and_eqbase);
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_does_not_compare_less_than_one_containing_show_and_eqbase() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto also_show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT(!(show_and_eqbase < also_show_and_eqbase));
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_does_not_compare_less_than_one_containing_eqbase_and_show() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto eqbase_and_show = deriving(nt::EqBase, nt::Show);
+
+ ASSERT(!(show_and_eqbase < eqbase_and_show));
+ }
+
+ auto a_derivation_clause_containing_only_show_does_not_compare_less_than_one_containing_only_eqbase() -> void
+ {
+ auto show = deriving(nt::Show);
+ auto eqbase = deriving(nt::EqBase);
+
+ ASSERT(!(show < eqbase));
+ }
+
+} // namespace less_than_tests
+
+inline namespace greater_than_tests
+{
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_compares_greater_than_one_containing_only_show() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto show = deriving(nt::Show);
+
+ ASSERT(show_and_eqbase > show);
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_does_not_compare_greater_than_one_containing_only_show_and_eqbase() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto also_show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT(!(show_and_eqbase > also_show_and_eqbase));
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_does_not_compare_greater_than_one_containing_only_eqbase_and_show() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto eqbase_and_show = deriving(nt::EqBase, nt::Show);
+
+ ASSERT(!(show_and_eqbase > eqbase_and_show));
+ }
+
+ auto a_derivation_clause_containing_only_show_does_not_compare_greater_than_one_containing_only_eqbase() -> void
+ {
+ auto show = deriving(nt::Show);
+ auto eqbase = deriving(nt::EqBase);
+
+ ASSERT(!(show > eqbase));
+ }
+
+} // namespace greater_than_tests
+
+inline namespace eqbaseuals_tests
+{
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_is_eqbaseual_to_one_containing_only_show_and_eqbase() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto also_show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT_EQUAL(show_and_eqbase, also_show_and_eqbase);
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_is_eqbaseual_to_one_containing_only_eqbase_and_show() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto eqbase_and_show = deriving(nt::EqBase, nt::Show);
+
+ ASSERT_EQUAL(show_and_eqbase, eqbase_and_show);
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_is_not_eqbaseual_to_one_containing_only_arithmetic() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto arithmetic = deriving(nt::Arithmetic);
+
+ ASSERT(!(show_and_eqbase == arithmetic));
+ }
+
+ auto a_derivation_clause_containing_only_show_is_not_eqbaseual_to_one_containing_only_arithmetic() -> void
+ {
+ auto show = deriving(nt::Show);
+ auto arithmetic = deriving(nt::Arithmetic);
+
+ ASSERT(!(show == arithmetic));
+ }
+
+} // namespace eqbaseuals_tests
+
+inline namespace not_eqbaseuals_tests
+{
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_is_not_not_eqbaseual_to_one_containing_only_show_and_eqbase() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto also_show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT(!(show_and_eqbase != also_show_and_eqbase));
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_is_not_not_eqbaseual_to_one_containing_only_eqbase_and_show() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto eqbase_and_show = deriving(nt::EqBase, nt::Show);
+
+ ASSERT(!(show_and_eqbase != eqbase_and_show));
+ }
+
+ auto a_derivation_clause_containing_only_eqbase_and_show_is_not_eqbaseual_to_one_containing_only_arithmetic() -> void
+ {
+ auto eqbase_and_show = deriving(nt::EqBase, nt::Show);
+ auto arithmetic = deriving(nt::Arithmetic);
+
+ ASSERT(eqbase_and_show != arithmetic);
+ }
+
+ auto a_derivation_clause_containing_only_eqbase_is_not_eqbaseual_to_one_containing_only_arithmetic() -> void
+ {
+ auto eqbase = deriving(nt::EqBase);
+ auto arithmetic = deriving(nt::Arithmetic);
+
+ ASSERT(eqbase != arithmetic);
+ }
+
+} // namespace not_eqbaseuals_tests
+
+inline namespace less_than_or_eqbaseual_tests
+{
+
+ auto a_derivation_clause_containing_only_show_is_less_than_or_eqbaseual_to_one_containing_only_show_and_eqbase() -> void
+ {
+ auto only_show = deriving(nt::Show);
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT(only_show <= show_and_eqbase);
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_is_less_than_or_eqbaseual_to_one_containing_only_show_and_eqbase() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto also_show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT(show_and_eqbase <= also_show_and_eqbase);
+ }
+
+ auto a_derivation_clause_containing_only_arithmetic_is_neither_less_than_nor_eqbaseual_to_on_containing_only_show_and_eqbase() -> void
+ {
+ auto arithmetic = deriving(nt::Arithmetic);
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT(!(arithmetic <= show_and_eqbase));
+ }
+
+} // namespace less_than_or_eqbaseual_tests
+
+inline namespace greater_than_or_eqbaseual_tests
+{
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_is_greater_than_or_eqbaseual_to_one_containing_only_show() -> void
+ {
+ auto only_show = deriving(nt::Show);
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT(show_and_eqbase >= only_show);
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_is_greater_than_or_eqbaseual_to_one_containing_only_show_and_eqbase() -> void
+ {
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+ auto also_show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT(show_and_eqbase >= also_show_and_eqbase);
+ }
+
+ auto a_derivation_clause_containing_only_show_and_eqbase_is_neither_greater_than_nor_eqbaseual_to_on_containing_only_arithmetic() -> void
+ {
+ auto arithmetic = deriving(nt::Arithmetic);
+ auto show_and_eqbase = deriving(nt::Show, nt::EqBase);
+
+ ASSERT(!(show_and_eqbase >= arithmetic));
+ }
+
+} // namespace greater_than_or_eqbaseual_tests
+
+auto derivation_clause_suite() -> std::pair<cute::suite, std::string>
+{
+ return {
+ {
+ /// Subset tests
+ KAWAII(an_empty_derivation_clause_does_not_contain_any_derivable),
+ KAWAII(a_derivation_clause_containing_only_show_does_not_contain_eqbase),
+ KAWAII(a_derivation_clause_containing_only_show_does_contain_show),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_does_contain_show),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_does_contain_both_show_and_eqbase),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_does_not_contain_arithmetic),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_does_not_contain_both_show_and_arithmetic),
+
+ /// Less-than tests
+ KAWAII(a_derivation_clause_containing_only_show_compares_less_than_one_containing_show_and_eqbase),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_does_not_compare_less_than_one_containing_show_and_eqbase),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_does_not_compare_less_than_one_containing_eqbase_and_show),
+ KAWAII(a_derivation_clause_containing_only_show_does_not_compare_less_than_one_containing_only_eqbase),
+
+ /// Greater-than tests
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_compares_greater_than_one_containing_only_show),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_does_not_compare_greater_than_one_containing_only_show_and_eqbase),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_does_not_compare_greater_than_one_containing_only_eqbase_and_show),
+ KAWAII(a_derivation_clause_containing_only_show_does_not_compare_greater_than_one_containing_only_eqbase),
+
+ /// Equals tests
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_is_eqbaseual_to_one_containing_only_show_and_eqbase),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_is_eqbaseual_to_one_containing_only_eqbase_and_show),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_is_not_eqbaseual_to_one_containing_only_arithmetic),
+ KAWAII(a_derivation_clause_containing_only_show_is_not_eqbaseual_to_one_containing_only_arithmetic),
+
+ /// Not-Equals tests
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_is_not_not_eqbaseual_to_one_containing_only_show_and_eqbase),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_is_not_not_eqbaseual_to_one_containing_only_eqbase_and_show),
+ KAWAII(a_derivation_clause_containing_only_eqbase_and_show_is_not_eqbaseual_to_one_containing_only_arithmetic),
+ KAWAII(a_derivation_clause_containing_only_eqbase_is_not_eqbaseual_to_one_containing_only_arithmetic),
+
+ /// Less-than or Equals tests
+ KAWAII(a_derivation_clause_containing_only_show_is_less_than_or_eqbaseual_to_one_containing_only_show_and_eqbase),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_is_less_than_or_eqbaseual_to_one_containing_only_show_and_eqbase),
+ KAWAII(a_derivation_clause_containing_only_arithmetic_is_neither_less_than_nor_eqbaseual_to_on_containing_only_show_and_eqbase),
+
+ /// Greater-than or Equals tests
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_is_greater_than_or_eqbaseual_to_one_containing_only_show),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_is_greater_than_or_eqbaseual_to_one_containing_only_show_and_eqbase),
+ KAWAII(a_derivation_clause_containing_only_show_and_eqbase_is_neither_greater_than_nor_eqbaseual_to_on_containing_only_arithmetic),
+ },
+ "Derivation Clause Tests"};
+} \ No newline at end of file
diff --git a/test/src/driver.cpp b/test/src/driver.cpp
new file mode 100644
index 0000000..22e1ab9
--- /dev/null
+++ b/test/src/driver.cpp
@@ -0,0 +1,89 @@
+#include "derivation_clause_suite.hpp"
+#include "new_type_constructor_suite.hpp"
+
+#include <cute/cute.h>
+#include <cute/cute_runner.h>
+#include <cute/tap_listener.h>
+
+#include <lyra/arg.hpp>
+#include <lyra/cli_parser.hpp>
+#include <lyra/help.hpp>
+#include <lyra/opt.hpp>
+
+#include <algorithm>
+#include <cstdlib>
+#include <iostream>
+#include <iterator>
+#include <numeric>
+#include <string>
+#include <utility>
+#include <vector>
+
+using suite_list = std::vector<std::pair<cute::suite, std::string>>;
+
+auto get_test_selectors(suite_list const & suites) -> std::vector<std::string>
+{
+ auto selectors = std::vector<std::string>{};
+
+ for_each(cbegin(suites), cend(suites), [&](auto descriptor) {
+ auto const & [suite, name] = descriptor;
+ transform(cbegin(suite), cend(suite), std::back_inserter(selectors), [&, name = name](auto test) {
+ return name + "#" + test.name();
+ });
+ });
+
+ return selectors;
+}
+
+auto do_run_tests(suite_list const & suites, int argc, char ** argv) -> bool
+{
+ auto listener = cute::tap_listener<>{};
+ auto runner = cute::makeRunner(listener, argc, argv);
+
+ return accumulate(cbegin(suites), cend(suites), true, [&](auto accumulator, auto const & descriptor) {
+ auto const & [suite, name] = descriptor;
+ return accumulator && runner(suite, name.c_str());
+ });
+}
+
+int main(int argc, char ** argv)
+{
+ auto suites = std::vector{
+ derivation_clause_suite(),
+ new_type_constructor_suite(),
+ };
+
+ auto selectors = get_test_selectors(suites);
+
+ auto list_tests{false};
+ auto list_suites{false};
+ auto show_help{false};
+ auto selected_tests = std::vector<std::string>{};
+
+ auto cli = lyra::cli_parser() | //
+ lyra::opt(list_tests)["-t"]["--tests"]("List all registered tests") | //
+ lyra::opt(list_suites)["-s"]["--suites"]("List all registered suites") | //
+ lyra::arg(selected_tests, "test selector")("A pattern to select a specific test") | //
+ lyra::help(show_help);
+ auto result = cli.parse({argc, argv});
+
+ if (list_tests)
+ {
+ copy(cbegin(selectors), cend(selectors), std::ostream_iterator<std::string>{std::cout, "\n"});
+ }
+ if (list_suites)
+ {
+ transform(cbegin(suites), cend(suites), std::ostream_iterator<std::string>{std::cout, "\n"}, [](auto descriptor) {
+ auto const & [_, name] = descriptor;
+ return name;
+ });
+ }
+ else if (!result || show_help)
+ {
+ std::cout << cli;
+ }
+ else
+ {
+ return do_run_tests(suites, argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE;
+ }
+} \ No newline at end of file
diff --git a/test/src/new_type_constructor_suite.cpp b/test/src/new_type_constructor_suite.cpp
new file mode 100644
index 0000000..fa4c0b7
--- /dev/null
+++ b/test/src/new_type_constructor_suite.cpp
@@ -0,0 +1,68 @@
+#include "new_type_constructor_suite.hpp"
+
+#include "kawaii.hpp"
+#include "newtype/derivable.hpp"
+#include "newtype/new_type.hpp"
+
+#include <cute/cute.h>
+
+#include <type_traits>
+
+inline namespace constructor_tests
+{
+
+ struct not_default_constructible
+ {
+ not_default_constructible() = delete;
+ };
+
+ auto a_new__type_based_on_a_type_that_is_default_constructible_is_default_constructible_too() -> void
+ {
+ using nt_float = nt::new_type<float, struct nt_float_tag>;
+
+ ASSERT(std::is_default_constructible_v<nt_float>);
+ }
+
+ auto a_new__type_based_on_a_type_that_is_not_default_constructible_is_not_default_constructible_too() -> void
+ {
+ using nt_not_default_constructible = nt::new_type<not_default_constructible, struct nt_not_default_constructible_tag>;
+
+ ASSERT(!std::is_default_constructible_v<nt_not_default_constructible>);
+ }
+
+ template<typename OldType>
+ auto a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type() -> void
+ {
+ using nt_old = nt::new_type<OldType, struct nt_old_tag>;
+
+ ASSERT((std::is_constructible_v<nt_old, OldType>));
+ }
+
+} // namespace constructor_tests
+
+auto new_type_constructor_suite() -> std::pair<cute::suite, std::string>
+{
+ return {{
+ KAWAII(a_new__type_based_on_a_type_that_is_default_constructible_is_default_constructible_too),
+ KAWAII(a_new__type_based_on_a_type_that_is_not_default_constructible_is_not_default_constructible_too),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<bool>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<char>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<unsigned char>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<signed char>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<wchar_t>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<char16_t>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<char32_t>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<short>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<unsigned short>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<int>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<unsigned int>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<long>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<unsigned long>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<long long>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<unsigned long long>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<float>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<double>),
+ KAWAII(a_new__type_based_on_a_fundamental_type_can_be_constructed_with_a_value_of_fundamental_type<long double>),
+ },
+ "new_type Constructor Tests"};
+} \ No newline at end of file