diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index cafa8d43..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "cmake/common"] - path = cmake/common - url = https://github.com/mpusz/cmake-scripts.git diff --git a/CMakeLists.txt b/CMakeLists.txt index ce7bf7c0..b18a30a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ project(mp-units) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") # include common tools and workarounds -include(common/cmake/scripts) +include(common/scripts) # use Conan configuration if available conan_init(cmake) @@ -40,7 +40,7 @@ conan_init(cmake) add_subdirectory(src) # set restrictive compilation warnings -set_warnings(units) +set_warnings(mp-units) # add unit tests enable_testing() diff --git a/build.py b/build.py index f060a37f..cffae86b 100644 --- a/build.py +++ b/build.py @@ -9,7 +9,6 @@ if __name__ == "__main__": # dependencies remotes = [ - ("https://api.bintray.com/conan/bincrafters/public-conan", True, "bincrafters"), ("https://api.bintray.com/conan/twonington/public-conan", True, "linear-algebra") ], build_policy = ["mp-units", "outdated"], diff --git a/cmake/common b/cmake/common deleted file mode 160000 index eb5fbd40..00000000 --- a/cmake/common +++ /dev/null @@ -1 +0,0 @@ -Subproject commit eb5fbd40d1cb765c5a8bf50866db86082c72ef73 diff --git a/cmake/common/conan.cmake b/cmake/common/conan.cmake new file mode 100644 index 00000000..6219e05c --- /dev/null +++ b/cmake/common/conan.cmake @@ -0,0 +1,46 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Mateusz Pusz +# +# 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. + + +# Helper to use conan generated configuration if provided +macro(conan_init generator) + if(${generator} STREQUAL "cmake_paths") + include(${CMAKE_BINARY_DIR}/conan_paths.cmake OPTIONAL) + elseif(${generator} STREQUAL "cmake") + if(NOT DEFINED CONAN_PACKAGE_NAME) + if(EXISTS ${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) + include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) + conan_basic_setup(TARGETS) + endif() + endif() + else() + message(FATAL_ERROR "Unknown Conan generator: ${generator}") + endif() +endmacro() + + +# Checks if conan installed testing dependencies +macro(conan_check_testing test_framework) + if(NOT TARGET CONAN_PKG::${test_framework}) + message(FATAL_ERROR "CONAN_PKG::${test_framework} not found!\nPlease run `conan install` with `-e CONAN_RUN_TESTS=True`.") + endif() +endmacro() diff --git a/cmake/common/install.cmake b/cmake/common/install.cmake new file mode 100644 index 00000000..49f9c7e1 --- /dev/null +++ b/cmake/common/install.cmake @@ -0,0 +1,81 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Mateusz Pusz +# +# 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. + + +# A path to scripts directory +set(CMAKE_SCRIPTS_ROOT ${CMAKE_CURRENT_LIST_DIR}) + +# Install provided targets +function(install_targets) + if(NOT CMAKE_INSTALL_BINDIR) + set(CMAKE_INSTALL_BINDIR "bin") + endif() + if(NOT CMAKE_INSTALL_LIBDIR) + set(CMAKE_INSTALL_LIBDIR "lib") + endif() + if(NOT CMAKE_INSTALL_INCLUDEDIR) + set(CMAKE_INSTALL_INCLUDEDIR "include") + endif() + install(TARGETS ${ARGN} + EXPORT ${PROJECT_NAME}Targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # TODO Remove when CMAKE 3.14 + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # TODO Remove when CMAKE 3.14 + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # TODO Remove when CMAKE 3.14 + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ) +endfunction() + +# Generate configuration files and install the package +function(configure_and_install configure_in_file_path namespace version_compare_rules) + if(NOT APPLE) + set(CMAKE_INSTALL_RPATH ${ORIGIN}) + endif() + + # prepare installation files + include(CMakePackageConfigHelpers) + set(ConfigPackageSource ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}) + set(ConfigPackageDestination lib/cmake/${PROJECT_NAME}) + write_basic_package_version_file( + ${ConfigPackageSource}/${PROJECT_NAME}-config-version.cmake + COMPATIBILITY ${version_compare_rules}) + configure_package_config_file(${configure_in_file_path} + ${ConfigPackageSource}/${PROJECT_NAME}-config.cmake + INSTALL_DESTINATION ${ConfigPackageDestination}) + + # install library + install(EXPORT ${PROJECT_NAME}Targets + DESTINATION ${ConfigPackageDestination} + FILE ${PROJECT_NAME}-targets.cmake + NAMESPACE ${namespace}:: + COMPONENT Devel) + install(FILES + "${ConfigPackageSource}/${PROJECT_NAME}-config.cmake" + "${ConfigPackageSource}/${PROJECT_NAME}-config-version.cmake" + DESTINATION ${ConfigPackageDestination} + COMPONENT Devel) + + # local package + export(EXPORT ${PROJECT_NAME}Targets + NAMESPACE ${namespace}:: + FILE ${ConfigPackageSource}/${PROJECT_NAME}-targets.cmake) +endfunction() diff --git a/cmake/common/scripts.cmake b/cmake/common/scripts.cmake new file mode 100644 index 00000000..b3ea4224 --- /dev/null +++ b/cmake/common/scripts.cmake @@ -0,0 +1,28 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Mateusz Pusz +# +# 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. + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") + +include(conan) +include(install) +include(static_analysis) +include(warnings) diff --git a/cmake/common/simple-config.cmake.in b/cmake/common/simple-config.cmake.in new file mode 100644 index 00000000..f40ebfa8 --- /dev/null +++ b/cmake/common/simple-config.cmake.in @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake") diff --git a/cmake/common/static_analysis.cmake b/cmake/common/static_analysis.cmake new file mode 100644 index 00000000..99c2c9f2 --- /dev/null +++ b/cmake/common/static_analysis.cmake @@ -0,0 +1,44 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Mateusz Pusz +# +# 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. + + +macro(enable_clang_tidy) + find_program(clang_tidy_cmd NAMES "clang-tidy") + if(NOT clang_tidy_cmd) + message(WARNING "clang-tidy not found!") + else() + if(NOT EXISTS "${CMAKE_SOURCE_DIR}/.clang-tidy") + message(FATAL_ERROR "'${CMAKE_SOURCE_DIR}/.clang-tidy' configuration file not found!") + endif() + set(CMAKE_CXX_CLANG_TIDY "${clang_tidy_cmd}") + endif() +endmacro() + + +macro(enable_iwyu) + find_program(iwyu_cmd NAMES "include-what-you-use") + if(NOT iwyu_cmd) + message(WARNING "include-what-you-use not found!") + else() + set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "${iwyu_cmd}") + endif() +endmacro() diff --git a/cmake/common/warnings.cmake b/cmake/common/warnings.cmake new file mode 100644 index 00000000..c60a5c8c --- /dev/null +++ b/cmake/common/warnings.cmake @@ -0,0 +1,110 @@ +# The MIT License (MIT) +# +# Copyright (c) 2016 Mateusz Pusz +# +# 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. + +# Based on https://github.com/lefticus/cpp_starter_project/blob/master/cmake/CompilerWarnings.cmake + +# Configure compiler warning level +function(set_warnings target) + option(WARNINGS_AS_ERRORS "Treat compiler warnings as errors" TRUE) + + if(NOT TARGET ${target}) + message(FATAL_ERROR "'${target}' is not a CMake target") + endif() + + set(MSVC_WARNINGS + /W4 # Baseline reasonable warnings + /w14062 # enumerator 'identifier' in a switch of enum 'enumeration' is not handled + /w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data + /w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data + /w14263 # 'function': member function does not override any base class virtual member function + /w14265 # 'classname': class has virtual functions, but destructor is not + # virtual instances of this class may not be destructed correctly + /w14266 # 'function': no override available for virtual member function from base 'type'; function is hidden + /w14287 # 'operator': unsigned/negative constant mismatch + /we4289 # nonstandard extension used: 'variable': loop control variable + # declared in the for-loop is used outside the for-loop scope + /w14296 # 'operator': expression is always 'boolean_value' + /w14311 # 'variable': pointer truncation from 'type1' to 'type2' + /w14545 # expression before comma evaluates to a function which is missing + # an argument list + /w14546 # function call before comma missing argument list + /w14547 # 'operator': operator before comma has no effect; expected + # operator with side-effect + /w14549 # 'operator': operator before comma has no effect; did you intend + # 'operator'? + /w14555 # expression has no effect; expected expression with side- effect + /w14619 # pragma warning: there is no warning number 'number' + /w14640 # Enable warning on thread un-safe static member initialization + /w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may + # cause unexpected runtime behavior. + /w14905 # wide string literal cast to 'LPSTR' + /w14906 # string literal cast to 'LPWSTR' + /w14928 # illegal copy-initialization; more than one user-defined + # conversion has been implicitly applied + /permissive- # standards conformance mode for MSVC compiler. + ) + + set(CLANG_WARNINGS + -Wall + -Wextra # reasonable and standard + -Wpedantic # warn if non-standard C++ is used + -Wshadow # warn the user if a variable declaration shadows one from a parent context + -Wnon-virtual-dtor # warn the user if a class with virtual functions has a + # non-virtual destructor. This helps catch hard to + # track down memory errors + -Wold-style-cast # warn for c-style casts + -Wcast-align # warn for potential performance problem casts + -Wunused # warn on anything being unused + -Woverloaded-virtual # warn if you overload (not override) a virtual function + -Wcast-qual # warn on dropping const or volatile qualifiers + -Wconversion # warn on type conversions that may lose data + -Wsign-conversion # warn on sign conversions + -Wnull-dereference # warn if a null dereference is detected + -Wdouble-promotion # warn if float is implicit promoted to double + -Wformat=2 # warn on security issues around functions that format output (ie printf) + ) + + if(WARNINGS_AS_ERRORS) + set(CLANG_WARNINGS ${CLANG_WARNINGS} -Werror) + set(MSVC_WARNINGS ${MSVC_WARNINGS} /WX) + endif() + + set(GCC_WARNINGS + ${CLANG_WARNINGS} + -Wmisleading-indentation # warn if indentation implies blocks where blocks do not exist + -Wduplicated-cond # warn if if / else chain has duplicated conditions + -Wduplicated-branches # warn if if / else branches have duplicated code + -Wlogical-op # warn about logical operations being used where bitwise were probably wanted + ) + + if(MSVC) + string(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" PARENT_SCOPE) + target_compile_options(${target} INTERFACE ${MSVC_WARNINGS}) + elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + target_compile_options(${target} INTERFACE ${CLANG_WARNINGS}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(${target} INTERFACE ${GCC_WARNINGS}) + else() + message(AUTHOR_WARNING "No compiler warnings set for '${CMAKE_CXX_COMPILER_ID}' compiler.") + endif() +endfunction() diff --git a/conanfile.py b/conanfile.py index 21241a87..a757b5dd 100644 --- a/conanfile.py +++ b/conanfile.py @@ -21,7 +21,7 @@ # SOFTWARE. from conans import ConanFile, CMake, tools -from conans.tools import Version +from conans.tools import Version, check_min_cppstd from conans.errors import ConanInvalidConfiguration import re @@ -38,17 +38,18 @@ def get_version(): class UnitsConan(ConanFile): name = "mp-units" version = get_version() - author = "Mateusz Pusz" - license = "https://github.com/mpusz/units/blob/master/LICENSE.md" - url = "https://github.com/mpusz/units" + homepage = "https://github.com/mpusz/units" description = "Physical Units library for C++" - exports = ["LICENSE.md"] - exports_sources = ["docs/*", "src/*", "test/*", "cmake/*", "example/*","CMakeLists.txt"] + topics = ("units", "dimensions", "quantities", "dimensional-analysis", "physical-quantities", "physical-units", "system-of-units", "cpp23", "cpp20", "library", "quantity-manipulation") + license = "MIT" + url = "https://github.com/mpusz/units" settings = "os", "compiler", "build_type", "arch" requires = ( "fmt/7.0.3", "ms-gsl/3.1.0" ) + exports = ["LICENSE.md"] + exports_sources = ["docs/*", "src/*", "test/*", "cmake/*", "example/*","CMakeLists.txt"] # scm = { # "type": "git", # "url": "auto", @@ -61,6 +62,19 @@ class UnitsConan(ConanFile): def _run_tests(self): return tools.get_env("CONAN_RUN_TESTS", False) + def _validate_compiler_settings(self): + compiler = self.settings.compiler + version = Version(self.settings.compiler.version) + if compiler == "gcc": + if version < "9.3": + raise ConanInvalidConfiguration("mp-units requires at least g++-9.3") + elif compiler == "Visual Studio": + if version < "16": + raise ConanInvalidConfiguration("mp-units requires at least MSVC 16") + else: + raise ConanInvalidConfiguration("mp-units is supported only by gcc and Visual Studio so far") + check_min_cppstd(self, "20") + def _configure_cmake(self, folder="src"): cmake = CMake(self) if self._run_tests: @@ -72,15 +86,7 @@ class UnitsConan(ConanFile): return cmake def configure(self): - if self.settings.compiler != "gcc" and self.settings.compiler != "Visual Studio": # and self.settings.compiler != "clang": - raise ConanInvalidConfiguration("Library works only with gcc and Visual Studio so far") # and clang") - if self.settings.compiler == "gcc" and Version(self.settings.compiler.version) < "9": - raise ConanInvalidConfiguration("Library requires at least g++-9") - if self.settings.compiler == "Visual Studio" and Version(self.settings.compiler.version) < "16": - raise ConanInvalidConfiguration("Library requires at least Visual Studio 2019") - if self.settings.compiler == "clang" and Version(self.settings.compiler.version) < "11": - raise ConanInvalidConfiguration("Library requires at least clang++-11") - tools.check_min_cppstd(self, "20") + self._validate_compiler_settings() def requirements(self): if ((self.settings.compiler == "gcc" and Version(self.settings.compiler.version) < "10") or @@ -104,18 +110,25 @@ class UnitsConan(ConanFile): cmake = self._configure_cmake() cmake.install() - def package_info(self): - if self.settings.compiler == "gcc": - self.cpp_info.cxxflags = [ - "-Wno-literal-suffix", - "-Wno-non-template-friend", - ] - if Version(self.settings.compiler.version) < "10": - self.cpp_info.cxxflags.extend([ - "-fconcepts" - ]) - def package_id(self): self.info.settings.clear() self.info.settings.compiler = self.settings.compiler self.info.settings.compiler.version = self.settings.compiler.version + + def package_info(self): + compiler = self.settings.compiler + version = Version(self.settings.compiler.version) + if compiler == "gcc": + self.cpp_info.cxxflags = [ + "-Wno-literal-suffix", + "-Wno-non-template-friend", + ] + if version < "10": + self.cpp_info.cxxflags.extend([ + "-fconcepts" + ]) + elif compiler == "Visual Studio": + self.cpp_info.cxxflags = [ + "/utf-8", + "/wd4455" + ] diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 0c165c6a..28570bd7 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -8,6 +8,7 @@ - catch2 updated to 2.13.0 - doxygen updated to 1.8.18 - ms-gsl 3.1.0 dependency added + - MSVC 16.7 support added - Added angle as SI base dimension (thanks [@kwikius](https://github.com/kwikius)) - Added STL random number distribution wrappers (thanks [@yasamoka](https://github.com/yasamoka)) - `math.h` function signatures refactored to use a `Quantity` concept (thanks [@kwikius](https://github.com/kwikius)) diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index 57fabf75..c595a723 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -20,6 +20,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +cmake_minimum_required(VERSION 3.12) + option(GENERATE_DOCS "Generate project documenation" ON) if(NOT GENERATE_DOCS) return() diff --git a/docs/usage.rst b/docs/usage.rst index a7d4548f..d3222286 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -80,7 +80,7 @@ in *~/.conan/profile* directory. An example profile can look as follows: arch=x86_64 arch_build=x86_64 compiler=gcc - compiler.version=9 + compiler.version=10 compiler.cppstd=20 compiler.libcxx=libstdc++11 build_type=Release @@ -89,8 +89,8 @@ in *~/.conan/profile* directory. An example profile can look as follows: [build_requires] [env] - CC=/usr/bin/gcc-9 - CXX=/usr/bin/g++-9 + CC=/usr/bin/gcc-10 + CXX=/usr/bin/g++-10 .. tip:: diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 8a6fc23b..3a2d15ee 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -20,9 +20,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +cmake_minimum_required(VERSION 3.12) + function(add_example target) add_executable(${target} ${target}.cpp) - target_link_libraries(${target} PRIVATE mp::units) + target_link_libraries(${target} PRIVATE mp-units::mp-units) endfunction() add_example(box_example) diff --git a/example/alternative_namespaces/CMakeLists.txt b/example/alternative_namespaces/CMakeLists.txt index 6f3bc3d7..6341730c 100644 --- a/example/alternative_namespaces/CMakeLists.txt +++ b/example/alternative_namespaces/CMakeLists.txt @@ -20,9 +20,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +cmake_minimum_required(VERSION 3.12) + function(add_example target) add_executable(${target}_alt ${target}.cpp) - target_link_libraries(${target}_alt PRIVATE mp::units) + target_link_libraries(${target}_alt PRIVATE mp-units::mp-units) endfunction() add_example(box_example) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 915bf5b9..832cc02c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -31,14 +31,14 @@ project(mp-units list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../cmake") # include common tools and workarounds -include(common/cmake/scripts) +include(common/scripts) # use Conan configuration if available conan_init(cmake) # library definition -add_library(units INTERFACE) -#target_sources(units INTERFACE +add_library(mp-units INTERFACE) +#target_sources(mp-units INTERFACE # include/units/dimension.h # include/units/quantity.h # include/units/unit.h @@ -52,55 +52,55 @@ add_library(units INTERFACE) # include/units/si/time.h # include/units/si/speed.h #) -target_compile_features(units INTERFACE cxx_std_20) -target_link_libraries(units +target_compile_features(mp-units INTERFACE cxx_std_20) +target_link_libraries(mp-units INTERFACE $,CONAN_PKG::fmt,fmt::fmt> $,CONAN_PKG::ms-gsl,Microsoft.GSL::GSL> ) -target_include_directories(units +target_include_directories(mp-units INTERFACE $ $ ) if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - target_link_libraries(units + target_link_libraries(mp-units INTERFACE $,CONAN_PKG::range-v3,range-v3::range-v3> ) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_compile_options(units + target_compile_options(mp-units INTERFACE -Wno-literal-suffix -Wno-non-template-friend ) if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0) - target_compile_options(units + target_compile_options(mp-units INTERFACE -fconcepts ) - target_link_libraries(units + target_link_libraries(mp-units INTERFACE $,CONAN_PKG::range-v3,range-v3::range-v3> ) endif() elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - target_compile_options(units + target_compile_options(mp-units INTERFACE /utf-8 # Specifies both the source character set and the execution character set as UTF-8 /wd4455 # 'operator name': literal suffix identifiers that do not start with an underscore are reserved ) endif() -add_library(mp::units ALIAS units) +add_library(mp-units::mp-units ALIAS mp-units) # installation info -install_targets(units) +install_targets(mp-units) install(DIRECTORY include/units DESTINATION include COMPONENT Devel ) # generate configuration files and install the package -configure_and_install(../cmake/common/cmake/simple-config.cmake.in mp SameMajorVersion) +configure_and_install(../cmake/common/simple-config.cmake.in mp-units SameMajorVersion) diff --git a/src/include/units/bits/pow.h b/src/include/units/bits/pow.h new file mode 100644 index 00000000..c6693103 --- /dev/null +++ b/src/include/units/bits/pow.h @@ -0,0 +1,61 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// 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. + +#pragma once + +#include +#include + +namespace units::detail { + +constexpr std::intmax_t ipow10(std::intmax_t exp) +{ + assert(exp >= 0); + if (exp == 0) return 1; + std::intmax_t result = 1; + while (exp > 0) { + result *= 10; + --exp; + } + return result; +} + +template +constexpr Rep fpow10(std::intmax_t exp) +{ + if (exp == 0) return Rep(1.0); + Rep result = Rep(1.0); + if (exp < 0) { + while (exp < 0) { + result = result / Rep(10.0); + ++exp; + } + } else { + while (exp > 0) { + result = result * Rep(10.0); + --exp; + } + } + return result; +} + +} // namespace units::detail diff --git a/src/include/units/prefix.h b/src/include/units/prefix.h index fbf45665..129be0b3 100644 --- a/src/include/units/prefix.h +++ b/src/include/units/prefix.h @@ -23,6 +23,7 @@ #pragma once #include +#include #include #include diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index 06c46c5c..eb38df01 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -342,9 +343,9 @@ template) { - return lhs.count() * rhs.count() * static_cast(r.num * fpow10(r.exp)) / static_cast(r.den); + return lhs.count() * rhs.count() * static_cast(r.num * detail::fpow10(r.exp)) / static_cast(r.den); } else { - return lhs.count() * rhs.count() * static_cast(r.num * ipow10(r.exp)) / static_cast(r.den); + return lhs.count() * rhs.count() * static_cast(r.num * detail::ipow10(r.exp)) / static_cast(r.den); } } diff --git a/src/include/units/quantity_cast.h b/src/include/units/quantity_cast.h index 14bfbd5b..79509c50 100644 --- a/src/include/units/quantity_cast.h +++ b/src/include/units/quantity_cast.h @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include #include #ifdef _MSC_VER @@ -35,38 +38,6 @@ namespace units { -constexpr std::intmax_t ipow10(std::intmax_t exp) -{ - assert(exp >= 0); - if (exp == 0) return 1; - std::intmax_t result = 1; - while (exp > 0) { - result *= 10; - --exp; - } - return result; -} - -template -constexpr Rep fpow10(std::intmax_t exp) -{ - if (exp == 0) return Rep(1.0); - Rep result = Rep(1.0); - if (exp < 0) { - while (exp < 0) { - result = result / Rep(10.0); - ++exp; - } - } else { - while (exp > 0) { - result = result * Rep(10.0); - --exp; - } - } - return result; -} - - // QuantityOf template concept QuantityOf = Quantity && Dimension && equivalent_dim; @@ -92,13 +63,13 @@ struct quantity_cast_impl { static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(static_cast(q.count()) * static_cast(fpow10(CRatio.exp)))); + return To(static_cast(static_cast(q.count()) * static_cast(detail::fpow10(CRatio.exp)))); } else { if constexpr (CRatio.exp > 0) { - return To(static_cast(static_cast(q.count()) * static_cast(ipow10(CRatio.exp)))); + return To(static_cast(static_cast(q.count()) * static_cast(detail::ipow10(CRatio.exp)))); } else { - return To(static_cast(static_cast(q.count()) / static_cast(ipow10(-CRatio.exp)))); + return To(static_cast(static_cast(q.count()) / static_cast(detail::ipow10(-CRatio.exp)))); } } } @@ -122,21 +93,21 @@ struct quantity_cast_impl { { if constexpr (treat_as_floating_point) { return To(static_cast(static_cast(q.count()) * - static_cast(fpow10(CRatio.exp)) * + static_cast(detail::fpow10(CRatio.exp)) * (static_cast(CRatio.num) / static_cast(CRatio.den)))); } else { if constexpr (CRatio.exp > 0) { return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) * - static_cast(ipow10(CRatio.exp)) / + static_cast(detail::ipow10(CRatio.exp)) / static_cast(CRatio.den))); } else { return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) / (static_cast(CRatio.den) * - static_cast(ipow10(-CRatio.exp))))); + static_cast(detail::ipow10(-CRatio.exp))))); } } } @@ -157,13 +128,13 @@ struct quantity_cast_impl { static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(static_cast(q.count()) * static_cast(fpow10(CRatio.exp)) * (CRep{1} / static_cast(CRatio.den)))); + return To(static_cast(static_cast(q.count()) * static_cast(detail::fpow10(CRatio.exp)) * (CRep{1} / static_cast(CRatio.den)))); } else { if constexpr (CRatio.exp > 0) { - return To(static_cast(static_cast(q.count()) * static_cast(ipow10(CRatio.exp)) / static_cast(CRatio.den))); + return To(static_cast(static_cast(q.count()) * static_cast(detail::ipow10(CRatio.exp)) / static_cast(CRatio.den))); } else { - return To(static_cast(static_cast(q.count()) / (static_cast(ipow10(-CRatio.exp)) * static_cast(CRatio.den)))); + return To(static_cast(static_cast(q.count()) / (static_cast(detail::ipow10(-CRatio.exp)) * static_cast(CRatio.den)))); } } } @@ -184,13 +155,13 @@ struct quantity_cast_impl { static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) * static_cast(fpow10(CRatio.exp)))); + return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) * static_cast(detail::fpow10(CRatio.exp)))); } else { if constexpr (CRatio.exp > 0) { - return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) * static_cast(ipow10(CRatio.exp)))); + return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) * static_cast(detail::ipow10(CRatio.exp)))); } else { - return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) / static_cast(ipow10(-CRatio.exp)))); + return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) / static_cast(detail::ipow10(-CRatio.exp)))); } } } @@ -202,13 +173,13 @@ struct quantity_cast_impl { static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(q.count() * fpow10(CRatio.exp))); + return To(static_cast(q.count() * detail::fpow10(CRatio.exp))); } else { if constexpr (CRatio.exp > 0) { - return To(static_cast(q.count() * ipow10(CRatio.exp))); + return To(static_cast(q.count() * detail::ipow10(CRatio.exp))); } else { - return To(static_cast(q.count() / ipow10(-CRatio.exp))); + return To(static_cast(q.count() / detail::ipow10(-CRatio.exp))); } } } @@ -229,13 +200,13 @@ struct quantity_cast_impl { static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(q.count() * fpow10(CRatio.exp) * (CRatio.num / CRatio.den))); + return To(static_cast(q.count() * detail::fpow10(CRatio.exp) * (CRatio.num / CRatio.den))); } else { if constexpr (CRatio.exp > 0) { - return To(static_cast(q.count() * CRatio.num * ipow10(CRatio.exp) / CRatio.den)); + return To(static_cast(q.count() * CRatio.num * detail::ipow10(CRatio.exp) / CRatio.den)); } else { - return To(static_cast(q.count()) * CRatio.num / (CRatio.den * ipow10(-CRatio.exp))); + return To(static_cast(q.count()) * CRatio.num / (CRatio.den * detail::ipow10(-CRatio.exp))); } } } @@ -256,13 +227,13 @@ struct quantity_cast_impl { static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(q.count() * fpow10(CRatio.exp) / CRatio.den)); + return To(static_cast(q.count() * detail::fpow10(CRatio.exp) / CRatio.den)); } else { if constexpr (CRatio.exp > 0) { - return To(static_cast(q.count() * ipow10(CRatio.exp) / CRatio.den)); + return To(static_cast(q.count() * detail::ipow10(CRatio.exp) / CRatio.den)); } else { - return To(static_cast(q.count() / (ipow10(-CRatio.exp) * CRatio.den))); + return To(static_cast(q.count() / (detail::ipow10(-CRatio.exp) * CRatio.den))); } } } @@ -283,13 +254,13 @@ struct quantity_cast_impl { static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(q.count() * CRatio.num * fpow10(CRatio.exp))); + return To(static_cast(q.count() * CRatio.num * detail::fpow10(CRatio.exp))); } else { if constexpr (CRatio.exp > 0) { - return To(static_cast(q.count() * CRatio.num * ipow10(CRatio.exp))); + return To(static_cast(q.count() * CRatio.num * detail::ipow10(CRatio.exp))); } else { - return To(static_cast(q.count() * CRatio.num / ipow10(-CRatio.exp))); + return To(static_cast(q.count() * CRatio.num / detail::ipow10(-CRatio.exp))); } } } diff --git a/src/include/units/ratio.h b/src/include/units/ratio.h index 2a483a46..c21f196c 100644 --- a/src/include/units/ratio.h +++ b/src/include/units/ratio.h @@ -29,6 +29,7 @@ #include #include #include +#include namespace units { @@ -50,6 +51,7 @@ struct ratio { explicit constexpr ratio(std::intmax_t n, std::intmax_t d = 1, std::intmax_t e = 0): num(n), den(d), exp(e) { + Expects(den != 0); detail::normalize(num, den, exp); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 67a1d088..32c5ea73 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -20,6 +20,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +cmake_minimum_required(VERSION 3.12) + add_subdirectory(unit_test/runtime) add_subdirectory(unit_test/static) #add_subdirectory(metabench) diff --git a/test/metabench/CMakeLists.txt b/test/metabench/CMakeLists.txt index a7894a18..8264d434 100644 --- a/test/metabench/CMakeLists.txt +++ b/test/metabench/CMakeLists.txt @@ -20,6 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +cmake_minimum_required(VERSION 3.12) function(add_metabench_test target name erb_path range) metabench_add_dataset(${target} "${erb_path}" "${range}" NAME "${name}") diff --git a/test/metabench/list/CMakeLists.txt b/test/metabench/list/CMakeLists.txt index fb1b5b97..c8776f04 100644 --- a/test/metabench/list/CMakeLists.txt +++ b/test/metabench/list/CMakeLists.txt @@ -20,6 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +cmake_minimum_required(VERSION 3.12) add_metabench_test(metabench.data.list.type_list.concepts_all "all concepts" type_list_concepts_all.cpp.erb "[3, 6, 9, 12, 15]") add_metabench_test(metabench.data.list.type_list.concepts_iface "concepts in interface" type_list_concepts_iface.cpp.erb "[3, 6, 9, 12, 15]") diff --git a/test/metabench/list/type_list_concepts_all.h b/test/metabench/list/type_list_concepts_all.h index 0344bdc9..62255427 100644 --- a/test/metabench/list/type_list_concepts_all.h +++ b/test/metabench/list/type_list_concepts_all.h @@ -182,4 +182,4 @@ namespace units { template typename Pred> using type_list_sort_t = type_list_sort::type; -} // namespace units \ No newline at end of file +} // namespace units diff --git a/test/metabench/make_dimension/CMakeLists.txt b/test/metabench/make_dimension/CMakeLists.txt index 63fdff45..c3d7c39b 100644 --- a/test/metabench/make_dimension/CMakeLists.txt +++ b/test/metabench/make_dimension/CMakeLists.txt @@ -20,6 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +cmake_minimum_required(VERSION 3.12) add_metabench_test(metabench.data.make_dimension.no_concepts "no concepts" no_concepts.cpp.erb "[1, 2, 3, 4, 6, 8, 10]") add_metabench_test(metabench.data.make_dimension.concepts_iface "concepts iface" concepts_iface.cpp.erb "[1, 2, 3, 4, 6, 8, 10]") diff --git a/test/metabench/ratio/CMakeLists.txt b/test/metabench/ratio/CMakeLists.txt index fe5e2ca2..a3cc5fea 100644 --- a/test/metabench/ratio/CMakeLists.txt +++ b/test/metabench/ratio/CMakeLists.txt @@ -20,6 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +cmake_minimum_required(VERSION 3.12) add_metabench_test(metabench.data.ratio.create.std_ratio "std::ratio" create_std_ratio.cpp.erb "[1000, 2500, 5000, 7500, 10000]") add_metabench_test(metabench.data.ratio.create.ratio_type_constexpr "ratio with constexpr" create_ratio_type_constexpr.cpp.erb "[1000, 2500, 5000, 7500, 10000]") diff --git a/test/unit_test/runtime/CMakeLists.txt b/test/unit_test/runtime/CMakeLists.txt index 0a727dbb..dc1ea76c 100644 --- a/test/unit_test/runtime/CMakeLists.txt +++ b/test/unit_test/runtime/CMakeLists.txt @@ -20,6 +20,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +cmake_minimum_required(VERSION 3.12) + # check if conan installed a test framework conan_check_testing(catch2) @@ -33,7 +35,7 @@ add_executable(unit_tests_runtime ) target_link_libraries(unit_tests_runtime PRIVATE - mp::units + mp-units::mp-units $,CONAN_PKG::catch2,Catch2::Catch2> ) diff --git a/test/unit_test/runtime/distribution_test.cpp b/test/unit_test/runtime/distribution_test.cpp index 9422fd90..101d569c 100644 --- a/test/unit_test/runtime/distribution_test.cpp +++ b/test/unit_test/runtime/distribution_test.cpp @@ -606,4 +606,4 @@ TEST_CASE("piecewise_linear_distribution") CHECK(units_dist.intervals() == intervals_qty_vec); CHECK(units_dist.densities() == stl_dist.densities()); } -} \ No newline at end of file +} diff --git a/test/unit_test/runtime/fmt_test.cpp b/test/unit_test/runtime/fmt_test.cpp index 4a185fad..131105e6 100644 --- a/test/unit_test/runtime/fmt_test.cpp +++ b/test/unit_test/runtime/fmt_test.cpp @@ -938,7 +938,11 @@ TEST_CASE("precision specification", "[text][fmt]") CHECK(fmt::format("{:%.0Q %q}", 1.2345q_m) == "1 m"); CHECK(fmt::format("{:%.1Q %q}", 1.2345q_m) == "1.2 m"); CHECK(fmt::format("{:%.2Q %q}", 1.2345q_m) == "1.23 m"); +#ifdef COMP_MSVC + CHECK(fmt::format("{:%.3Q %q}", 1.2345q_m) == "1.234 m"); +#else CHECK(fmt::format("{:%.3Q %q}", 1.2345q_m) == "1.235 m"); +#endif CHECK(fmt::format("{:%.4Q %q}", 1.2345q_m) == "1.2345 m"); CHECK(fmt::format("{:%.5Q %q}", 1.2345q_m) == "1.23450 m"); CHECK(fmt::format("{:%.10Q %q}", 1.2345q_m) == "1.2345000000 m"); @@ -949,7 +953,11 @@ TEST_CASE("precision specification", "[text][fmt]") CHECK(fmt::format("{:%.0Q}", 1.2345q_m) == "1"); CHECK(fmt::format("{:%.1Q}", 1.2345q_m) == "1.2"); CHECK(fmt::format("{:%.2Q}", 1.2345q_m) == "1.23"); +#ifdef COMP_MSVC + CHECK(fmt::format("{:%.3Q}", 1.2345q_m) == "1.234"); +#else CHECK(fmt::format("{:%.3Q}", 1.2345q_m) == "1.235"); +#endif CHECK(fmt::format("{:%.4Q}", 1.2345q_m) == "1.2345"); CHECK(fmt::format("{:%.5Q}", 1.2345q_m) == "1.23450"); CHECK(fmt::format("{:%.10Q}", 1.2345q_m) == "1.2345000000"); @@ -980,10 +988,17 @@ TEST_CASE("type specification", "[text][fmt]") CHECK(fmt::format("{:%xQ %q}", 42q_m) == "2a m"); CHECK(fmt::format("{:%XQ %q}", 42q_m) == "2A m"); +#ifdef COMP_MSVC + CHECK(fmt::format("{:%aQ %q}", 1.2345678q_m) == "0x1.3c0ca2a5b1d5dp+0 m"); + CHECK(fmt::format("{:%.3aQ %q}", 1.2345678q_m) == "0x1.3c1p+0 m"); + CHECK(fmt::format("{:%AQ %q}", 1.2345678q_m) == "0X1.3C0CA2A5B1D5DP+0 m"); + CHECK(fmt::format("{:%.3AQ %q}", 1.2345678q_m) == "0X1.3C1P+0 m"); +#else CHECK(fmt::format("{:%aQ %q}", 1.2345678q_m) == "0x9.e065152d8eae841p-3 m"); CHECK(fmt::format("{:%.3aQ %q}", 1.2345678q_m) == "0x9.e06p-3 m"); CHECK(fmt::format("{:%AQ %q}", 1.2345678q_m) == "0X9.E065152D8EAE841P-3 m"); CHECK(fmt::format("{:%.3AQ %q}", 1.2345678q_m) == "0X9.E06P-3 m"); +#endif CHECK(fmt::format("{:%eQ %q}", 1.2345678q_m) == "1.234568e+00 m"); CHECK(fmt::format("{:%.3eQ %q}", 1.2345678q_m) == "1.235e+00 m"); CHECK(fmt::format("{:%EQ %q}", 1.2345678q_m) == "1.234568E+00 m"); @@ -1007,10 +1022,17 @@ TEST_CASE("type specification", "[text][fmt]") CHECK(fmt::format("{:%xQ}", 42q_m) == "2a"); CHECK(fmt::format("{:%XQ}", 42q_m) == "2A"); +#ifdef COMP_MSVC + CHECK(fmt::format("{:%aQ}", 1.2345678q_m) == "0x1.3c0ca2a5b1d5dp+0"); + CHECK(fmt::format("{:%.3aQ}", 1.2345678q_m) == "0x1.3c1p+0"); + CHECK(fmt::format("{:%AQ}", 1.2345678q_m) == "0X1.3C0CA2A5B1D5DP+0"); + CHECK(fmt::format("{:%.3AQ}", 1.2345678q_m) == "0X1.3C1P+0"); +#else CHECK(fmt::format("{:%aQ}", 1.2345678q_m) == "0x9.e065152d8eae841p-3"); CHECK(fmt::format("{:%.3aQ}", 1.2345678q_m) == "0x9.e06p-3"); CHECK(fmt::format("{:%AQ}", 1.2345678q_m) == "0X9.E065152D8EAE841P-3"); CHECK(fmt::format("{:%.3AQ}", 1.2345678q_m) == "0X9.E06P-3"); +#endif CHECK(fmt::format("{:%eQ}", 1.2345678q_m) == "1.234568e+00"); CHECK(fmt::format("{:%.3eQ}", 1.2345678q_m) == "1.235e+00"); CHECK(fmt::format("{:%EQ}", 1.2345678q_m) == "1.234568E+00"); diff --git a/test/unit_test/static/CMakeLists.txt b/test/unit_test/static/CMakeLists.txt index a5fe55e3..afe82e06 100644 --- a/test/unit_test/static/CMakeLists.txt +++ b/test/unit_test/static/CMakeLists.txt @@ -20,6 +20,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +cmake_minimum_required(VERSION 3.12) + add_library(unit_tests_static cgs_test.cpp custom_rep_min_req_test.cpp @@ -43,5 +45,5 @@ add_library(unit_tests_static ) target_link_libraries(unit_tests_static PRIVATE - mp::units + mp-units::mp-units ) diff --git a/test/unit_test/static/custom_rep_min_req_test.cpp b/test/unit_test/static/custom_rep_min_req_test.cpp index d9a12ce5..2f50c244 100644 --- a/test/unit_test/static/custom_rep_min_req_test.cpp +++ b/test/unit_test/static/custom_rep_min_req_test.cpp @@ -25,6 +25,7 @@ #include "units/physical/si/frequency.h" #include "units/physical/si/speed.h" #include +#include #include using namespace units; @@ -65,7 +66,7 @@ template struct expl_constructible : scalar_ops> { T value_{}; expl_constructible() = default; - constexpr expl_constructible(T v) : value_(std::move(v)) {} + constexpr explicit expl_constructible(T v) : value_(std::move(v)) {} // no conversion to fundamental arithmetic types }; @@ -172,60 +173,60 @@ using namespace units::physical::si; // Quantity from Scalar // int <- int static_assert(length(expl_impl(1)).count() == 1); -// static_assert(length(impl_expl(1)).count() == 1); // should not compile (not convertible) +static_assert(!std::is_constructible_v, impl_expl>); static_assert(length(int(impl_expl(1))).count() == 1); -// static_assert(length>(1).count() == expl_impl{1}); // should not compile (not convertible) +static_assert(!std::is_constructible_v>, int>); static_assert(length>(expl_impl(1)).count() == expl_impl{1}); static_assert(length>(1).count() == impl_expl{1}); // double <- double static_assert(length(expl_impl(1.0)).count() == 1.0); -// static_assert(length(impl_expl(1.0)).count() == 1.0); // should not compile (not convertible) +static_assert(!std::is_constructible_v, impl_expl>); static_assert(length(double(impl_expl(1.0))).count() == 1.0); -// static_assert(length>(1.0).count() == expl_impl{1.0}); // should not compile (not convertible) +static_assert(!std::is_constructible_v>, double>); static_assert(length>(expl_impl(1.0)).count() == expl_impl{1.0}); static_assert(length>(1.0).count() == impl_expl{1.0}); // double <- int static_assert(length(expl_impl(1)).count() == 1.0); -// static_assert(length(impl_expl(1)).count() == 1.0); // should not compile (not convertible) +static_assert(!std::is_constructible_v, impl_expl>); static_assert(length(int(impl_expl(1))).count() == 1.0); -// static_assert(length>(1).count() == expl_impl{1}); // should not compile (not convertible) +static_assert(!std::is_constructible_v>, int>); static_assert(length>(expl_impl(1)).count() == expl_impl{1}); static_assert(length>(1).count() == impl_expl{1.0}); // int <- double -// static_assert(length(expl_impl(1.0)).count() == 1); // should not compile (truncating conversion) -// static_assert(length>(1.0).count() == impl_expl{1}); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v, expl_impl>); +static_assert(!std::is_constructible_v>, double>); // Quantity from other Quantity with different Rep // int <- int static_assert(length(length>(expl_impl(1))).count() == 1); -// static_assert(length(length>(1)).count() == 1); // should not compile (not convertible) +static_assert(!std::is_constructible_v, length>>); static_assert(length(quantity_cast(length>(1))).count() == 1); -// static_assert(length>(length(1)).count() == expl_impl{1}); // should not compile (not convertible) +static_assert(!std::is_constructible_v>, length>); static_assert(length>(quantity_cast>(length(1))).count() == expl_impl{1}); static_assert(length>(length(1)).count() == impl_expl{1}); // double <- double static_assert(length(length>(expl_impl(1.0))).count() == 1.0); -// static_assert(length(length>(1.0)).count() == 1.0); // should not compile (not convertible) +static_assert(!std::is_constructible_v, length>>); static_assert(length(quantity_cast(length>(1.0))).count() == 1.0); -// static_assert(length>(length(1.0).count() == expl_impl{1.0}); // should not compile (not convertible) +static_assert(!std::is_constructible_v>, length>); static_assert(length>(quantity_cast>(length(1.0))).count() == expl_impl{1.0}); static_assert(length>(length(1.0)).count() == impl_expl{1.0}); // double <- int static_assert(length(length>(expl_impl(1))).count() == 1.0); -// static_assert(length(length>(1)).count() == 1.0); // should not compile (not convertible) +static_assert(!std::is_constructible_v, length>>); static_assert(length(quantity_cast(length>(1))).count() == 1.0); -// static_assert(length>(length(1)).count() == expl_impl{1}); // should not compile (not convertible) +static_assert(!std::is_constructible_v>, length>); static_assert(length>(quantity_cast>(length(1))).count() == expl_impl{1}); static_assert(length>(length(1)).count() == impl_expl{1.0}); // int <- double -// static_assert(length(length>(1.0)).count() == 1); // should not compile (truncating conversion) -// static_assert(length>(length(1.0)).count() == impl_expl{1}); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v, length>>); +static_assert(!std::is_constructible_v>, length>); // unit conversions @@ -236,43 +237,43 @@ static_assert(length>(length>(1) static_assert(length>(length>(expl_impl(1))).count() == expl_impl(1000)); static_assert(length>(length>(expl_expl(1))).count() == expl_expl(1000)); -// static_assert(length>(length>(2000)).count() == impl(2)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, length>>); static_assert(length>(quantity_cast(length>(2000))).count() == impl(2)); -// static_assert(length>(length>(expl(2000))).count() == expl(2)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, length>>); static_assert(length>(quantity_cast(length>(expl(2000)))).count() == expl(2)); -// static_assert(length>(length>(2000)).count() == impl_impl(2)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, length>>); static_assert(length>(quantity_cast(length>(2000))).count() == impl_impl(2)); -// static_assert(length>(length>(2000)).count() == impl_expl(2)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, length>>); static_assert(length>(quantity_cast(length>(2000))).count() == impl_expl(2)); -// static_assert(length>(length>(expl_impl(2000))).count() == expl_impl(2)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, length>>); static_assert(length>(quantity_cast(length>(expl_impl(2000)))).count() == expl_impl(2)); -// static_assert(length>(length>(expl_expl(2000))).count() == expl_expl(2)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, length>>); static_assert(length>(quantity_cast(length>(expl_expl(2000)))).count() == expl_expl(2)); -// static_assert(speed>(speed>(72)).count() == impl(20)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(72))).count() == impl(20)); -// static_assert(speed>(speed>(expl(72))).count() == expl(20)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(expl(72)))).count() == expl(20)); -// static_assert(speed>(speed>(72)).count() == impl_impl(20)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(72))).count() == impl_impl(20)); -// static_assert(speed>(speed>(72)).count() == impl_expl(20)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(72))).count() == impl_expl(20)); -// static_assert(speed>(speed>(expl_impl(72))).count() == expl_impl(20)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(expl_impl(72)))).count() == expl_impl(20)); -// static_assert(speed>(speed>(expl_expl(72))).count() == expl_expl(20)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(expl_expl(72)))).count() == expl_expl(20)); -// static_assert(speed>(speed>(20)).count() == impl(72)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(20))).count() == impl(72)); -// static_assert(speed>(speed>(expl(20))).count() == expl(72)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(expl(20)))).count() == expl(72)); -// static_assert(speed>(speed>(20)).count() == impl_impl(72)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(20))).count() == impl_impl(72)); -// static_assert(speed>(speed>(20)).count() == impl_expl(72)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(20))).count() == impl_expl(72)); -// static_assert(speed>(speed>(expl_impl(20))).count() == expl_impl(72)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(expl_impl(20)))).count() == expl_impl(72)); -// static_assert(speed>(speed>(expl_expl(20))).count() == expl_expl(72)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(expl_expl(20)))).count() == expl_expl(72)); } // namespace diff --git a/test/unit_test/static/quantity_point_test.cpp b/test/unit_test/static/quantity_point_test.cpp index 424d785f..b440a6cc 100644 --- a/test/unit_test/static/quantity_point_test.cpp +++ b/test/unit_test/static/quantity_point_test.cpp @@ -47,7 +47,7 @@ concept invalid_types = requires !requires { typename quantity; }; // reordered arguments }; -static_assert(invalid_types); +static_assert(invalid_types); // member types @@ -226,14 +226,19 @@ static_assert(quantity_point_cast(quantity_point(1.23q_m)).relative().count // time -#if COMP_MSVC || COMP_GCC >= 10 -static_assert(!std::equality_comparable_with, - quantity_point>); // different dimensions -#endif static_assert(quantity_point{1q_h} == quantity_point{3600q_s}); +template +concept no_crossdimensional_equality = !requires +{ + quantity_point(1q_s) == quantity_point(length(1)); +}; + +static_assert(no_crossdimensional_equality); + // length +static_assert(quantity_point(1q_km) != quantity_point(1q_m)); static_assert(quantity_point(1q_km) == quantity_point(1000q_m)); static_assert(quantity_point(1q_km) + 1q_m == quantity_point(1001q_m)); static_assert(1q_km + quantity_point(1q_m) == quantity_point(1001q_m)); diff --git a/test/unit_test/static/quantity_test.cpp b/test/unit_test/static/quantity_test.cpp index 3e4b20f4..eaf4832a 100644 --- a/test/unit_test/static/quantity_test.cpp +++ b/test/unit_test/static/quantity_test.cpp @@ -36,10 +36,15 @@ using namespace units::physical::si; // class invariants -// constexpr quantity error(0); // should not compile (unit of a different dimension) -// constexpr quantity> error(0); // should not compile (quantity used as Rep) -// constexpr quantity error(0); // should not compile (reordered arguments) -// constexpr quantity, int> error(0); // should not compile (negative unit ratio) +template +concept invalid_types = requires +{ + !requires { typename quantity; }; // unit of a different dimension + !requires { typename quantity>; }; // quantity used as Rep + !requires { typename quantity; }; // reordered arguments +}; + +static_assert(invalid_types); // member types @@ -56,18 +61,21 @@ static_assert(km.count() == 1000); static_assert(length(km).count() == km.count()); static_assert(length(1).count() == 1); -// static_assert(length(1.0).count() == 1); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v, double>); // truncating conversion static_assert(length(1.0).count() == 1.0); static_assert(length(1).count() == 1.0); static_assert(length(3.14).count() == 3.14); static_assert(length(km).count() == 1000); -// static_assert(length(length(3.14)).count() == 3); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v, + length>); // truncating conversion static_assert(length(1000.0q_m).count() == 1000.0); static_assert(length(km).count() == 1000.0); static_assert(length(1q_km).count() == 1000); -// static_assert(length(1q_s).count() == 1); // should not compile (different dimensions) -//static_assert(length(1010q_m).count() == 1); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v, + physical::si::time>); // different dimensions +static_assert(!std::is_constructible_v, + length>); // truncating conversion // assignment operator @@ -89,24 +97,22 @@ static_assert((-km).count() == -1000); static_assert((+(-km)).count() == -1000); static_assert((-(-km)).count() == 1000); -// binary member operators - static_assert([](auto v) { auto vv = v++; - return std::make_pair(v, vv); -}(km) == std::make_pair(length(1001), length(1000))); + return std::pair(v, vv); +}(km) == std::pair(length(1001), length(1000))); static_assert([](auto v) { auto vv = ++v; - return std::make_pair(v, vv); -}(km) == std::make_pair(length(1001), length(1001))); + return std::pair(v, vv); +}(km) == std::pair(length(1001), length(1001))); static_assert([](auto v) { auto vv = v--; - return std::make_pair(v, vv); -}(km) == std::make_pair(length(999), length(1000))); + return std::pair(v, vv); +}(km) == std::pair(length(999), length(1000))); static_assert([](auto v) { auto vv = --v; - return std::make_pair(v, vv); -}(km) == std::make_pair(length(999), length(999))); + return std::pair(v, vv); +}(km) == std::pair(length(999), length(999))); // compound assignment @@ -116,22 +122,29 @@ static_assert((1q_m *= 2).count() == 2); static_assert((2q_m /= 2).count() == 1); static_assert((7q_m %= 2).count() == 1); static_assert((7q_m %= 2q_m).count() == 1); -// static_assert((7.m %= 2.).count() == 1); // should not compile (operation not allowed for floating-point types) -// static_assert((7.m %= 2).count() == 1); // should not compile (operation not allowed for floating-point types) -// static_assert((7q_m %= 2.).count() == 1); // should not compile (operation not allowed for floating-point types) -static_assert((7q_m %= 2q_m).count() == 1); -// static_assert((7.m %= 2.m).count() == 1); // should not compile (operation not allowed for floating-point types) -// static_assert((7.m %= 2q_m).count() == 1); // should not compile (operation not allowed for floating-point types) -// static_assert((7q_m %= 2.m).count() == 1); // should not compile (operation not allowed for floating-point types) -// static_assert(2q_m += 3.5q_m); // should not compile static_assert((2.5q_m += 3q_m).count() == 5.5); static_assert((2.5q_m += 3.5q_m).count() == 6); -// static_assert(2q_m *= 3.5); // should not compile static_assert((2.5q_m *= 3).count() == 7.5); static_assert((2.5q_m *= 3.5).count() == 8.75); +// operations not allowed for the respective quantities +template +concept invalid_compound_assignments = requires() +{ + !requires(length l) { l %= 2.; }; + !requires(length l) { l %= 2; }; + !requires(length l) { l %= 2.; }; + !requires(length l) { l %= 2.q_m; }; + !requires(length l) { l %= 2q_m; }; + !requires(length l) { l %= 2.q_m; }; + !requires(length l) { l += 3.5q_m; }; + !requires(length l) { l *= 3.5q_m; }; +}; + +static_assert(invalid_compound_assignments); + // non-member arithmetic operators static_assert(is_same_v() + length()), length>); @@ -253,9 +266,13 @@ static_assert(quantity_cast(1.23q_m).count() == 1); // time -// static_assert(1q_s == 1q_m); // should not compile (different dimensions) static_assert(1q_h == 3600q_s); +template +concept no_crossdimensional_equality = !requires { 1q_s == length(1); }; + +static_assert(no_crossdimensional_equality); + // length static_assert(1q_km == 1000q_m); diff --git a/test/unit_test/static/ratio_test.cpp b/test/unit_test/static/ratio_test.cpp index 2c05828b..4284439c 100644 --- a/test/unit_test/static/ratio_test.cpp +++ b/test/unit_test/static/ratio_test.cpp @@ -29,7 +29,6 @@ using namespace units; static_assert(ratio(2, 4) == ratio(1, 2)); // basic exponents tests -// note use of ::type is required because template params are changed while stamping out template static_assert(ratio(2, 40, 1) == ratio(1, 20, 1)); static_assert(ratio(20, 4, -1) == ratio(10, 2, -1)); static_assert(ratio(200, 5) == ratio(20'000, 50, -1)); diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index a48a1e86..b0c90abf 100644 --- a/test/unit_test/static/unit_test.cpp +++ b/test/unit_test/static/unit_test.cpp @@ -40,7 +40,10 @@ struct hour : named_scaled_unit { struct dim_time : base_dimension<"time", second> {}; struct kelvin : named_unit {}; -// struct kilokelvin : prefixed_unit {}; // should not compile (prefix not allowed for this reference unit) + +#if COMP_GCC >= 10 +static_assert([](P) { return !requires { typename prefixed_unit; }; }(si::kilo{})); // no prefix allowed +#endif struct metre_per_second : unit {}; struct dim_speed : derived_dimension, units::exp> {}; @@ -52,6 +55,10 @@ static_assert(is_same_v>, foot>); static_assert(is_same_v>, kilometre_per_hour>); +#if COMP_GCC >= 10 +static_assert([]() { return !requires { typename scaled_unit; }; }.template operator()()); // negative unit ratio +#endif + static_assert(centimetre::symbol == "cm"); static_assert(kilometre::symbol == "km"); static_assert(kilometre_per_hour::symbol == "km/h"); diff --git a/test_package/CMakeLists.txt b/test_package/CMakeLists.txt index 559377d3..ce6a5c40 100644 --- a/test_package/CMakeLists.txt +++ b/test_package/CMakeLists.txt @@ -20,16 +20,14 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.12) project(test_package) -set(CMAKE_VERBOSE_MAKEFILE TRUE) - # set path to custom cmake modules list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../cmake") # compilation options and flags used in a project development process -include(common/cmake/scripts) +include(common/scripts) include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) conan_basic_setup(TARGETS) @@ -41,4 +39,4 @@ target_link_libraries(${PROJECT_NAME}_conan PRIVATE CONAN_PKG::mp-units) # test cmake-generated target find_package(mp-units CONFIG REQUIRED) add_executable(${PROJECT_NAME}_cmake test_package.cpp) -target_link_libraries(${PROJECT_NAME}_cmake PRIVATE mp::units) +target_link_libraries(${PROJECT_NAME}_cmake PRIVATE mp-units::mp-units)