diff --git a/.github/workflows/ci-conan.yml b/.github/workflows/ci-conan.yml index 0c1ad283..3903e4b1 100644 --- a/.github/workflows/ci-conan.yml +++ b/.github/workflows/ci-conan.yml @@ -41,14 +41,12 @@ jobs: - { name: "Windows MSVC 14.2", os: windows-2019, - compiler: - { type: VISUAL, version: 16, std: 20, cc: "cl", cxx: "cl" }, + compiler: { type: VISUAL, version: 16, cc: "", cxx: "", std: 20 }, } - { name: "Windows MSVC 14.3", os: windows-2022, - compiler: - { type: MSVC, version: 193, std: 23, cc: "cl", cxx: "cl" }, + compiler: { type: MSVC, version: 193, cc: "", cxx: "", std: 23 }, } - { name: "Ubuntu GCC-10", @@ -140,17 +138,26 @@ jobs: std: 20, }, } - # In case a Conan docker image will be needed to provide a specific configuration we can use a Docker image as follows - # - { - # name: "Ubuntu GCC 10.2.0", - # os: ubuntu-20.04, - # compiler: { type: GCC, version: 10, cc: "gcc-10", cxx: "g++-10" }, - # docker_image: conanio/gcc10 - # } build_type: ["Release", "Debug"] downcast_mode: ["on", "auto"] + + env: + CC: ${{ matrix.config.compiler.cc }} + CXX: ${{ matrix.config.compiler.cxx }} + steps: - uses: actions/checkout@v2 + - name: Cache Conan data + uses: actions/cache@v2 + env: + cache-name: cache-conan-data + with: + path: ~/.conan/data + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/metadata.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- - uses: hendrikmuhs/ccache-action@v1 if: runner.os == 'Linux' with: @@ -180,8 +187,6 @@ jobs: run: | sudo xcode-select -s "/Applications/Xcode_13.0.app" - name: Install Ninja - # TODO Find a proper syntax to make the below work - # if: !matrix.config.docker_image shell: bash run: | if [ $RUNNER_OS == 'Linux' ]; then @@ -199,20 +204,21 @@ jobs: uses: actions/setup-python@v2 with: python-version: "3.8" - - name: Install Conan Package Tools + - name: Install Conan + shell: bash run: | - pip install -U conan_package_tools + pip install -U conan - name: Configure Conan - # TODO Find a proper syntax to make the below work - # if: !matrix.config.docker_image shell: bash run: | conan config init + conan remote add artifactory https://mpusz.jfrog.io/artifactory/api/conan/conan-oss if [[ "${{ matrix.config.compiler.type }}" == "GCC" || "${{ matrix.config.compiler.type }}" == "CLANG" ]]; then conan profile update settings.compiler.libcxx=${{ matrix.config.lib }} default fi conan profile update settings.compiler.cppstd=${{ matrix.config.compiler.std }} default - conan profile update conf.tools.cmake.cmaketoolchain:generator=Ninja default + conan profile update settings.build_type=${{ matrix.build_type }} default + conan profile update conf.tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" default conan profile show default # - name: Add support for clang-13 to Conan's settings.yml # # TODO Remove when Conan will support clang-13 @@ -220,21 +226,22 @@ jobs: # shell: bash # run: | # sed -i -e 's/"8", "9", "10", "11", "12"]/"8", "9", "10", "11", "12", "13"]/' ~/.conan/settings.yml - - name: Run Conan Package Tools + - name: Set channel shell: bash - env: - CONAN_USERNAME: mpusz - CONAN_OPTIONS: mp-units:build_docs=False,mp-units:downcast_mode=${{ matrix.downcast_mode }} - CONAN_UPLOAD: https://mpusz.jfrog.io/artifactory/api/conan/conan-oss - CONAN_LOGIN_USERNAME: ${{ secrets.CONAN_LOGIN_USERNAME }} - CONAN_PASSWORD: ${{ secrets.CONAN_PASSWORD }} - CONAN_CMAKE_GENERATOR: Ninja - CONAN_BUILD_TYPES: ${{ matrix.build_type }} - CC: ${{ matrix.config.compiler.cc }} - CXX: ${{ matrix.config.compiler.cxx }} - CONAN_${{ matrix.config.compiler.type }}_VERSIONS: ${{ matrix.config.compiler.version }} run: | - if [ ! -z "${{ matrix.config.docker_image }}" ]; then - export CONAN_DOCKER_IMAGE=${{ matrix.config.docker_image }} - fi - python build.py + [[ `git tag --contains ${{ github.sha }}` =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] && CHANNEL=stable || CHANNEL=testing + echo "CHANNEL=${CHANNEL}" >> ${GITHUB_ENV} + - name: Create Conan package + shell: bash + run: | + conan create . mpusz/testing -o mp-units:downcast_mode=${{ matrix.downcast_mode }} -c user.build:all=True -c user.build:skip_docs=True -b mp-units -b outdated -u + - name: Upload mp-units Conan package + if: github.ref == 'refs/heads/master' || env.CHANNEL == 'stable' + shell: bash + run: | + conan user ${{ secrets.CONAN_LOGIN_USERNAME }} -r artifactory -p ${{ secrets.CONAN_PASSWORD }} + conan upload "mp-units*" --all -r artifactory --confirm + - name: Remove mp-units package from Conan local cache + shell: bash + run: | + conan remove mp-units -f diff --git a/.github/workflows/ci-test-package-cmake.yml b/.github/workflows/ci-test-package-cmake.yml index 2c6440ac..7dfaa473 100644 --- a/.github/workflows/ci-test-package-cmake.yml +++ b/.github/workflows/ci-test-package-cmake.yml @@ -43,48 +43,91 @@ jobs: matrix: config: - { - name: "Windows MSVC 2019", - os: windows-latest, - compiler: { type: VISUAL, version: 16, cc: "", cxx: "" }, + name: "Windows MSVC 14.2", + os: windows-2019, + compiler: { type: VISUAL, version: 16, cc: "", cxx: "", std: 20 }, } - { - name: "Ubuntu GCC 10.3.0", - os: ubuntu-20.04, - compiler: { type: GCC, version: 10, cc: "gcc-10", cxx: "g++-10" }, - lib: "libstdc++11", + name: "Windows MSVC 14.3", + os: windows-2022, + compiler: { type: MSVC, version: 193, cc: "", cxx: "", std: 23 }, } - { - name: "Ubuntu GCC 11.1.0", - os: ubuntu-20.04, - compiler: { type: GCC, version: 11, cc: "gcc-11", cxx: "g++-11" }, - lib: "libstdc++11", - } - - { - name: "Ubuntu Clang 12.0.0 + libstdc++11", + name: "Ubuntu GCC-10", os: ubuntu-20.04, compiler: - { type: CLANG, version: 12, cc: "clang-12", cxx: "clang++-12" }, + { + type: GCC, + version: 10, + cc: "gcc-10", + cxx: "g++-10", + std: 20, + }, lib: "libstdc++11", } - { - name: "Ubuntu Clang 12.0.0 + libc++", + name: "Ubuntu GCC-11", os: ubuntu-20.04, compiler: - { type: CLANG, version: 12, cc: "clang-12", cxx: "clang++-12" }, + { + type: GCC, + version: 11, + cc: "gcc-11", + cxx: "g++-11", + std: 20, + }, + lib: "libstdc++11", + } + - { + name: "Ubuntu Clang-12 + libstdc++11", + os: ubuntu-20.04, + compiler: + { + type: CLANG, + version: 12, + cc: "clang-12", + cxx: "clang++-12", + std: 20, + }, + lib: "libstdc++11", + } + - { + name: "Ubuntu Clang-12 + libc++", + os: ubuntu-20.04, + compiler: + { + type: CLANG, + version: 12, + cc: "clang-12", + cxx: "clang++-12", + std: 20, + }, lib: "libc++", } - { - name: "Ubuntu Clang 13.0.0 + libc++", + name: "Ubuntu Clang-13 + libc++", os: ubuntu-20.04, compiler: - { type: CLANG, version: 13, cc: "clang-13", cxx: "clang++-13" }, + { + type: CLANG, + version: 13, + cc: "clang-13", + cxx: "clang++-13", + std: 20, + }, lib: "libc++", } - { - name: "Ubuntu Clang 14.0.0 + libc++", + name: "Ubuntu Clang-14 + libc++", os: ubuntu-20.04, compiler: - { type: CLANG, version: 14, cc: "clang-14", cxx: "clang++-14" }, + { + type: CLANG, + version: 14, + cc: "clang-14", + cxx: "clang++-14", + std: 20, + }, lib: "libc++", } - { @@ -96,23 +139,21 @@ jobs: version: "13.0", cc: "clang", cxx: "clang++", + std: 20, }, } - # In case a Conan docker image will be needed to provide a specific configuration we can use a Docker image as follows - # - { - # name: "Ubuntu GCC 10.2.0", - # os: ubuntu-20.04, - # compiler: { type: GCC, version: 10, cc: "gcc-10", cxx: "g++-10" }, - # docker_image: conanio/gcc10 - # } build_type: ["Release", "Debug"] env: CC: ${{ matrix.config.compiler.cc }} CXX: ${{ matrix.config.compiler.cxx }} - CMAKE_GENERATOR: Ninja steps: + - name: Downcase 'build_type' + id: build_type + uses: ASzc/change-string-case-action@v2 + with: + string: ${{ matrix.build_type }} - uses: actions/checkout@v2 - name: Cache Conan data uses: actions/cache@v2 @@ -149,8 +190,6 @@ jobs: run: | sudo xcode-select -s "/Applications/Xcode_13.0.app" - name: Install Ninja - # TODO Find a proper syntax to make the below work - # if: !matrix.config.docker_image shell: bash run: | if [ $RUNNER_OS == 'Linux' ]; then @@ -176,13 +215,13 @@ jobs: shell: bash run: | conan config init - conan remote add upload https://mpusz.jfrog.io/artifactory/api/conan/conan-oss - conan profile update settings.build_type=${{ matrix.build_type }} default - conan profile update settings.compiler.cppstd=20 default + conan remote add artifactory https://mpusz.jfrog.io/artifactory/api/conan/conan-oss if [[ "${{ matrix.config.compiler.type }}" == "GCC" || "${{ matrix.config.compiler.type }}" == "CLANG" ]]; then conan profile update settings.compiler.libcxx=${{ matrix.config.lib }} default fi - conan profile update conf.tools.cmake.cmaketoolchain:generator=Ninja default + conan profile update settings.compiler.cppstd=${{ matrix.config.compiler.std }} default + conan profile update settings.build_type=${{ matrix.build_type }} default + conan profile update conf.tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" default conan profile show default # - name: Add support for clang-13 to Conan's settings.yml # # TODO Remove when Conan will support clang-13 @@ -193,63 +232,70 @@ jobs: - name: Install Conan dependencies shell: bash run: | - mkdir -p build/${{ matrix.build_type }} && cd build/${{ matrix.build_type }} - conan install ../.. -b outdated -u + conan install . -b outdated -u + mv CMakeUserPresets.json src - name: Configure mp-units CMake - if: matrix.config.compiler.type == 'VISUAL' + if: matrix.config.compiler.type == 'VISUAL' || matrix.config.compiler.type == 'MSVC' shell: cmd - working-directory: build/${{ matrix.build_type }} + working-directory: src run: | - call conanvcvars.bat - cmake ../../src -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + cmake --version + call ..\build\generators\conanvcvars.bat + cmake --preset default -DCMAKE_INSTALL_PREFIX=../out - name: Configure mp-units CMake - if: matrix.config.compiler.type != 'VISUAL' + if: matrix.config.compiler.type != 'VISUAL' && matrix.config.compiler.type != 'MSVC' shell: bash - working-directory: build/${{ matrix.build_type }} + working-directory: src run: | - cmake ../../src -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + cmake --version + cmake --preset default -DCMAKE_INSTALL_PREFIX=../out - name: Install mp-units shell: bash - working-directory: build/${{ matrix.build_type }} + working-directory: src run: | - cmake --install . --prefix test_package - - name: Install dependencies for test_package + cmake --build --preset ${{ steps.build_type.outputs.lowercase }} --target install + - name: Provide dependencies for test_package shell: bash + working-directory: test_package run: | - mkdir -p test_package/build/${{ matrix.build_type }} && cd test_package/build/${{ matrix.build_type }} - conan install ../../.. + cp ../src/CMakeUserPresets.json . - name: Build test_package CMake (local build) - if: matrix.config.compiler.type == 'VISUAL' + if: matrix.config.compiler.type == 'VISUAL' || matrix.config.compiler.type == 'MSVC' shell: cmd - working-directory: test_package/build/${{ matrix.build_type }} + working-directory: test_package run: | - call conanvcvars.bat - cmake ../.. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -Dmp-units_DIR=${{ github.workspace }}/build/${{ matrix.build_type }} - cmake --build . + call ..\build\generators\conanvcvars.bat + cmake --preset default -Dmp-units_DIR=../build -Bbuild/local + cmake --build build/local --config ${{ matrix.build_type }} - name: Build test_package CMake (local build) - if: matrix.config.compiler.type != 'VISUAL' + if: matrix.config.compiler.type != 'VISUAL' && matrix.config.compiler.type != 'MSVC' shell: bash - working-directory: test_package/build/${{ matrix.build_type }} + working-directory: test_package run: | - cmake ../.. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -Dmp-units_DIR=${{ github.workspace }}/build/${{ matrix.build_type }} - cmake --build . - - name: Build test_package CMake (installation) - if: matrix.config.compiler.type == 'VISUAL' - shell: cmd - working-directory: test_package/build/${{ matrix.build_type }} - run: | - call conanvcvars.bat - cmake ../.. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_INSTALL_PREFIX=../../../build/${{ matrix.build_type }}/test_package - cmake --build . - - name: Build test_package CMake (installation) - if: matrix.config.compiler.type != 'VISUAL' + cmake --preset default -Dmp-units_DIR=../build -Bbuild/local + cmake --build build/local --config ${{ matrix.build_type }} + - name: Run test_package (local build) shell: bash - working-directory: test_package/build/${{ matrix.build_type }} - run: | - cmake ../.. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_INSTALL_PREFIX=../../../build/${{ matrix.build_type }}/test_package - cmake --build . - - name: Run test_package - shell: bash - working-directory: test_package/build/${{ matrix.build_type }} + working-directory: test_package/build/local/${{ matrix.build_type }} + run: | + ./test_package + - name: Build test_package CMake (installation) + if: matrix.config.compiler.type == 'VISUAL' || matrix.config.compiler.type == 'MSVC' + shell: cmd + working-directory: test_package + run: | + call ..\build\generators\conanvcvars.bat + cmake --preset default -DCMAKE_INSTALL_PREFIX=../out -Bbuild/install + cmake --build build/install --config ${{ matrix.build_type }} + - name: Build test_package CMake (installation) + if: matrix.config.compiler.type != 'VISUAL' && matrix.config.compiler.type != 'MSVC' + shell: bash + working-directory: test_package + run: | + cmake --preset default -DCMAKE_INSTALL_PREFIX=../out -Bbuild/install + cmake --build build/install --config ${{ matrix.build_type }} + - name: Run test_package (installation) + shell: bash + working-directory: test_package/build/install/${{ matrix.build_type }} run: | ./test_package diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 931eadf4..092c1deb 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -14,13 +14,13 @@ name: "CodeQL" on: push: - branches: [ master ] + branches: [master] paths-ignore: - - 'docs/**' + - "docs/**" pull_request: - branches: [ master ] + branches: [master] paths-ignore: - - 'docs/**' + - "docs/**" jobs: analyze: @@ -33,59 +33,59 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'cpp', 'python' ] + language: ["cpp", "python"] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # Learn more... # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection steps: - - name: Checkout repository - uses: actions/checkout@v2 + - name: Checkout repository + uses: actions/checkout@v2 - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - if: matrix.language != 'cpp' - uses: github/codeql-action/autobuild@v1 + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + if: matrix.language != 'cpp' + uses: github/codeql-action/autobuild@v2 - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl - - name: Cache Conan data - if: matrix.language == 'cpp' - uses: actions/cache@v2 - env: - cache-name: cache-conan-data - with: - path: ~/.conan/data - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/metadata.json') }} - restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- - - name: Set up Python - if: matrix.language == 'cpp' - uses: actions/setup-python@v2 - with: - python-version: '3.8' - - name: Conan build - if: matrix.language == 'cpp' - run: | - pip install -U conan - conan config init - conan remote add upload https://mpusz.jfrog.io/artifactory/api/conan/conan-oss - mkdir _lgtm_build_dir && cd _lgtm_build_dir - conan install .. -s compiler.cppstd=20 -s compiler.libcxx=libstdc++11 -o mp-units:build_docs=False -e mp-units:CONAN_RUN_TESTS=True -b outdated -u - conan build .. - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + - name: Cache Conan data + if: matrix.language == 'cpp' + uses: actions/cache@v2 + env: + cache-name: cache-conan-data + with: + path: ~/.conan/data + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/metadata.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - name: Set up Python + if: matrix.language == 'cpp' + uses: actions/setup-python@v2 + with: + python-version: "3.8" + - name: Conan build + if: matrix.language == 'cpp' + run: | + pip install -U conan + conan config init + conan remote add upload https://mpusz.jfrog.io/artifactory/api/conan/conan-oss + mkdir _lgtm_build_dir && cd _lgtm_build_dir + conan install .. -s compiler.cppstd=20 -s compiler.libcxx=libstdc++11 -c mp-units:user.build:all=True -c user.build:skip_docs=True -b outdated -u + conan build .. + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 9ab7c1ed..0655eae8 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -78,16 +78,13 @@ jobs: conan remote add -i 0 upload https://mpusz.jfrog.io/artifactory/api/conan/conan-oss - name: Install Conan dependencies run: | - mkdir build && cd build - conan install .. -s compiler.cppstd=20 -s compiler.libcxx=libstdc++11 -e mp-units:CONAN_RUN_TESTS=True -b outdated -u + conan install . -s compiler.cppstd=20 -s compiler.libcxx=libstdc++11 -c user.build:all=True -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" -b outdated -u - name: Configure CMake - working-directory: build run: | - cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release + cmake --preset default - name: Generate documentation - working-directory: build run: | - cmake --build . --target documentation --config Release + cmake --build --preset release --target documentation - name: Deploy documentation if: github.ref == 'refs/heads/master' uses: peaceiris/actions-gh-pages@v3 diff --git a/.gitignore b/.gitignore index a4547914..f957836c 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,11 @@ CMakeUserPresets.json # Conan *.pyc /test_package/build/ + +# Conan internal files +# TODO Remove for Conan 2.0 +conan_paths.cmake +conan.lock +conanbuildinfo.txt +conaninfo.txt +graph_info.json diff --git a/build.py b/build.py deleted file mode 100644 index e3bc1a56..00000000 --- a/build.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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. - -# The following environment variables are required -# - CONAN_USERNAME -# - CONAN_UPLOAD -# - CONAN_LOGIN_USERNAME -# - CONAN_PASSWORD - -from cpt.packager import ConanMultiPackager - -if __name__ == "__main__": - builder = ConanMultiPackager( - # package id - channel="testing", - stable_branch_pattern=r"v\d+\.\d+\.\d+.*", - # dependencies - build_policy=["mp-units", "outdated"], - upload_dependencies="all", - pip_install=["sphinx", "recommonmark", "breathe"], - # build configurations - archs=["x86_64"], # limit to 64-bit only - ) - builder.add_common_builds(pure_c=True) - for settings, options, env_vars, build_requires, reference in builder.items: - env_vars["mp-units:CONAN_RUN_TESTS"] = "True" - builder.run() diff --git a/cmake/documentation.cmake b/cmake/documentation.cmake index 3e6b239d..6d272bcd 100644 --- a/cmake/documentation.cmake +++ b/cmake/documentation.cmake @@ -22,8 +22,8 @@ cmake_minimum_required(VERSION 3.5) -find_package(Doxygen MODULE REQUIRED -)# TODO Switch to CONFIG when Conan will start supporting imported executables in CMakeDeps +# TODO Switch to CONFIG when Conan will start supporting imported executables in CMakeDeps +find_package(Doxygen MODULE REQUIRED) find_package(Sphinx REQUIRED) # diff --git a/conanfile.py b/conanfile.py index a673412e..208b50ba 100644 --- a/conanfile.py +++ b/conanfile.py @@ -24,15 +24,13 @@ import os import re from conan import ConanFile -from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain +from conan.tools.build import check_min_cppstd +from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout from conan.tools.files import copy, load, rmdir from conan.tools.scm import Version from conans.errors import ConanInvalidConfiguration -# TODO replace with new tools for Conan 2.0 -from conans.tools import check_min_cppstd, get_env - -required_conan_version = ">=1.48.0" +required_conan_version = ">=1.50.0" class MPUnitsConan(ConanFile): @@ -56,8 +54,8 @@ class MPUnitsConan(ConanFile): url = "https://github.com/mpusz/units" settings = "os", "compiler", "build_type", "arch" requires = "gsl-lite/0.40.0" - options = {"downcast_mode": ["off", "on", "auto"], "build_docs": [True, False]} - default_options = {"downcast_mode": "on", "build_docs": True} + options = {"downcast_mode": ["off", "on", "auto"]} + default_options = {"downcast_mode": "on"} exports = ["LICENSE.md"] exports_sources = [ "docs/*", @@ -71,8 +69,12 @@ class MPUnitsConan(ConanFile): generators = "cmake_paths" @property - def _run_tests(self): - return get_env("CONAN_RUN_TESTS", False) + def _build_all(self): + return bool(self.conf["user.build:all"]) + + @property + def _skip_docs(self): + return bool(self.conf["user.build:skip_docs"]) @property def _use_libfmt(self): @@ -112,10 +114,10 @@ class MPUnitsConan(ConanFile): self.requires("range-v3/0.11.0") def build_requirements(self): - if self._run_tests: + if self._build_all: self.test_requires("catch2/2.13.9") self.test_requires("wg21-linear_algebra/0.7.2") - if self.options.build_docs: + if not self._skip_docs: self.tool_requires("doxygen/1.9.4") # TODO Replace with `valdate()` for Conan 2.0 (https://github.com/conan-io/conan/issues/10723) @@ -145,17 +147,13 @@ class MPUnitsConan(ConanFile): raise ConanInvalidConfiguration("Unsupported compiler") check_min_cppstd(self, 20) - # TODO Uncomment this when environment is supported in the Conan toolchain - # def config_options(self): - # if not self._run_tests: - # # build_docs has sense only in a development or CI build - # del self.options.build_docs + def layout(self): + cmake_layout(self) def generate(self): tc = CMakeToolchain(self) tc.variables["UNITS_DOWNCAST_MODE"] = str(self.options.downcast_mode).upper() - # if self._run_tests: # TODO Enable this when environment is supported in the Conan toolchain - tc.variables["UNITS_BUILD_DOCS"] = bool(self.options.build_docs) + tc.variables["UNITS_BUILD_DOCS"] = self._build_all and not self._skip_docs tc.variables["UNITS_USE_LIBFMT"] = self._use_libfmt tc.generate() deps = CMakeDeps(self) @@ -163,13 +161,13 @@ class MPUnitsConan(ConanFile): def build(self): cmake = CMake(self) - cmake.configure(build_script_folder=None if self._run_tests else "src") + cmake.configure(build_script_folder=None if self._build_all else "src") cmake.build() - if self._run_tests: + if self._build_all: cmake.test() def package_id(self): - self.info.header_only() + self.info.clear() def package(self): copy( @@ -187,7 +185,6 @@ class MPUnitsConan(ConanFile): # core self.cpp_info.components["core"].requires = ["gsl-lite::gsl-lite"] - self.cpp_info.components["core"].includedirs = ["include"] if compiler == "Visual Studio": self.cpp_info.components["core"].cxxflags = ["/utf-8"] if self._use_range_v3: @@ -195,49 +192,21 @@ class MPUnitsConan(ConanFile): # rest self.cpp_info.components["core-io"].requires = ["core"] - self.cpp_info.components["core-io"].includedirs = ["include"] - self.cpp_info.components["core-fmt"].requires = ["core"] - self.cpp_info.components["core-fmt"].includedirs = ["include"] if self._use_libfmt: self.cpp_info.components["core-fmt"].requires.append("fmt::fmt") - self.cpp_info.components["isq"].requires = ["core"] - self.cpp_info.components["isq"].includedirs = ["include"] - self.cpp_info.components["isq-natural"].requires = ["isq"] - self.cpp_info.components["isq-natural"].includedirs = ["include"] - self.cpp_info.components["si"].requires = ["isq"] - self.cpp_info.components["si"].includedirs = ["include"] - self.cpp_info.components["si-cgs"].requires = ["si"] - self.cpp_info.components["si-cgs"].includedirs = ["include"] - self.cpp_info.components["si-fps"].requires = ["si-international"] - self.cpp_info.components["si-fps"].includedirs = ["include"] - self.cpp_info.components["si-hep"].requires = ["si"] - self.cpp_info.components["si-hep"].includedirs = ["include"] - self.cpp_info.components["si-iau"].requires = ["si"] - self.cpp_info.components["si-iau"].includedirs = ["include"] - self.cpp_info.components["si-imperial"].requires = ["si"] - self.cpp_info.components["si-imperial"].includedirs = ["include"] - self.cpp_info.components["si-international"].requires = ["si"] - self.cpp_info.components["si-international"].includedirs = ["include"] - self.cpp_info.components["si-typographic"].requires = ["si"] - self.cpp_info.components["si-typographic"].includedirs = ["include"] - self.cpp_info.components["si-uscs"].requires = ["si"] - self.cpp_info.components["si-uscs"].includedirs = ["include"] - self.cpp_info.components["isq-iec80000"].requires = ["si"] - self.cpp_info.components["isq-iec80000"].includedirs = ["include"] - self.cpp_info.components["systems"].requires = [ "isq", "isq-natural", diff --git a/docs/conf.py b/docs/conf.py index 54aa719b..06acf0a1 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,488 +1,11 @@ -# -*- coding: utf-8 -*- -""" - pygments.lexers.c_cpp - ~~~~~~~~~~~~~~~~~~~~~ - - Lexers for C/C++ languages. - - :copyright: Copyright 2006-2019 by the Pygments team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -import os -import re -import subprocess - -from pygments.lexer import ( - RegexLexer, - bygroups, - default, - include, - inherit, - this, - using, - words, -) -from pygments.token import ( - Comment, - Error, - Keyword, - Name, - Number, - Operator, - Punctuation, - String, - Text, -) -from pygments.util import get_bool_opt -from sphinx.highlighting import lexers - - -class MyCFamilyLexer(RegexLexer): - #: optional Comment or Whitespace - _ws = r"(?:\s|//.*?\n|/[*].*?[*]/)+" - - # The trailing ?, rather than *, avoids a geometric performance drop here. - #: only one /* */ style comment - _ws1 = r"\s*(?:/[*].*?[*]/\s*)?" - - tokens = { - "whitespace": [ - # preprocessor directives: without whitespace - (r"^#if\s+0", Comment.Preproc, "if0"), - ("^#", Comment.Preproc, "macro"), - # or with whitespace - ( - "^(" + _ws1 + r")(#if\s+0)", - bygroups(using(this), Comment.Preproc), - "if0", - ), - ("^(" + _ws1 + ")(#)", bygroups(using(this), Comment.Preproc), "macro"), - (r"\n", Text), - (r"\s+", Text), - (r"\\\n", Text), # line continuation - (r"//(\n|[\w\W]*?[^\\]\n)", Comment.Single), - (r"/(\\\n)?[*][\w\W]*?[*](\\\n)?/", Comment.Multiline), - # Open until EOF, so no ending delimeter - (r"/(\\\n)?[*][\w\W]*", Comment.Multiline), - ], - "statements": [ - (r'(L?)(")', bygroups(String.Affix, String), "string"), - ( - r"(L?)(')(\\.|\\[0-7]{1,3}|\\x[a-fA-F0-9]{1,2}|[^\\\'\n])(')", - bygroups(String.Affix, String.Char, String.Char, String.Char), - ), - (r"\*/", Error), - (r"[~!%^&*+=|?:<>/-]", Operator), - (r"[()\[\],.]", Punctuation), - ( - words( - ( - "asm", - "auto", - "break", - "case", - "const", - "continue", - "default", - "do", - "else", - "enum", - "extern", - "for", - "goto", - "if", - "register", - "restricted", - "return", - "sizeof", - "static", - "struct", - "switch", - "typedef", - "union", - "volatile", - "while", - ), - suffix=r"\b", - ), - Keyword, - ), - ( - r"(bool|int|long|float|short|double|char|unsigned|signed|void)\b", - Keyword.Type, - ), - ( - words( - ( - "inline", - "_inline", - "__inline", - "naked", - "restrict", - "thread", - "typename", - ), - suffix=r"\b", - ), - Keyword.Reserved, - ), - # Vector intrinsics - (r"(__m(128i|128d|128|64))\b", Keyword.Reserved), - # Microsoft-isms - ( - words( - ( - "asm", - "int8", - "based", - "except", - "int16", - "stdcall", - "cdecl", - "fastcall", - "int32", - "declspec", - "finally", - "int64", - "try", - "leave", - "wchar_t", - "w64", - "unaligned", - "raise", - "noop", - "identifier", - "forceinline", - "assume", - ), - prefix=r"__", - suffix=r"\b", - ), - Keyword.Reserved, - ), - (r"(true|false|NULL)\b", Name.Builtin), - (r"([a-zA-Z_]\w*)(\s*)(:)(?!:)", bygroups(Name.Label, Text, Punctuation)), - (r"[a-zA-Z_]\w*", Name), - ], - "root": [ - include("whitespace"), - # functions - ( - r"((?:[\w*\s])+?(?:\s|[*]))" # return arguments - r"([a-zA-Z_]\w*)" # method name - r"(\s*\([^;]*?\))" # signature - r"([^;{]*)(\{)", - bygroups( - using(this), Name.Function, using(this), using(this), Punctuation - ), - "function", - ), - # function declarations - ( - r"((?:[\w*\s])+?(?:\s|[*]))" # return arguments - r"([a-zA-Z_]\w*)" # method name - r"(\s*\([^;]*?\))" # signature - r"([^;]*)(;)", - bygroups( - using(this), Name.Function, using(this), using(this), Punctuation - ), - ), - default("statement"), - ], - "statement": [ - include("whitespace"), - include("statements"), - ("[{}]", Punctuation), - (";", Punctuation, "#pop"), - ], - "function": [ - include("whitespace"), - include("statements"), - (";", Punctuation), - (r"\{", Punctuation, "#push"), - (r"\}", Punctuation, "#pop"), - ], - "string": [ - (r'"', String, "#pop"), - ( - r'\\([\\abfnrtv"\']|x[a-fA-F0-9]{2,4}|' - r"u[a-fA-F0-9]{4}|U[a-fA-F0-9]{8}|[0-7]{1,3})", - String.Escape, - ), - (r'[^\\"\n]+', String), # all other characters - (r"\\\n", String), # line continuation - (r"\\", String), # stray backslash - ], - "macro": [ - ( - r"(include)(" + _ws1 + r")([^\n]+)", - bygroups(Comment.Preproc, Text, Comment.PreprocFile), - ), - (r"[^/\n]+", Comment.Preproc), - (r"/[*](.|\n)*?[*]/", Comment.Multiline), - (r"//.*?\n", Comment.Single, "#pop"), - (r"/", Comment.Preproc), - (r"(?<=\\)\n", Comment.Preproc), - (r"\n", Comment.Preproc, "#pop"), - ], - "if0": [ - (r"^\s*#if.*?(?)", Text, "#pop"), - ], - } - - def analyse_text(text): - if re.search("#include <[a-z_]+>", text): - return 0.2 - if re.search("using namespace ", text): - return 0.4 - - -lexers["cpp"] = MyCppLexer(startinline=True) - -# TODO Remove the above when Pygments release with fixes is available - - # Configuration file for the Sphinx documentation builder. # # This file only contains a selection of the most common options. # For a full list see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html +import re + def get_version(): try: @@ -598,29 +121,9 @@ html_css_files = ["css/custom.css"] # -- Breathe configuration --------------------------------------------------- -def configureDoxyfile(input_dir, output_dir): - with open("Doxyfile.in", "r") as file: - filedata = file.read() - - filedata = filedata.replace("@DOXYGEN_INPUT_DIR@", input_dir) - filedata = filedata.replace("@DOXYGEN_OUTPUT_DIR@", output_dir) - - with open("Doxyfile", "w") as file: - file.write(filedata) - - -# Check if we're running on Read the Docs' servers -read_the_docs_build = os.environ.get("READTHEDOCS", None) == "True" - # This should be a dictionary in which the keys are project names and the values # are paths to the folder containing the doxygen output for that project. -breathe_projects = {} -if read_the_docs_build: - input_dir = "../src" - output_dir = "build" - configureDoxyfile(input_dir, output_dir) - subprocess.call("doxygen", shell=True) - breathe_projects["mp-units"] = output_dir + "/xml" +breathe_projects = {"mp-units": "build/xml"} # This should match one of the keys in the breathe_projects dictionary and # indicates which project should be used when the project is not specified on diff --git a/docs/framework/units.rst b/docs/framework/units.rst index c29977b0..11243103 100644 --- a/docs/framework/units.rst +++ b/docs/framework/units.rst @@ -188,9 +188,6 @@ and define units like:: struct electronvolt : named_scaled_unit {}; -.. - TODO Submit a bug for above lexing problem - Finally, the last of the `named_scaled_unit` class template parameters provide a reference unit for scaling. Please note that it can be a dimension's base/coherent unit (like ``si::second``) or any other unit (i.e. ``si::minute``, diff --git a/docs/reference/core/concepts.rst b/docs/reference/core/concepts.rst index 99f38ce9..e835c0bd 100644 --- a/docs/reference/core/concepts.rst +++ b/docs/reference/core/concepts.rst @@ -45,6 +45,11 @@ Concepts A concept matching all unit types that have an atomic text symbol that can be used to aggregate it with other named units to form a final symbol of a derived unit. +.. concept:: template AliasUnit + + A concept matching all alias unit types in the library. Satisfied by all unit types derived + from the instantiation of :class:`alias_unit`. + .. concept:: template UnitOf A concept matching only units of a specified dimension. Satisfied by all unit types that @@ -63,6 +68,11 @@ Concepts A concept matching all quantity-like types other than specialization of :class:`quantity`. Satisfied by all types for which a correct specialization of :class:`quantity_like_traits` type trait is provided. +.. concept:: template QuantityPointLike + + A concept matching all quantity-point-like types other than specialization of :class:`quantity_point`. + Satisfied by all types for which a correct specialization of :class:`quantity_point_like_traits` type trait is provided. + .. concept:: template WrappedQuantity A concept matching types that wrap quantity objects. Satisfied by all wrapper types that diff --git a/docs/usage.rst b/docs/usage.rst index 9e1ebe10..c8a22e3b 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -22,8 +22,8 @@ This repository contains three independent CMake-based projects: - when this library will become part of the C++ standard it will have no external dependencies but until then it depends on: - - `{fmt} `_ to provide text formatting of quantities. - `gsl-lite `_ to verify runtime contracts with the ``gsl_Expects`` macro. + - [for compilers other than VS2022] `{fmt} `_ to provide text formatting of quantities. - [only for clang < 14 with libc++] `range-v3 `_ to provide needed C++20 concepts and utilities. - *.* @@ -36,9 +36,13 @@ This repository contains three independent CMake-based projects: - `linear algebra `_ library based on proposal `P1385 `_ used in some examples and tests. + + - in case you also want to build the project's documentation you will need: + - `Doxygen `_ to extract C++ entities information from the source code. - `Sphinx `_ to build the documentation. + - `Sphinx ReadTheDocs Theme `_ - `Sphinx recommonmark `_. - `Breathe `_ as a bridge between the Sphinx and Doxygen documentation systems. @@ -82,47 +86,47 @@ in *~/.conan/profiles* directory. An example profile can look as follows: arch=x86_64 arch_build=x86_64 compiler=gcc - compiler.version=10 + compiler.version=12 compiler.cppstd=20 compiler.libcxx=libstdc++11 build_type=Release - [options] - [build_requires] - - [conf] - tools.cmake.cmaketoolchain:generator=Ninja - [env] - CC=/usr/bin/gcc-10 - CXX=/usr/bin/g++-10 + CC=/usr/bin/gcc-12 + CXX=/usr/bin/g++-12 .. tip:: Please note that **mp-units** library requires C++20 to be set either in a Conan profile or forced via Conan command line. If you do the former, you will not need to provide ``-s compiler.cppstd=20`` - every time your run a Conan command line (as it is suggested below). + every time your run a Conan command line (as provided in the command line instructions below). + +Additionally, it is recommended to set Ninja as a CMake generator for Conan. To do so you should create +a *~/.conan/global.conf* file that will set ``tools.cmake.cmaketoolchain:generator`` to one of Ninja +generators. For example: + +.. code-block:: text + + tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" + +.. note:: + + *~/.conan/global.conf* file may also set ``tools.cmake.cmake_layout:build_folder_vars``` which + `makes working with several compilers or build configurations easier + `_. + For example the below line will force Conan to generate separate CMake presets and folders for each compiler: + + .. code-block:: text + + tools.cmake.cmake_layout:build_folder_vars=["settings.compiler", "settings.compiler.version"] + + In such a case you will need to use a configuration specific preset name in the Conan instructions provided below + rather then just ``default`` and ``release`` (i.e. ``gcc-11`` and ``gcc-11-release``) Build Options ------------- -Environment Variables -^^^^^^^^^^^^^^^^^^^^^ - -CONAN_RUN_TESTS -+++++++++++++++ - -**Values**: ``True``/``False`` - -**Defaulted to**: ``False`` - -Enables compilation of all the source code (tests and examples) and building the documentation. -To support this it requires some additional Conan build dependencies described in -`Repository Structure and Dependencies`_. -It also runs unit tests during Conan build. - - Conan Options ^^^^^^^^^^^^^ @@ -139,15 +143,30 @@ Specifies how :ref:`design/downcasting:The Downcasting Facility` works: - ``on`` - downcasting always forced -> compile-time errors in case of duplicated definitions - ``automatic`` - downcasting automatically enabled if no collisions are present -build_docs -++++++++++ +Conan Configuration Properties +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +build_all ++++++++++ **Values**: ``True``/``False`` -**Defaulted to**: ``True`` +**Defaulted to**: ``False`` -If enabled, Conan installs the documentation generation dependencies (i.e. doxygen). -Additionally, enables project documentation generation when the project is being built by Conan. +Enables compilation of all the source code (tests and examples) and generating the documentation. +To support this it requires some additional Conan build dependencies described in +`Repository Structure and Dependencies`_. +It also runs unit tests during Conan build (unless ``tools.build:skip_test`` configuration property is set to ``True``) + +skip_docs ++++++++++ + +**Values**: ``True``/``False`` + +**Defaulted to**: ``False`` + +If `build_all`_ is enabled, among others, Conan installs the documentation generation dependencies (i.e. doxygen) and +turns on the project documentation generation. Such behavior can be disabled with this option. CMake Options ^^^^^^^^^^^^^ @@ -203,6 +222,21 @@ UNITS_USE_LIBFMT Enables usage of `{fmt} `_ library instead of the C++20 Standard Library feature. +CMake with Presets Support +-------------------------- + +It is recommended to use at least CMake 3.23 to build this project as this version introduced a support +for CMake Presets schema version 4 used now by Conan to generate presets files. All build instructions +below assume that you have such a support. If not, your CMake invocations have to be replaced to something +like: + +.. code-block:: shell + + mkdir build && cd build + cmake .. -G "Ninja Multi-Config" -DCMAKE_TOOLCHAIN_FILE=/conan_toolchain.cmake + cmake --build . --config Release + + Installation and Reuse ---------------------- @@ -294,8 +328,8 @@ library release the following steps may be performed: mkdir my_project/build && cd my_project/build conan install .. -pr -s compiler.cppstd=20 -b=missing - cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release - cmake --build . + cmake .. -G "Ninja Multi-Config" -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake + cmake --build . --config Release Conan + CMake (Live At Head) @@ -329,6 +363,9 @@ differences: [requires] mp-units/0.8.0@mpusz/testing + [layout] + cmake_layout + [generators] CMakeToolchain CMakeDeps @@ -343,10 +380,9 @@ differences: .. code-block:: shell - mkdir my_project/build && cd my_project/build - conan install .. -pr -s compiler.cppstd=20 -b=outdated -u - cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release - cmake --build . + conan install . -pr -s compiler.cppstd=20 -b=outdated -u + cmake --preset default + cmake --build --preset release Install @@ -358,43 +394,60 @@ to find it, it is enough to perform the following steps: .. code-block:: shell - mkdir units/build && cd units/build - conan install .. -pr -s compiler.cppstd=20 -b=missing - cmake ../src -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release - cmake --install . --prefix + conan install . -pr -s compiler.cppstd=20 -b=missing + mv CMakeUserPresets.json src + cd src + cmake --preset default -DCMAKE_INSTALL_PREFIX= + cmake --build --preset release --target install -Contributing (or just building all the tests, examples, and documentation) --------------------------------------------------------------------------- +Contributing (or just building all the tests and examples) +---------------------------------------------------------- -In case you would like to build all the source code (with unit tests and examples) and documentation -in **mp-units** repository, you should: +In case you would like to build all the source code (with unit tests and examples) in **mp-units** repository, +you should: -1. Add remotes of additional Conan dependencies. -2. Use the *CMakeLists.txt* from the top-level directory. -3. Obtain Python dependencies. -4. Run Conan with `CONAN_RUN_TESTS`_ = ``True``. +1. Use the *CMakeLists.txt* from the top-level directory. +2. Run Conan with `build_all`_ = ``True`` + (use ``-o build_docs=False`` if you want to skip the documentation generation). + +.. code-block:: shell + + git clone https://github.com/mpusz/units.git && cd units + conan install . -pr -s compiler.cppstd=20 -c mp-units:user.build:all=True -c user.build:skip_docs=True -b outdated -u + conan build . + +The above will download and install all of the dependencies needed for the development of the library, +build all of the source code and run unit tests. + +If you prefer to build the project via CMake rather then Conan, then you should replace the last ``conan build .`` +step with the explicit CMake build: + +.. code-block:: shell + + cmake --preset default + cmake --build --preset release + cmake --build --preset release --target test + + +Building documentation +---------------------- + +In case you would like to build the project's documentation, you should: + +1. Use the *CMakeLists.txt* from the top-level directory. +2. Obtain Python dependencies. +3. Run Conan with `build_all`_ = ``True``. .. code-block:: shell git clone https://github.com/mpusz/units.git && cd units pip3 install -r docs/requirements.txt - mkdir units/build && cd units/build - conan install .. -pr -s compiler.cppstd=20 -e mp-units:CONAN_RUN_TESTS=True -b outdated -u - conan build .. + conan install . -pr -s compiler.cppstd=20 -c mp-units:user.build:all=True -b missing + cmake --preset default + cmake --build --preset release --target documentation -The above will download and install all of the dependencies needed for the development of the library, -build all of the source code and documentation, and run unit tests. - -If you prefer to build the project via CMake rather then Conan, then you should replace the last ``conan build ..`` -step with the CMake build: - -.. code-block:: shell - - # ... - cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release - cmake --build . - ctest +The above will download and install all of the dependencies needed and build the documentation. Packaging @@ -404,7 +457,7 @@ To test CMake installation and Conan packaging or create a Conan package run: .. code-block:: shell - conan create . / -pr -s compiler.cppstd=20 -e mp-units:CONAN_RUN_TESTS=True -b outdated -u + conan create . / -pr -s compiler.cppstd=20 -c mp-units:user.build:all=True -c user.build:skip_docs=True -b outdated -u The above will create a Conan package and run tests provided in *./test_package* directory. diff --git a/docs/use_cases/extensions.rst b/docs/use_cases/extensions.rst index fd9db565..fd2281a8 100644 --- a/docs/use_cases/extensions.rst +++ b/docs/use_cases/extensions.rst @@ -258,14 +258,14 @@ Even though the base dimension of ``si::fps`` is defined in terms of ``si::metre`` foot is preserved as the base unit of length in both systems:: constexpr auto fps_yard = fps::length(1.); - constexpr auto fps_area = quantity_cast(fps_yard * fps_yard); - std::cout << fps_yard << "\n"; // 1 yd - std::cout << fps_area << "\n"; // 9 ft² + constexpr auto fps_area = fps_yard * fps_yard; + std::cout << fps_yard << "\n"; // 1 yd + std::cout << quantity_cast(fps_area) << "\n"; // 9 ft² constexpr auto si_fps_yard = si::fps::length(1.); - constexpr auto si_fps_area = quantity_cast(si_fps_yard * si_fps_yard); - std::cout << si_fps_yard << "\n"; // 1 yd - std::cout << si_fps_area << "\n"; // 9 ft² + constexpr auto si_fps_area = si_fps_yard * si_fps_yard; + std::cout << si_fps_yard << "\n"; // 1 yd + std::cout << quantity_cast(si_fps_area) << "\n"; // 9 ft² In most cases we want conversions between systems and that is why nearly all systems provided with this library are implemented in terms on the :term:`SI` diff --git a/docs/use_cases/interoperability.rst b/docs/use_cases/interoperability.rst index a229b018..40f58501 100644 --- a/docs/use_cases/interoperability.rst +++ b/docs/use_cases/interoperability.rst @@ -59,7 +59,7 @@ It works just like `quantity_like_traits`, except that ``number(T)`` is replaced with ``relative(T)`` that returns the `QuantityLike` value and ``dimension`` is replaced with ``origin``. -Similar to `quantity` and `quantity_kind`, `quantity_point` and `quantity_kind_point` +Similar to `quantity` and `quantity_kind`, `quantity_point` and `quantity_point_kind` provide a deduction guide from `QuantityPointLike`:: using namespace std::chrono_literals; diff --git a/docs/use_cases/unknown_dimensions.rst b/docs/use_cases/unknown_dimensions.rst index d23e227f..f3e3140d 100644 --- a/docs/use_cases/unknown_dimensions.rst +++ b/docs/use_cases/unknown_dimensions.rst @@ -52,7 +52,7 @@ dimensions used in the division operation: static_assert(std::is_same_v, exponent>>); static_assert(std::is_same_v>); + scaled_unit, exponent>>>>); .. important:: @@ -81,7 +81,7 @@ this particular unknown derived dimension. In case we would like to print the result in terms of base units we can simply do the following:: - auto s = quantity_cast(result); + auto s = quantity_cast(result); std::cout << "Speed: " << s << '\n'; // prints 'Speed: 20 m/s' .. seealso:: diff --git a/example/custom_systems.cpp b/example/custom_systems.cpp index b2c3419d..c708bf0f 100644 --- a/example/custom_systems.cpp +++ b/example/custom_systems.cpp @@ -33,7 +33,7 @@ using namespace units; namespace fps { struct foot : named_unit {}; -struct yard : named_scaled_unit(), foot> {}; +struct yard : named_scaled_unit(), foot> {}; struct dim_length : base_dimension<"L", foot> {}; @@ -54,8 +54,8 @@ using length = quantity; namespace fps { -struct foot : named_scaled_unit(), metre> {}; -struct yard : named_scaled_unit(), foot> {}; +struct foot : named_scaled_unit(), metre> {}; +struct yard : named_scaled_unit(), foot> {}; struct dim_length : base_dimension<"L", foot> {}; @@ -84,20 +84,17 @@ void conversions() void unknown_dimensions() { constexpr auto fps_yard = fps::length(1.); - constexpr auto fps_area = quantity_cast(fps_yard * fps_yard); + constexpr auto fps_area = fps_yard * fps_yard; std::cout << fps_yard << "\n"; - std::cout << fps_area << "\n"; + std::cout << quantity_cast(fps_area) << "\n"; constexpr auto si_fps_yard = si::fps::length(1.); - constexpr auto si_fps_area = quantity_cast(si_fps_yard * si_fps_yard); + constexpr auto si_fps_area = si_fps_yard * si_fps_yard; std::cout << si_fps_yard << "\n"; - std::cout << si_fps_area << "\n"; + std::cout << quantity_cast(si_fps_area) << "\n"; } -std::ostream& operator<<(std::ostream& os, const ratio& r) -{ - return os << "ratio{" << r.num << ", " << r.den << ", " << r.exp << "}"; -} +std::ostream& operator<<(std::ostream& os, const ratio& r) { return os << "ratio{" << r.num << ", " << r.den << "}"; } template std::ostream& operator<<(std::ostream& os, const U& u) diff --git a/example/glide_computer/include/glide_computer.h b/example/glide_computer/include/glide_computer.h index c515868c..c846b72d 100644 --- a/example/glide_computer/include/glide_computer.h +++ b/example/glide_computer/include/glide_computer.h @@ -157,9 +157,8 @@ public: using legs = std::vector; template - requires std::same_as, - waypoint> explicit task(const R& r) : - waypoints_(std::ranges::begin(r), std::ranges::end(r)) + requires std::same_as, waypoint> + explicit task(const R& r) : waypoints_(std::ranges::begin(r), std::ranges::end(r)) { } @@ -215,8 +214,7 @@ constexpr height agl(altitude glider_alt, altitude terrain_level) { return glide inline units::isq::si::length length_3d(distance dist, height h) { - // TODO support for hypot(q, q) - return sqrt(pow<2>(dist.common()) + pow<2>(h.common())); + return hypot(dist.common(), h.common()); } distance glide_distance(const flight_point& pos, const glider& g, const task& t, const safety& s, altitude ground_alt); diff --git a/src/core/include/units/base_dimension.h b/src/core/include/units/base_dimension.h index 7515d97b..0af40adb 100644 --- a/src/core/include/units/base_dimension.h +++ b/src/core/include/units/base_dimension.h @@ -54,6 +54,7 @@ template struct base_dimension { static constexpr auto symbol = Symbol; ///< Unique base dimension identifier using base_unit = U; ///< Base unit adopted for this dimension + static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = magnitude{}; }; // base_dimension_less diff --git a/src/core/include/units/bits/base_units_ratio.h b/src/core/include/units/bits/absolute_magnitude.h similarity index 95% rename from src/core/include/units/bits/base_units_ratio.h rename to src/core/include/units/bits/absolute_magnitude.h index 2d9703e3..0216313f 100644 --- a/src/core/include/units/bits/base_units_ratio.h +++ b/src/core/include/units/bits/absolute_magnitude.h @@ -42,7 +42,7 @@ namespace units::detail { template constexpr Magnitude auto absolute_magnitude(exponent_list) { - return (pow(Es::dimension::base_unit::mag) * ... * magnitude<>{}); + return (magnitude<>{} * ... * pow(Es::dimension::base_unit::mag)); } } // namespace units::detail diff --git a/src/core/include/units/bits/basic_concepts.h b/src/core/include/units/bits/basic_concepts.h index 28776ab9..ac073b28 100644 --- a/src/core/include/units/bits/basic_concepts.h +++ b/src/core/include/units/bits/basic_concepts.h @@ -29,6 +29,7 @@ #include #include #include +#include // IWYU pragma: end_exports #include @@ -86,6 +87,20 @@ inline constexpr bool is_named = false; template concept NamedUnit = Unit && detail::is_named; +template +struct alias_unit; + +// TODO: Remove when P1985 accepted +namespace detail { + +template +void to_base_alias_unit(const volatile alias_unit*); + +} // namespace detail + +template +concept AliasUnit = requires(T* t) { detail::to_base_alias_unit(t); }; + // BaseDimension template struct base_dimension; diff --git a/src/core/include/units/bits/common_type.h b/src/core/include/units/bits/common_type.h index 46f7cb5f..8955d5ab 100644 --- a/src/core/include/units/bits/common_type.h +++ b/src/core/include/units/bits/common_type.h @@ -66,15 +66,13 @@ struct common_quantity_reference_impl, reference> { template struct common_quantity_reference_impl, reference> { + static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = common_magnitude(reference::mag, + reference::mag); using dimension = conditional, D2, D1>; - static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto m1 = D1::base_units_ratio * U1::mag; - static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto m2 = D2::base_units_ratio * U2::mag; - static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto cm = common_magnitude(m1, m2); - using unit = downcast_unit; + using unit = downcast_unit; using type = reference; }; - template Q2> using common_quantity_reference = TYPENAME detail::common_quantity_reference_impl, diff --git a/src/core/include/units/bits/derived_scaled_unit.h b/src/core/include/units/bits/derived_scaled_unit.h index 5a8b4cd7..d62b6f45 100644 --- a/src/core/include/units/bits/derived_scaled_unit.h +++ b/src/core/include/units/bits/derived_scaled_unit.h @@ -39,8 +39,7 @@ inline constexpr bool compatible_units, Us...> = (UnitOf constexpr Magnitude auto derived_mag(exponent_list) { - return (as_magnitude<1>() * ... * - pow(Us::mag / dimension_unit::mag)); + return (magnitude<>{} * ... * pow(Us::mag / dimension_unit::mag)); } template diff --git a/src/core/include/units/bits/dimension_op.h b/src/core/include/units/bits/dimension_op.h index 88580625..820fbc82 100644 --- a/src/core/include/units/bits/dimension_op.h +++ b/src/core/include/units/bits/dimension_op.h @@ -37,12 +37,12 @@ namespace units { * * Sometimes a temporary partial result of a complex calculation may not result in a predefined * dimension. In such a case an `unknown_dimension` is created with a coherent unit of `unknown_coherent_unit` - * and ratio(1). + * with a magnitude being the absolute one of all the exponents of such a dimension. * * @tparam Es the list of exponents of ingredient dimensions */ template -struct unknown_dimension : derived_dimension, unknown_coherent_unit, Es...> {}; +struct unknown_dimension : derived_dimension, unknown_coherent_unit, Es...> {}; namespace detail { diff --git a/src/core/include/units/bits/equivalent.h b/src/core/include/units/bits/equivalent.h index 5bdac612..2fa174ba 100644 --- a/src/core/include/units/bits/equivalent.h +++ b/src/core/include/units/bits/equivalent.h @@ -23,6 +23,7 @@ #pragma once #include +#include #include namespace units { @@ -72,8 +73,7 @@ struct equivalent_impl : // additionally accounts for unknown dimensions template struct equivalent_unit : - std::disjunction, - std::bool_constant::mag == U2::mag / dimension_unit::mag>> {}; + std::disjunction, std::bool_constant::mag == reference::mag>> {}; // point origins diff --git a/src/core/include/units/bits/quantity_of.h b/src/core/include/units/bits/quantity_of.h index ef7e8d77..1e53d22b 100644 --- a/src/core/include/units/bits/quantity_of.h +++ b/src/core/include/units/bits/quantity_of.h @@ -37,9 +37,10 @@ inline constexpr bool same_exponents_of = false; template typename DimTemplate> inline constexpr bool same_exponents_of, DimTemplate> = - requires { typename DimTemplate, unknown_coherent_unit, typename Es::dimension...>; } && - std::same_as, typename DimTemplate, unknown_coherent_unit, - typename Es::dimension...>::recipe>; + requires { + typename DimTemplate, unknown_coherent_unit, typename Es::dimension...>; + }&& std::same_as, typename DimTemplate, unknown_coherent_unit, + typename Es::dimension...>::recipe>; } // namespace detail diff --git a/src/core/include/units/bits/ratio_maths.h b/src/core/include/units/bits/ratio_maths.h index d9a6494a..a6cbea4c 100644 --- a/src/core/include/units/bits/ratio_maths.h +++ b/src/core/include/units/bits/ratio_maths.h @@ -39,108 +39,20 @@ template return v < 0 ? -v : v; } -// the following functions enable gcd and related computations on ratios -// with exponents. They avoid overflow. Further information here: -// https://github.com/mpusz/units/issues/62#issuecomment-588152833 - -// Computes (a * b) mod m relies on unsigned integer arithmetic, should not -// overflow -[[nodiscard]] constexpr std::uint64_t mulmod(std::uint64_t a, std::uint64_t b, std::uint64_t m) -{ - std::uint64_t res = 0; - - if (b >= m) { - if (m > UINT64_MAX / 2u) { - b -= m; - } else { - b %= m; - } - } - - while (a != 0) { - if (a & 1) { - if (b >= m - res) { - res -= m; - } - res += b; - } - a >>= 1; - - std::uint64_t temp_b = b; - if (b >= m - b) { - temp_b -= m; - } - b += temp_b; - } - - return res; -} - -// Calculates (a ^ e) mod m , should not overflow. -[[nodiscard]] constexpr std::uint64_t modpow(std::uint64_t a, std::uint64_t e, std::uint64_t m) -{ - a %= m; - std::uint64_t result = 1; - - while (e > 0) { - if (e & 1) { - result = mulmod(result, a, m); - } - a = mulmod(a, a, m); - e >>= 1; - } - return result; -} - -// gcd(a * 10 ^ e, b), should not overflow -[[nodiscard]] constexpr std::intmax_t gcdpow(std::intmax_t a, std::intmax_t e, std::intmax_t b) noexcept -{ - assert(a > 0); - assert(e >= 0); - assert(b > 0); - - // gcd(i, j) = gcd(j, i mod j) for j != 0 Euclid; - // - // gcd(a 10^e, b) = gcd(b, a 10^e mod b) - // - // (a 10^e) mod b -> [ (a mod b) (10^e mod b) ] mod b - - return std::gcd( - b, static_cast(mulmod(static_cast(a % b), - modpow(10, static_cast(e), static_cast(b)), - static_cast(b)))); -} - -constexpr void cwap(std::intmax_t& lhs, std::intmax_t& rhs) -{ - std::intmax_t tmp = lhs; - lhs = rhs; - rhs = tmp; -} - -// Computes the rational gcd of n1/d1 x 10^e1 and n2/d2 x 10^e2 -[[nodiscard]] constexpr auto gcd_frac(std::intmax_t n1, std::intmax_t d1, std::intmax_t e1, std::intmax_t n2, - std::intmax_t d2, std::intmax_t e2) noexcept +// Computes the rational gcd of n1/d1 and n2/d2 +[[nodiscard]] constexpr auto gcd_frac(std::intmax_t n1, std::intmax_t d1, std::intmax_t n2, std::intmax_t d2) noexcept { // Short cut for equal ratios - if (n1 == n2 && d1 == d2 && e1 == e2) { - return std::array{n1, d1, e1}; + if (n1 == n2 && d1 == d2) { + return std::array{n1, d1}; } - if (e2 > e1) { - detail::cwap(n1, n2); - detail::cwap(d1, d2); - detail::cwap(e1, e2); - } - - std::intmax_t exp = e2; // minimum - // gcd(a/b,c/d) = gcd(a⋅d, c⋅b) / b⋅d assert(std::numeric_limits::max() / n1 > d2); assert(std::numeric_limits::max() / n2 > d1); - std::intmax_t num = detail::gcdpow(n1 * d2, e1 - e2, n2 * d1); + std::intmax_t num = std::gcd(n1 * d2, n2 * d1); assert(std::numeric_limits::max() / d1 > d2); @@ -148,29 +60,19 @@ constexpr void cwap(std::intmax_t& lhs, std::intmax_t& rhs) std::intmax_t gcd = std::gcd(num, den); - return std::array{num / gcd, den / gcd, exp}; + return std::array{num / gcd, den / gcd}; } -constexpr void normalize(std::intmax_t& num, std::intmax_t& den, std::intmax_t& exp) +constexpr void normalize(std::intmax_t& num, std::intmax_t& den) { if (num == 0) { den = 1; - exp = 0; return; } std::intmax_t gcd = std::gcd(num, den); num = num * (den < 0 ? -1 : 1) / gcd; den = detail::abs(den) / gcd; - - while (num % 10 == 0) { - num /= 10; - ++exp; - } - while (den % 10 == 0) { - den /= 10; - --exp; - } } [[nodiscard]] constexpr std::intmax_t safe_multiply(std::intmax_t lhs, std::intmax_t rhs) diff --git a/src/core/include/units/bits/unit_text.h b/src/core/include/units/bits/unit_text.h index f546a251..db0cf31a 100644 --- a/src/core/include/units/bits/unit_text.h +++ b/src/core/include/units/bits/unit_text.h @@ -37,7 +37,7 @@ constexpr auto magnitude_text() { constexpr auto exp10 = extract_power_of_10(M); - constexpr Magnitude auto base = M / pow(as_magnitude<10>()); + constexpr Magnitude auto base = M / pow(mag<10>()); constexpr Magnitude auto num = numerator(base); constexpr Magnitude auto den = denominator(base); static_assert(base == num / den, "Printing rational powers, or irrational bases, not yet supported"); @@ -72,7 +72,7 @@ constexpr auto magnitude_text() template constexpr auto prefix_or_magnitude_text() { - if constexpr (M == as_magnitude<1>()) { + if constexpr (M == mag<1>()) { // no ratio/prefix return basic_fixed_string(""); } else { diff --git a/src/core/include/units/chrono.h b/src/core/include/units/chrono.h index 91b4ab6b..d7b900a1 100644 --- a/src/core/include/units/chrono.h +++ b/src/core/include/units/chrono.h @@ -34,7 +34,7 @@ namespace units { template struct quantity_like_traits> { private: - static constexpr auto mag = as_magnitude(); + static constexpr auto mag = ::units::mag(); public: using dimension = isq::si::dim_time; using unit = downcast_unit; @@ -48,7 +48,7 @@ struct clock_origin : point_origin {}; template struct quantity_point_like_traits>> { private: - static constexpr auto mag = as_magnitude(); + static constexpr auto mag = ::units::mag(); public: using origin = clock_origin; using unit = downcast_unit; @@ -75,12 +75,7 @@ constexpr std::intmax_t pow_10(std::intmax_t v) template constexpr auto to_std_ratio_impl() { - if constexpr (R.exp == 0) - return std::ratio{}; - else if constexpr (R.exp > 0) - return std::ratio{}; - else - return std::ratio{}; + return std::ratio{}; } } // namespace detail diff --git a/src/core/include/units/derived_dimension.h b/src/core/include/units/derived_dimension.h index cfe0830c..0af2e096 100644 --- a/src/core/include/units/derived_dimension.h +++ b/src/core/include/units/derived_dimension.h @@ -23,7 +23,7 @@ #pragma once #include -#include +#include #include #include #include @@ -72,8 +72,8 @@ using make_dimension = TYPENAME to_derived_dimension_base< * and in CGS barye. Those two units are not directly related with each other with some ratio. As they both are * coherent units of their dimensions, the ratio between them is directly determined by the ratios of base units * defined in base dimensions end their exponents in the derived dimension recipe. To provide interoperability of - * such quantities of different systems base_units_ratio is being used. The result of the division of two - * base_units_ratio of two quantities of equivalent dimensions in two different systems gives a ratio between their + * such quantities of different systems mag is being used. The result of the division of two + * mag of two quantities of equivalent dimensions in two different systems gives a ratio between their * coherent units. Alternatively, the user would always have to directly define a barye in terms of pascal or vice * versa. * @@ -85,8 +85,8 @@ template struct derived_dimension : downcast_dispatch> { using recipe = exponent_list; using coherent_unit = U; - static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto base_units_ratio = - detail::absolute_magnitude(typename derived_dimension::exponents()); + static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = + detail::absolute_magnitude(typename derived_dimension::exponents()) / U::mag; }; } // namespace units diff --git a/src/core/include/units/generic/dimensionless.h b/src/core/include/units/generic/dimensionless.h index fc7b97e6..576547e6 100644 --- a/src/core/include/units/generic/dimensionless.h +++ b/src/core/include/units/generic/dimensionless.h @@ -31,7 +31,7 @@ namespace units { struct one : derived_unit {}; -struct percent : named_scaled_unit(), one> {}; +struct percent : named_scaled_unit(), one> {}; /** * @brief Dimension one diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index 1257e132..c81fb538 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -189,9 +189,6 @@ constexpr widen_t compute_base_power(BasePower auto bp) if (bp.power.den != 1) { throw std::invalid_argument{"Rational powers not yet supported"}; } - if (bp.power.exp < 0) { - throw std::invalid_argument{"Unsupported exp value"}; - } if (bp.power.num < 0) { if constexpr (std::is_integral_v) { @@ -201,7 +198,7 @@ constexpr widen_t compute_base_power(BasePower auto bp) } } - auto power = numerator(bp.power); + auto power = bp.power.num; return int_power(static_cast>(bp.get_base()), power); } @@ -344,7 +341,7 @@ inline constexpr bool is_base_power_pack_valid = all_base_powers_valid & constexpr bool is_rational(BasePower auto bp) { - return std::is_integral_v && (bp.power.den == 1) && (bp.power.exp >= 0); + return std::is_integral_v && (bp.power.den == 1); } constexpr bool is_integral(BasePower auto bp) { return is_rational(bp) && bp.power.num > 0; } @@ -498,8 +495,8 @@ namespace detail { template constexpr auto integer_part(magnitude) { - constexpr auto power_num = numerator(BP.power); - constexpr auto power_den = denominator(BP.power); + constexpr auto power_num = BP.power.num; + constexpr auto power_den = BP.power.den; if constexpr (std::is_integral_v && (power_num >= power_den)) { constexpr auto largest_integer_power = [=](BasePower auto bp) { @@ -556,7 +553,7 @@ namespace detail { template constexpr auto remove_positive_power(magnitude m) { - if constexpr (numerator(BP.power) < 0) { + if constexpr (BP.power.num < 0) { return m; } else { return magnitude<>{}; @@ -599,7 +596,7 @@ constexpr auto common_magnitude(magnitude, magnitude) } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// `as_magnitude()` implementation. +// `mag()` implementation. // Sometimes we need to give the compiler a "shortcut" when factorizing large numbers (specifically, numbers whose // _first factor_ is very large). If we don't, we can run into limits on the number of constexpr steps or iterations. @@ -650,10 +647,19 @@ inline constexpr auto prime_factorization_v = prime_factorization::value; */ template requires(R.num > 0) -constexpr Magnitude auto as_magnitude() +constexpr Magnitude auto mag() { - return pow(detail::prime_factorization_v<10>) * detail::prime_factorization_v / - detail::prime_factorization_v; + return detail::prime_factorization_v / detail::prime_factorization_v; +} + +/** + * @brief Create a Magnitude which is some rational number raised to a rational power. + */ +template + requires(Base.num > 0) +constexpr Magnitude auto mag_power() +{ + return pow(mag()); } namespace detail { @@ -663,7 +669,7 @@ constexpr ratio get_power(T base, magnitude) return ((BPs.get_base() == base ? BPs.power : ratio{0}) + ... + ratio{0}); } -constexpr std::intmax_t integer_part(ratio r) { return numerator(r) / denominator(r); } +constexpr std::intmax_t integer_part(ratio r) { return r.num / r.den; } constexpr std::intmax_t extract_power_of_10(Magnitude auto m) { diff --git a/src/core/include/units/math.h b/src/core/include/units/math.h index 0de45139..3c67e945 100644 --- a/src/core/include/units/math.h +++ b/src/core/include/units/math.h @@ -301,4 +301,42 @@ template D, typename U, std::s return ::units::round(q); } +/** + * @brief Computes the square root of the sum of the squares of x and y, + * without undue overflow or underflow at intermediate stages of the computation + */ +template +[[nodiscard]] inline std::common_type_t hypot(const Q1& x, const Q2& y) noexcept + requires requires { typename std::common_type_t; } && + requires(std::common_type_t q) { + requires requires { hypot(q.number(), q.number()); } || requires { std::hypot(q.number(), q.number()); }; + } +{ + using type = std::common_type_t; + type xx = x; + type yy = y; + using std::hypot; + return type(hypot(xx.number(), yy.number())); +} + +/** + * @brief Computes the square root of the sum of the squares of x, y, and z, + * without undue overflow or underflow at intermediate stages of the computation + */ +template +[[nodiscard]] inline std::common_type_t hypot(const Q1& x, const Q2& y, const Q3& z) noexcept + requires requires { typename std::common_type_t; } && + requires(std::common_type_t q) { + requires requires { hypot(q.number(), q.number(), q.number()); } || + requires { std::hypot(q.number(), q.number(), q.number()); }; + } +{ + using type = std::common_type_t; + type xx = x; + type yy = y; + type zz = z; + using std::hypot; + return type(hypot(xx.number(), yy.number(), zz.number())); +} + } // namespace units diff --git a/src/core/include/units/quantity.h b/src/core/include/units/quantity.h index 6f9f9d0c..c1e99d29 100644 --- a/src/core/include/units/quantity.h +++ b/src/core/include/units/quantity.h @@ -405,11 +405,7 @@ public: requires(!Quantity) && (invoke_result_convertible_to_, const Value&, rep>) [[nodiscard]] friend constexpr Quantity auto operator/(const Value& v, const quantity& q) { - gsl_ExpectsAudit(q.number() != quantity_values::zero()); - using dim = dim_invert; - using ret_unit = downcast_unit(U::mag)>; - using ret = quantity, Value, rep>>; - return ret(v / q.number()); + return detail::make_quantity<::units::reference{} / reference>(v / q.number()); } template @@ -444,7 +440,7 @@ public: }; // CTAD -#if !UNITS_COMP_CLANG || UNITS_COMP_CLANG > 15 +#if !UNITS_COMP_CLANG || UNITS_COMP_CLANG > 16 template explicit(false) quantity(Rep&&)->quantity; #endif diff --git a/src/core/include/units/quantity_cast.h b/src/core/include/units/quantity_cast.h index fdb90aef..5295f3a1 100644 --- a/src/core/include/units/quantity_cast.h +++ b/src/core/include/units/quantity_cast.h @@ -28,6 +28,7 @@ #include #include #include +#include UNITS_DIAGNOSTIC_PUSH // warning C4244: 'argument': conversion from 'intmax_t' to 'T', possible loss of data with T=int @@ -49,17 +50,8 @@ class quantity_point_kind; namespace detail { -template -inline constexpr Magnitude auto quantity_magnitude = std::enable_if_t, magnitude<>>{}; - -template -inline constexpr Magnitude auto quantity_magnitude> = [] { - if constexpr (BaseDimension) { - return U::mag; - } else { - return D::base_units_ratio * U::mag / D::coherent_unit::mag; - } -}(); +template +inline constexpr Magnitude auto quantity_magnitude = decltype(Q::reference)::mag; template inline constexpr ratio quantity_ratio = as_ratio(quantity_magnitude); @@ -79,8 +71,8 @@ template struct cast_traits; template - requires common_type_with_, - std::intmax_t> struct cast_traits { + requires common_type_with_, std::intmax_t> +struct cast_traits { using ratio_type = std::common_type_t, std::intmax_t>; using rep_type = ratio_type; }; diff --git a/src/core/include/units/ratio.h b/src/core/include/units/ratio.h index b57ce06d..d0bd8ec3 100644 --- a/src/core/include/units/ratio.h +++ b/src/core/include/units/ratio.h @@ -43,42 +43,28 @@ constexpr ratio inverse(const ratio& r); /** * @brief Provides compile-time rational arithmetic support. * - * This class is really similar to @c std::ratio but gets an additional `Exp` - * template parameter that defines the exponent of the ratio. Another important - * difference is the fact that the objects of that class are used as class NTTPs - * rather then a type template parameter kind. + * This class is really similar to @c std::ratio. An important difference is the fact that the objects of that class + * are used as class NTTPs rather then a type template parameter kind. */ struct ratio { std::intmax_t num; std::intmax_t den; - std::intmax_t exp; - constexpr explicit(false) ratio(std::intmax_t n, std::intmax_t d = 1, std::intmax_t e = 0) : num(n), den(d), exp(e) + constexpr explicit(false) ratio(std::intmax_t n, std::intmax_t d = 1) : num(n), den(d) { gsl_Expects(den != 0); - detail::normalize(num, den, exp); + detail::normalize(num, den); } [[nodiscard]] friend constexpr bool operator==(const ratio&, const ratio&) = default; [[nodiscard]] friend constexpr auto operator<=>(const ratio& lhs, const ratio& rhs) { return (lhs - rhs).num <=> 0; } - [[nodiscard]] friend constexpr ratio operator-(const ratio& r) { return ratio(-r.num, r.den, r.exp); } + [[nodiscard]] friend constexpr ratio operator-(const ratio& r) { return ratio(-r.num, r.den); } [[nodiscard]] friend constexpr ratio operator+(ratio lhs, ratio rhs) { - // First, get the inputs into a common exponent. - const auto common_exp = std::min(lhs.exp, rhs.exp); - auto commonify = [common_exp](ratio& r) { - while (r.exp > common_exp) { - r.num *= 10; - --r.exp; - } - }; - commonify(lhs); - commonify(rhs); - - return ratio{lhs.num * rhs.den + lhs.den * rhs.num, lhs.den * rhs.den, common_exp}; + return ratio{lhs.num * rhs.den + lhs.den * rhs.num, lhs.den * rhs.den}; } [[nodiscard]] friend constexpr ratio operator-(const ratio& lhs, const ratio& rhs) { return lhs + (-rhs); } @@ -88,96 +74,27 @@ struct ratio { const std::intmax_t gcd1 = std::gcd(lhs.num, rhs.den); const std::intmax_t gcd2 = std::gcd(rhs.num, lhs.den); return ratio(detail::safe_multiply(lhs.num / gcd1, rhs.num / gcd2), - detail::safe_multiply(lhs.den / gcd2, rhs.den / gcd1), lhs.exp + rhs.exp); + detail::safe_multiply(lhs.den / gcd2, rhs.den / gcd1)); } [[nodiscard]] friend constexpr ratio operator/(const ratio& lhs, const ratio& rhs) { return lhs * inverse(rhs); } - - [[nodiscard]] friend constexpr std::intmax_t numerator(const ratio& r) - { - std::intmax_t true_num = r.num; - for (auto i = r.exp; i > 0; --i) { - true_num *= 10; - } - return true_num; - } - - [[nodiscard]] friend constexpr std::intmax_t denominator(const ratio& r) - { - std::intmax_t true_den = r.den; - for (auto i = r.exp; i < 0; ++i) { - true_den *= 10; - } - return true_den; - } }; -[[nodiscard]] constexpr ratio inverse(const ratio& r) { return ratio(r.den, r.num, -r.exp); } +[[nodiscard]] constexpr ratio inverse(const ratio& r) { return ratio(r.den, r.num); } -[[nodiscard]] constexpr bool is_integral(const ratio& r) -{ - if (r.exp < 0) { - return false; - } else { - return detail::gcdpow(r.num, r.exp, r.den) == r.den; - } -} +[[nodiscard]] constexpr bool is_integral(const ratio& r) { return r.num % r.den == 0; } -namespace detail { - -[[nodiscard]] constexpr auto make_exp_align(const ratio& r, std::intmax_t alignment) -{ - gsl_Expects(alignment > 0); - const std::intmax_t rem = r.exp % alignment; - - if (rem == 0) { // already aligned - return std::array{r.num, r.den, r.exp}; - } - - if (r.exp > 0) { // remainder is positive - return std::array{r.num * ipow10(rem), r.den, r.exp - rem}; - } - - // remainder is negative - return std::array{r.num, r.den * ipow10(-rem), r.exp - rem}; -} - -template - requires gt_zero -[[nodiscard]] constexpr ratio root(const ratio& r) -{ - if constexpr (N == 1) { - return r; - } else { - if (r.num == 0) { - return ratio(0); - } - - const auto aligned = make_exp_align(r, N); - return ratio(iroot(aligned[0]), iroot(aligned[1]), aligned[2] / N); - } -} - -} // namespace detail - -template - requires detail::non_zero +template [[nodiscard]] constexpr ratio pow(const ratio& r) { if constexpr (Num == 0) { return ratio(1); - } else if constexpr (Num == Den) { + } else if constexpr (Num == 1) { return r; } else { - // simplify factors first and compute power for positive exponent - constexpr std::intmax_t gcd = std::gcd(Num, Den); - constexpr std::intmax_t num = detail::abs(Num / gcd); - constexpr std::intmax_t den = detail::abs(Den / gcd); + const ratio result = detail::pow_impl(r); - // integer root loses precision so do pow first - const ratio result = detail::root(detail::pow_impl(r)); - - if constexpr (Num * Den < 0) { // account for negative exponent + if constexpr (Num < 0) { // account for negative exponent return inverse(result); } else { return result; @@ -185,15 +102,11 @@ template } } -[[nodiscard]] constexpr ratio sqrt(const ratio& r) { return pow<1, 2>(r); } - -[[nodiscard]] constexpr ratio cbrt(const ratio& r) { return pow<1, 3>(r); } - // common_ratio [[nodiscard]] constexpr ratio common_ratio(const ratio& r1, const ratio& r2) { - const auto res = detail::gcd_frac(r1.num, r1.den, r1.exp, r2.num, r2.den, r2.exp); - return ratio(res[0], res[1], res[2]); + const auto res = detail::gcd_frac(r1.num, r1.den, r2.num, r2.den); + return ratio(res[0], res[1]); } } // namespace units diff --git a/src/core/include/units/reference.h b/src/core/include/units/reference.h index 200cc2fb..c6a68fb1 100644 --- a/src/core/include/units/reference.h +++ b/src/core/include/units/reference.h @@ -35,27 +35,21 @@ struct reference; namespace detail { -template -using reference_multiply_impl = - reference::mag) * (U2::mag / dimension_unit::mag) * - dimension_unit::mag>>; +template +using reference_multiply_impl = reference>; -template -using reference_divide_impl = - reference::mag) / (U2::mag / dimension_unit::mag) * - dimension_unit::mag>>; +template +using reference_divide_impl = reference>; } // namespace detail template using reference_multiply = - detail::reference_multiply_impl, - typename R1::dimension, typename R1::unit, typename R2::dimension, typename R2::unit>; + detail::reference_multiply_impl, R1, R2>; template using reference_divide = - detail::reference_divide_impl, - typename R1::dimension, typename R1::unit, typename R2::dimension, typename R2::unit>; + detail::reference_divide_impl, R1, R2>; /** * @brief The type for quantity references @@ -100,6 +94,7 @@ template U> struct reference { using dimension = D; using unit = U; + static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = dimension::mag * unit::mag; // Hidden Friends // Below friend functions are to be found via argument-dependent lookup only diff --git a/src/core/include/units/unit.h b/src/core/include/units/unit.h index 52b9779c..c388369c 100644 --- a/src/core/include/units/unit.h +++ b/src/core/include/units/unit.h @@ -24,6 +24,7 @@ #include #include +#include // IWYU pragma: begin_exports #include @@ -57,6 +58,9 @@ inline constexpr bool can_be_prefixed = false; * * @tparam M a Magnitude representing the (relative) size of this unit * @tparam U a unit to use as a reference for this dimension + * + * @note U cannot be constrained with Unit as for some specializations (i.e. named_unit) + * it gets the incomplete child's type with the CRTP idiom. */ template struct scaled_unit : downcast_base> { @@ -80,7 +84,7 @@ struct same_unit_reference : is_same -struct named_unit : downcast_dispatch(), Child>> { +struct named_unit : downcast_dispatch(), Child>> { static constexpr auto symbol = Symbol; }; @@ -126,7 +130,7 @@ struct prefixed_unit : downcast_dispatch -struct derived_unit : downcast_dispatch(), Child>> {}; +struct derived_unit : downcast_dispatch(), Child>> {}; /** * @brief A unit with a deduced ratio and symbol @@ -173,12 +177,8 @@ struct alias_unit : U { * @tparam P prefix to be appied to the reference unit * @tparam AU reference alias unit */ -// TODO gcc bug: 95015 -// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95015 -// template -// requires (!AliasUnit) -template - requires detail::can_be_prefixed +template + requires(!AliasUnit) struct prefixed_alias_unit : U { static constexpr auto symbol = P::symbol + AU::symbol; }; @@ -188,7 +188,10 @@ struct prefixed_alias_unit : U { * * Used as a coherent unit of an unknown dimension. */ -struct unknown_coherent_unit : derived_unit {}; +template +struct unknown_coherent_unit : + downcast_dispatch, + scaled_unit()), unknown_coherent_unit>> {}; namespace detail { diff --git a/src/mp-unitsConfig.cmake b/src/mp-unitsConfig.cmake index 8b63a787..668fd97d 100644 --- a/src/mp-unitsConfig.cmake +++ b/src/mp-unitsConfig.cmake @@ -30,6 +30,7 @@ function(__check_libcxx_in_use variable) set(${variable} ${${variable}} PARENT_SCOPE) list(POP_BACK CMAKE_MESSAGE_INDENT) + if(${variable}) message(CHECK_PASS "found") else() @@ -39,15 +40,21 @@ function(__check_libcxx_in_use variable) endfunction() include(CMakeFindDependencyMacro) -find_dependency(fmt) + +if(UNITS_USE_LIBFMT) + find_dependency(fmt) +endif() + find_dependency(gsl-lite) # add range-v3 dependency only for clang + libc++ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") __check_libcxx_in_use(__units_libcxx) + if(__units_libcxx AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "14") find_dependency(range-v3) endif() + unset(__units_libcxx) endif() diff --git a/src/systems/isq-iec80000/include/units/isq/iec80000/binary_prefixes.h b/src/systems/isq-iec80000/include/units/isq/iec80000/binary_prefixes.h index 25fdaaa9..a9fe73dd 100644 --- a/src/systems/isq-iec80000/include/units/isq/iec80000/binary_prefixes.h +++ b/src/systems/isq-iec80000/include/units/isq/iec80000/binary_prefixes.h @@ -26,13 +26,13 @@ namespace units::isq::iec80000 { -struct kibi : prefix(as_magnitude<2>())> {}; -struct mebi : prefix(as_magnitude<2>())> {}; -struct gibi : prefix(as_magnitude<2>())> {}; -struct tebi : prefix(as_magnitude<2>())> {}; -struct pebi : prefix(as_magnitude<2>())> {}; -struct exbi : prefix(as_magnitude<2>())> {}; -struct zebi : prefix(as_magnitude<2>())> {}; -struct yobi : prefix(as_magnitude<2>())> {}; +struct kibi : prefix(mag<2>())> {}; +struct mebi : prefix(mag<2>())> {}; +struct gibi : prefix(mag<2>())> {}; +struct tebi : prefix(mag<2>())> {}; +struct pebi : prefix(mag<2>())> {}; +struct exbi : prefix(mag<2>())> {}; +struct zebi : prefix(mag<2>())> {}; +struct yobi : prefix(mag<2>())> {}; } // namespace units::isq::iec80000 diff --git a/src/systems/isq-iec80000/include/units/isq/iec80000/storage_capacity.h b/src/systems/isq-iec80000/include/units/isq/iec80000/storage_capacity.h index eaf7a7ca..2ca2f0d7 100644 --- a/src/systems/isq-iec80000/include/units/isq/iec80000/storage_capacity.h +++ b/src/systems/isq-iec80000/include/units/isq/iec80000/storage_capacity.h @@ -53,7 +53,7 @@ struct tebibit : prefixed_unit {}; struct pebibit : prefixed_unit {}; struct exbibit : prefixed_unit {}; -struct byte : named_scaled_unit(), bit> {}; +struct byte : named_scaled_unit(), bit> {}; struct kilobyte : prefixed_unit {}; struct megabyte : prefixed_unit {}; diff --git a/src/systems/si-fps/include/units/isq/si/fps/force.h b/src/systems/si-fps/include/units/isq/si/fps/force.h index a026bd79..f8569719 100644 --- a/src/systems/si-fps/include/units/isq/si/fps/force.h +++ b/src/systems/si-fps/include/units/isq/si/fps/force.h @@ -40,7 +40,7 @@ namespace units::isq::si::fps { struct poundal : named_unit {}; // https://en.wikipedia.org/wiki/Pound_(force) -struct pound_force : named_scaled_unit(), poundal> {}; +struct pound_force : named_scaled_unit(), poundal> {}; struct kilopound_force : prefixed_unit {}; diff --git a/src/systems/si-fps/include/units/isq/si/fps/length.h b/src/systems/si-fps/include/units/isq/si/fps/length.h index 04a9e930..adc29a8d 100644 --- a/src/systems/si-fps/include/units/isq/si/fps/length.h +++ b/src/systems/si-fps/include/units/isq/si/fps/length.h @@ -48,7 +48,7 @@ struct thousandth : alias_unit {}; struct kiloyard : prefixed_unit {}; -struct nautical_mile : named_scaled_unit(), yard> {}; +struct nautical_mile : named_scaled_unit(), yard> {}; struct dim_length : isq::dim_length {}; diff --git a/src/systems/si-fps/include/units/isq/si/fps/mass.h b/src/systems/si-fps/include/units/isq/si/fps/mass.h index 16b23f0a..246aba06 100644 --- a/src/systems/si-fps/include/units/isq/si/fps/mass.h +++ b/src/systems/si-fps/include/units/isq/si/fps/mass.h @@ -35,28 +35,28 @@ namespace units::isq::si::fps { // https://en.wikipedia.org/wiki/Pound_(mass) -struct pound : named_scaled_unit(), si::kilogram> {}; +struct pound : named_scaled_unit(), si::kilogram> {}; struct dim_mass : isq::dim_mass {}; template U, Representation Rep = double> using mass = quantity; -struct grain : named_scaled_unit(), pound> {}; +struct grain : named_scaled_unit(), pound> {}; -struct dram : named_scaled_unit(), pound> {}; +struct dram : named_scaled_unit(), pound> {}; -struct ounce : named_scaled_unit(), pound> {}; +struct ounce : named_scaled_unit(), pound> {}; -struct stone : named_scaled_unit(), pound> {}; +struct stone : named_scaled_unit(), pound> {}; -struct quarter : named_scaled_unit(), pound> {}; +struct quarter : named_scaled_unit(), pound> {}; -struct hundredweight : named_scaled_unit(), pound> {}; +struct hundredweight : named_scaled_unit(), pound> {}; -struct short_ton : named_scaled_unit(), pound> {}; +struct short_ton : named_scaled_unit(), pound> {}; -struct long_ton : named_scaled_unit(), pound> {}; +struct long_ton : named_scaled_unit(), pound> {}; #ifndef UNITS_NO_LITERALS diff --git a/src/systems/si-fps/include/units/isq/si/fps/power.h b/src/systems/si-fps/include/units/isq/si/fps/power.h index 931c08f4..85930563 100644 --- a/src/systems/si-fps/include/units/isq/si/fps/power.h +++ b/src/systems/si-fps/include/units/isq/si/fps/power.h @@ -42,7 +42,7 @@ struct dim_power : isq::dim_power {}; -struct horse_power : named_scaled_unit(), foot_pound_force_per_second> {}; +struct horse_power : named_scaled_unit(), foot_pound_force_per_second> {}; template U, Representation Rep = double> using power = quantity; diff --git a/src/systems/si-fps/include/units/isq/si/fps/pressure.h b/src/systems/si-fps/include/units/isq/si/fps/pressure.h index 4b036fc1..e155d961 100644 --- a/src/systems/si-fps/include/units/isq/si/fps/pressure.h +++ b/src/systems/si-fps/include/units/isq/si/fps/pressure.h @@ -44,11 +44,10 @@ template U, Representation Rep = double> using pressure = quantity; struct pound_force_per_foot_sq : - named_scaled_unit(), - poundal_per_foot_sq> {}; + named_scaled_unit(), poundal_per_foot_sq> {}; struct pound_force_per_inch_sq : - named_scaled_unit(), pound_force_per_foot_sq> {}; + named_scaled_unit(), pound_force_per_foot_sq> {}; struct kilopound_force_per_inch_sq : prefixed_unit {}; diff --git a/src/systems/si-hep/include/units/isq/si/hep/area.h b/src/systems/si-hep/include/units/isq/si/hep/area.h index e08b59eb..65130f8a 100644 --- a/src/systems/si-hep/include/units/isq/si/hep/area.h +++ b/src/systems/si-hep/include/units/isq/si/hep/area.h @@ -37,7 +37,7 @@ namespace units::isq::si::hep { // effective cross-sectional area according to EU council directive 80/181/EEC // https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:01980L0181-20090527#page=10 // https://www.fedlex.admin.ch/eli/cc/1994/3109_3109_3109/de -struct barn : named_scaled_unit(), square_metre> {}; +struct barn : named_scaled_unit(), square_metre> {}; struct yocto_barn : prefixed_unit {}; struct zepto_barn : prefixed_unit {}; struct atto_barn : prefixed_unit {}; diff --git a/src/systems/si-hep/include/units/isq/si/hep/mass.h b/src/systems/si-hep/include/units/isq/si/hep/mass.h index e4a895e0..cf97b869 100644 --- a/src/systems/si-hep/include/units/isq/si/hep/mass.h +++ b/src/systems/si-hep/include/units/isq/si/hep/mass.h @@ -44,7 +44,7 @@ namespace units::isq::si::hep { struct eV_per_c2 : named_scaled_unit(), kilogram> {}; + mag() * mag_power<10, -35>(), kilogram> {}; struct feV_per_c2 : prefixed_unit {}; struct peV_per_c2 : prefixed_unit {}; struct neV_per_c2 : prefixed_unit {}; @@ -60,11 +60,14 @@ struct PeV_per_c2 : prefixed_unit {}; struct EeV_per_c2 : prefixed_unit {}; struct YeV_per_c2 : prefixed_unit {}; struct electron_mass : - named_scaled_unit(), kilogram> {}; + named_scaled_unit() * mag_power<10, -31>(), + kilogram> {}; struct proton_mass : - named_scaled_unit(), kilogram> {}; + named_scaled_unit() * mag_power<10, -27>(), + kilogram> {}; struct neutron_mass : - named_scaled_unit(), kilogram> {}; + named_scaled_unit() * mag_power<10, -27>(), + kilogram> {}; struct dim_mass : isq::dim_mass {}; diff --git a/src/systems/si-hep/include/units/isq/si/hep/momentum.h b/src/systems/si-hep/include/units/isq/si/hep/momentum.h index c6791501..a03ae821 100644 --- a/src/systems/si-hep/include/units/isq/si/hep/momentum.h +++ b/src/systems/si-hep/include/units/isq/si/hep/momentum.h @@ -41,7 +41,7 @@ namespace units::isq::si::hep { struct kilogram_metre_per_second : derived_unit {}; struct eV_per_c : - named_scaled_unit(), + named_scaled_unit() * mag_power<10, -35>(), kilogram_metre_per_second> {}; struct feV_per_c : prefixed_unit {}; struct peV_per_c : prefixed_unit {}; diff --git a/src/systems/si-iau/include/units/isq/si/iau/length.h b/src/systems/si-iau/include/units/isq/si/iau/length.h index d2e184a1..974dd8fc 100644 --- a/src/systems/si-iau/include/units/isq/si/iau/length.h +++ b/src/systems/si-iau/include/units/isq/si/iau/length.h @@ -36,13 +36,13 @@ namespace units::isq::si::iau { // https://en.wikipedia.org/wiki/Light-year -struct light_year : named_scaled_unit(), si::metre> {}; +struct light_year : named_scaled_unit(), si::metre> {}; // https://en.wikipedia.org/wiki/Parsec -struct parsec : named_scaled_unit(), si::metre> {}; +struct parsec : named_scaled_unit(), si::metre> {}; // https://en.wikipedia.org/wiki/Angstrom -struct angstrom : named_scaled_unit(), si::metre> {}; +struct angstrom : named_scaled_unit(), si::metre> {}; #ifndef UNITS_NO_LITERALS diff --git a/src/systems/si-imperial/include/units/isq/si/imperial/length.h b/src/systems/si-imperial/include/units/isq/si/imperial/length.h index 5f4be435..f6c278f4 100644 --- a/src/systems/si-imperial/include/units/isq/si/imperial/length.h +++ b/src/systems/si-imperial/include/units/isq/si/imperial/length.h @@ -35,10 +35,10 @@ namespace units::isq::si::imperial { // https://en.wikipedia.org/wiki/Chain_(unit) -struct chain : named_scaled_unit(), si::international::yard> {}; +struct chain : named_scaled_unit(), si::international::yard> {}; // https://en.wikipedia.org/wiki/Rod_(unit) -struct rod : named_scaled_unit(), chain> {}; +struct rod : named_scaled_unit(), chain> {}; #ifndef UNITS_NO_LITERALS diff --git a/src/systems/si-international/include/units/isq/si/international/length.h b/src/systems/si-international/include/units/isq/si/international/length.h index b61bd2cf..3940cb34 100644 --- a/src/systems/si-international/include/units/isq/si/international/length.h +++ b/src/systems/si-international/include/units/isq/si/international/length.h @@ -37,30 +37,30 @@ namespace units::isq::si::international { // si::international yard // https://en.wikipedia.org/wiki/International_yard_and_pound -struct yard : named_scaled_unit(), si::metre> {}; +struct yard : named_scaled_unit(), si::metre> {}; // si::international foot // https://en.wikipedia.org/wiki/Foot_(unit)#International_foot -struct foot : named_scaled_unit(), yard> {}; +struct foot : named_scaled_unit(), yard> {}; // https://en.wikipedia.org/wiki/Fathom#International_fathom -struct fathom : named_scaled_unit(), yard> {}; +struct fathom : named_scaled_unit(), yard> {}; // si::international inch // https://en.wikipedia.org/wiki/Inch#Equivalences -struct inch : named_scaled_unit(), yard> {}; +struct inch : named_scaled_unit(), yard> {}; // intrnational mile // https://en.wikipedia.org/wiki/Mile#International_mile -struct mile : named_scaled_unit(), si::kilometre> {}; +struct mile : named_scaled_unit(), si::kilometre> {}; // si::international nautical mile // https://en.wikipedia.org/wiki/Nautical_mile -struct nautical_mile : named_scaled_unit(), si::metre> {}; +struct nautical_mile : named_scaled_unit(), si::metre> {}; // thou // https://en.wikipedia.org/wiki/Thousandth_of_an_inch -struct thou : named_scaled_unit(), inch> {}; +struct thou : named_scaled_unit(), inch> {}; // mil - different name for thou // https://en.wikipedia.org/wiki/Thousandth_of_an_inch diff --git a/src/systems/si-typographic/include/units/isq/si/typographic/length.h b/src/systems/si-typographic/include/units/isq/si/typographic/length.h index e69c31c6..e5b409c6 100644 --- a/src/systems/si-typographic/include/units/isq/si/typographic/length.h +++ b/src/systems/si-typographic/include/units/isq/si/typographic/length.h @@ -37,12 +37,13 @@ namespace units::isq::si::typographic { // TODO Conflicts with (https://en.wikipedia.org/wiki/Pica_(typography)), verify correctness of below conversion factors // and provide hyperlinks to definitions -struct pica_comp : - named_scaled_unit(), si::metre> {}; -struct pica_prn : named_scaled_unit(), si::metre> {}; +struct pica_comp : named_scaled_unit() * mag_power<10, -9>(), si::metre> {}; +struct pica_prn : + named_scaled_unit() * mag_power<10, -3>(), si::metre> {}; struct point_comp : - named_scaled_unit(), si::metre> {}; -struct point_prn : named_scaled_unit(), si::metre> {}; + named_scaled_unit() * mag_power<10, -4>(), si::metre> {}; +struct point_prn : + named_scaled_unit() * mag_power<10, -4>(), si::metre> {}; #ifndef UNITS_NO_LITERALS diff --git a/src/systems/si-uscs/include/units/isq/si/uscs/length.h b/src/systems/si-uscs/include/units/isq/si/uscs/length.h index fe4eb9f1..e2dafbb5 100644 --- a/src/systems/si-uscs/include/units/isq/si/uscs/length.h +++ b/src/systems/si-uscs/include/units/isq/si/uscs/length.h @@ -36,14 +36,14 @@ namespace units::isq::si::uscs { // https://en.wikipedia.org/wiki/Foot_(unit)#US_survey_foot // https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6 -struct foot : named_scaled_unit(), si::metre> {}; +struct foot : named_scaled_unit(), si::metre> {}; // https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6 -struct fathom : named_scaled_unit(), foot> {}; +struct fathom : named_scaled_unit(), foot> {}; // https://en.wikipedia.org/wiki/Mile#U.S._survey_mile // https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6 -struct mile : named_scaled_unit(), foot> {}; +struct mile : named_scaled_unit(), foot> {}; #ifndef UNITS_NO_LITERALS diff --git a/src/systems/si/include/units/isq/si/catalytic_activity.h b/src/systems/si/include/units/isq/si/catalytic_activity.h index 07ca426f..926705d6 100644 --- a/src/systems/si/include/units/isq/si/catalytic_activity.h +++ b/src/systems/si/include/units/isq/si/catalytic_activity.h @@ -58,7 +58,7 @@ struct exakatal : prefixed_unit {}; struct zettakatal : prefixed_unit {}; struct yottakatal : prefixed_unit {}; -struct enzyme_unit : named_scaled_unit(), katal> {}; +struct enzyme_unit : named_scaled_unit() * mag_power<10, -6>(), katal> {}; struct dim_catalytic_activity : isq::dim_catalytic_activity {}; diff --git a/src/systems/si/include/units/isq/si/energy.h b/src/systems/si/include/units/isq/si/energy.h index cf0eba26..7511c4d0 100644 --- a/src/systems/si/include/units/isq/si/energy.h +++ b/src/systems/si/include/units/isq/si/energy.h @@ -56,7 +56,7 @@ struct yottajoule : prefixed_unit {}; // N.B. electron charge (and eV) is an exact constant: // https://www.bipm.org/documents/20126/41483022/SI-Brochure-9.pdf#page=147 struct electronvolt : - named_scaled_unit(), joule> {}; + named_scaled_unit() * mag_power<10, -19>(), joule> {}; struct gigaelectronvolt : prefixed_unit {}; struct dim_energy : isq::dim_energy {}; diff --git a/src/systems/si/include/units/isq/si/length.h b/src/systems/si/include/units/isq/si/length.h index aefebdbb..6585ed92 100644 --- a/src/systems/si/include/units/isq/si/length.h +++ b/src/systems/si/include/units/isq/si/length.h @@ -56,7 +56,7 @@ struct exametre : prefixed_unit {}; struct zettametre : prefixed_unit {}; struct yottametre : prefixed_unit {}; -struct astronomical_unit : named_scaled_unit(), metre> {}; +struct astronomical_unit : named_scaled_unit(), metre> {}; struct dim_length : isq::dim_length {}; diff --git a/src/systems/si/include/units/isq/si/luminous_flux.h b/src/systems/si/include/units/isq/si/luminous_flux.h index f2b30290..a1de8d48 100644 --- a/src/systems/si/include/units/isq/si/luminous_flux.h +++ b/src/systems/si/include/units/isq/si/luminous_flux.h @@ -36,7 +36,7 @@ namespace units::isq::si { // TODO Is this correct? Should we account for steradian here? How? -struct lumen : named_scaled_unit(), watt> {}; +struct lumen : named_scaled_unit(), watt> {}; using dim_luminous_flux = dim_power; diff --git a/src/systems/si/include/units/isq/si/magnetic_induction.h b/src/systems/si/include/units/isq/si/magnetic_induction.h index 74bd7ca6..529a7f9a 100644 --- a/src/systems/si/include/units/isq/si/magnetic_induction.h +++ b/src/systems/si/include/units/isq/si/magnetic_induction.h @@ -56,7 +56,7 @@ struct exatesla : prefixed_unit {}; struct zettatesla : prefixed_unit {}; struct yottatesla : prefixed_unit {}; -struct gauss : named_scaled_unit(), tesla> {}; +struct gauss : named_scaled_unit(), tesla> {}; struct dim_magnetic_induction : isq::dim_magnetic_induction {}; diff --git a/src/systems/si/include/units/isq/si/mass.h b/src/systems/si/include/units/isq/si/mass.h index 53a574f4..d57069d8 100644 --- a/src/systems/si/include/units/isq/si/mass.h +++ b/src/systems/si/include/units/isq/si/mass.h @@ -79,7 +79,8 @@ struct zettatonne : prefixed_unit {}; struct yottatonne : prefixed_unit {}; struct dalton : - named_scaled_unit(), kilogram> {}; + named_scaled_unit() * mag_power<10, -27>(), + kilogram> {}; struct dim_mass : isq::dim_mass {}; diff --git a/src/systems/si/include/units/isq/si/prefixes.h b/src/systems/si/include/units/isq/si/prefixes.h index 5ee1ec04..562370b6 100644 --- a/src/systems/si/include/units/isq/si/prefixes.h +++ b/src/systems/si/include/units/isq/si/prefixes.h @@ -27,26 +27,26 @@ namespace units::isq::si { // clang-format off -struct yocto : prefix(as_magnitude<10>())> {}; -struct zepto : prefix(as_magnitude<10>())> {}; -struct atto : prefix(as_magnitude<10>())> {}; -struct femto : prefix(as_magnitude<10>())> {}; -struct pico : prefix(as_magnitude<10>())> {}; -struct nano : prefix(as_magnitude<10>())> {}; -struct micro : prefix(as_magnitude<10>())> {}; -struct milli : prefix(as_magnitude<10>())> {}; -struct centi : prefix(as_magnitude<10>())> {}; -struct deci : prefix(as_magnitude<10>())> {}; -struct deca : prefix(as_magnitude<10>())> {}; -struct hecto : prefix(as_magnitude<10>())> {}; -struct kilo : prefix(as_magnitude<10>())> {}; -struct mega : prefix(as_magnitude<10>())> {}; -struct giga : prefix(as_magnitude<10>())> {}; -struct tera : prefix(as_magnitude<10>())> {}; -struct peta : prefix(as_magnitude<10>())> {}; -struct exa : prefix(as_magnitude<10>())> {}; -struct zetta : prefix(as_magnitude<10>())> {}; -struct yotta : prefix(as_magnitude<10>())> {}; +struct yocto : prefix(mag<10>())> {}; +struct zepto : prefix(mag<10>())> {}; +struct atto : prefix(mag<10>())> {}; +struct femto : prefix(mag<10>())> {}; +struct pico : prefix(mag<10>())> {}; +struct nano : prefix(mag<10>())> {}; +struct micro : prefix(mag<10>())> {}; +struct milli : prefix(mag<10>())> {}; +struct centi : prefix(mag<10>())> {}; +struct deci : prefix(mag<10>())> {}; +struct deca : prefix(mag<10>())> {}; +struct hecto : prefix(mag<10>())> {}; +struct kilo : prefix(mag<10>())> {}; +struct mega : prefix(mag<10>())> {}; +struct giga : prefix(mag<10>())> {}; +struct tera : prefix(mag<10>())> {}; +struct peta : prefix(mag<10>())> {}; +struct exa : prefix(mag<10>())> {}; +struct zetta : prefix(mag<10>())> {}; +struct yotta : prefix(mag<10>())> {}; // clang-format on } // namespace units::isq::si diff --git a/src/systems/si/include/units/isq/si/time.h b/src/systems/si/include/units/isq/si/time.h index 17f47804..f023377a 100644 --- a/src/systems/si/include/units/isq/si/time.h +++ b/src/systems/si/include/units/isq/si/time.h @@ -43,9 +43,9 @@ struct picosecond : prefixed_unit {}; struct nanosecond : prefixed_unit {}; struct microsecond : prefixed_unit {}; struct millisecond : prefixed_unit {}; -struct minute : named_scaled_unit(), second> {}; -struct hour : named_scaled_unit(), minute> {}; -struct day : named_scaled_unit(), hour> {}; +struct minute : named_scaled_unit(), second> {}; +struct hour : named_scaled_unit(), minute> {}; +struct day : named_scaled_unit(), hour> {}; struct dim_time : isq::dim_time {}; diff --git a/test/unit_test/runtime/fmt_test.cpp b/test/unit_test/runtime/fmt_test.cpp index 7cc0f748..bd317b91 100644 --- a/test/unit_test/runtime/fmt_test.cpp +++ b/test/unit_test/runtime/fmt_test.cpp @@ -84,7 +84,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") { SECTION("in terms of base units") { - const length(as_magnitude<10>()), metre>> q(123); + const length(mag<10>()), metre>> q(123); os << q; SECTION("iostream") { CHECK(os.str() == "123 Mm"); } @@ -96,7 +96,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("in terms of derived units") { - const energy(as_magnitude<10>()), joule>> q(60); + const energy(mag<10>()), joule>> q(60); os << q; SECTION("iostream") { CHECK(os.str() == "60 cJ"); } diff --git a/test/unit_test/runtime/fmt_units_test.cpp b/test/unit_test/runtime/fmt_units_test.cpp index 0f611eed..002e58fd 100644 --- a/test/unit_test/runtime/fmt_units_test.cpp +++ b/test/unit_test/runtime/fmt_units_test.cpp @@ -327,7 +327,7 @@ TEST_CASE("std::format on synthesized unit symbols", "[text][fmt]") SECTION("unknown scaled unit with reference different than the dimension's coherent unit") { // TODO(chogg): Reinstate after format/Magnitude redesign. - // constexpr auto mag = units::as_magnitude(); + // constexpr auto mag = units::mag(); // CHECK(STD_FMT::format("{}", mass>(1)) == "1 [2/3 × 10⁻³] kg"); // CHECK(STD_FMT::format("{:%Q %Aq}", mass>(1)) == "1 [2/3 x 10^-3] kg"); } diff --git a/test/unit_test/runtime/magnitude_test.cpp b/test/unit_test/runtime/magnitude_test.cpp index 9fa0aa98..a861b66c 100644 --- a/test/unit_test/runtime/magnitude_test.cpp +++ b/test/unit_test/runtime/magnitude_test.cpp @@ -66,7 +66,7 @@ void check_same_type_and_value(T actual, U expected) template void check_ratio_round_trip_is_identity() { - constexpr Magnitude auto m = as_magnitude(); + constexpr Magnitude auto m = mag(); constexpr ratio round_trip = ratio{ get_value(numerator(m)), get_value(denominator(m)), @@ -125,10 +125,10 @@ TEST_CASE("base_power") SECTION("product with inverse equals identity") { - auto check_product_with_inverse_is_identity = [](auto x) { CHECK(x * pow<-1>(x) == as_magnitude<1>()); }; + auto check_product_with_inverse_is_identity = [](auto x) { CHECK(x * pow<-1>(x) == mag<1>()); }; - check_product_with_inverse_is_identity(as_magnitude<3>()); - check_product_with_inverse_is_identity(as_magnitude()); + check_product_with_inverse_is_identity(mag<3>()); + check_product_with_inverse_is_identity(mag()); check_product_with_inverse_is_identity(pi_to_the()); } @@ -144,28 +144,21 @@ TEST_CASE("make_ratio performs prime factorization correctly") { SECTION("Performs prime factorization when denominator is 1") { - CHECK(as_magnitude<1>() == magnitude<>{}); - CHECK(as_magnitude<2>() == magnitude{}); - CHECK(as_magnitude<3>() == magnitude{}); - CHECK(as_magnitude<4>() == magnitude{}); + CHECK(mag<1>() == magnitude<>{}); + CHECK(mag<2>() == magnitude{}); + CHECK(mag<3>() == magnitude{}); + CHECK(mag<4>() == magnitude{}); - CHECK(as_magnitude<792>() == magnitude{}); + CHECK(mag<792>() == magnitude{}); } - SECTION("Supports fractions") { CHECK(as_magnitude() == magnitude{}); } - - SECTION("Supports nonzero exp") - { - constexpr ratio r{3, 1, 2}; - REQUIRE(r.exp == 2); - CHECK(as_magnitude() == as_magnitude<300>()); - } + SECTION("Supports fractions") { CHECK(mag() == magnitude{}); } SECTION("Can handle prime factor which would be large enough to overflow int") { // This was taken from a case which failed when we used `int` for our base to store prime numbers. // The failure was due to a prime factor which is larger than 2^31. - as_magnitude(); + mag(); } SECTION("Can bypass computing primes by providing known_first_factor") @@ -176,7 +169,7 @@ TEST_CASE("make_ratio performs prime factorization correctly") // In this case, we test that we can represent the largest prime that fits in a signed 64-bit int. The reason this // test can pass is that we have provided the answer, by specializing the `known_first_factor` variable template // above in this file. - as_magnitude<9'223'372'036'854'775'783>(); + mag<9'223'372'036'854'775'783>(); } } @@ -184,7 +177,7 @@ TEST_CASE("magnitude converts to numerical value") { SECTION("Positive integer powers of integer bases give integer values") { - constexpr auto mag_412 = as_magnitude<412>(); + constexpr auto mag_412 = mag<412>(); check_same_type_and_value(get_value(mag_412), 412); check_same_type_and_value(get_value(mag_412), std::size_t{412}); check_same_type_and_value(get_value(mag_412), 412.0f); @@ -193,7 +186,7 @@ TEST_CASE("magnitude converts to numerical value") SECTION("Negative integer powers of integer bases compute correct values") { - constexpr auto mag_0p125 = as_magnitude(); + constexpr auto mag_0p125 = mag(); check_same_type_and_value(get_value(mag_0p125), 0.125f); check_same_type_and_value(get_value(mag_0p125), 0.125); } @@ -226,20 +219,20 @@ TEST_CASE("magnitude converts to numerical value") // Naturally, we cannot actually write a test to verify a compiler error. But any of these can // be uncommented if desired to verify that it breaks the build. - // get_value(as_magnitude<412>()); + // get_value(mag<412>()); // Would work for pow<62>: - // get_value(pow<63>(as_magnitude<2>())); + // get_value(pow<63>(mag<2>())); // Would work for pow<63>: - // get_value(pow<64>(as_magnitude<2>())); + // get_value(pow<64>(mag<2>())); - get_value(pow<308>(as_magnitude<10>())); // Compiles, correctly. - // get_value(pow<309>(as_magnitude<10>())); - // get_value(pow<3099>(as_magnitude<10>())); - // get_value(pow<3099999>(as_magnitude<10>())); + get_value(pow<308>(mag<10>())); // Compiles, correctly. + // get_value(pow<309>(mag<10>())); + // get_value(pow<3099>(mag<10>())); + // get_value(pow<3099999>(mag<10>())); - auto sqrt_2 = pow(as_magnitude<2>()); + auto sqrt_2 = pow(mag<2>()); CHECK(!is_integral(sqrt_2)); // get_value(sqrt_2); } @@ -249,46 +242,40 @@ TEST_CASE("Equality works for magnitudes") { SECTION("Equivalent ratios are equal") { - CHECK(as_magnitude<1>() == as_magnitude<1>()); - CHECK(as_magnitude<3>() == as_magnitude<3>()); - CHECK(as_magnitude() == as_magnitude()); + CHECK(mag<1>() == mag<1>()); + CHECK(mag<3>() == mag<3>()); + CHECK(mag() == mag()); } SECTION("Different ratios are unequal") { - CHECK(as_magnitude<3>() != as_magnitude<5>()); - CHECK(as_magnitude<3>() != as_magnitude()); + CHECK(mag<3>() != mag<5>()); + CHECK(mag<3>() != mag()); } SECTION("Supports constexpr") { - constexpr auto eq = (as_magnitude() == as_magnitude()); + constexpr auto eq = (mag() == mag()); CHECK(!eq); } } TEST_CASE("Multiplication works for magnitudes") { - SECTION("Reciprocals reduce to null magnitude") - { - CHECK(as_magnitude() * as_magnitude() == as_magnitude<1>()); - } + SECTION("Reciprocals reduce to null magnitude") { CHECK(mag() * mag() == mag<1>()); } - SECTION("Products work as expected") - { - CHECK(as_magnitude() * as_magnitude() == as_magnitude()); - } + SECTION("Products work as expected") { CHECK(mag() * mag() == mag()); } SECTION("Products handle pi correctly") { - CHECK(pi_to_the<1>() * as_magnitude() * pi_to_the() == + CHECK(pi_to_the<1>() * mag() * pi_to_the() == magnitude{ratio{1, 2}}>{}); } SECTION("Supports constexpr") { - constexpr auto p = as_magnitude() * as_magnitude(); - CHECK(p == as_magnitude()); + constexpr auto p = mag() * mag(); + CHECK(p == mag()); } } @@ -296,20 +283,20 @@ TEST_CASE("Common Magnitude") { SECTION("Identity for identical magnitudes") { - CHECK(common_magnitude(as_magnitude<1>(), as_magnitude<1>()) == as_magnitude<1>()); - CHECK(common_magnitude(as_magnitude<15>(), as_magnitude<15>()) == as_magnitude<15>()); + CHECK(common_magnitude(mag<1>(), mag<1>()) == mag<1>()); + CHECK(common_magnitude(mag<15>(), mag<15>()) == mag<15>()); CHECK(common_magnitude(pi_to_the(), pi_to_the()) == pi_to_the()); } SECTION("Greatest Common Factor for integers") { - CHECK(common_magnitude(as_magnitude<24>(), as_magnitude<36>()) == as_magnitude<12>()); - CHECK(common_magnitude(as_magnitude<24>(), as_magnitude<37>()) == as_magnitude<1>()); + CHECK(common_magnitude(mag<24>(), mag<36>()) == mag<12>()); + CHECK(common_magnitude(mag<24>(), mag<37>()) == mag<1>()); } SECTION("Handles fractions") { - CHECK(common_magnitude(as_magnitude(), as_magnitude()) == as_magnitude()); + CHECK(common_magnitude(mag(), mag()) == mag()); } } @@ -317,19 +304,16 @@ TEST_CASE("Division works for magnitudes") { SECTION("Dividing anything by itself reduces to null magnitude") { - CHECK(as_magnitude() / as_magnitude() == as_magnitude<1>()); - CHECK(as_magnitude<15>() / as_magnitude<15>() == as_magnitude<1>()); + CHECK(mag() / mag() == mag<1>()); + CHECK(mag<15>() / mag<15>() == mag<1>()); } - SECTION("Quotients work as expected") - { - CHECK(as_magnitude() / as_magnitude() == as_magnitude()); - } + SECTION("Quotients work as expected") { CHECK(mag() / mag() == mag()); } SECTION("Supports constexpr") { - constexpr auto q = as_magnitude() / as_magnitude(); - CHECK(q == as_magnitude()); + constexpr auto q = mag() / mag(); + CHECK(q == mag()); } } @@ -337,17 +321,17 @@ TEST_CASE("Can raise Magnitudes to rational powers") { SECTION("Anything to the 0 is 1") { - CHECK(pow<0>(as_magnitude<1>()) == as_magnitude<1>()); - CHECK(pow<0>(as_magnitude<123>()) == as_magnitude<1>()); - CHECK(pow<0>(as_magnitude()) == as_magnitude<1>()); - CHECK(pow<0>(pi_to_the()) == as_magnitude<1>()); + CHECK(pow<0>(mag<1>()) == mag<1>()); + CHECK(pow<0>(mag<123>()) == mag<1>()); + CHECK(pow<0>(mag()) == mag<1>()); + CHECK(pow<0>(pi_to_the()) == mag<1>()); } SECTION("Anything to the 1 is itself") { - CHECK(pow<1>(as_magnitude<1>()) == as_magnitude<1>()); - CHECK(pow<1>(as_magnitude<123>()) == as_magnitude<123>()); - CHECK(pow<1>(as_magnitude()) == as_magnitude()); + CHECK(pow<1>(mag<1>()) == mag<1>()); + CHECK(pow<1>(mag<123>()) == mag<123>()); + CHECK(pow<1>(mag()) == mag()); CHECK(pow<1>(pi_to_the()) == pi_to_the()); } @@ -366,11 +350,11 @@ TEST_CASE("can distinguish integral, rational, and irrational magnitudes") CHECK(is_rational(m)); }; check_rational_and_integral(magnitude<>{}); - check_rational_and_integral(as_magnitude<1>()); - check_rational_and_integral(as_magnitude<3>()); - check_rational_and_integral(as_magnitude<8>()); - check_rational_and_integral(as_magnitude<412>()); - check_rational_and_integral(as_magnitude()); + check_rational_and_integral(mag<1>()); + check_rational_and_integral(mag<3>()); + check_rational_and_integral(mag<8>()); + check_rational_and_integral(mag<412>()); + check_rational_and_integral(mag()); } SECTION("Fractional magnitudes are rational, but not integral") @@ -379,8 +363,8 @@ TEST_CASE("can distinguish integral, rational, and irrational magnitudes") CHECK(!is_integral(m)); CHECK(is_rational(m)); }; - check_rational_but_not_integral(as_magnitude()); - check_rational_but_not_integral(as_magnitude()); + check_rational_but_not_integral(mag()); + check_rational_but_not_integral(mag()); } } @@ -397,17 +381,17 @@ TEST_CASE("Constructing ratio from rational magnitude") SECTION("Rational magnitude converts to ratio") { - constexpr ratio r = as_ratio(as_magnitude()); + constexpr ratio r = as_ratio(mag()); CHECK(r == ratio{22, 7}); } SECTION("Irrational magnitude does not convert to ratio") { // The following code should not compile. - // as_ratio(pow(as_magnitude<2>())); + // as_ratio(pow(mag<2>())); // The following code should not compile. - // as_ratio(as_magnitude<180>() / pi_to_the<1>()); + // as_ratio(mag<180>() / pi_to_the<1>()); } } @@ -614,26 +598,26 @@ TEST_CASE("extract_power_of_10") { SECTION("Picks out positive powers") { - CHECK(extract_power_of_10(as_magnitude<10>()) == 1); - CHECK(extract_power_of_10(as_magnitude<20>()) == 1); - CHECK(extract_power_of_10(as_magnitude<40>()) == 1); - CHECK(extract_power_of_10(as_magnitude<50>()) == 1); - CHECK(extract_power_of_10(as_magnitude<100>()) == 2); + CHECK(extract_power_of_10(mag<10>()) == 1); + CHECK(extract_power_of_10(mag<20>()) == 1); + CHECK(extract_power_of_10(mag<40>()) == 1); + CHECK(extract_power_of_10(mag<50>()) == 1); + CHECK(extract_power_of_10(mag<100>()) == 2); } SECTION("Picks out negative powers") { - constexpr auto ONE = as_magnitude<1>(); - CHECK(extract_power_of_10(ONE / as_magnitude<10>()) == -1); - CHECK(extract_power_of_10(ONE / as_magnitude<20>()) == -1); - CHECK(extract_power_of_10(ONE / as_magnitude<40>()) == -1); - CHECK(extract_power_of_10(ONE / as_magnitude<50>()) == -1); - CHECK(extract_power_of_10(ONE / as_magnitude<100>()) == -2); + constexpr auto ONE = mag<1>(); + CHECK(extract_power_of_10(ONE / mag<10>()) == -1); + CHECK(extract_power_of_10(ONE / mag<20>()) == -1); + CHECK(extract_power_of_10(ONE / mag<40>()) == -1); + CHECK(extract_power_of_10(ONE / mag<50>()) == -1); + CHECK(extract_power_of_10(ONE / mag<100>()) == -2); } - SECTION("Zero if signs disagree") { CHECK(extract_power_of_10(as_magnitude<2>() / as_magnitude<5>()) == 0); } + SECTION("Zero if signs disagree") { CHECK(extract_power_of_10(mag<2>() / mag<5>()) == 0); } - SECTION("Handles rational powers") { CHECK(extract_power_of_10(sqrt(as_magnitude<1000>())) == 1); } + SECTION("Handles rational powers") { CHECK(extract_power_of_10(sqrt(mag<1000>())) == 1); } } } // namespace diff --git a/test/unit_test/runtime/math_test.cpp b/test/unit_test/runtime/math_test.cpp index d74c93ef..783480db 100644 --- a/test/unit_test/runtime/math_test.cpp +++ b/test/unit_test/runtime/math_test.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -30,7 +31,7 @@ using namespace units; using namespace units::isq; -using namespace units::isq::si; +using namespace units::isq::si::literals; // classical @@ -186,10 +187,10 @@ TEST_CASE("ceil functions", "[ceil]") { REQUIRE(ceil(1999._q_ms) == 2_q_s); } - // TODO does not work, probably due to a bug in fpow10() see #311 - // SECTION ("ceil -1000. milliseconds with target unit second should be -1 second") { - // REQUIRE(ceil(-1000._q_ms) == -1_q_s); - // } + SECTION("ceil -1000. milliseconds with target unit second should be -1 second") + { + REQUIRE(ceil(-1000._q_ms) == -1_q_s); + } SECTION("ceil -999. milliseconds with target unit second should be 0 seconds") { REQUIRE(ceil(-999._q_ms) == 0_q_s); @@ -363,3 +364,24 @@ TEMPLATE_PRODUCT_TEST_CASE_SIG("detail::iroot()", "[math][pow][iroot]", (std: ROOT_TEST_CASE(CompileRoot) ROOT_TEST_CASE(RuntimeRoot) + +TEST_CASE("hypot functions", "[hypot]") +{ + using namespace units::aliases::isq::si; + + SECTION("hypot should work on the same quantities") + { + REQUIRE(hypot(km<>(3.), km<>(4.)) == km<>(5.)); + REQUIRE(hypot(km<>(2.), km<>(3.), km<>(6.)) == km<>(7.)); + } + SECTION("hypot should work with different units of the same dimension") + { + REQUIRE(hypot(km<>(3.), m<>(4000.)) == km<>(5.)); + REQUIRE(hypot(km<>(2.), m<>(3000.), km<>(6.)) == km<>(7.)); + } + SECTION("hypot should work with different but equivalent dimensions") + { + REQUIRE(hypot(km<>(3.), cgs::length::cm<>(400'000.)) == km<>(5.)); + REQUIRE(hypot(km<>(2.), cgs::length::cm<>(300'000.), km<>(6.)) == km<>(7.)); + } +} diff --git a/test/unit_test/static/dimensions_concepts_test.cpp b/test/unit_test/static/dimensions_concepts_test.cpp index 2b237250..f96a11dc 100644 --- a/test/unit_test/static/dimensions_concepts_test.cpp +++ b/test/unit_test/static/dimensions_concepts_test.cpp @@ -58,7 +58,8 @@ static_assert(!Area>); static_assert(Volume>); static_assert(!Volume>); #if UNITS_DOWNCAST_MODE == 0 -static_assert(Volume>, unknown_coherent_unit>>); +static_assert( + Volume>, unknown_coherent_unit>>>); #endif static_assert(Speed>); @@ -68,21 +69,23 @@ static_assert(Acceleration>); static_assert(!Acceleration>); #if UNITS_DOWNCAST_MODE == 0 static_assert(Acceleration, exponent>, - unknown_coherent_unit>>); + unknown_coherent_unit, exponent>>>); #endif static_assert(Force>); static_assert(!Force>); #if UNITS_DOWNCAST_MODE == 0 // static_assert(Force, exponent, -// exponent>, unknown_coherent_unit>>); +// exponent>, unknown_coherent_unit, +// exponent>>); #endif static_assert(Energy>); static_assert(!Energy>); #if UNITS_DOWNCAST_MODE == 0 // static_assert(Energy, exponent, -// exponent>, unknown_coherent_unit>>); +// exponent>, unknown_coherent_unit, +// exponent>>); #endif static_assert(Power>); diff --git a/test/unit_test/static/quantity_kind_test.cpp b/test/unit_test/static/quantity_kind_test.cpp index c421ec12..1d246f23 100644 --- a/test/unit_test/static/quantity_kind_test.cpp +++ b/test/unit_test/static/quantity_kind_test.cpp @@ -456,9 +456,9 @@ concept invalid_compound_assignments = requires !requires { w *= m; }; requires !requires { w /= m; }; requires !requires { w %= m; }; - requires !requires { w *= quantity_kind, scaled_unit(), one>, int>{1}; }; - requires !requires { w /= quantity_kind, scaled_unit(), one>, int>{1}; }; - requires !requires { w %= quantity_kind, scaled_unit(), one>, int>{1}; }; + requires !requires { w *= quantity_kind, scaled_unit(), one>, int>{1}; }; + requires !requires { w /= quantity_kind, scaled_unit(), one>, int>{1}; }; + requires !requires { w %= quantity_kind, scaled_unit(), one>, int>{1}; }; requires !requires { w %= 1.0; }; requires !requires { w %= quantity(1.0); }; requires !requires { w %= 1.0 * (w / w); }; diff --git a/test/unit_test/static/quantity_test.cpp b/test/unit_test/static/quantity_test.cpp index cf346a27..f510e2b4 100644 --- a/test/unit_test/static/quantity_test.cpp +++ b/test/unit_test/static/quantity_test.cpp @@ -287,7 +287,7 @@ static_assert(get_length_derived_quantity() == 1_q_m); // CTAD ///////// -#if UNITS_COMP_GCC >= 11 || UNITS_COMP_CLANG > 15 +#if UNITS_COMP_GCC >= 11 || UNITS_COMP_CLANG > 16 static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert( @@ -498,7 +498,7 @@ static_assert(compare> static_assert(compare>); static_assert(compare>); static_assert(compare(1) / 1_q_s), - frequency(), hertz>, std::int64_t>>); + frequency(), hertz>, std::int64_t>>); static_assert(is_same_v); static_assert(is_same_v); @@ -529,7 +529,7 @@ static_assert(compare> static_assert(compare>); static_assert(compare>); static_assert(compare(1) / 1._q_s), - frequency(), hertz>, long double>>); + frequency(), hertz>, long double>>); static_assert(compare>); static_assert(compare>); static_assert(compare(1)), length>); @@ -551,7 +551,7 @@ static_assert(compare> static_assert(compare>); static_assert(compare>); static_assert(compare(1) / 1_q_s), - frequency(), hertz>, long double>>); + frequency(), hertz>, long double>>); // different units static_assert(compare>); @@ -579,25 +579,28 @@ static_assert(is_same_v>); +static_assert(compare(), metre>, std::int64_t>>); static_assert( - compare(), metre>, std::int64_t>>); -static_assert( - compare, exponent>, - scaled_unit(), unknown_coherent_unit>, std::int64_t>>); + compare, exponent>, + scaled_unit(), unknown_coherent_unit, exponent>>, + std::int64_t>>); static_assert(compare>); -static_assert( - compare(), hertz>, std::int64_t>>); +static_assert(compare(), hertz>, std::int64_t>>); static_assert(compare>); -static_assert(compare>, - scaled_unit(), unknown_coherent_unit>, std::int64_t>>); -static_assert(compare(), one>, std::int64_t>>); +static_assert( + compare>, + scaled_unit(), unknown_coherent_unit>>, std::int64_t>>); +static_assert(compare(), one>, std::int64_t>>); static_assert(compare>); static_assert( - compare(), metre_per_second>, std::int64_t>>); + compare(), metre_per_second>, std::int64_t>>); static_assert( - compare, exponent>, - scaled_unit(), unknown_coherent_unit>, std::int64_t>>); + compare, exponent>, + scaled_unit(), unknown_coherent_unit, exponent>>, + std::int64_t>>); static_assert((1_q_m + 1_q_m).number() == 2); static_assert((1_q_m + 1_q_km).number() == 1001); @@ -885,11 +888,13 @@ static_assert(!is_same_v(2_q_dm3)), volume, units::exponent>, - scaled_unit(), unknown_coherent_unit>, std::int64_t>>); static_assert( - is_same_v(), metre>, std::int64_t>>); + is_same_v, units::exponent>, + scaled_unit(), + unknown_coherent_unit, units::exponent>>, + std::int64_t>>); +static_assert(is_same_v(), metre>, std::int64_t>>); #else @@ -919,8 +924,7 @@ static_assert(same(quotient_remainder_theorem(3'000 * m, 400), 3'000 * m)); static_assert(comp(quotient_remainder_theorem(3'000 * m, quantity(400)), 3'000 * m)); static_assert(comp(quotient_remainder_theorem(3 * km, quantity(400)), 3 * km)); static_assert(comp(quotient_remainder_theorem(3 * km, quantity(2)), 3 * km)); -static_assert( - comp(quotient_remainder_theorem(3 * km, dimensionless(), one>, int>(400)), - 3 * km)); +static_assert(comp(quotient_remainder_theorem(3 * km, dimensionless(), one>, int>(400)), + 3 * km)); } // namespace diff --git a/test/unit_test/static/ratio_test.cpp b/test/unit_test/static/ratio_test.cpp index ad966950..02828115 100644 --- a/test/unit_test/static/ratio_test.cpp +++ b/test/unit_test/static/ratio_test.cpp @@ -28,11 +28,6 @@ using namespace units; static_assert(ratio(2, 4) == ratio(1, 2)); -// basic exponents tests -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)); - static_assert(ratio(1) * ratio(3, 8) == ratio(3, 8)); static_assert(ratio(3, 8) * ratio(1) == ratio(3, 8)); static_assert(ratio(4) * ratio(1, 8) == ratio(1, 2)); @@ -45,21 +40,12 @@ static_assert(-ratio(3, 8) == ratio(-3, 8)); // ratio addition static_assert(ratio(1, 2) + ratio(1, 3) == ratio(5, 6)); -static_assert(ratio(1, 3, 2) + ratio(11, 6) == ratio(211, 6)); // 100/3 + 11/6 - -// multiply with exponents -static_assert(ratio(1, 8, 2) * ratio(2, 1, 4) == ratio(1, 4, 6)); -static_assert(ratio(1, 2, -4) * ratio(8, 1, 3) == ratio(4, 1, -1)); static_assert(ratio(4) / ratio(2) == ratio(2)); static_assert(ratio(2) / ratio(8) == ratio(1, 4)); static_assert(ratio(1, 8) / ratio(2) == ratio(1, 16)); static_assert(ratio(6) / ratio(3) == ratio(2)); -// divide with exponents -static_assert(ratio(1, 8, -6) / ratio(2, 1, -8) == ratio(1, 16, 2)); -static_assert(ratio(6, 1, 4) / ratio(3) == ratio(2, 1, 4)); - static_assert(pow<0>(ratio(2)) == ratio(1)); static_assert(pow<1>(ratio(2)) == ratio(2)); static_assert(pow<2>(ratio(2)) == ratio(4)); @@ -69,27 +55,6 @@ static_assert(pow<1>(ratio(1, 2)) == ratio(1, 2)); static_assert(pow<2>(ratio(1, 2)) == ratio(1, 4)); static_assert(pow<3>(ratio(1, 2)) == ratio(1, 8)); -// pow with exponents -static_assert(pow<2>(ratio(1, 2, 3)) == ratio(1, 4, 6)); -static_assert(pow<4, 2>(ratio(1, 2, 3)) == ratio(1, 4, 6)); -static_assert(pow<3>(ratio(1, 2, -6)) == ratio(1, 8, -18)); - -static_assert(sqrt(ratio(9)) == ratio(3)); -static_assert(cbrt(ratio(27)) == ratio(3)); -static_assert(sqrt(ratio(4)) == ratio(2)); -static_assert(cbrt(ratio(8)) == ratio(2)); -static_assert(sqrt(ratio(1)) == ratio(1)); -static_assert(cbrt(ratio(1)) == ratio(1)); -static_assert(sqrt(ratio(0)) == ratio(0)); -static_assert(cbrt(ratio(0)) == ratio(0)); -static_assert(sqrt(ratio(1, 4)) == ratio(1, 2)); -static_assert(cbrt(ratio(1, 8)) == ratio(1, 2)); - -// sqrt with exponents -static_assert(sqrt(ratio(9, 1, 2)) == ratio(3, 1, 1)); -static_assert(cbrt(ratio(27, 1, 3)) == ratio(3, 1, 1)); -static_assert(cbrt(ratio(27, 1, 2)) == ratio(13, 1, 0)); - // common_ratio static_assert(common_ratio(ratio(1), ratio(1000)) == ratio(1)); static_assert(common_ratio(ratio(1000), ratio(1)) == ratio(1)); @@ -98,20 +63,9 @@ static_assert(common_ratio(ratio(1, 1000), ratio(1)) == ratio(1, 1000)); static_assert(common_ratio(ratio(100, 1), ratio(10, 1)) == ratio(10, 1)); static_assert(common_ratio(ratio(100, 1), ratio(1, 10)) == ratio(1, 10)); -// common ratio with exponents -static_assert(common_ratio(ratio(1), ratio(1, 1, 3)) == ratio(1)); -static_assert(common_ratio(ratio(10, 1, -1), ratio(1, 1, -3)) == ratio(1, 1, -3)); - -// numerator and denominator -static_assert(numerator(ratio(3, 4)) == 3); -static_assert(numerator(ratio(3, 7, 2)) == 300); -static_assert(denominator(ratio(3, 4)) == 4); -static_assert(denominator(ratio(3, 7, -2)) == 700); - // comparison static_assert((ratio(3, 4) <=> ratio(6, 8)) == (0 <=> 0)); static_assert((ratio(3, 4) <=> ratio(-3, 4)) == (0 <=> -1)); static_assert((ratio(-3, 4) <=> ratio(3, -4)) == (0 <=> 0)); -static_assert((ratio(1, 1, 1) <=> ratio(10)) == (0 <=> 0)); } // namespace diff --git a/test/unit_test/static/si_cgs_test.cpp b/test/unit_test/static/si_cgs_test.cpp index 42e91ad8..8c817f08 100644 --- a/test/unit_test/static/si_cgs_test.cpp +++ b/test/unit_test/static/si_cgs_test.cpp @@ -116,7 +116,7 @@ static_assert(si::length(1) + quantity_cast>(10 static_assert(100_q_cm + quantity_cast>(si::length(1)) == 200_q_cm); static_assert(quantity_cast>(si::length(1)) + 100_q_cm == 200_q_cm); -// substraction +// subtraction static_assert(500_q_cm - si::length(1) == si::length(4)); static_assert(si::length(5) - 100_q_cm == si::length(4)); @@ -127,8 +127,8 @@ static_assert(quantity_cast>(si::length(2) == si::area(4)); // TODO Add support for -// comparing of an unknown_dimension +// TODO Add support for comparing of an unknown_dimension +// static_assert(200._q_cm * si::length(2) == si::area(4)); static_assert(quantity_cast(200._q_cm) * si::length(2) == si::area(4)); static_assert(200._q_cm * quantity_cast(si::length(2)) == 40'000_q_cm2); @@ -142,14 +142,32 @@ static_assert(200._q_cm * quantity_cast(si::length(4) / 200_q_cm == si::length(2)); // TODO Add support for -// comparing of an unknown_dimension +// TODO Add support for comparing of an unknown_dimension +// static_assert(si::area(4) / 200_q_cm == si::length(2)); +// static_assert(400._q_cm / si::length(2) == 2); static_assert(si::area(4) / quantity_cast>(200_q_cm) == si::length(2)); static_assert(quantity_cast>(si::area(4)) / 200._q_cm == 200_q_cm); +static_assert(si::cgs::length(50) == si::length(50)); +static_assert(si::cgs::mass(50) == si::mass(50)); + +static_assert(1 / si::cgs::length(50) == 1 / si::length(50)); +static_assert(1 / si::cgs::length(50) == 1 / si::length(50)); + +static_assert(1 / si::cgs::mass(50) == 1 / si::mass(50)); +static_assert(1 / si::cgs::mass(50) == 1 / si::mass(50)); + +static_assert(si::cgs::length(50) * si::cgs::mass(50) == + si::length(50) * si::mass(50)); +static_assert(si::cgs::length(50) * si::cgs::mass(50) == + si::length(50) * si::mass(50)); + +static_assert(si::cgs::length(50) / si::cgs::mass(50) == + si::length(50) / si::mass(50)); + } // namespace cgs_test } // namespace diff --git a/test/unit_test/static/si_fps_test.cpp b/test/unit_test/static/si_fps_test.cpp index bb023c8c..0f630fa3 100644 --- a/test/unit_test/static/si_fps_test.cpp +++ b/test/unit_test/static/si_fps_test.cpp @@ -157,8 +157,8 @@ static_assert(quantity_cast>(si::length(2) == si::area(1.2192)); // TODO Add support for -// comparing of an unknown_dimension +// TODO Add support for comparing of an unknown_dimension +// static_assert(2 * ft * si::length(2) == si::area(1.2192)); static_assert(quantity_cast>(2. * ft) * si::length(2) == si::area(1.2192)); static_assert(quantity_cast>(2. * ft) * si::length(0.6096) == @@ -167,8 +167,10 @@ static_assert(2. * ft * quantity_cast>(si::length // division -// static_assert(si::area(4) / 200_q_cm == si::length(2)); // TODO Add support for -// comparing of an unknown_dimension +// TODO Add support for comparing of an unknown_dimension +// static_assert(si::area(4) / 200_q_cm == si::length(2)); +// static_assert(400._q_cm / si::length(2) == 2); + static_assert(si::area(1.48644864) / quantity_cast>(4 * ft) == si::length(1.2192)); // 16 ft2 / 4 ft = 4 ft static_assert(quantity_cast>(si::area(1.48644864)) / (4. * ft) == diff --git a/test/unit_test/static/si_test.cpp b/test/unit_test/static/si_test.cpp index 02960463..103e1837 100644 --- a/test/unit_test/static/si_test.cpp +++ b/test/unit_test/static/si_test.cpp @@ -43,7 +43,7 @@ static_assert(1_q_au == 149'597'870'700_q_m); static_assert(1_q_km + 1_q_m == 1001_q_m); static_assert(10_q_km / 5_q_km == 2); static_assert(10_q_km / 5_q_km < 3); -static_assert(100_q_mm / 5_q_cm == dimensionless(), one>>(20)); +static_assert(100_q_mm / 5_q_cm == dimensionless(), one>>(20)); static_assert(100_q_mm / 5_q_cm == dimensionless(2)); static_assert(10_q_km / 2 == 5_q_km); @@ -107,7 +107,7 @@ static_assert(1000 / 1_q_s == 1_q_kHz); static_assert(1 / 1_q_ms == 1_q_kHz); static_assert(3.2_q_GHz == 3'200'000'000_q_Hz); static_assert((10_q_Hz * 1_q_min).number() == 10); -static_assert(10_q_Hz * 1_q_min == dimensionless(), one>>(10)); +static_assert(10_q_Hz * 1_q_min == dimensionless(), one>>(10)); static_assert(10_q_Hz * 1_q_min == dimensionless(600)); static_assert(2 / 1_q_Hz == 2_q_s); diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index 128fac3d..ded5edad 100644 --- a/test/unit_test/static/unit_test.cpp +++ b/test/unit_test/static/unit_test.cpp @@ -36,12 +36,12 @@ using namespace units::isq; struct metre : named_unit {}; struct centimetre : prefixed_unit {}; struct kilometre : prefixed_unit {}; -struct yard : named_scaled_unit(), metre> {}; -struct foot : named_scaled_unit(), yard> {}; +struct yard : named_scaled_unit(), metre> {}; +struct foot : named_scaled_unit(), yard> {}; struct dim_length : base_dimension<"length", metre> {}; struct second : named_unit {}; -struct hour : named_scaled_unit(), second> {}; +struct hour : named_scaled_unit(), second> {}; struct dim_time : base_dimension<"time", second> {}; struct kelvin : named_unit {}; @@ -59,10 +59,10 @@ struct kilometre_per_hour : derived_scaled_unit); static_assert(equivalent); -static_assert(compare(), metre>>, metre>); -static_assert(compare(), metre>>, centimetre>); +static_assert(compare(), metre>>, metre>); +static_assert(compare(), metre>>, centimetre>); static_assert(compare>, yard>); -static_assert(compare(), metre>>, foot>); +static_assert(compare(), metre>>, foot>); static_assert(compare>, kilometre_per_hour>); static_assert(centimetre::symbol == "cm"); diff --git a/test_package/conanfile.py b/test_package/conanfile.py index 4f4506f5..e71afd55 100644 --- a/test_package/conanfile.py +++ b/test_package/conanfile.py @@ -20,14 +20,17 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +import os + from conan import ConanFile from conan.tools.build import cross_building -from conan.tools.cmake import CMake +from conan.tools.cmake import CMake, cmake_layout class TestPackageConan(ConanFile): settings = "os", "compiler", "build_type", "arch" - generators = "CMakeToolchain", "CMakeDeps" + generators = "CMakeDeps", "CMakeToolchain", "VirtualBuildEnv", "VirtualRunEnv" + apply_env = False test_type = "explicit" # TODO Remove for Conan 2.0 def requirements(self): @@ -38,6 +41,10 @@ class TestPackageConan(ConanFile): cmake.configure() cmake.build() + def layout(self): + cmake_layout(self) + def test(self): if not cross_building(self): - self.run("test_package", run_environment=True) + cmd = os.path.join(self.cpp.build.bindirs[0], "test_package") + self.run(cmd, env="conanrun")