From 6370b3fc6ffb973cc272f18d18db521c02fea0f1 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sun, 22 Dec 2019 19:47:21 +0100 Subject: newtype: initial commit --- .clang-format | 111 ++++++ .gitignore | 1 + .vscode/c_cpp_properties.json | 16 + .vscode/settings.json | 72 ++++ CMakeLists.txt | 145 ++++++++ LICENSE | 26 ++ cmake/Modules/Conan.cmake | 554 ++++++++++++++++++++++++++++ cmake/Modules/DiscoverTests.cmake | 55 +++ cmake/Modules/DiscoverTestsImpl.cmake | 39 ++ cmake/config.cmake.in | 4 + conanfile.py | 33 ++ doc/Pipfile | 14 + doc/Pipfile.lock | 232 ++++++++++++ doc/src/conf.py | 44 +++ doc/src/index.rst | 154 ++++++++ include/newtype/derivable.hpp | 63 ++++ include/newtype/deriving.hpp | 123 ++++++ include/newtype/new_type.hpp | 124 +++++++ include/newtype/type.hpp | 29 ++ test/include/derivation_clause_suite.hpp | 11 + test/include/kawaii.hpp | 142 +++++++ test/include/new_type_constructor_suite.hpp | 11 + test/src/derivation_clause_suite.cpp | 312 ++++++++++++++++ test/src/driver.cpp | 89 +++++ test/src/new_type_constructor_suite.cpp | 68 ++++ 25 files changed, 2472 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/settings.json create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 cmake/Modules/Conan.cmake create mode 100644 cmake/Modules/DiscoverTests.cmake create mode 100644 cmake/Modules/DiscoverTestsImpl.cmake create mode 100644 cmake/config.cmake.in create mode 100644 conanfile.py create mode 100644 doc/Pipfile create mode 100644 doc/Pipfile.lock create mode 100644 doc/src/conf.py create mode 100644 doc/src/index.rst create mode 100644 include/newtype/derivable.hpp create mode 100644 include/newtype/deriving.hpp create mode 100644 include/newtype/new_type.hpp create mode 100644 include/newtype/type.hpp create mode 100644 test/include/derivation_clause_suite.hpp create mode 100644 test/include/kawaii.hpp create mode 100644 test/include/new_type_constructor_suite.hpp create mode 100644 test/src/derivation_clause_suite.cpp create mode 100644 test/src/driver.cpp create mode 100644 test/src/new_type_constructor_suite.cpp 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: '' + 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 + $ + $ +) + +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 , 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 " + 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=$" + "-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 "], + 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 \ + 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 + + using width = nt::new_type; + +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 + + using width = nt::new_type; + +Synopsis +~~~~~~~~ + +.. code-block:: + + namespace nt + { + template + class new_type + { + public: + + // Constructors + + constexpr explicit new_type() noexcept(std::is_nothrow_default_constructible_v) = /*see below*/; + + constexpr explicit new_type(BaseType const &) noexcept(std::is_nothrow_copy_constructible_v); + + constexpr explicit new_type(BaseType &&) noexcept(std::is_nothrow_move_constructible_v); + + // Assignment operators + + auto constexpr operator=(new_type const &) noexcept(std::is_nothrow_copy_assignable_v) -> new_type & = /*see below*/ + + auto constexpr operator=(new_type &&) noexcept(std::is_nothrow_move_assignable_v) -> 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; + + auto constexpr operator->() const noexcept -> std::enable_if_t; + + private: + BaseType m_value; + }; + + // Equality comparison operators + + template + auto constexpr operator==(new_type const &, + new_type const &) noexcept(/*see below*/) + -> bool; + + template + auto constexpr operator!=(new_type const &, + new_type const &) noexcept(/*see below*/) + -> bool; + + // Relational operators + + template + auto constexpr operator<(new_type const &, + new_type const &) noexcept(/*see below*/) + -> bool; + + template + auto constexpr operator>(new_type const &, + new_type const &) noexcept(/*see below*/) + -> bool; + + template + auto constexpr operator<=(new_type const &, + new_type const &) noexcept(/*see below*/) + -> bool; + + template + auto constexpr operator>=(new_type const &, + new_type 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 + using derivable = type; + + /** + * 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{}; + + /** + * A tag to enable derivation of equality comparison operators + * + * @since 1.0.0 + */ + auto constexpr EqBase = derivable{}; + + /** + * 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{}; + + /** + * A tag to enable derivation of the stream input operator + * + * @since 1.0.0 + */ + auto constexpr Read = derivable{}; + + /** + * A tag to enable derivation of the stream output operator + * + * @since 1.0.0 + */ + auto constexpr Show = derivable{}; + + } // 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 + +namespace nt +{ + + /** + * A @p deriving clause type + * + * @tparam DerivableTags A list of tag types defining a set of derivable features + */ + template + struct derivation_clause + { + constexpr derivation_clause(derivable...) noexcept + { + } + + /** + * Check whether the derivation clause contains a given derivable + */ + template + auto constexpr operator()(derivable) const noexcept -> bool + { + return (std::is_same_v || ...); + } + + /** + * Check whether the derivation clause contains all derivables in a given lists + */ + template + auto constexpr operator()(derivable, derivable...) const noexcept -> bool + { + return (*this)(derivable{}) && (*this)(derivable{}...); + } + + /** + * 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 + auto constexpr operator<(derivation_clause other) const noexcept -> bool + { + return (sizeof...(DerivableTags) < sizeof...(OtherDerivableTags)) && other(derivable{}...); + } + + /** + * 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 + auto constexpr operator>(derivation_clause other) const noexcept -> bool + { + return other < *this; + } + + /** + * Check whether this d