diff --git a/.github/workflows/ci-check.yml b/.github/workflows/ci-check.yml index 97df38b9..53c99c66 100644 --- a/.github/workflows/ci-check.yml +++ b/.github/workflows/ci-check.yml @@ -26,17 +26,17 @@ on: [push, pull_request] jobs: check: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.8 - uses: actions/setup-python@v2 - with: - python-version: 3.8 - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - - name: Check - run: | - pre-commit run --all-files + - uses: actions/checkout@v3 + - name: Set up Python 3.8 + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + - name: Check + run: | + pre-commit run --all-files diff --git a/.github/workflows/ci-conan.yml b/.github/workflows/ci-conan.yml index 7ae8d63e..93cca9f9 100644 --- a/.github/workflows/ci-conan.yml +++ b/.github/workflows/ci-conan.yml @@ -30,6 +30,9 @@ on: paths-ignore: - "docs/**" +env: + CHANNEL: ${{ fromJSON('["testing", "stable"]')[github.ref_type == 'tag' && startsWith(github.ref_name, 'v')] }} + jobs: build: name: ${{ matrix.config.name }} ${{ matrix.build_type }} [downcast=${{ matrix.downcast_mode }}] @@ -42,15 +45,17 @@ jobs: name: "Windows MSVC 14.2", os: windows-2019, compiler: { type: VISUAL, version: 16, cc: "", cxx: "", std: 20 }, + conan-config: "-c user.build:skip_la=True", } - { name: "Windows MSVC 14.3", os: windows-2022, compiler: { type: MSVC, version: 193, cc: "", cxx: "", std: 23 }, + conan-config: "", } - { name: "Ubuntu GCC-10", - os: ubuntu-20.04, + os: ubuntu-22.04, compiler: { type: GCC, @@ -59,11 +64,11 @@ jobs: cxx: "g++-10", std: 20, }, - lib: "libstdc++11", + conan-config: "", } - { name: "Ubuntu GCC-11", - os: ubuntu-20.04, + os: ubuntu-22.04, compiler: { type: GCC, @@ -72,7 +77,20 @@ jobs: cxx: "g++-11", std: 20, }, - lib: "libstdc++11", + conan-config: "", + } + - { + name: "Ubuntu GCC-12", + os: ubuntu-22.04, + compiler: + { + type: GCC, + version: 12, + cc: "gcc-12", + cxx: "g++-12", + std: 20, + }, + conan-config: "", } - { name: "Ubuntu Clang-12 + libstdc++11", @@ -86,6 +104,7 @@ jobs: std: 20, }, lib: "libstdc++11", + conan-config: "", } - { name: "Ubuntu Clang-12 + libc++", @@ -99,10 +118,11 @@ jobs: std: 20, }, lib: "libc++", + conan-config: "", } - { name: "Ubuntu Clang-13 + libc++", - os: ubuntu-20.04, + os: ubuntu-22.04, compiler: { type: CLANG, @@ -112,10 +132,11 @@ jobs: std: 20, }, lib: "libc++", + conan-config: "", } - { name: "Ubuntu Clang-14 + libc++", - os: ubuntu-20.04, + os: ubuntu-22.04, compiler: { type: CLANG, @@ -125,6 +146,35 @@ jobs: std: 20, }, lib: "libc++", + conan-config: "", + } + - { + name: "Ubuntu Clang-15 + libc++", + os: ubuntu-22.04, + compiler: + { + type: CLANG, + version: 15, + cc: "clang-15", + cxx: "clang++-15", + std: 20, + }, + lib: "libc++", + conan-config: "", + } + - { + name: "Ubuntu Clang-16 + libc++", + os: ubuntu-22.04, + compiler: + { + type: CLANG, + version: 16, + cc: "clang-16", + cxx: "clang++-16", + std: 20, + }, + lib: "libc++", + conan-config: "", } - { name: "MacOS Apple Clang 13", @@ -137,6 +187,7 @@ jobs: cxx: "clang++", std: 20, }, + conan-config: "", } build_type: ["Release", "Debug"] downcast_mode: ["on", "auto"] @@ -146,25 +197,27 @@ jobs: CXX: ${{ matrix.config.compiler.cxx }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Cache Conan data - uses: actions/cache@v2 + uses: actions/cache@v3 env: cache-name: cache-conan-data with: - path: ~/.conan/data - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/metadata.json') }} + path: ~/.conan2/p + key: build-${{ matrix.config.os }}-${{ matrix.build_type }}-${{ matrix.config.compiler.type }}-${{ matrix.config.compiler.version }}-${{ matrix.config.lib }} restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- - - uses: hendrikmuhs/ccache-action@v1 + build-${{ matrix.config.os }}-${{ matrix.build_type }}-${{ matrix.config.compiler.type }}-${{ matrix.config.compiler.version }}-${{ matrix.config.lib }} + build-${{ matrix.config.os }}-${{ matrix.build_type }}-${{ matrix.config.compiler.type }}-${{ matrix.config.compiler.version }}- + build-${{ matrix.config.os }}-${{ matrix.build_type }}-${{ matrix.config.compiler.type }}- + build-${{ matrix.config.os }}-${{ matrix.build_type }}- + build-${{ matrix.config.os }}- + - uses: hendrikmuhs/ccache-action@v1.2 if: runner.os == 'Linux' with: key: ${{ matrix.config.os }}-${{ matrix.config.compiler.type }}-${{ matrix.config.compiler.version }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.downcast_mode }} max-size: 50M - - name: Install gcc-11 - if: matrix.config.compiler.type == 'GCC' && matrix.config.compiler.version == '11' + - name: Install gcc-12 + if: matrix.config.compiler.type == 'GCC' && matrix.config.compiler.version == '12' shell: bash run: | sudo apt install -y g++-${{ matrix.config.compiler.version }} @@ -201,7 +254,7 @@ jobs: exit 1 fi - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: "3.8" - name: Install Conan @@ -211,37 +264,76 @@ jobs: - name: Configure Conan 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 + conan profile detect --force + if [[ "${{ matrix.config.compiler.type }}" == "CLANG" ]]; then + sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.libcxx=.*/compiler.libcxx=${{ matrix.config.lib }}/' ~/.conan2/profiles/default fi - 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 + sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.cppstd=.*/compiler.cppstd=${{ matrix.config.compiler.std }}/' ~/.conan2/profiles/default + sed -i.backup '/^\[settings\]$/,/^\[/ s/^build_type=.*/build_type=${{ matrix.build_type }}/' ~/.conan2/profiles/default + conan profile show -pr default # - name: Add support for clang-13 to Conan's settings.yml # # TODO Remove when Conan will support clang-13 # if: matrix.config.compiler.type == 'CLANG' # shell: bash # run: | # sed -i -e 's/"8", "9", "10", "11", "12"]/"8", "9", "10", "11", "12", "13"]/' ~/.conan/settings.yml - - name: Set channel - shell: bash - run: | - [[ `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 + conan create . --user mpusz --channel ${CHANNEL} --lockfile-out=package.lock \ + -b mp-units/* -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" \ + -o downcast_mode=${{ matrix.downcast_mode }} -c user.build:all=True -c user.build:skip_docs=True ${{ matrix.config.conan-config }} + - name: Obtain package reference + id: get-package-ref + shell: bash + run: | + echo "PACKAGE_REF=`egrep -o mp-units[^%]+ package.lock`" >> ${GITHUB_OUTPUT} + - name: Upload mp-units Conan package to Conan CI repository 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 + conan remote add conan-mpusz-ci https://mpusz.jfrog.io/artifactory/api/conan/conan-ci + conan remote login conan-mpusz-ci ${{ secrets.CONAN_LOGIN_USERNAME }} -p ${{ secrets.CONAN_CI_PASSWORD }} + conan upload ${{ steps.get-package-ref.outputs.package_ref }} -r conan-mpusz-ci --confirm + - name: Clean Conan cache before backup shell: bash run: | - conan remove mp-units -c + conan remove mp-units --confirm + conan remove *#!latest --confirm + conan remove *:*#!latest --confirm + conan cache clean "*" -s -b -d + outputs: + package_ref: ${{ steps.get-package-ref.outputs.PACKAGE_REF }} + + promote_package: + if: github.ref == 'refs/heads/master' || (github.ref_type == 'tag' && startsWith(github.ref_name, 'v')) + needs: build + name: Promote Conan package + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.8" + - name: Install Conan + shell: bash + run: | + pip install -U conan + - name: Configure Conan + shell: bash + run: | + conan remote add conan-mpusz-ci https://mpusz.jfrog.io/artifactory/api/conan/conan-ci + conan remote add conan-mpusz-oss https://mpusz.jfrog.io/artifactory/api/conan/conan-oss + - name: Promote mp-units Conan package + shell: bash + run: | + conan remote login conan-mpusz-ci ${{ secrets.CONAN_LOGIN_USERNAME }} -p ${{ secrets.CONAN_CI_PASSWORD }} + conan remote login conan-mpusz-oss ${{ secrets.CONAN_LOGIN_USERNAME }} -p ${{ secrets.CONAN_PASSWORD }} + conan download ${{ needs.build.outputs.package_ref }} -r conan-mpusz-ci + conan upload ${{ needs.build.outputs.package_ref }} -r conan-mpusz-oss --confirm + - name: Do housekeeping on conan-mpusz-oss + shell: bash + run: | + conan remove mp-units/*#!latest --confirm -r conan-mpusz-oss + conan remove mp-units/*:*#!latest --confirm -r conan-mpusz-oss diff --git a/.github/workflows/ci-test-package-cmake.yml b/.github/workflows/ci-test-package-cmake.yml index 7dfaa473..b6a9d747 100644 --- a/.github/workflows/ci-test-package-cmake.yml +++ b/.github/workflows/ci-test-package-cmake.yml @@ -54,7 +54,7 @@ jobs: } - { name: "Ubuntu GCC-10", - os: ubuntu-20.04, + os: ubuntu-22.04, compiler: { type: GCC, @@ -63,11 +63,10 @@ jobs: cxx: "g++-10", std: 20, }, - lib: "libstdc++11", } - { name: "Ubuntu GCC-11", - os: ubuntu-20.04, + os: ubuntu-22.04, compiler: { type: GCC, @@ -76,7 +75,18 @@ jobs: cxx: "g++-11", std: 20, }, - lib: "libstdc++11", + } + - { + name: "Ubuntu GCC-12", + os: ubuntu-22.04, + compiler: + { + type: GCC, + version: 12, + cc: "gcc-12", + cxx: "g++-12", + std: 20, + }, } - { name: "Ubuntu Clang-12 + libstdc++11", @@ -106,7 +116,7 @@ jobs: } - { name: "Ubuntu Clang-13 + libc++", - os: ubuntu-20.04, + os: ubuntu-22.04, compiler: { type: CLANG, @@ -119,7 +129,7 @@ jobs: } - { name: "Ubuntu Clang-14 + libc++", - os: ubuntu-20.04, + os: ubuntu-22.04, compiler: { type: CLANG, @@ -130,6 +140,32 @@ jobs: }, lib: "libc++", } + - { + name: "Ubuntu Clang-15 + libc++", + os: ubuntu-22.04, + compiler: + { + type: CLANG, + version: 15, + cc: "clang-15", + cxx: "clang++-15", + std: 20, + }, + lib: "libc++", + } + - { + name: "Ubuntu Clang-16 + libc++", + os: ubuntu-22.04, + compiler: + { + type: CLANG, + version: 16, + cc: "clang-16", + cxx: "clang++-16", + std: 20, + }, + lib: "libc++", + } - { name: "MacOS Apple Clang 13", os: macos-11, @@ -154,18 +190,20 @@ jobs: uses: ASzc/change-string-case-action@v2 with: string: ${{ matrix.build_type }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Cache Conan data - uses: actions/cache@v2 + uses: actions/cache@v3 env: cache-name: cache-conan-data with: - path: ~/.conan/data - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/metadata.json') }} + path: ~/.conan2/p + key: build-${{ matrix.config.os }}-${{ matrix.build_type }}-${{ matrix.config.compiler.type }}-${{ matrix.config.compiler.version }}-${{ matrix.config.lib }} restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- + build-${{ matrix.config.os }}-${{ matrix.build_type }}-${{ matrix.config.compiler.type }}-${{ matrix.config.compiler.version }}-${{ matrix.config.lib }} + build-${{ matrix.config.os }}-${{ matrix.build_type }}-${{ matrix.config.compiler.type }}-${{ matrix.config.compiler.version }}- + build-${{ matrix.config.os }}-${{ matrix.build_type }}-${{ matrix.config.compiler.type }}- + build-${{ matrix.config.os }}-${{ matrix.build_type }}- + build-${{ matrix.config.os }}- - name: Install gcc-11 if: matrix.config.compiler.type == 'GCC' && matrix.config.compiler.version == '11' shell: bash @@ -204,7 +242,7 @@ jobs: exit 1 fi - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: "3.8" - name: Install Conan @@ -214,15 +252,14 @@ jobs: - name: Configure Conan shell: bash run: | - conan config init + conan profile detect --force 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 + if [[ "${{ matrix.config.compiler.type }}" == "CLANG" ]]; then + sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.libcxx=.*/compiler.libcxx=${{ matrix.config.lib }}/' ~/.conan2/profiles/default fi - 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 + sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.cppstd=.*/compiler.cppstd=${{ matrix.config.compiler.std }}/' ~/.conan2/profiles/default + sed -i.backup '/^\[settings\]$/,/^\[/ s/^build_type=.*/build_type=${{ matrix.build_type }}/' ~/.conan2/profiles/default + conan profile show -pr default # - name: Add support for clang-13 to Conan's settings.yml # # TODO Remove when Conan will support clang-13 # if: matrix.config.compiler.type == 'CLANG' @@ -232,7 +269,7 @@ jobs: - name: Install Conan dependencies shell: bash run: | - conan install . -b outdated -u + conan install . -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" -c user.build:all=False mv CMakeUserPresets.json src - name: Configure mp-units CMake if: matrix.config.compiler.type == 'VISUAL' || matrix.config.compiler.type == 'MSVC' @@ -241,19 +278,19 @@ jobs: run: | cmake --version call ..\build\generators\conanvcvars.bat - cmake --preset default -DCMAKE_INSTALL_PREFIX=../out + cmake --preset conan-default -DCMAKE_INSTALL_PREFIX=../out - name: Configure mp-units CMake if: matrix.config.compiler.type != 'VISUAL' && matrix.config.compiler.type != 'MSVC' shell: bash working-directory: src run: | cmake --version - cmake --preset default -DCMAKE_INSTALL_PREFIX=../out + cmake --preset conan-default -DCMAKE_INSTALL_PREFIX=../out - name: Install mp-units shell: bash working-directory: src run: | - cmake --build --preset ${{ steps.build_type.outputs.lowercase }} --target install + cmake --build --preset conan-${{ steps.build_type.outputs.lowercase }} --target install - name: Provide dependencies for test_package shell: bash working-directory: test_package @@ -265,14 +302,14 @@ jobs: working-directory: test_package run: | call ..\build\generators\conanvcvars.bat - cmake --preset default -Dmp-units_DIR=../build -Bbuild/local + cmake --preset conan-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' && matrix.config.compiler.type != 'MSVC' shell: bash working-directory: test_package run: | - cmake --preset default -Dmp-units_DIR=../build -Bbuild/local + cmake --preset conan-default -Dmp-units_DIR=../build -Bbuild/local cmake --build build/local --config ${{ matrix.build_type }} - name: Run test_package (local build) shell: bash @@ -285,14 +322,14 @@ jobs: working-directory: test_package run: | call ..\build\generators\conanvcvars.bat - cmake --preset default -DCMAKE_INSTALL_PREFIX=../out -Bbuild/install + cmake --preset conan-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 --preset conan-default -DCMAKE_INSTALL_PREFIX=../out -Bbuild/install cmake --build build/install --config ${{ matrix.build_type }} - name: Run test_package (installation) shell: bash diff --git a/.github/workflows/citation.yml b/.github/workflows/citation.yml index 3b387aca..56423856 100644 --- a/.github/workflows/citation.yml +++ b/.github/workflows/citation.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 # This is needed for workflows running on # ubuntu-20.04 or later diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 2d85b143..01327199 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -25,22 +25,26 @@ on: jobs: analyze: name: Analyze - runs-on: ubuntu-20.04 + runs-on: ${{ matrix.os }} env: CC: gcc-10 CXX: g++-10 - + BUILD_TYPE: Debug + COMPILER_TYPE: GCC + COMPILER_VERSION: 10 + STDLIB: libstdc++11 strategy: fail-fast: false matrix: language: ["cpp", "python"] + os: ["ubuntu-latest"] # 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 + uses: actions/checkout@v3 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL @@ -63,29 +67,29 @@ jobs: - name: Cache Conan data if: matrix.language == 'cpp' - uses: actions/cache@v2 + uses: actions/cache@v3 env: cache-name: cache-conan-data with: - path: ~/.conan/data - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/metadata.json') }} + path: ~/.conan2/p + key: build-${{ matrix.os }}-$BUILD_TYPE-$COMPILER_TYPE-$COMPILER_VERSION-$STDLIB restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- + build-${{ matrix.os }}-$BUILD_TYPE-$COMPILER_TYPE-$COMPILER_VERSION- + build-${{ matrix.os }}-$BUILD_TYPE-$COMPILER_TYPE- + build-${{ matrix.os }}-$BUILD_TYPE- + build-${{ matrix.os }}- - name: Set up Python if: matrix.language == 'cpp' - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 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 + conan profile detect --force + conan remote add artifactory 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 user.build:all=True -c user.build:skip_docs=True -b outdated -u - conan build .. + conan build .. -s compiler.cppstd=20 -s compiler.libcxx=$STDLIB -c user.build:all=True -c user.build:skip_docs=True -b missing - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 0655eae8..71708b47 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -35,35 +35,44 @@ on: - "docs/**" - "src/**" - "example/**" - -env: - CC: gcc-10 - CXX: g++-10 - CMAKE_GENERATOR: Ninja - CONAN_CMAKE_GENERATOR: Ninja - jobs: docs: name: Generate documentation - runs-on: ubuntu-20.04 + runs-on: ${{ matrix.os }} + env: + CC: gcc-10 + CXX: g++-10 + CMAKE_GENERATOR: Ninja + CONAN_CMAKE_GENERATOR: Ninja + OS: ubuntu-22.04 + BUILD_TYPE: Debug + COMPILER_TYPE: GCC + COMPILER_VERSION: 10 + STDLIB: libstdc++11 + strategy: + fail-fast: false + matrix: + os: ["ubuntu-latest"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Cache Conan data - uses: actions/cache@v2 + uses: actions/cache@v3 env: cache-name: cache-conan-data with: - path: ~/.conan/data - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/metadata.json') }} + path: ~/.conan2/p + key: build-${{ matrix.os }}-$BUILD_TYPE-$COMPILER_TYPE-$COMPILER_VERSION-$STDLIB-docs restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- + build-${{ matrix.os }}-$BUILD_TYPE-$COMPILER_TYPE-$COMPILER_VERSION-$STDLIB + build-${{ matrix.os }}-$BUILD_TYPE-$COMPILER_TYPE-$COMPILER_VERSION- + build-${{ matrix.os }}-$BUILD_TYPE-$COMPILER_TYPE- + build-${{ matrix.os }}-$BUILD_TYPE- + build-${{ matrix.os }}- - name: Install Ninja run: | sudo apt install -y ninja-build - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: "3.8" - name: Install Python dependencies @@ -74,17 +83,17 @@ jobs: pip install -U conan - name: Configure Conan run: | - conan config init - conan remote add -i 0 upload https://mpusz.jfrog.io/artifactory/api/conan/conan-oss + conan profile detect --force + conan remote add artifactory https://mpusz.jfrog.io/artifactory/api/conan/conan-oss - name: Install Conan dependencies run: | - 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 + conan install . -s compiler.cppstd=20 -s compiler.libcxx=$STDLIB -c user.build:all=True -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" -b missing - name: Configure CMake run: | - cmake --preset default + cmake --preset conan-default - name: Generate documentation run: | - cmake --build --preset release --target documentation + cmake --build --preset conan-release --target documentation - name: Deploy documentation if: github.ref == 'refs/heads/master' uses: peaceiris/actions-gh-pages@v3 diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index 49164bc2..ab5f4a53 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -2,13 +2,13 @@ FROM trainiteu/gitpod-cpp # Add clang-12 and clang-15 apt repositories RUN lsb_rel=`lsb_release -cs` \ - && sudo add-apt-repository "deb http://apt.llvm.org/${lsb_rel}/ llvm-toolchain-${lsb_rel}-12 main" \ && sudo add-apt-repository "deb http://apt.llvm.org/${lsb_rel}/ llvm-toolchain-${lsb_rel}-13 main" \ + && sudo add-apt-repository "deb http://apt.llvm.org/${lsb_rel}/ llvm-toolchain-${lsb_rel}-14 main" \ && sudo add-apt-repository "deb http://apt.llvm.org/${lsb_rel}/ llvm-toolchain-${lsb_rel} main" # Install older compilers supported by the project as well as clang-format-15 for code formatting RUN sudo install-packages \ g++-10 \ - clang-12 \ + g++-11 \ clang-13 \ - clang-format-15 + clang-14 diff --git a/.gitpod.yml b/.gitpod.yml index fe00fec5..86541729 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -36,8 +36,7 @@ vscode: - vivaxy.vscode-conventional-commits - hbenl.vscode-test-explorer - matepek.vscode-catch2-test-adapter - - trond-snekvik.simple-rst - - lextudio.restructuredtext + - redhat.vscode-yaml - ritwickdey.liveserver - ms-python.python @@ -49,24 +48,28 @@ tasks: mkdir -p "$PWD/.vscode"; cat << 'EOF' > "$PWD/.vscode/settings.json" { - "cmake.buildDirectory": "${workspaceFolder}/build/${buildKitVendor}-${buildKitVersionMajor}", - "cmake.configureArgs": [ - "--toolchain conan_toolchain.cmake" - ], "cmake.generator": "Ninja Multi-Config", "cmake.configureOnOpen": true, "clang-format.executable": "/usr/bin/clang-format-15", "editor.tabSize": 2, - "editor.formatOnPaste": true, "editor.formatOnSave": true, - "editor.formatOnType": true, "editor.bracketPairColorization.enabled": true, "editor.guides.bracketPairs": "active", "restructuredtext.preview.scrollEditorWithPreview": false, "restructuredtext.preview.scrollPreviewWithEditor": false, "liveServer.settings.root": "/build/docs/docs/sphinx/", "esbonio.sphinx.confDir": "${workspaceFolder}/docs", - "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools" + "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools", + "yaml.schemas": { + "https://squidfunk.github.io/mkdocs-material/schema.json": "mkdocs.yml" + }, + "yaml.customTags": [ + "!ENV scalar", + "!ENV sequence", + "tag:yaml.org,2002:python/name:materialx.emoji.to_svg", + "tag:yaml.org,2002:python/name:materialx.emoji.twemoji", + "tag:yaml.org,2002:python/name:pymdownx.superfences.fence_code_format" + ] } EOF @@ -77,96 +80,103 @@ tasks: conan config init conan profile update settings.compiler.libcxx=libstdc++11 default conan profile update settings.compiler.cppstd=20 default - conan profile update conf.tools.cmake.cmaketoolchain:generator=Ninja default conan remote add -i 0 conan-mpusz https://mpusz.jfrog.io/artifactory/api/conan/conan-oss pushd /workspace/.conan/profiles cp default gcc10 cp default gcc11 - cp default clang12 + cp default gcc12 cp default clang13 cp default clang14 + cp default clang15 popd conan profile update settings.compiler.version=10 gcc10 - conan profile update env.CXX=/usr/bin/g++-10 gcc10 - conan profile update env.CC=/usr/bin/gcc-10 gcc10 - conan profile update settings.compiler=clang clang12 - conan profile update settings.compiler.version=12 clang12 - conan profile update settings.compiler.libcxx=libstdc++11 clang12 - conan profile update env.CXX=/usr/bin/clang++-12 clang12 - conan profile update env.CC=/usr/bin/clang-12 clang12 + conan profile update 'conf.tools.build.compiler_executables={"c": "gcc-10", "cpp": "g++-10"}' gcc10 + conan profile update settings.compiler.version=11 gcc11 + conan profile update 'conf.tools.build.compiler_executables={"c": "gcc-11", "cpp": "g++-11"}' gcc11 + conan profile update settings.compiler.version=11 gcc12 + conan profile update 'conf.tools.build.compiler_executables={"c": "gcc-12", "cpp": "g++-12"}' gcc12 conan profile update settings.compiler=clang clang13 conan profile update settings.compiler.version=13 clang13 conan profile update settings.compiler.libcxx=libstdc++11 clang13 - conan profile update env.CXX=/usr/bin/clang++-13 clang13 - conan profile update env.CC=/usr/bin/clang-13 clang13 + conan profile update 'conf.tools.build.compiler_executables={"c": "clang-13", "cpp": "clang++-13"}' clang13 conan profile update settings.compiler=clang clang14 conan profile update settings.compiler.version=14 clang14 - conan profile update settings.compiler.libcxx=libc++ clang14 - conan profile update env.CXX=/usr/bin/clang++-14 clang14 - conan profile update env.CC=/usr/bin/clang-14 clang14 + conan profile update settings.compiler.libcxx=libstdc++11 clang14 + conan profile update 'conf.tools.build.compiler_executables={"c": "clang-14", "cpp": "clang++-14"}' clang14 + conan profile update settings.compiler=clang clang15 + conan profile update settings.compiler.version=15 clang15 + conan profile update settings.compiler.libcxx=libc++ clang15 + conan profile update 'conf.tools.build.compiler_executables={"c": "clang-15", "cpp": "clang++-15"}' clang15 + echo 'tools.cmake.cmaketoolchain:generator=Ninja Multi-Config' > /workspace/.conan/global.conf + echo 'tools.cmake.cmake_layout:build_folder_vars=["settings.compiler", "settings.compiler.version"]' >> /workspace/.conan/global.conf gp sync-done conan-init exit - name: gcc-10 init: | gp sync-await conan-init - mkdir -p build/GCC-10 && cd build/GCC-10 - conan install ../.. -pr gcc10 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated - conan install ../.. -pr gcc10 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated -s build_type=Debug - cmake ../.. --no-warn-unused-cli --toolchain conan_toolchain.cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=TRUE -DCMAKE_C_COMPILER=/usr/bin/gcc-10 -DCMAKE_CXX_COMPILER=/usr/bin/g++-10 - cmake --build . --config Release -j - cmake --build . --config Debug -j + conan install . -pr gcc10 -c user.build:all=True -c user.build:skip_docs=True -b outdated + conan install . -pr gcc10 -c user.build:all=True -c user.build:skip_docs=True -b outdated -s build_type=Debug + cmake --preset gcc-10 + cmake --build --preset gcc-10-release -j + cmake --build --preset gcc-10-debug -j ctest -C Release ctest -C Debug echo "🛠️ gcc-10 pre-build done! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️" - name: gcc-11 init: | gp sync-await conan-init - mkdir -p build/GCC-11 && cd build/GCC-11 - conan install ../.. -pr gcc11 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated - gp sync-done conan-gcc-install - conan install ../.. -pr gcc11 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated -s build_type=Debug - cmake ../.. --no-warn-unused-cli --toolchain conan_toolchain.cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=TRUE -DCMAKE_C_COMPILER=/usr/bin/gcc -DCMAKE_CXX_COMPILER=/usr/bin/g++ - cmake --build . --config Release -j - cmake --build . --config Debug -j + conan install . -pr gcc11 -c user.build:all=True -c user.build:skip_docs=True -b outdated + conan install . -pr gcc11 -c user.build:all=True -c user.build:skip_docs=True -b outdated -s build_type=Debug + cmake --preset gcc-11 + cmake --build --preset gcc-11-release -j + cmake --build --preset gcc-11-debug -j ctest -C Release ctest -C Debug echo "🛠️ gcc-11 pre-build done! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️" - - name: clang-12 + - name: gcc-12 init: | gp sync-await conan-init - mkdir -p build/Clang-12 && cd build/Clang-12 - conan install ../.. -pr clang12 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated - conan install ../.. -pr clang12 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated -s build_type=Debug - cmake ../.. --no-warn-unused-cli --toolchain conan_toolchain.cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=TRUE -DCMAKE_C_COMPILER=/usr/bin/clang-12 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-12 - cmake --build . --config Release -j - cmake --build . --config Debug -j + conan install . -pr gcc12 -c user.build:all=True -c user.build:skip_docs=True -b outdated + conan install . -pr gcc12 -c user.build:all=True -c user.build:skip_docs=True -b outdated -s build_type=Debug + cmake --preset gcc-12 + cmake --build --preset gcc-12-release -j + cmake --build --preset gcc-12-debug -j ctest -C Release ctest -C Debug - echo "🛠️ clang-12 pre-build done! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️" + echo "🛠️ gcc-12 pre-build done! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️" - name: clang-13 init: | gp sync-await conan-init - mkdir -p build/Clang-13 && cd build/Clang-13 - conan install ../.. -pr clang13 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated - conan install ../.. -pr clang13 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated -s build_type=Debug - cmake ../.. --no-warn-unused-cli --toolchain conan_toolchain.cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=TRUE -DCMAKE_C_COMPILER=/usr/bin/clang-13 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-13 - cmake --build . --config Release -j - cmake --build . --config Debug -j + conan install . -pr clang13 -c user.build:all=True -c user.build:skip_docs=True -b outdated + conan install . -pr clang13 -c user.build:all=True -c user.build:skip_docs=True -b outdated -s build_type=Debug + cmake --preset clang-13 + cmake --build --preset clang-13-release -j + cmake --build --preset clang-13-debug -j ctest -C Release ctest -C Debug echo "🛠️ clang-13 pre-build done! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️" - name: clang-14 init: | gp sync-await conan-init - mkdir -p build/Clang-14 && cd build/Clang-14 - conan install ../.. -pr clang4 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated - conan install ../.. -pr clang14 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated -s build_type=Debug - cmake ../.. --no-warn-unused-cli --toolchain conan_toolchain.cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=TRUE -DCMAKE_C_COMPILER=/usr/bin/clang-14 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-14 - cmake --build . --config Release -j - cmake --build . --config Debug -j + conan install . -pr clang14 -c user.build:all=True -c user.build:skip_docs=True -b outdated + conan install . -pr clang14 -c user.build:all=True -c user.build:skip_docs=True -b outdated -s build_type=Debug + cmake --preset clang-14 + cmake --build --preset clang-14-release -j + cmake --build --preset clang-14-debug -j ctest -C Release ctest -C Debug echo "🛠️ clang-14 pre-build done! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️" + - name: clang-15 + init: | + gp sync-await conan-init + conan install . -pr clang15 -c user.build:all=True -c user.build:skip_docs=True -b outdated + conan install . -pr clang15 -c user.build:all=True -c user.build:skip_docs=True -b outdated -s build_type=Debug + cmake --preset clang-15 + cmake --build --preset clang-15-release -j + cmake --build --preset clang-15-debug -j + ctest -C Release + ctest -C Debug + echo "🛠️ clang-15 pre-build done! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️" - name: documentation init: | gp sync-await conan-init diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1a890db6..cc9f58eb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,15 +1,19 @@ default_stages: [commit] repos: + - repo: meta + hooks: + - id: check-hooks-apply + - id: check-useless-excludes - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.2.0 + rev: v4.3.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - # - repo: https://github.com/pocc/pre-commit-hooks - # rev: v1.3.5 - # hooks: - # - id: clang-format + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: v16.0.0 + hooks: + - id: clang-format - repo: https://github.com/cheshirekow/cmake-format-precommit rev: v0.6.13 hooks: @@ -19,16 +23,16 @@ repos: # additional_dependencies: ["cmakelang"] # exclude: "cmake/.*" - repo: https://github.com/psf/black - rev: 22.3.0 + rev: 22.10.0 hooks: - id: black language_version: python3 - repo: https://github.com/PyCQA/isort - rev: 5.10.1 + rev: 5.12.0 hooks: - id: isort args: [--profile, black, --multi-line, "3"] - repo: https://github.com/PyCQA/flake8 - rev: 4.0.1 + rev: 5.0.4 hooks: - id: flake8 diff --git a/CITATION.cff b/CITATION.cff index 27a65871..00692afb 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -27,6 +27,8 @@ authors: orcid: https://orcid.org/0000-0003-0680-0765 - given-names: "Johel Ernesto" family-names: "Guerrero Peña" + - given-names: "Chip" + family-names: "Hogg" - name: "The mp-units project team" contact: @@ -34,9 +36,9 @@ contact: given-names: Mateusz family-names: Pusz -repository-code: 'https://github.com/mpusz/units' -url: 'https://mpusz.github.io/units' -repository-artifact: 'https://conan.io/center/mp-units' +repository-code: "https://github.com/mpusz/units" +url: "https://mpusz.github.io/units" +repository-artifact: "https://conan.io/center/mp-units" version: 0.7.0 date-released: "2021-05-11" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 573ce5c4..295cd15a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,11 +12,11 @@ below or prefix any `mp-units` URL (main branch, other branches, issues, PRs, .. The above environment provides you with: -- all supported compilers for Linux development (`g++-10`, `g++-11`, `clang-12`, `clang-13`) and build tools like `cmake` and `conan` +- all supported compilers for Linux development (`g++-10`, `g++-11`, `g++12`, `clang-13`, `clang-14`, and `clang-15`) + and the latest version of build tools like `cmake` and `conan` - all Conan dependencies preinstalled on the machine - all documentation generation tools ready to use -- completed prebuilds for all targets (each compiler as well as a documentation) -- `clang-format-15` for source code formatting +- completed prebuilds for all targets (Debug and Release builds for each compiler as well as a documentation) - VSCode preconfigured to benefit from all the above ## Download, Build, Install @@ -24,13 +24,19 @@ The above environment provides you with: Alternatively, please refer to our official docs for [download, build, and install instructions](https://mpusz.github.io/units/usage.html) if you want to setup a development environment on your local machine. -## Where to start? +## Code Formatting -If you are looking for a good issue to start with, please check the following: +There is a formatting standard enforced with the `pre-commit` script. Before committing your changes please do the following: + +```bash +pip3 install -U pre-commit +pre-commit run --all-files +``` + +This will run `clang-format` for code formatting with the `.clang-format` file provided in the repo, `cmake-format` to format the CMake files, and some other check as well. +The script will run on all the files in the repo and will apply the changes in-place when needed. +After the script is done please make sure to stage all those changes to git commit. -- [good first issue](https://github.com/mpusz/units/labels/good%20first%20issue) - issues that should be pretty simple to implement. -- [help wanted](https://github.com/mpusz/units/labels/help%20wanted) - issues that typically are a bit more involved than beginner issues. -- [high priority](https://github.com/mpusz/units/labels/high%20priority) - things to fix ASAP but often of higher complexity. ## Naming Conventions @@ -40,18 +46,6 @@ Here are the main rules for naming things in this repo: - template parameters in a `PascalCase` - C++20 concepts names for now in a `PascalCase` but we plan to change it (see for more details) -## Code Formatting - -Please use `clang-format-15` for code formatting with the `.clang-format` file provided in the repo. - -If you are unable to install or use `clang-format-15`, here are the main rules to follow: - -- LF line endings -- 2 spaces for indentation - - `requires` clause indented with 2 spaces as well -- no indentation for namespaces -- function opening brace in the new line - - all other opening braces attached to the current context at the end of the same line ## Backward Compatibility @@ -59,3 +53,12 @@ Before submission, please remember to check if the code compiles fine on the sup The CI will check it anyway but it is good to check at least some of the configurations before pushing changes. Especially older compilers can be tricky as those do not support all the C++20 features well enough. The official list of supported compilers can be always found on our [Usage page](https://mpusz.github.io/units/usage.html). + + +## Where to start? + +If you are looking for a good issue to start with, please check the following: + +- [good first issue](https://github.com/mpusz/units/labels/good%20first%20issue) - issues that should be pretty simple to implement. +- [help wanted](https://github.com/mpusz/units/labels/help%20wanted) - issues that typically are a bit more involved than beginner issues. +- [high priority](https://github.com/mpusz/units/labels/high%20priority) - things to fix ASAP but often of higher complexity. diff --git a/README.md b/README.md index 0815c65d..c0c6b1bd 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,15 @@ [![GitHub license](https://img.shields.io/github/license/mpusz/units?cacheSeconds=3600&color=informational&label=License)](./LICENSE.md) -[![Conan CI](https://img.shields.io/github/workflow/status/mpusz/units/Conan%20CI/master?label=Conan%20CI)](https://github.com/mpusz/units/actions?query=workflow%3A%22Conan%20CI%22+branch%3Amaster) -[![CMake CI](https://img.shields.io/github/workflow/status/mpusz/units/CMake%20Test%20Package%20CI/master?label=CMake%20CI)](https://github.com/mpusz/units/actions?query=workflow%3A%22CMake+Test+Package+CI%22+branch%3Amaster) -[![Check CI](https://img.shields.io/github/workflow/status/mpusz/units/Check%20CI/master?label=Check%20CI)](https://github.com/mpusz/units/actions?query=workflow%3A%22Check%20CI%22+branch%3Amaster) -[![GitHub Workflow Documentation](https://img.shields.io/github/workflow/status/mpusz/units/Documentation/master?label=Documentation%20CI)](https://github.com/mpusz/units/actions?query=workflow%3ADocumentation+branch%3Amaster) -[![Conan stable](https://img.shields.io/badge/ConanCenter-0.7.0%3Astable-blue)](https://conan.io/center/mp-units) +[![GitHub license](https://img.shields.io/badge/C%2B%2B-20-blue)](https://en.cppreference.com/w/cpp/compiler_support#cpp20) + +[![Conan CI](https://img.shields.io/github/actions/workflow/status/mpusz/units/ci-conan.yml?branch=master&label=Conan%20CI)](https://github.com/mpusz/units/actions?query=workflow%3A%22Conan%20CI%22+branch%3Amaster) +[![CMake CI](https://img.shields.io/github/actions/workflow/status/mpusz/units/ci-test-package-cmake.yml?branch=master&label=CMake%20CI)](https://github.com/mpusz/units/actions?query=workflow%3A%22CMake+Test+Package+CI%22+branch%3Amaster) +[![Check CI](https://img.shields.io/github/actions/workflow/status/mpusz/units/ci-check.yml?branch=master&label=Check%20CI)](https://github.com/mpusz/units/actions?query=workflow%3A%22Check%20CI%22+branch%3Amaster) +[![GitHub Workflow Documentation](https://img.shields.io/github/actions/workflow/status/mpusz/units/documentation.yml?branch=master&label=Documentation)](https://github.com/mpusz/units/actions?query=workflow%3ADocumentation+branch%3Amaster) + +[![Conan stable](https://img.shields.io/conan/v/mp-units?label=ConanCenter&color=blue)](https://conan.io/center/mp-units) [![Conan testing](https://img.shields.io/badge/mpusz.jfrog.io-0.8.0%3Atesting-blue)](https://mpusz.jfrog.io/ui/packages/conan:%2F%2Fmp-units/0.8.0) + # `mp-units` - A Units Library for C++ **The mp-units library is the subject of ISO standardization for C++23/26. More on this can @@ -22,6 +26,15 @@ An extensive project documentation including installation instructions and user' guide can be found on [mp-units GitHub Pages](https://mpusz.github.io/units). +## Terms and Definitions + +This project uses the official metrology vocabulary defined by the ISO and BIPM. +Please familiarize yourself with those terms to better understand the documentation +and improve domain-related communication and discussions. You can find essential +project-related definitions in [our documentation's "Glossary" chapter](https://mpusz.github.io/units/glossary.html). +Even more terms are provided in the official vocabulary of the [ISO](https://www.iso.org/obp/ui#iso:std:iso-iec:guide:99:ed-1:v2:en) +and [BIPM](https://jcgm.bipm.org/vim/en). + ## TL;DR `mp-units` is a compile-time enabled Modern C++ library that provides compile-time dimensional @@ -59,7 +72,7 @@ static_assert(10 * km / (5 * km) == 2); static_assert(1000 / (1 * s) == 1 * kHz); ``` -_Try it on the [Compiler Explorer](https://godbolt.org/z/5dvY8Woh1)._ +_Try it on the [Compiler Explorer](https://godbolt.org/z/qbbbnfK3s)._ This library requires some C++20 features (concepts, classes as NTTPs, ...). Thanks to them the user gets a powerful but still easy to use interface and all unit conversions @@ -107,4 +120,4 @@ int main() } ``` -_Try it on the [Compiler Explorer](https://godbolt.org/z/bcb87Kvea)._ +_Try it on the [Compiler Explorer](https://godbolt.org/z/b4a3Ya6dY)._ diff --git a/conanfile.py b/conanfile.py index 0ab25811..7abe41a2 100644 --- a/conanfile.py +++ b/conanfile.py @@ -24,19 +24,19 @@ import os import re from conan import ConanFile +from conan.errors import ConanInvalidConfiguration 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 -required_conan_version = ">=1.50.0" +required_conan_version = ">=2.0.0" class MPUnitsConan(ConanFile): name = "mp-units" homepage = "https://github.com/mpusz/units" - description = "Physical Units library for C++" + description = "Physical Quantities and Units library for C++" topics = ( "units", "dimensions", @@ -45,15 +45,15 @@ class MPUnitsConan(ConanFile): "physical-quantities", "physical-units", "system-of-units", - "cpp23", - "cpp20", + "system-of-quantities", + "isq", + "si", "library", "quantity-manipulation", ) license = "MIT" url = "https://github.com/mpusz/units" - settings = "os", "compiler", "build_type", "arch" - requires = "gsl-lite/0.40.0" + settings = "os", "arch", "compiler", "build_type" exports = ["LICENSE.md"] exports_sources = [ "docs/*", @@ -63,24 +63,34 @@ class MPUnitsConan(ConanFile): "example/*", "CMakeLists.txt", ] + package_type = "header-library" no_copy_source = True - generators = "cmake_paths" + + @property + def _min_cppstd(self): + return "20" + + @property + def _minimum_compilers_version(self): + return {"gcc": "10.3", "clang": "12", "apple-clang": "13", "msvc": "192"} @property def _build_all(self): - return bool(self.conf["user.build:all"]) + return bool(self.conf.get("user.build:all", default=False)) + + @property + def _skip_la(self): + return bool(self.conf.get("user.build:skip_la", default=False)) @property def _skip_docs(self): - return bool(self.conf["user.build:skip_docs"]) + return bool(self.conf.get("user.build:skip_docs", default=True)) @property def _use_libfmt(self): compiler = self.settings.compiler version = Version(self.settings.compiler.version) - std_support = ( - compiler == "Visual Studio" and version >= 17 and compiler.cppstd == 23 - ) or (compiler == "msvc" and version >= 193 and compiler.cppstd == 23) + std_support = compiler == "msvc" and version >= 193 and compiler.cppstd == 23 return not std_support @property @@ -89,14 +99,6 @@ class MPUnitsConan(ConanFile): version = Version(self.settings.compiler.version) return "clang" in compiler and compiler.libcxx == "libc++" and version < 14 - @property - def _msvc_version(self): - compiler = self.settings.compiler - if compiler.update: - return int(f"{compiler.version}{compiler.update}") - else: - return int(f"{compiler.version}0") - def set_version(self): content = load(self, os.path.join(self.recipe_folder, "src/CMakeLists.txt")) version = re.search( @@ -105,51 +107,42 @@ class MPUnitsConan(ConanFile): self.version = version.strip() def requirements(self): + self.requires("gsl-lite/0.40.0") if self._use_libfmt: self.requires("fmt/8.1.1") - if self._use_range_v3: self.requires("range-v3/0.11.0") def build_requirements(self): if self._build_all: self.test_requires("catch2/3.1.0") - self.test_requires("wg21-linear_algebra/0.7.2") + if not self._skip_la: + self.test_requires("wg21-linear_algebra/0.7.3") 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) - def configure(self): + def validate(self): + check_min_cppstd(self, self._min_cppstd) + + def loose_lt_semver(v1, v2): + lv1 = [int(v) for v in v1.split(".")] + lv2 = [int(v) for v in v2.split(".")] + min_length = min(len(lv1), len(lv2)) + return lv1[:min_length] < lv2[:min_length] + compiler = self.settings.compiler - version = Version(self.settings.compiler.version) - if compiler == "gcc": - if version < 10: - raise ConanInvalidConfiguration("mp-units requires at least g++-10") - elif compiler == "clang": - if version < 12: - raise ConanInvalidConfiguration("mp-units requires at least clang++-12") - elif compiler == "apple-clang": - if version < 13: - raise ConanInvalidConfiguration( - "mp-units requires at least AppleClang 13" - ) - elif compiler == "Visual Studio": - if version < 16: - raise ConanInvalidConfiguration( - "mp-units requires at least Visual Studio 16.9" - ) - elif compiler == "msvc": - if self._msvc_version < 1928: - raise ConanInvalidConfiguration("mp-units requires at least MSVC 19.28") - else: - raise ConanInvalidConfiguration("Unsupported compiler") - check_min_cppstd(self, 20) + min_version = self._minimum_compilers_version.get(str(compiler)) + if min_version and loose_lt_semver(str(compiler.version), min_version): + raise ConanInvalidConfiguration( + f"{self.ref} requires at least {compiler} {min_version} ({compiler.version} in use)" + ) def layout(self): cmake_layout(self) def generate(self): tc = CMakeToolchain(self) + tc.variables["UNITS_BUILD_LA"] = self._build_all and not self._skip_la tc.variables["UNITS_BUILD_DOCS"] = self._build_all and not self._skip_docs tc.variables["UNITS_USE_LIBFMT"] = self._use_libfmt tc.generate() @@ -182,7 +175,7 @@ class MPUnitsConan(ConanFile): # core self.cpp_info.components["core"].requires = ["gsl-lite::gsl-lite"] - if compiler == "Visual Studio": + if compiler == "msvc": self.cpp_info.components["core"].cxxflags = ["/utf-8"] if self._use_range_v3: self.cpp_info.components["core"].requires.append("range-v3::range-v3") @@ -192,29 +185,32 @@ class MPUnitsConan(ConanFile): self.cpp_info.components["core-fmt"].requires = ["core"] if self._use_libfmt: self.cpp_info.components["core-fmt"].requires.append("fmt::fmt") + self.cpp_info.components["utility"].requires = ["core", "isq", "si", "angular"] self.cpp_info.components["isq"].requires = ["core"] - self.cpp_info.components["isq-natural"].requires = ["isq"] + self.cpp_info.components["angular"].requires = ["isq"] + self.cpp_info.components["isq_angular"].requires = ["isq", "angular"] + self.cpp_info.components["natural"].requires = ["isq"] self.cpp_info.components["si"].requires = ["isq"] - self.cpp_info.components["si-cgs"].requires = ["si"] - self.cpp_info.components["si-fps"].requires = ["si-international"] - self.cpp_info.components["si-hep"].requires = ["si"] - self.cpp_info.components["si-iau"].requires = ["si"] - self.cpp_info.components["si-imperial"].requires = ["si"] - self.cpp_info.components["si-international"].requires = ["si"] - self.cpp_info.components["si-typographic"].requires = ["si"] - self.cpp_info.components["si-uscs"].requires = ["si"] - self.cpp_info.components["isq-iec80000"].requires = ["si"] + self.cpp_info.components["cgs"].requires = ["si"] + self.cpp_info.components["hep"].requires = ["si"] + self.cpp_info.components["iau"].requires = ["si"] + self.cpp_info.components["imperial"].requires = ["si"] + self.cpp_info.components["international"].requires = ["si"] + self.cpp_info.components["typographic"].requires = ["usc"] + self.cpp_info.components["usc"].requires = ["international"] + self.cpp_info.components["iec80000"].requires = ["isq", "si"] self.cpp_info.components["systems"].requires = [ "isq", - "isq-natural", + "angular", + "isq_angular", + "natural", "si", - "si-cgs", - "si-fps", - "si-hep", - "si-iau", - "si-imperial", - "si-international", - "si-typographic", - "si-uscs", - "isq-iec80000", + "cgs", + "hep", + "iau", + "imperial", + "international", + "typographic", + "usc", + "iec80000", ] diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index 6714f686..297261a7 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -46,6 +46,10 @@ set(unitsSphinxDocs "${CMAKE_CURRENT_SOURCE_DIR}/_static/img/units.svg" "${CMAKE_CURRENT_SOURCE_DIR}/_static/img/quantity_like.svg" "${CMAKE_CURRENT_SOURCE_DIR}/CHANGELOG.md" + "${CMAKE_CURRENT_SOURCE_DIR}/defining_systems.rst" + "${CMAKE_CURRENT_SOURCE_DIR}/defining_systems/angular_units.rst" + "${CMAKE_CURRENT_SOURCE_DIR}/defining_systems/isq.rst" + "${CMAKE_CURRENT_SOURCE_DIR}/defining_systems/si.rst" "${CMAKE_CURRENT_SOURCE_DIR}/design.rst" "${CMAKE_CURRENT_SOURCE_DIR}/design/directories.rst" "${CMAKE_CURRENT_SOURCE_DIR}/design/downcasting.rst" @@ -82,7 +86,6 @@ set(unitsSphinxDocs "${CMAKE_CURRENT_SOURCE_DIR}/examples/kalman_filter/kalman.rst" "${CMAKE_CURRENT_SOURCE_DIR}/faq.rst" "${CMAKE_CURRENT_SOURCE_DIR}/framework.rst" - "${CMAKE_CURRENT_SOURCE_DIR}/framework/angular_units.rst" "${CMAKE_CURRENT_SOURCE_DIR}/framework/arithmetics.rst" "${CMAKE_CURRENT_SOURCE_DIR}/framework/basic_concepts.rst" "${CMAKE_CURRENT_SOURCE_DIR}/framework/constants.rst" diff --git a/docs/defining_systems.rst b/docs/defining_systems.rst new file mode 100644 index 00000000..f40c9f8e --- /dev/null +++ b/docs/defining_systems.rst @@ -0,0 +1,9 @@ +Defining Systems +================ + +.. toctree:: + :maxdepth: 2 + + defining_systems/isq + defining_systems/si + defining_systems/angular_units diff --git a/docs/framework/angular_units.rst b/docs/defining_systems/angular_units.rst similarity index 77% rename from docs/framework/angular_units.rst rename to docs/defining_systems/angular_units.rst index a4303f84..8574efb6 100644 --- a/docs/framework/angular_units.rst +++ b/docs/defining_systems/angular_units.rst @@ -59,6 +59,36 @@ Paul Quincey summarizes that with the above in action: can be measured in terms of the Planck constant. +Angular quantities in the :term:`SI` +------------------------------------ + +Even though the :term:`SI` somehow ignores the dimensionality of angle: + + Plane and solid angles, when expressed in radians and steradians respectively, are in effect + also treated within the SI as quantities with the unit one. The symbols :math:`rad` + and :math:`sr` are written explicitly where appropriate, in order to emphasize that, for radians or + steradians, the quantity being considered is, or involves the plane angle or solid angle + respectively. For steradians it emphasizes the distinction between units of flux and intensity + in radiometry and photometry for example. However, it is a long-established practice in + mathematics and across all areas of science to make use of :math:`rad = 1` and :math:`sr = 1`. + For historical reasons the radian and steradian are treated as derived units. + +It also explicitly states: + + The SI unit of frequency is hertz, the SI unit of angular velocity and angular frequency is + radian per second, and the SI unit of activity is becquerel, implying counts per second. + Although it is formally correct to write all three of these units as the reciprocal second, the + use of the different names emphasizes the different nature of the quantities concerned. It is + especially important to carefully distinguish frequencies from angular frequencies, because + by definition their numerical values differ by a factor of :math:`2\pi`. Ignoring this fact may cause + an error of :math:`2\pi`. Note that in some countries, frequency values are conventionally expressed + using “cycle/s” or “cps” instead of the SI unit :math:`Hz`, although “cycle” and “cps” are not units + in the SI. Note also that it is common, although not recommended, to use the term + frequency for quantities expressed in :math:`rad/s`. Because of this, it is recommended that + quantities called “frequency”, “angular frequency”, and “angular velocity” always be given + explicit units of :math:`Hz` or :math:`rad/s`` and not :math:`s^{-1}`. + + Angular quantities in the library --------------------------------- diff --git a/docs/defining_systems/isq.rst b/docs/defining_systems/isq.rst new file mode 100644 index 00000000..3c243afd --- /dev/null +++ b/docs/defining_systems/isq.rst @@ -0,0 +1,48 @@ +.. namespace:: units + +International System of Quantities (ISQ) +======================================== + +According to [ISO80000]_: + + The system of quantities, including the relations among them the quantities used as the basis of the units of + the SI, is named the **International System of Quantities**, denoted "ISQ", in all languages. + + +Base dimensions and their symbols +--------------------------------- + +According to the [SIBrochure]_: + + Physical quantities can be organized in a system of dimensions, where the system used is + decided by convention. Each of the seven base quantities used in the SI is regarded as + having its own dimension. The symbols used for the base quantities and the symbols used + to denote their dimension are shown in Table 3. + +Following that the library provides the following definitions:: + + namespace units::isq { + + template struct dim_time : base_dimension<"T", U> {}; + template struct dim_length : base_dimension<"L", U> {}; + template struct dim_mass : base_dimension<"M", U> {}; + template struct dim_electric_current : base_dimension<"I", U> {}; + template struct dim_thermodynamic_temperature : base_dimension<"Θ", U> {}; + template struct dim_amount_of_substance : base_dimension<"N", U> {}; + template struct dim_luminous_intensity : base_dimension<"J", U> {}; + + } + +Base dimension symbols provided above are consistently defined by both [SIBrochure]_ and [ISO80000]_. + + +Derived dimensions +------------------ + +[SIBrochure]_ states: + + Since the number of quantities is without limit, it is not possible to + provide a complete list of derived quantities and derived units. + +The authors of this library decided to limit the set of defined quantities to all +quantities defined in the [ISO80000]_ series of documents. diff --git a/docs/defining_systems/si.rst b/docs/defining_systems/si.rst new file mode 100644 index 00000000..22706e39 --- /dev/null +++ b/docs/defining_systems/si.rst @@ -0,0 +1,231 @@ +.. namespace:: units + +The International System of Units (SI) +====================================== + +The :term:`SI` system is defined in the [SIBrochure]_ and standardizes units for quantities provided in +the :term:`ISQ` system: + + The SI is a consistent system of units for use in all aspects of life, including international + trade, manufacturing, security, health and safety, protection of the environment, and in the + basic science that underpins all of these. The system of quantities underlying the SI and the + equations relating them are based on the present description of nature and are familiar to all + scientists, technologists and engineers. + +As the :term:`SI` is defined on top of the :term:`ISQ` the C++ namespace for all of its definitions +is ``units::isq::si``. + +Base units +---------- + +Even though from 2019 the :term:`SI` system is defined in terms of the `Defining constants`_, +base units still are really important. As the [SIBrochure]_ states: + + Prior to the definitions adopted in 2018, the SI was defined through seven base units from + which the derived units were constructed as products of powers of the base units. Defining + the SI by fixing the numerical values of seven defining constants has the effect that this + distinction is, in principle, not needed, since all units, base as well as derived units, may be + constructed directly from the defining constants. Nevertheless, the concept of base and + derived units is maintained because it is useful and historically well established, noting + also that the ISO/IEC 80000 series of Standards specify base and derived quantities which + necessarily correspond to the SI base and derived units defined here. + +The base units and quantities of the :term:`SI` system are defined as follows:: + + namespace units::isq::si { + + struct second : named_unit {}; + struct dim_time : isq::dim_time {}; + template U, Representation Rep = double> + using time = quantity; + + struct metre : named_unit {}; + struct dim_length : isq::dim_length {}; + template U, Representation Rep = double> + using length = quantity; + + struct gram : named_unit {}; + struct kilogram : prefixed_unit {}; + struct dim_mass : isq::dim_mass {}; + template U, Representation Rep = double> + using mass = quantity; + + struct ampere : named_unit {}; + struct dim_electric_current : isq::dim_electric_current {}; + template U, Representation Rep = double> + using electric_current = quantity; + + struct kelvin : named_unit {}; + struct dim_thermodynamic_temperature : isq::dim_thermodynamic_temperature {}; + template U, Representation Rep = double> + using thermodynamic_temperature = quantity; + + struct mole : named_unit {}; + struct dim_amount_of_substance : isq::dim_amount_of_substance {}; + template U, Representation Rep = double> + using amount_of_substance = quantity; + + struct candela : named_unit {}; + struct dim_luminous_intensity : isq::dim_luminous_intensity {}; + template U, Representation Rep = double> + using luminous_intensity = quantity; + + } + + +Derived units +------------- + +The [SIBrochure]_ states: + + Derived units are defined as products of powers of the base units. When the numerical + factor of this product is one, the derived units are called coherent derived units. The base + and coherent derived units of the SI form a coherent set, designated the set of coherent SI + units. The word “coherent” here means that equations between the numerical values of + quantities take exactly the same form as the equations between the quantities themselves. + Some of the coherent derived units in the SI are given special names. ... Together with the + seven base units they form the core of the set of SI units. All other SI units are combinations + of some of these 29 units. + + +Unit prefixes +------------- + +According to [SIBrochure]_: + + Prefixes may be used with any of the 29 SI units with special names with + the exception of the base unit kilogram. + +Here is a complete list of all the :term:`SI` prefixes supported by the library:: + + namespace si { + + struct yocto : prefix()> {}; + struct zepto : prefix()> {}; + struct atto : prefix()> {}; + struct femto : prefix()> {}; + struct pico : prefix()> {}; + struct nano : prefix()> {}; + struct micro : prefix()> {}; + struct milli : prefix()> {}; + struct centi : prefix()> {}; + struct deci : prefix()> {}; + struct deca : prefix()> {}; + struct hecto : prefix()> {}; + struct kilo : prefix()> {}; + struct mega : prefix()> {}; + struct giga : prefix()> {}; + struct tera : prefix()> {}; + struct peta : prefix()> {}; + struct exa : prefix()> {}; + struct zetta : prefix()> {}; + struct yotta : prefix()> {}; + + } + + +Other definitions for names units +--------------------------------- + +For all of the above units the library also provides: + +- prefixed versions using SI prefixes:: + + namespace units::isq::si { + + struct millisecond : prefixed_unit {}; + + } + +- :ref:`framework/quantities:Quantity References (Experimental)`:: + + namespace units::isq::si { + + namespace time_references { + + inline constexpr auto s = reference{}; + + } + + namespace references { + + using namespace time_references; + + } + +- :ref:`framework/quantities:Unit-Specific Aliases (Experimental)`:: + + namespace units::aliases::isq::si::inline time { + + template + using s = units::isq::si::time; + + } + +- :ref:`framework/quantities:User Defined Literals (Experimental)`:: + + namespace units::isq::si::inline literals { + + constexpr auto operator"" _q_s(unsigned long long l) + { + gsl_ExpectsAudit(std::in_range(l)); + return time(static_cast(l)); + } + constexpr auto operator"" _q_s(long double l) { return time(l); } + + } + +For some of the units, when accepted by the :term:`SI`, other non-standard scaled versions are also provided:: + + namespace units::isq::si { + + struct minute : named_scaled_unit(), second> {}; + struct hour : named_scaled_unit(), minute> {}; + struct day : named_scaled_unit(), hour> {}; + + } + + + + +Defining constants +------------------ + +[SIBrochure]_ states that: + + The definition of the SI units is established in terms of a set of seven defining constants. + The complete system of units can be derived from the fixed values of these defining + constants, expressed in the units of the SI. + +Those constants are provided in the *units/isq/si/constants.h* header file as:: + + namespace units::isq::si::si2019 { + + template + inline constexpr auto hyperfine_structure_transition_frequency = frequency(Rep{9'192'631'770}); + + template + inline constexpr auto speed_of_light = speed(299'792'458); + + template + inline constexpr auto planck_constant = energy(6.62607015e-34) * time(1); + + template + inline constexpr auto elementary_charge = electric_charge(1.602176634e-19); + + template + inline constexpr auto boltzmann_constant = energy(1.380649e-23) / thermodynamic_temperature(1); + + template + inline constexpr auto avogadro_constant = Rep(6.02214076e23) / amount_of_substance(1); + + template + inline constexpr auto luminous_efficacy = luminous_flux(683) / power(1); + + } + +.. note:: + + Please note the nested `si2019` namespace. It is introduced in case those constants were changed/updated + by the :term:`SI` in the future. diff --git a/docs/framework.rst b/docs/framework.rst index 05866a96..952d66fc 100644 --- a/docs/framework.rst +++ b/docs/framework.rst @@ -22,5 +22,4 @@ Framework Basics framework/arithmetics framework/constants framework/conversions_and_casting - framework/angular_units framework/text_output diff --git a/docs/framework/dimensions.rst b/docs/framework/dimensions.rst index fbcc02a0..1e1c298d 100644 --- a/docs/framework/dimensions.rst +++ b/docs/framework/dimensions.rst @@ -50,6 +50,20 @@ The quantities of derived dimensions are called quantities. This means that they are created by multiplying or dividing quantities of other dimensions. +The [SIBrochure]_ states: + + All other quantities, with the exception of counts, are derived quantities, which may be + written in terms of base quantities according to the equations of physics. The dimensions of + the derived quantities are written as products of powers of the dimensions of the base + quantities using the equations that relate the derived quantities to the base quantities. + In general the dimension of any quantity :math:`Q` is written in the form of a dimensional product, + + :math:`dim Q = T^\alpha L^\beta M^\gamma I^\delta \Theta^\varepsilon N^\zeta J^\eta` + + where the exponents :math:`\alpha`, :math:`\beta`, :math:`\gamma`, :math:`\delta`, :math:`\varepsilon`, + :math:`\zeta` and :math:`\eta`, which are generally small integers, which can be positive, + negative, or zero, are called the dimensional exponents. + Looking at the previous code snippets the area, speed, or frequency are the examples of such quantities. Each derived quantity can be represented as a unique list of exponents of base quantities. For example: diff --git a/docs/framework/quantities.rst b/docs/framework/quantities.rst index 661485ac..32bb228a 100644 --- a/docs/framework/quantities.rst +++ b/docs/framework/quantities.rst @@ -313,7 +313,7 @@ Quantity References vs Unit-specific Aliases - Unit-specific Aliases As aliases are defined in terms of types rather variables no major shadowing issues were found - so far. In case of identifiers abiguity it was always possible to disambiguate with more + so far. In case of identifiers ambiguity it was always possible to disambiguate with more namespaces prefixed in front of the alias. 2. Adjustable verbosity @@ -385,7 +385,7 @@ Quantity References vs Unit-specific Aliases - Quantity References The syntax for references uses ``*`` operator which has some predefined precedence. This operator - always takes a magnitude or a reference as ``lhs`` and a reference as ``rhs``. All other comibnations + always takes a magnitude or a reference as ``lhs`` and a reference as ``rhs``. All other combinations are not allowed. It means that in order to satisfy the operators precedence sometimes quite a lot of parenthesis have to be sprinkled in the code in order for the code to compile:: @@ -399,7 +399,7 @@ Quantity References vs Unit-specific Aliases - Quantity References - References have only to be defined for named units. Also for the user's conveniance references are + References have only to be defined for named units. Also for the user's convenience references are predefined for units raised to a specific power (e.g. ``m2``, ``km3``, etc). All other derived units can be constructed using the provided ones already even if they do not correspond to any predefined dimension:: diff --git a/docs/framework/quantity_points.rst b/docs/framework/quantity_points.rst index a13ee71b..dd652c69 100644 --- a/docs/framework/quantity_points.rst +++ b/docs/framework/quantity_points.rst @@ -49,4 +49,4 @@ Unlike `quantity`, the library provides: - no dimension-specific concepts, such as ``LengthPoint`` (there's the dimension-agnostic `QuantityPoint`), - a more limited set of operations on quantity points - (see the :ref:`framework/dimensions:Quantity Points` chapter) + (see the :ref:`framework/arithmetics:Quantity Points` chapter) diff --git a/docs/framework/units.rst b/docs/framework/units.rst index d0541ccf..226fb24d 100644 --- a/docs/framework/units.rst +++ b/docs/framework/units.rst @@ -203,27 +203,27 @@ complete list of all the :term:`SI` prefixes supported by the library:: namespace si { - 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 yocto : prefix()> {}; + struct zepto : prefix()> {}; + struct atto : prefix()> {}; + struct femto : prefix()> {}; + struct pico : prefix()> {}; + struct nano : prefix()> {}; 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>())> {}; + mag_power<10, -6>()> {}; + struct milli : prefix()> {}; + struct centi : prefix()> {}; + struct deci : prefix()> {}; + struct deca : prefix()> {}; + struct hecto : prefix()> {}; + struct kilo : prefix()> {}; + struct mega : prefix()> {}; + struct giga : prefix()> {}; + struct tera : prefix()> {}; + struct peta : prefix()> {}; + struct exa : prefix()> {}; + struct zetta : prefix()> {}; + struct yotta : prefix()> {}; } @@ -232,14 +232,14 @@ domain:: namespace iec80000 { - 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>())> {}; + struct kibi : prefix()> {}; + struct mebi : prefix()> {}; + struct gibi : prefix()> {}; + struct tebi : prefix()> {}; + struct pebi : prefix()> {}; + struct exbi : prefix()> {}; + struct zebi : prefix()> {}; + struct yobi : prefix()> {}; } @@ -260,6 +260,14 @@ example we can define ``si::kilometre`` as:: as ``km²`` would be invalid). +.. note:: + + [SIBrochure]_ states: + + However, when prefixes are used with SI units, the resulting units are no + longer coherent, because the prefix introduces a numerical factor other than one. + + Derived Units ------------- @@ -268,10 +276,21 @@ Derived Units names (i.e. ``N`` (newton)) or can be composed from the names of units of quantities used to define thier derived quantity (i.e. ``km/h``). +The [SIBrochure]_ states: + + Derived units are defined as products of powers of the base units. When the numerical + factor of this product is one, the derived units are called coherent derived units. The base + and coherent derived units of the SI form a coherent set, designated the set of coherent SI + units. The word “coherent” here means that equations between the numerical values of + quantities take exactly the same form as the equations between the quantities themselves. Derived Named Units ^^^^^^^^^^^^^^^^^^^ +The [SIBrochure]_ also says: + + Some of the coherent derived units in the SI are given special names. + Derived named units have a unique symbol (i.e. ``N`` (newton) or ``Pa`` (pascal)) and they are defined in the same way as base units (which always have to be a named unit):: diff --git a/docs/glossary.rst b/docs/glossary.rst index 67d2aaaf..3e2a4e42 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -73,9 +73,9 @@ ISO 80000 [1]_ definitions tensor character. - In a given `system of quantities`: - - `quantities ` of the same `kind` have the same quantity dimension, - - `quantities ` of different quantity dimensions are always of different `kinds `, - - `quantities ` having the same quantity dimension are not necessarily of the same `kind`. + - `quantities ` of the same `kind` have the same quantity dimension, + - `quantities ` of different quantity dimensions are always of different `kinds `, + - `quantities ` having the same quantity dimension are not necessarily of the same `kind`. quantity of dimension one dimensionless quantity @@ -168,7 +168,15 @@ ISO 80000 [1]_ definitions value of a quantity value - Number and reference together expressing magnitude of a `quantity`. + - The number can be complex. - A quantity value can be presented in more than one way. + - In the case of vector or tensor quantities, each component has a quantity value. + + - For example, force acting on a given particle, e.g. in Cartesian components + :math:`(F_x; F_y; F_z) = (−31,5; 43,2; 17,0) N`, where + :math:`(−31,5; 43,2; 17,0)` is a numerical-value vector and :math:`N` (newton) + is the unit, or :math:`(F_x; F_y; F_z) = (−31,5 N; 43,2 N; 17,0 N)` + where each component is a quantity. Other definitions diff --git a/docs/index.rst b/docs/index.rst index 0cfb607f..45d40188 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -30,6 +30,7 @@ with a permissive `MIT license `_ + `Example #1 `_ This library requires some C++20 features (concepts, classes as :abbr:`NTTP (Non-Type Template Parameter)`, ...). Thanks to them the user gets a powerful @@ -81,7 +81,7 @@ of basic library features:: .. admonition:: Try it on Compiler Explorer - `Example #2 `_ + `Example #2 `_ .. seealso:: diff --git a/docs/reference/systems/isq/iec80000/storage_capacity.rst b/docs/reference/systems/isq/iec80000/storage_capacity.rst index 7d24043b..12d84b75 100644 --- a/docs/reference/systems/isq/iec80000/storage_capacity.rst +++ b/docs/reference/systems/isq/iec80000/storage_capacity.rst @@ -1,4 +1,4 @@ -Sotrage Capacity +Storage Capacity ================ .. doxygenfile:: iec80000/storage_capacity.h diff --git a/docs/reference/systems/isq/si/uscs/pressure.rst b/docs/reference/systems/isq/si/uscs/pressure.rst new file mode 100644 index 00000000..a0e3e605 --- /dev/null +++ b/docs/reference/systems/isq/si/uscs/pressure.rst @@ -0,0 +1,4 @@ +Pressure +======== + +.. doxygenfile:: si/uscs/pressure.h diff --git a/docs/reference/systems/isq/si_uscs.rst b/docs/reference/systems/isq/si_uscs.rst index 7312b5c3..7d1da787 100644 --- a/docs/reference/systems/isq/si_uscs.rst +++ b/docs/reference/systems/isq/si_uscs.rst @@ -5,3 +5,4 @@ U.S. Customary Units (based on SI) :maxdepth: 2 si/uscs/length + si/uscs/pressure diff --git a/docs/references.rst b/docs/references.rst index 71220b98..f721b004 100644 --- a/docs/references.rst +++ b/docs/references.rst @@ -2,4 +2,5 @@ References ========== .. [ISO80000] `ISO 80000-1:2009(E) "Quantities and units — Part 1: General" `_, International Organization for Standardization. -.. [Quincey] `"Angles in the SI: a detailed proposal for solving the problem" `_, Quincey, Paul (1 October 2021) +.. [SIBrochure] `The International System of Units (SI) `_, International Bureau of Weights and Measures (20 May 2019), ISBN 978-92-822-2272-0. +.. [Quincey] `"Angles in the SI: a detailed proposal for solving the problem" `_, Quincey, Paul (1 October 2021). diff --git a/docs/usage.rst b/docs/usage.rst index e0e21afa..021d0b59 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -111,7 +111,7 @@ generators. For example: .. note:: - *~/.conan/global.conf* file may also set ``tools.cmake.cmake_layout:build_folder_vars``` which + *~/.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: @@ -146,8 +146,8 @@ Specifies how :ref:`design/downcasting:The Downcasting Facility` works: Conan Configuration Properties ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -build_all -+++++++++ +user.build:all +++++++++++++++ **Values**: ``True``/``False`` @@ -158,14 +158,24 @@ To support this it requires some additional Conan build dependencies described i `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 -+++++++++ +user.build:skip_la +++++++++++++++++++ **Values**: ``True``/``False`` **Defaulted to**: ``False`` -If `build_all`_ is enabled, among others, Conan installs the documentation generation dependencies (i.e. doxygen) and +If `user.build:all`_ is enabled, among others, Conan installs the external `wg21-linear_algebra `_ +dependency and enables compilation of linear algebra based usage examples. Such behavior can be disabled with this option. + +user.build:skip_docs +++++++++++++++++++++ + +**Values**: ``True``/``False`` + +**Defaulted to**: ``False`` + +If `user.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 @@ -181,6 +191,16 @@ UNITS_AS_SYSTEM_HEADERS Exports library as system headers. +UNITS_BUILD_LA +++++++++++++++ + +**Values**: ``ON``/``OFF`` + +**Defaulted to**: ``ON`` + +Enables building code depending on the linear algebra library. + + UNITS_BUILD_DOCS ++++++++++++++++ @@ -387,8 +407,8 @@ differences: .. code-block:: shell conan install . -pr -s compiler.cppstd=20 -b=outdated -u - cmake --preset default - cmake --build --preset release + cmake --preset conan-default + cmake --build --preset conan-release Install @@ -403,8 +423,8 @@ to find it, it is enough to perform the following steps: 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 + cmake --preset conan-default -DCMAKE_INSTALL_PREFIX= + cmake --build --preset conan-release --target install Contributing (or just building all the tests and examples) @@ -414,13 +434,13 @@ In case you would like to build all the source code (with unit tests and example you should: 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). +2. Run Conan with `user.build:all`_ = ``True`` + (use ``-c user.build:skip_docs=True`` 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 user.build:all=True -c user.build:skip_docs=True -b outdated -u + conan install . -pr -s compiler.cppstd=20 -c user.build:all=True -c user.build:skip_docs=True -b missing conan build . The above will download and install all of the dependencies needed for the development of the library, @@ -431,9 +451,9 @@ step with the explicit CMake build: .. code-block:: shell - cmake --preset default - cmake --build --preset release - cmake --build --preset release --target test + cmake --preset conan-default + cmake --build --preset conan-release + cmake --build --preset conan-release --target test Building documentation @@ -443,15 +463,15 @@ 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``. +3. Run Conan with `user.build:all`_ = ``True``. .. code-block:: shell git clone https://github.com/mpusz/units.git && cd units pip3 install -r docs/requirements.txt conan install . -pr -s compiler.cppstd=20 -c user.build:all=True -b missing - cmake --preset default - cmake --build --preset release --target documentation + cmake --preset conan-default + cmake --build --preset conan-release --target documentation The above will download and install all of the dependencies needed and build the documentation. @@ -463,7 +483,7 @@ To test CMake installation and Conan packaging or create a Conan package run: .. code-block:: shell - conan create . / -pr -s compiler.cppstd=20 -c user.build:all=True -c user.build:skip_docs=True -b outdated -u + conan create . / -pr -s compiler.cppstd=20 -c user.build:all=True -c user.build:skip_docs=True -b missing The above will create a Conan package and run tests provided in *./test_package* directory. diff --git a/docs/use_cases/linear_algebra.rst b/docs/use_cases/linear_algebra.rst index bb3c1111..0aa7de52 100644 --- a/docs/use_cases/linear_algebra.rst +++ b/docs/use_cases/linear_algebra.rst @@ -14,7 +14,7 @@ enough to be used with other Linear Algebra libraries existing on the market. or `Conan `_. Also, to simplify the examples all of them assume:: - using namespace std::math; + using namespace STD_LA; Linear Algebra of Quantities @@ -27,16 +27,16 @@ The official :term:`quantity` definition states: So the most common use case would be to create a vector or matrix of quantities:: - fs_vector, 3> v = { 1 * m, 2 * m, 3 * m }; - fs_vector, 3> u = { 3 * m, 2 * m, 1 * m }; - fs_vector, 3> t = { 3 * km, 2 * km, 1 * km }; + fixed_size_column_vector, 3> v = { 1 * m, 2 * m, 3 * m }; + fixed_size_column_vector, 3> u = { 3 * m, 2 * m, 1 * m }; + fixed_size_column_vector, 3> t = { 3 * km, 2 * km, 1 * km }; Having such definitions we can perform full dimensional analysis operations for the operations allowed by the Linear Algebra rules. For example:: std::cout << "v + u = " << v + u << "\n"; std::cout << "v + t = " << v + t << "\n"; - std::cout << "t[m] = " << fs_vector, 3>(t) << "\n"; + std::cout << "t[m] = " << fixed_size_column_vector, 3>(t) << "\n"; std::cout << "v * u = " << v * u << "\n"; std::cout << "2 * m * v = " << 2 * m * v << "\n"; @@ -66,9 +66,9 @@ types provided to a `quantity` class template. With this the above vector definitions can be rewritten as follows:: - si::length> v(fs_vector{ 1, 2, 3 }); - si::length> u(fs_vector{ 3, 2, 1 }); - si::length> t(fs_vector{ 3, 2, 1 }); + si::length> v(fixed_size_column_vector{ 1, 2, 3 }); + si::length> u(fixed_size_column_vector{ 3, 2, 1 }); + si::length> t(fixed_size_column_vector{ 3, 2, 1 }); Now the same code doing basic Linear Algebra operations will provide the following output: diff --git a/docs_disabled/reference/math.rst b/docs_disabled/reference/math.rst index b3f30d09..5620120e 100644 --- a/docs_disabled/reference/math.rst +++ b/docs_disabled/reference/math.rst @@ -1,10 +1,10 @@ Math ==== -.. doxygenfunction:: units::pow +.. doxygenfunction:: mp_units::pow -.. doxygenfunction:: units::sqrt +.. doxygenfunction:: mp_units::sqrt -.. doxygenfunction:: units::abs +.. doxygenfunction:: mp_units::abs -.. doxygenfunction:: units::epsilon +.. doxygenfunction:: mp_units::epsilon diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 8b704120..f796a102 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -60,9 +60,5 @@ add_example( add_example(total_energy mp-units::core-io mp-units::si mp-units::natural mp-units::utility) add_example(unmanned_aerial_vehicle mp-units::core-fmt mp-units::core-io mp-units::si mp-units::international mp-units::utility example_utils) -find_package(wg21_linear_algebra CONFIG REQUIRED) -add_example(linear_algebra mp-units::core-fmt mp-units::core-io mp-units::si) -target_link_libraries(linear_algebra PRIVATE wg21_linear_algebra::wg21_linear_algebra) - add_subdirectory(glide_computer) add_subdirectory(kalman_filter) diff --git a/example/clcpp_response.cpp b/example/clcpp_response.cpp index e79fa067..fcf11604 100644 --- a/example/clcpp_response.cpp +++ b/example/clcpp_response.cpp @@ -113,7 +113,7 @@ void calcs_comparison() const auto L1A = 2.f * fm; const auto L2A = 3.f * fm; const auto LrA = L1A + L2A; - std::cout << STD_FMT::format("{:%.30Q %q}\n + {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1A, L2A, LrA); + std::cout << UNITS_STD_FMT::format("{:%.30Q %q}\n + {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1A, L2A, LrA); std::cout << "The single unit method must convert large\n" "or small values in other units to the base unit.\n" @@ -122,17 +122,17 @@ void calcs_comparison() const auto L1B = L1A[m]; const auto L2B = L2A[m]; const auto LrB = L1B + L2B; - std::cout << STD_FMT::format("{:%.30eQ %q}\n + {:%.30eQ %q}\n = {:%.30eQ %q}\n\n", L1B, L2B, LrB); + std::cout << UNITS_STD_FMT::format("{:%.30eQ %q}\n + {:%.30eQ %q}\n = {:%.30eQ %q}\n\n", L1B, L2B, LrB); std::cout << "In multiplication and division:\n\n"; const quantity], float> ArA = L1A * L2A; - std::cout << STD_FMT::format("{:%.30Q %q}\n * {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1A, L2A, ArA); + std::cout << UNITS_STD_FMT::format("{:%.30Q %q}\n * {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1A, L2A, ArA); std::cout << "similar problems arise\n\n"; const quantity ArB = L1B * L2B; - std::cout << STD_FMT::format("{:%.30eQ %q}\n * {:%.30eQ %q}\n = {:%.30eQ %q}\n\n", L1B, L2B, ArB); + std::cout << UNITS_STD_FMT::format("{:%.30eQ %q}\n * {:%.30eQ %q}\n = {:%.30eQ %q}\n\n", L1B, L2B, ArB); } } // namespace diff --git a/example/conversion_factor.cpp b/example/conversion_factor.cpp index 4dd40b4b..dbb39cab 100644 --- a/example/conversion_factor.cpp +++ b/example/conversion_factor.cpp @@ -48,13 +48,13 @@ int main() constexpr auto lengthA = 2.0 * m; constexpr auto lengthB = lengthA[mm]; - std::cout << STD_FMT::format("lengthA( {} ) and lengthB( {} )\n", lengthA, lengthB) + std::cout << UNITS_STD_FMT::format("lengthA( {} ) and lengthB( {} )\n", lengthA, lengthB) << "represent the same length in different units.\n\n"; - std::cout << STD_FMT::format("therefore ratio lengthA / lengthB == {}\n\n", lengthA / lengthB); + std::cout << UNITS_STD_FMT::format("therefore ratio lengthA / lengthB == {}\n\n", lengthA / lengthB); - std::cout << STD_FMT::format("conversion factor from lengthA::unit of {:%q} to lengthB::unit of {:%q}:\n\n", lengthA, - lengthB) - << STD_FMT::format("lengthB.number( {} ) == lengthA.number( {} ) * conversion_factor( {} )\n", - lengthB.number(), lengthA.number(), conversion_factor(lengthB, lengthA)); + std::cout << UNITS_STD_FMT::format("conversion factor from lengthA::unit of {:%q} to lengthB::unit of {:%q}:\n\n", + lengthA, lengthB) + << UNITS_STD_FMT::format("lengthB.number( {} ) == lengthA.number( {} ) * conversion_factor( {} )\n", + lengthB.number(), lengthA.number(), conversion_factor(lengthB, lengthA)); } diff --git a/example/foot_pound_second.cpp b/example/foot_pound_second.cpp index 1187e27d..83701926 100644 --- a/example/foot_pound_second.cpp +++ b/example/foot_pound_second.cpp @@ -52,24 +52,25 @@ struct Ship { template auto fmt_line(const Q& q) { - return STD_FMT::format("{:22}", q) + (STD_FMT::format(",{:20}", value_cast(q)) + ...); + return UNITS_STD_FMT::format("{:22}", q) + (UNITS_STD_FMT::format(",{:20}", value_cast(q)) + ...); } // Print the ship details in the units as defined in the Ship struct, in other si::imperial units, and in SI void print_details(std::string_view description, const Ship& ship) { const auto waterDensity = 62.4 * isq::density[lb / cubic]; - std::cout << STD_FMT::format("{}\n", description); - std::cout << STD_FMT::format("{:20} : {}\n", "length", fmt_line(ship.length)) - << STD_FMT::format("{:20} : {}\n", "draft", fmt_line(ship.draft)) - << STD_FMT::format("{:20} : {}\n", "beam", fmt_line(ship.beam)) - << STD_FMT::format("{:20} : {}\n", "mass", fmt_line(ship.mass)) - << STD_FMT::format("{:20} : {}\n", "speed", fmt_line(ship.speed)) - << STD_FMT::format("{:20} : {}\n", "power", fmt_line(ship.power)) - << STD_FMT::format("{:20} : {}\n", "main guns", fmt_line(ship.mainGuns)) - << STD_FMT::format("{:20} : {}\n", "fire shells weighing", fmt_line(ship.shellMass)) - << STD_FMT::format("{:20} : {}\n", "fire shells at", fmt_line(ship.shellSpeed)) - << STD_FMT::format("{:20} : {}\n", "volume underwater", fmt_line(ship.mass / waterDensity)); + std::cout << UNITS_STD_FMT::format("{}\n", description); + std::cout << UNITS_STD_FMT::format("{:20} : {}\n", "length", fmt_line(ship.length)) + << UNITS_STD_FMT::format("{:20} : {}\n", "draft", fmt_line(ship.draft)) + << UNITS_STD_FMT::format("{:20} : {}\n", "beam", fmt_line(ship.beam)) + << UNITS_STD_FMT::format("{:20} : {}\n", "mass", fmt_line(ship.mass)) + << UNITS_STD_FMT::format("{:20} : {}\n", "speed", fmt_line(ship.speed)) + << UNITS_STD_FMT::format("{:20} : {}\n", "power", fmt_line(ship.power)) + << UNITS_STD_FMT::format("{:20} : {}\n", "main guns", fmt_line(ship.mainGuns)) + << UNITS_STD_FMT::format("{:20} : {}\n", "fire shells weighing", + fmt_line(ship.shellMass)) + << UNITS_STD_FMT::format("{:20} : {}\n", "fire shells at", fmt_line(ship.shellSpeed)) + << UNITS_STD_FMT::format("{:20} : {}\n", "volume underwater", fmt_line(ship.mass / waterDensity)); } int main() diff --git a/example/glide_computer/glide_computer.cpp b/example/glide_computer/glide_computer.cpp index 718f92c2..b6d3a881 100644 --- a/example/glide_computer/glide_computer.cpp +++ b/example/glide_computer/glide_computer.cpp @@ -76,7 +76,7 @@ using namespace glide_computer; void print(std::string_view phase_name, timestamp start_ts, const glide_computer::flight_point& point, const glide_computer::flight_point& new_point) { - std::cout << STD_FMT::format( + std::cout << UNITS_STD_FMT::format( "| {:<12} | {:>9%.1Q %q} (Total: {:>9%.1Q %q}) | {:>8%.1Q %q} (Total: {:>8%.1Q %q}) | {:>7%.0Q %q} ({:>6%.0Q %q}) " "|\n", phase_name, value_cast(new_point.ts - point.ts), value_cast(new_point.ts - start_ts), @@ -142,9 +142,9 @@ namespace glide_computer { void estimate(timestamp start_ts, const glider& g, const weather& w, const task& t, const safety& s, const aircraft_tow& at) { - std::cout << STD_FMT::format("| {:<12} | {:^28} | {:^26} | {:^21} |\n", "Flight phase", "Duration", "Distance", - "Height"); - std::cout << STD_FMT::format("|{0:-^14}|{0:-^30}|{0:-^28}|{0:-^23}|\n", ""); + std::cout << UNITS_STD_FMT::format("| {:<12} | {:^28} | {:^26} | {:^21} |\n", "Flight phase", "Duration", "Distance", + "Height"); + std::cout << UNITS_STD_FMT::format("|{0:-^14}|{0:-^30}|{0:-^28}|{0:-^23}|\n", ""); // ready to takeoff flight_point pos = takeoff(start_ts, t); diff --git a/example/glide_computer_example.cpp b/example/glide_computer_example.cpp index 177e41aa..fe72baa1 100644 --- a/example/glide_computer_example.cpp +++ b/example/glide_computer_example.cpp @@ -84,7 +84,7 @@ void print(const R& gliders) std::cout << "- Polar:\n"; for (const auto& p : g.polar) { const auto ratio = value_cast(glide_ratio(g.polar[0])); - std::cout << STD_FMT::format(" * {:%.4Q %q} @ {:%.1Q %q} -> {:%.1Q %q} ({:%.1Q %q})\n", p.climb, p.v, ratio, + std::cout << UNITS_STD_FMT::format(" * {:%.4Q %q} @ {:%.1Q %q} -> {:%.1Q %q} ({:%.1Q %q})\n", p.climb, p.v, ratio, // TODO is it possible to make ADL work below (we need another set of trig functions // for strong angle in a different namespace) value_cast(isq::asin(1 / ratio))); @@ -102,8 +102,8 @@ void print(const R& conditions) for (const auto& c : conditions) { std::cout << "- " << c.first << "\n"; const auto& w = c.second; - std::cout << " * Cloud base: " << STD_FMT::format("{:%.0Q %q}", w.cloud_base) << " AGL\n"; - std::cout << " * Thermals strength: " << STD_FMT::format("{:%.1Q %q}", w.thermal_strength) << "\n"; + std::cout << " * Cloud base: " << UNITS_STD_FMT::format("{:%.0Q %q}", w.cloud_base) << " AGL\n"; + std::cout << " * Thermals strength: " << UNITS_STD_FMT::format("{:%.1Q %q}", w.thermal_strength) << "\n"; std::cout << "\n"; } } @@ -115,7 +115,7 @@ void print(const R& waypoints) std::cout << "Waypoints:\n"; std::cout << "==========\n"; for (const auto& w : waypoints) - std::cout << STD_FMT::format("- {}: {} {}, {:%.1Q %q}\n", w.name, w.pos.lat, w.pos.lon, w.alt); + std::cout << UNITS_STD_FMT::format("- {}: {} {}, {:%.1Q %q}\n", w.name, w.pos.lat, w.pos.lon, w.alt); std::cout << "\n"; } @@ -126,12 +126,12 @@ void print(const task& t) std::cout << "- Start: " << t.get_start().name << "\n"; std::cout << "- Finish: " << t.get_finish().name << "\n"; - std::cout << "- Length: " << STD_FMT::format("{:%.1Q %q}", t.get_distance()) << "\n"; + std::cout << "- Length: " << UNITS_STD_FMT::format("{:%.1Q %q}", t.get_distance()) << "\n"; std::cout << "- Legs: " << "\n"; for (const auto& l : t.get_legs()) - std::cout << STD_FMT::format(" * {} -> {} ({:%.1Q %q})\n", l.begin().name, l.end().name, l.get_distance()); + std::cout << UNITS_STD_FMT::format(" * {} -> {} ({:%.1Q %q})\n", l.begin().name, l.end().name, l.get_distance()); std::cout << "\n"; } @@ -139,7 +139,7 @@ void print(const safety& s) { std::cout << "Safety:\n"; std::cout << "=======\n"; - std::cout << "- Min AGL separation: " << STD_FMT::format("{:%.0Q %q}", s.min_agl_height) << "\n"; + std::cout << "- Min AGL separation: " << UNITS_STD_FMT::format("{:%.0Q %q}", s.min_agl_height) << "\n"; std::cout << "\n"; } @@ -148,8 +148,8 @@ void print(const aircraft_tow& tow) std::cout << "Tow:\n"; std::cout << "====\n"; std::cout << "- Type: aircraft\n"; - std::cout << "- Height: " << STD_FMT::format("{:%.0Q %q}", tow.height_agl) << "\n"; - std::cout << "- Performance: " << STD_FMT::format("{:%.1Q %q}", tow.performance) << "\n"; + std::cout << "- Height: " << UNITS_STD_FMT::format("{:%.0Q %q}", tow.height_agl) << "\n"; + std::cout << "- Performance: " << UNITS_STD_FMT::format("{:%.1Q %q}", tow.performance) << "\n"; std::cout << "\n"; } @@ -178,7 +178,7 @@ void example() for (const auto& c : weather_conditions) { std::string txt = "Scenario: Glider = " + g.name + ", Weather = " + c.first; std::cout << txt << "\n"; - std::cout << STD_FMT::format("{0:=^{1}}\n\n", "", txt.size()); + std::cout << UNITS_STD_FMT::format("{0:=^{1}}\n\n", "", txt.size()); estimate(start_time, g, c.second, t, sfty, tow); diff --git a/example/hello_units.cpp b/example/hello_units.cpp index c19e67b2..c6b7445d 100644 --- a/example/hello_units.cpp +++ b/example/hello_units.cpp @@ -47,11 +47,11 @@ int main() constexpr auto v6 = value_cast(v4); constexpr auto v7 = value_cast(v6); - std::cout << v1 << '\n'; // 110 km/h - std::cout << v2 << '\n'; // 70 mi/h - std::cout << STD_FMT::format("{}", v3) << '\n'; // 110 km/h - std::cout << STD_FMT::format("{:*^14}", v4) << '\n'; // ***70 mi/h**** - std::cout << STD_FMT::format("{:%Q in %q}", v5) << '\n'; // 30.5556 in m/s - std::cout << STD_FMT::format("{0:%Q} in {0:%q}", v6) << '\n'; // 31.2928 in m/s - std::cout << STD_FMT::format("{:%Q}", v7) << '\n'; // 31 + std::cout << v1 << '\n'; // 110 km/h + std::cout << v2 << '\n'; // 70 mi/h + std::cout << UNITS_STD_FMT::format("{}", v3) << '\n'; // 110 km/h + std::cout << UNITS_STD_FMT::format("{:*^14}", v4) << '\n'; // ***70 mi/h**** + std::cout << UNITS_STD_FMT::format("{:%Q in %q}", v5) << '\n'; // 30.5556 in m/s + std::cout << UNITS_STD_FMT::format("{0:%Q} in {0:%q}", v6) << '\n'; // 31.2928 in m/s + std::cout << UNITS_STD_FMT::format("{:%Q}", v7) << '\n'; // 31 } diff --git a/example/include/geographic.h b/example/include/geographic.h index 07a8399c..c9c55dc7 100644 --- a/example/include/geographic.h +++ b/example/include/geographic.h @@ -53,12 +53,12 @@ std::basic_ostream& operator<<(std::basic_ostream& } // namespace geographic template<> -struct STD_FMT::formatter : formatter { +struct UNITS_STD_FMT::formatter : formatter { template auto format(const geographic::msl_altitude& a, FormatContext& ctx) { formatter::format(a.absolute(), ctx); - return STD_FMT::format_to(ctx.out(), " AMSL"); + return UNITS_STD_FMT::format_to(ctx.out(), " AMSL"); } }; @@ -145,21 +145,21 @@ class std::numeric_limits> : public numeric_limits { }; template -struct STD_FMT::formatter> : formatter { +struct UNITS_STD_FMT::formatter> : formatter { template auto format(geographic::latitude lat, FormatContext& ctx) { - STD_FMT::format_to(ctx.out(), "{}", lat > geographic::latitude::zero() ? 'N' : 'S'); + UNITS_STD_FMT::format_to(ctx.out(), "{}", lat > geographic::latitude::zero() ? 'N' : 'S'); return formatter::format(lat > geographic::latitude::zero() ? lat.number() : -lat.number(), ctx); } }; template -struct STD_FMT::formatter> : formatter { +struct UNITS_STD_FMT::formatter> : formatter { template auto format(geographic::longitude lon, FormatContext& ctx) { - STD_FMT::format_to(ctx.out(), "{}", lon > geographic::longitude::zero() ? 'E' : 'W'); + UNITS_STD_FMT::format_to(ctx.out(), "{}", lon > geographic::longitude::zero() ? 'E' : 'W'); return formatter::format(lon > geographic::longitude::zero() ? lon.number() : -lon.number(), ctx); } }; diff --git a/example/include/ranged_representation.h b/example/include/ranged_representation.h index 20f2a9e3..a6c3ecfc 100644 --- a/example/include/ranged_representation.h +++ b/example/include/ranged_representation.h @@ -26,21 +26,29 @@ #include #include #include +#include #include -template +template) auto Min, + UNITS_CONSTRAINED_NTTP_WORKAROUND(std::convertible_to) auto Max> inline constexpr auto is_in_range = [](const auto& v) { return std::clamp(v, T{Min}, T{Max}) == v; }; -template +template) auto Min, + UNITS_CONSTRAINED_NTTP_WORKAROUND(std::convertible_to) auto Max> using is_in_range_t = decltype(is_in_range); -template +template) auto Min, + UNITS_CONSTRAINED_NTTP_WORKAROUND(std::convertible_to) auto Max> class ranged_representation : public validated_type> { public: using validated_type>::validated_type; constexpr ranged_representation() : validated_type>(T{}) {} - [[nodiscard]] constexpr ranged_representation operator-() const { return ranged_representation(-this->value()); } + [[nodiscard]] constexpr ranged_representation operator-() const + requires requires(T t) { -t; } + { + return ranged_representation(-this->value()); + } }; template diff --git a/example/include/validated_type.h b/example/include/validated_type.h index 8e84152a..a99b43b5 100644 --- a/example/include/validated_type.h +++ b/example/include/validated_type.h @@ -39,7 +39,7 @@ public: constexpr explicit validated_type(const T& value) noexcept(std::is_nothrow_copy_constructible_v) requires std::copyable - : value_(value) + : value_(value) { gsl_Expects(validate(value_)); } @@ -52,7 +52,7 @@ public: constexpr validated_type(const T& value, validated_tag) noexcept(std::is_nothrow_copy_constructible_v) requires std::copyable - : value_(value) + : value_(value) { } @@ -61,12 +61,30 @@ public: { } + +#if UNITS_COMP_MSVC && UNITS_COMP_MSVC < 1930 + constexpr explicit(false) operator T() const noexcept(std::is_nothrow_copy_constructible_v) requires std::copyable { return value_; } +#else + + constexpr explicit(false) operator T() const& noexcept(std::is_nothrow_copy_constructible_v) + requires std::copyable + { + return value_; + } + + constexpr explicit(false) operator T() && noexcept(std::is_nothrow_move_constructible_v) + { + return std::move(value_); + } + +#endif + constexpr T& value() & noexcept = delete; constexpr const T& value() const& noexcept { return value_; } constexpr T&& value() && noexcept { return std::move(value_); } diff --git a/example/kalman_filter/kalman.h b/example/kalman_filter/kalman.h index d7b1537a..fe84c498 100644 --- a/example/kalman_filter/kalman.h +++ b/example/kalman_filter/kalman.h @@ -79,13 +79,9 @@ public: uncertainty_type uncertainty; }; -#if UNITS_COMP_CLANG <= 14 - template estimation(state, U) -> estimation; -#endif - // kalman gain template constexpr mp_units::quantity kalman_gain(Q estimate_uncertainty, @@ -159,7 +155,7 @@ constexpr Q covariance_extrapolation(Q uncertainty, Q process_noise_variance) } // namespace kalman template -struct STD_FMT::formatter> { +struct UNITS_STD_FMT::formatter> { constexpr auto parse(format_parse_context& ctx) { mp_units::detail::dynamic_specs_handler handler(specs, ctx); @@ -173,35 +169,35 @@ struct STD_FMT::formatter> { auto to_value_buffer = std::back_inserter(value_buffer); if (specs.precision != -1) { if constexpr (sizeof...(Qs) == 1) - STD_FMT::format_to(to_value_buffer, "{1:%.{0}Q %q}", specs.precision, kalman::get<0>(s)); + UNITS_STD_FMT::format_to(to_value_buffer, "{1:%.{0}Q %q}", specs.precision, kalman::get<0>(s)); else if constexpr (sizeof...(Qs) == 2) - STD_FMT::format_to(to_value_buffer, "{{ {1:%.{0}Q %q}, {2:%.{0}Q %q} }}", specs.precision, kalman::get<0>(s), - kalman::get<1>(s)); + UNITS_STD_FMT::format_to(to_value_buffer, "{{ {1:%.{0}Q %q}, {2:%.{0}Q %q} }}", specs.precision, + kalman::get<0>(s), kalman::get<1>(s)); else - STD_FMT::format_to(to_value_buffer, "{{ {1:%.{0}Q %q}, {2:%.{0}Q %q}, {3:%.{0}Q %q} }}", specs.precision, - kalman::get<0>(s), kalman::get<1>(s), kalman::get<2>(s)); + UNITS_STD_FMT::format_to(to_value_buffer, "{{ {1:%.{0}Q %q}, {2:%.{0}Q %q}, {3:%.{0}Q %q} }}", specs.precision, + kalman::get<0>(s), kalman::get<1>(s), kalman::get<2>(s)); } else { if constexpr (sizeof...(Qs) == 1) - STD_FMT::format_to(to_value_buffer, "{}", kalman::get<0>(s)); + UNITS_STD_FMT::format_to(to_value_buffer, "{}", kalman::get<0>(s)); else if constexpr (sizeof...(Qs) == 2) - STD_FMT::format_to(to_value_buffer, "{{ {}, {} }}", kalman::get<0>(s), kalman::get<1>(s)); + UNITS_STD_FMT::format_to(to_value_buffer, "{{ {}, {} }}", kalman::get<0>(s), kalman::get<1>(s)); else - STD_FMT::format_to(to_value_buffer, "{{ {}, {}, {} }}", kalman::get<0>(s), kalman::get<1>(s), - kalman::get<2>(s)); + UNITS_STD_FMT::format_to(to_value_buffer, "{{ {}, {}, {} }}", kalman::get<0>(s), kalman::get<1>(s), + kalman::get<2>(s)); } std::string global_format_buffer; mp_units::detail::quantity_global_format_specs global_specs = {specs.fill, specs.align, specs.width}; mp_units::detail::format_global_buffer(std::back_inserter(global_format_buffer), global_specs); - return STD_FMT::vformat_to(ctx.out(), global_format_buffer, STD_FMT::make_format_args(value_buffer)); + return UNITS_STD_FMT::vformat_to(ctx.out(), global_format_buffer, UNITS_STD_FMT::make_format_args(value_buffer)); } private: mp_units::detail::dynamic_format_specs specs; }; template -struct STD_FMT::formatter> { +struct UNITS_STD_FMT::formatter> { constexpr auto parse(format_parse_context& ctx) { mp_units::detail::dynamic_specs_handler handler(specs, ctx); @@ -221,16 +217,17 @@ struct STD_FMT::formatter> { std::string value_buffer; auto to_value_buffer = std::back_inserter(value_buffer); if (specs.precision != -1) { - STD_FMT::format_to(to_value_buffer, "{0:%.{2}Q} ± {1:%.{2}Q} {0:%q}", q, sqrt(e.uncertainty), specs.precision); + UNITS_STD_FMT::format_to(to_value_buffer, "{0:%.{2}Q} ± {1:%.{2}Q} {0:%q}", q, sqrt(e.uncertainty), + specs.precision); } else { - STD_FMT::format_to(to_value_buffer, "{0:%Q} ± {1:%Q} {0:%q}", q, sqrt(e.uncertainty)); + UNITS_STD_FMT::format_to(to_value_buffer, "{0:%Q} ± {1:%Q} {0:%q}", q, sqrt(e.uncertainty)); } std::string global_format_buffer; mp_units::detail::quantity_global_format_specs global_specs = {specs.fill, specs.align, specs.width}; mp_units::detail::format_global_buffer(std::back_inserter(global_format_buffer), global_specs); - return STD_FMT::vformat_to(ctx.out(), global_format_buffer, STD_FMT::make_format_args(value_buffer)); + return UNITS_STD_FMT::vformat_to(ctx.out(), global_format_buffer, UNITS_STD_FMT::make_format_args(value_buffer)); } private: mp_units::detail::dynamic_format_specs specs; diff --git a/example/kalman_filter/kalman_filter-example_1.cpp b/example/kalman_filter/kalman_filter-example_1.cpp index 006883f1..c9bc542f 100644 --- a/example/kalman_filter/kalman_filter-example_1.cpp +++ b/example/kalman_filter/kalman_filter-example_1.cpp @@ -33,15 +33,15 @@ using namespace mp_units; void print_header(const kalman::State auto& initial) { - std::cout << STD_FMT::format("Initial: {}\n", initial); - std::cout << STD_FMT::format("{:>2} | {:>9} | {:>8} | {:>14} | {:>14}\n", "N", "Gain", "Measured", "Curr. Estimate", - "Next Estimate"); + std::cout << UNITS_STD_FMT::format("Initial: {}\n", initial); + std::cout << UNITS_STD_FMT::format("{:>2} | {:>9} | {:>8} | {:>14} | {:>14}\n", "N", "Gain", "Measured", + "Curr. Estimate", "Next Estimate"); } void print(auto iteration, QuantityOf auto gain, Quantity auto measured, const kalman::State auto& current, const kalman::State auto& next) { - std::cout << STD_FMT::format("{:2} | {:9} | {:8} | {:14} | {:14}\n", iteration, gain, measured, current, next); + std::cout << UNITS_STD_FMT::format("{:2} | {:9} | {:8} | {:14} | {:14}\n", iteration, gain, measured, current, next); } int main() diff --git a/example/kalman_filter/kalman_filter-example_2.cpp b/example/kalman_filter/kalman_filter-example_2.cpp index 5a257915..47760f27 100644 --- a/example/kalman_filter/kalman_filter-example_2.cpp +++ b/example/kalman_filter/kalman_filter-example_2.cpp @@ -37,13 +37,14 @@ using namespace mp_units; void print_header(const kalman::State auto& initial) { - std::cout << STD_FMT::format("Initial: {}\n", initial); - std::cout << STD_FMT::format("{:>2} | {:>8} | {:>23} | {:>23}\n", "N", "Measured", "Curr. Estimate", "Next Estimate"); + std::cout << UNITS_STD_FMT::format("Initial: {}\n", initial); + std::cout << UNITS_STD_FMT::format("{:>2} | {:>8} | {:>23} | {:>23}\n", "N", "Measured", "Curr. Estimate", + "Next Estimate"); } void print(auto iteration, Quantity auto measured, const kalman::State auto& current, const kalman::State auto& next) { - std::cout << STD_FMT::format("{:2} | {:8} | {:.1} | {:.1}\n", iteration, measured, current, next); + std::cout << UNITS_STD_FMT::format("{:2} | {:8} | {:.1} | {:.1}\n", iteration, measured, current, next); } int main() diff --git a/example/kalman_filter/kalman_filter-example_3.cpp b/example/kalman_filter/kalman_filter-example_3.cpp index 713cf84d..a223bb25 100644 --- a/example/kalman_filter/kalman_filter-example_3.cpp +++ b/example/kalman_filter/kalman_filter-example_3.cpp @@ -37,13 +37,14 @@ using namespace mp_units; void print_header(const kalman::State auto& initial) { - std::cout << STD_FMT::format("Initial: {}\n", initial); - std::cout << STD_FMT::format("{:>2} | {:>8} | {:>24} | {:>24}\n", "N", "Measured", "Curr. Estimate", "Next Estimate"); + std::cout << UNITS_STD_FMT::format("Initial: {}\n", initial); + std::cout << UNITS_STD_FMT::format("{:>2} | {:>8} | {:>24} | {:>24}\n", "N", "Measured", "Curr. Estimate", + "Next Estimate"); } void print(auto iteration, Quantity auto measured, const kalman::State auto& current, const kalman::State auto& next) { - std::cout << STD_FMT::format("{:2} | {:8} | {:>24.1} | {:>24.1}\n", iteration, measured, current, next); + std::cout << UNITS_STD_FMT::format("{:2} | {:8} | {:>24.1} | {:>24.1}\n", iteration, measured, current, next); } int main() diff --git a/example/kalman_filter/kalman_filter-example_4.cpp b/example/kalman_filter/kalman_filter-example_4.cpp index 74b3c444..f255cee7 100644 --- a/example/kalman_filter/kalman_filter-example_4.cpp +++ b/example/kalman_filter/kalman_filter-example_4.cpp @@ -37,13 +37,14 @@ using namespace mp_units; void print_header(const kalman::State auto& initial) { - std::cout << STD_FMT::format("Initial: {}\n", initial); - std::cout << STD_FMT::format("{:>2} | {:>8} | {:>35} | {:>35}\n", "N", "Measured", "Curr. Estimate", "Next Estimate"); + std::cout << UNITS_STD_FMT::format("Initial: {}\n", initial); + std::cout << UNITS_STD_FMT::format("{:>2} | {:>8} | {:>35} | {:>35}\n", "N", "Measured", "Curr. Estimate", + "Next Estimate"); } void print(auto iteration, Quantity auto measured, const kalman::State auto& current, const kalman::State auto& next) { - std::cout << STD_FMT::format("{:2} | {:8} | {:>35.1} | {:>35.1}\n", iteration, measured, current, next); + std::cout << UNITS_STD_FMT::format("{:2} | {:8} | {:>35.1} | {:>35.1}\n", iteration, measured, current, next); } int main() diff --git a/example/kalman_filter/kalman_filter-example_5.cpp b/example/kalman_filter/kalman_filter-example_5.cpp index 2e94c9be..de74ec33 100644 --- a/example/kalman_filter/kalman_filter-example_5.cpp +++ b/example/kalman_filter/kalman_filter-example_5.cpp @@ -35,16 +35,16 @@ using namespace mp_units; template void print_header(kalman::estimation initial) { - std::cout << STD_FMT::format("Initial: {}\n", initial); - std::cout << STD_FMT::format("{:>2} | {:>5} | {:>8} | {:>16} | {:>16}\n", "N", "Gain", "Measured", "Curr. Estimate", - "Next Estimate"); + std::cout << UNITS_STD_FMT::format("Initial: {}\n", initial); + std::cout << UNITS_STD_FMT::format("{:>2} | {:>5} | {:>8} | {:>16} | {:>16}\n", "N", "Gain", "Measured", + "Curr. Estimate", "Next Estimate"); } template K> void print(auto iteration, K gain, Q measured, kalman::estimation current, kalman::estimation next) { - std::cout << STD_FMT::format("{:2} | {:5%.2Q} | {:8} | {:>16.2} | {:>16.2}\n", iteration, gain, measured, current, - next); + std::cout << UNITS_STD_FMT::format("{:2} | {:5%.2Q} | {:8} | {:>16.2} | {:>16.2}\n", iteration, gain, measured, + current, next); } int main() diff --git a/example/kalman_filter/kalman_filter-example_6.cpp b/example/kalman_filter/kalman_filter-example_6.cpp index 827f508e..18e2ef11 100644 --- a/example/kalman_filter/kalman_filter-example_6.cpp +++ b/example/kalman_filter/kalman_filter-example_6.cpp @@ -36,16 +36,16 @@ using namespace mp_units; template void print_header(kalman::estimation initial) { - std::cout << STD_FMT::format("Initial: {}\n", initial); - std::cout << STD_FMT::format("{:>2} | {:>7} | {:>10} | {:>18} | {:>18}\n", "N", "Gain", "Measured", "Curr. Estimate", - "Next Estimate"); + std::cout << UNITS_STD_FMT::format("Initial: {}\n", initial); + std::cout << UNITS_STD_FMT::format("{:>2} | {:>7} | {:>10} | {:>18} | {:>18}\n", "N", "Gain", "Measured", + "Curr. Estimate", "Next Estimate"); } template K> void print(auto iteration, K gain, QP measured, kalman::estimation current, kalman::estimation next) { - std::cout << STD_FMT::format("{:2} | {:7%.4Q} | {:10%.3Q %q} | {:>18.3} | {:>18.3}\n", iteration, gain, - measured.relative(), current, next); + std::cout << UNITS_STD_FMT::format("{:2} | {:7%.4Q} | {:10%.3Q %q} | {:>18.3} | {:>18.3}\n", iteration, gain, + measured.relative(), current, next); } int main() diff --git a/example/kalman_filter/kalman_filter-example_7.cpp b/example/kalman_filter/kalman_filter-example_7.cpp index b53444db..567014b5 100644 --- a/example/kalman_filter/kalman_filter-example_7.cpp +++ b/example/kalman_filter/kalman_filter-example_7.cpp @@ -36,16 +36,16 @@ using namespace mp_units; template void print_header(kalman::estimation initial) { - std::cout << STD_FMT::format("Initial: {}\n", initial); - std::cout << STD_FMT::format("{:>2} | {:>7} | {:>10} | {:>18} | {:>18}\n", "N", "Gain", "Measured", "Curr. Estimate", - "Next Estimate"); + std::cout << UNITS_STD_FMT::format("Initial: {}\n", initial); + std::cout << UNITS_STD_FMT::format("{:>2} | {:>7} | {:>10} | {:>18} | {:>18}\n", "N", "Gain", "Measured", + "Curr. Estimate", "Next Estimate"); } template K> void print(auto iteration, K gain, QP measured, kalman::estimation current, kalman::estimation next) { - std::cout << STD_FMT::format("{:2} | {:7%.4Q} | {:10%.3Q %q} | {:>18.3} | {:>18.3}\n", iteration, gain, - measured.relative(), current, next); + std::cout << UNITS_STD_FMT::format("{:2} | {:7%.4Q} | {:10%.3Q %q} | {:>18.3} | {:>18.3}\n", iteration, gain, + measured.relative(), current, next); } int main() diff --git a/example/kalman_filter/kalman_filter-example_8.cpp b/example/kalman_filter/kalman_filter-example_8.cpp index 10791143..65fe47d0 100644 --- a/example/kalman_filter/kalman_filter-example_8.cpp +++ b/example/kalman_filter/kalman_filter-example_8.cpp @@ -36,16 +36,16 @@ using namespace mp_units; template void print_header(kalman::estimation initial) { - std::cout << STD_FMT::format("Initial: {}\n", initial); - std::cout << STD_FMT::format("{:>2} | {:>7} | {:>10} | {:>16} | {:>16}\n", "N", "Gain", "Measured", "Curr. Estimate", - "Next Estimate"); + std::cout << UNITS_STD_FMT::format("Initial: {}\n", initial); + std::cout << UNITS_STD_FMT::format("{:>2} | {:>7} | {:>10} | {:>16} | {:>16}\n", "N", "Gain", "Measured", + "Curr. Estimate", "Next Estimate"); } template K> void print(auto iteration, K gain, QP measured, kalman::estimation current, kalman::estimation next) { - std::cout << STD_FMT::format("{:2} | {:7%.3Q} | {:10%.3Q %q} | {:>16.2} | {:>16.2}\n", iteration, gain, - measured.relative(), current, next); + std::cout << UNITS_STD_FMT::format("{:2} | {:7%.3Q} | {:10%.3Q %q} | {:>16.2} | {:>16.2}\n", iteration, gain, + measured.relative(), current, next); } int main() diff --git a/example/si_constants.cpp b/example/si_constants.cpp index fabbbc79..aea66b59 100644 --- a/example/si_constants.cpp +++ b/example/si_constants.cpp @@ -35,20 +35,20 @@ int main() using namespace mp_units::si::unit_symbols; std::cout << "The seven defining constants of the SI and the seven corresponding units they define:\n"; - std::cout << STD_FMT::format("- hyperfine transition frequency of Cs: {} = {:%.0Q %q}\n", + std::cout << UNITS_STD_FMT::format("- hyperfine transition frequency of Cs: {} = {:%.0Q %q}\n", 1. * si2019::hyperfine_structure_transition_frequency_of_cs, (1. * si2019::hyperfine_structure_transition_frequency_of_cs)[Hz]); - std::cout << STD_FMT::format("- speed of light in vacuum: {} = {:%.0Q %q}\n", + std::cout << UNITS_STD_FMT::format("- speed of light in vacuum: {} = {:%.0Q %q}\n", 1. * si2019::speed_of_light_in_vacuum, (1. * si2019::speed_of_light_in_vacuum)[m / s]); - std::cout << STD_FMT::format("- Planck constant: {} = {:%.8eQ %q}\n", + std::cout << UNITS_STD_FMT::format("- Planck constant: {} = {:%.8eQ %q}\n", 1. * si2019::planck_constant, (1. * si2019::planck_constant)[J * s]); - std::cout << STD_FMT::format("- elementary charge: {} = {:%.9eQ %q}\n", + std::cout << UNITS_STD_FMT::format("- elementary charge: {} = {:%.9eQ %q}\n", 1. * si2019::elementary_charge, (1. * si2019::elementary_charge)[C]); - std::cout << STD_FMT::format("- Boltzmann constant: {} = {:%.6eQ %q}\n", + std::cout << UNITS_STD_FMT::format("- Boltzmann constant: {} = {:%.6eQ %q}\n", 1. * si2019::boltzmann_constant, (1. * si2019::boltzmann_constant)[J / K]); - std::cout << STD_FMT::format("- Avogadro constant: {} = {:%.8eQ %q}\n", + std::cout << UNITS_STD_FMT::format("- Avogadro constant: {} = {:%.8eQ %q}\n", 1. * si2019::avogadro_constant, (1. * si2019::avogadro_constant)[1 / mol]); // TODO uncomment the below when ISQ is done - // std::cout << STD_FMT::format("- luminous efficacy: {} = {}\n", si2019::luminous_efficacy(1.), + // std::cout << UNITS_STD_FMT::format("- luminous efficacy: {} = {}\n", si2019::luminous_efficacy(1.), // si2019::luminous_efficacy(1.)[lm / W]); } diff --git a/example/storage_tank_example.cpp b/example/storage_tank_example.cpp index c4dc3d36..71460bfa 100644 --- a/example/storage_tank_example.cpp +++ b/example/storage_tank_example.cpp @@ -119,10 +119,10 @@ int main() const auto fill_ratio = fill_level / height; - std::cout << STD_FMT::format("fill height at {} = {} ({} full)\n", fill_time, fill_level, fill_ratio[percent]); - std::cout << STD_FMT::format("fill weight at {} = {} ({})\n", fill_time, filled_weight, filled_weight[N]); - std::cout << STD_FMT::format("spare capacity at {} = {}\n", fill_time, spare_capacity); - std::cout << STD_FMT::format("input flow rate = {}\n", input_flow_rate); - std::cout << STD_FMT::format("float rise rate = {}\n", float_rise_rate); - std::cout << STD_FMT::format("tank full E.T.A. at current flow rate = {}\n", fill_time_left[s]); + std::cout << UNITS_STD_FMT::format("fill height at {} = {} ({} full)\n", fill_time, fill_level, fill_ratio[percent]); + std::cout << UNITS_STD_FMT::format("fill weight at {} = {} ({})\n", fill_time, filled_weight, filled_weight[N]); + std::cout << UNITS_STD_FMT::format("spare capacity at {} = {}\n", fill_time, spare_capacity); + std::cout << UNITS_STD_FMT::format("input flow rate = {}\n", input_flow_rate); + std::cout << UNITS_STD_FMT::format("float rise rate = {}\n", float_rise_rate); + std::cout << UNITS_STD_FMT::format("tank full E.T.A. at current flow rate = {}\n", fill_time_left[s]); } diff --git a/example/unmanned_aerial_vehicle.cpp b/example/unmanned_aerial_vehicle.cpp index cac9ab2d..a31bd9f5 100644 --- a/example/unmanned_aerial_vehicle.cpp +++ b/example/unmanned_aerial_vehicle.cpp @@ -75,12 +75,12 @@ std::basic_ostream& operator<<(std::basic_ostream& template requires(is_hae(QP::absolute_point_origin)) -struct STD_FMT::formatter : formatter { +struct UNITS_STD_FMT::formatter : formatter { template auto format(const QP& a, FormatContext& ctx) { formatter::format(a.absolute(), ctx); - return STD_FMT::format_to(ctx.out(), " HAE({})", to_text(QP::absolute_point_origin.egm)); + return UNITS_STD_FMT::format_to(ctx.out(), " HAE({})", to_text(QP::absolute_point_origin.egm)); } }; @@ -116,12 +116,12 @@ std::basic_ostream& operator<<(std::basic_ostream& } template<> -struct STD_FMT::formatter : formatter { +struct UNITS_STD_FMT::formatter : formatter { template auto format(const hal_altitude& a, FormatContext& ctx) { formatter::format(a.absolute(), ctx); - return STD_FMT::format_to(ctx.out(), " HAL"); + return UNITS_STD_FMT::format_to(ctx.out(), " HAL"); } }; @@ -150,10 +150,10 @@ int main() unmanned_aerial_vehicle uav; uav.take_off(msl_altitude{6'000 * ft}); uav.current(msl_altitude{10'000 * ft}); - std::cout << STD_FMT::format("hal = {}\n", uav.hal()); + std::cout << UNITS_STD_FMT::format("hal = {}\n", uav.hal()); msl_altitude ground_level{123 * m}; - std::cout << STD_FMT::format("agl = {}\n", uav.current() - ground_level); + std::cout << UNITS_STD_FMT::format("agl = {}\n", uav.current() - ground_level); struct waypoint { std::string name; @@ -162,6 +162,6 @@ int main() }; waypoint wpt = {"EPPR", {54.24772_N, 18.6745_E}, msl_altitude{16. * ft}}; - std::cout << STD_FMT::format("{}: {} {}, {:%.2Q %q}, {:%.2Q %q}\n", wpt.name, wpt.pos.lat, wpt.pos.lon, wpt.msl_alt, + std::cout << UNITS_STD_FMT::format("{}: {} {}, {:%.2Q %q}, {:%.2Q %q}\n", wpt.name, wpt.pos.lat, wpt.pos.lon, wpt.msl_alt, to_hae(wpt.msl_alt, wpt.pos)); } diff --git a/requirements.txt b/requirements.txt index bca6c1c8..b29065ba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ conan pre-commit +identify diff --git a/src/core-fmt/CMakeLists.txt b/src/core-fmt/CMakeLists.txt index 120add03..99f2ce04 100644 --- a/src/core-fmt/CMakeLists.txt +++ b/src/core-fmt/CMakeLists.txt @@ -29,6 +29,8 @@ add_units_module(core-fmt DEPENDENCIES mp-units::core HEADERS include/mp-units/f target_compile_definitions(mp-units-core-fmt INTERFACE ${projectPrefix}USE_LIBFMT=$) if(${projectPrefix}USE_LIBFMT) - find_package(fmt CONFIG REQUIRED) + if(NOT TARGET fmt::fmt) + find_package(fmt CONFIG REQUIRED) + endif() target_link_libraries(mp-units-core-fmt INTERFACE fmt::fmt) endif() diff --git a/src/core-fmt/include/mp-units/bits/fmt.h b/src/core-fmt/include/mp-units/bits/fmt.h index 84124b82..5c76d991 100644 --- a/src/core-fmt/include/mp-units/bits/fmt.h +++ b/src/core-fmt/include/mp-units/bits/fmt.h @@ -55,7 +55,7 @@ public: constexpr fill_t& operator=(std::basic_string_view str) { auto size = str.size(); - if (size > max_size) throw STD_FMT::format_error("invalid fill"); + if (size > max_size) UNITS_THROW(UNITS_STD_FMT::format_error("invalid fill")); for (size_t i = 0; i < size; ++i) data_[i] = str[i]; size_ = static_cast(size); return *this; @@ -98,11 +98,11 @@ struct width_checker { { if constexpr (is_integer) { if constexpr (std::numeric_limits::is_signed) { - if (value < 0) throw STD_FMT::format_error("negative width"); + if (value < 0) UNITS_THROW(UNITS_STD_FMT::format_error("negative width")); } return static_cast(value); } else { - throw STD_FMT::format_error("width is not integer"); + UNITS_THROW(UNITS_STD_FMT::format_error("width is not integer")); } } }; @@ -113,11 +113,11 @@ struct precision_checker { { if constexpr (is_integer) { if constexpr (std::numeric_limits::is_signed) { - if (value < 0) throw STD_FMT::format_error("negative precision"); + if (value < 0) UNITS_THROW(UNITS_STD_FMT::format_error("negative precision")); } return static_cast(value); } else { - throw STD_FMT::format_error("precision is not integer"); + UNITS_THROW(UNITS_STD_FMT::format_error("precision is not integer")); } } }; @@ -147,31 +147,31 @@ struct dynamic_format_specs : basic_format_specs { [[nodiscard]] constexpr int verify_dynamic_arg_index_in_range(size_t idx) { if (idx > static_cast(std::numeric_limits::max())) { - throw STD_FMT::format_error("Dynamic width or precision index too large."); + UNITS_THROW(UNITS_STD_FMT::format_error("Dynamic width or precision index too large.")); } return static_cast(idx); } template -[[nodiscard]] constexpr int on_dynamic_arg(size_t arg_id, STD_FMT::basic_format_parse_context& context) +[[nodiscard]] constexpr int on_dynamic_arg(size_t arg_id, UNITS_STD_FMT::basic_format_parse_context& context) { - context.check_arg_id(FMT_TO_ARG_ID(arg_id)); + context.check_arg_id(UNITS_FMT_TO_ARG_ID(arg_id)); return verify_dynamic_arg_index_in_range(arg_id); } template -[[nodiscard]] constexpr int on_dynamic_arg(auto_id, STD_FMT::basic_format_parse_context& context) +[[nodiscard]] constexpr int on_dynamic_arg(auto_id, UNITS_STD_FMT::basic_format_parse_context& context) { - return verify_dynamic_arg_index_in_range(FMT_FROM_ARG_ID(context.next_arg_id())); + return verify_dynamic_arg_index_in_range(UNITS_FMT_FROM_ARG_ID(context.next_arg_id())); } template [[nodiscard]] constexpr int get_dynamic_spec(int index, FormatContext& ctx) { const unsigned long long value = - STD_FMT::visit_format_arg(Handler{}, ctx.arg(FMT_TO_ARG_ID(static_cast(index)))); + UNITS_STD_FMT::visit_format_arg(Handler{}, ctx.arg(UNITS_FMT_TO_ARG_ID(static_cast(index)))); if (value > static_cast(std::numeric_limits::max())) { - throw STD_FMT::format_error("number is too big"); + UNITS_THROW(UNITS_STD_FMT::format_error("number is too big")); } return static_cast(value); } @@ -195,7 +195,7 @@ template S> ++begin; } while (begin != end && '0' <= *begin && *begin <= '9'); - if (value > max_int) throw STD_FMT::format_error("Number is too big"); + if (value > max_int) UNITS_THROW(UNITS_STD_FMT::format_error("Number is too big")); return begin; } @@ -222,12 +222,12 @@ template S, typename IDHandler> else ++begin; if (begin == end || (*begin != '}' && *begin != ':')) - throw STD_FMT::format_error("invalid format string"); + UNITS_THROW(UNITS_STD_FMT::format_error("invalid format string")); else handler(index); return begin; } - throw STD_FMT::format_error("invalid format string"); + UNITS_THROW(UNITS_STD_FMT::format_error("invalid format string")); } template S, typename IDHandler> @@ -278,11 +278,11 @@ template S, typename Handler> if (width != -1) handler.on_width(width); else - throw STD_FMT::format_error("number is too big"); + UNITS_THROW(UNITS_STD_FMT::format_error("number is too big")); } else if (*begin == '{') { ++begin; if (begin != end) begin = parse_arg_id(begin, end, width_adapter{handler}); - if (begin == end || *begin != '}') throw STD_FMT::format_error("invalid format string"); + if (begin == end || *begin != '}') UNITS_THROW(UNITS_STD_FMT::format_error("invalid format string")); ++begin; } return begin; @@ -305,13 +305,13 @@ template S, typename Handler> if (precision != -1) handler.on_precision(precision); else - throw STD_FMT::format_error("number is too big"); + UNITS_THROW(UNITS_STD_FMT::format_error("number is too big")); } else if (c == '{') { ++begin; if (begin != end) begin = parse_arg_id(begin, end, precision_adapter{handler}); - if (begin == end || *begin++ != '}') throw STD_FMT::format_error("invalid format string"); + if (begin == end || *begin++ != '}') UNITS_THROW(UNITS_STD_FMT::format_error("invalid format string")); } else { - throw STD_FMT::format_error("missing precision specifier"); + UNITS_THROW(UNITS_STD_FMT::format_error("missing precision specifier")); } return begin; } @@ -355,7 +355,7 @@ template S, typename Handler> if (align != fmt_align::none) { if (p != begin) { auto c = *begin; - if (c == '{') throw STD_FMT::format_error("invalid fill character '{'"); + if (c == '{') UNITS_THROW(UNITS_STD_FMT::format_error("invalid fill character '{'")); handler.on_fill(std::basic_string_view>(&*begin, static_cast(p - begin))); begin = p + 1; } else diff --git a/src/core-fmt/include/mp-units/bits/fmt_hacks.h b/src/core-fmt/include/mp-units/bits/fmt_hacks.h index d4b73ce1..bbfc2adc 100644 --- a/src/core-fmt/include/mp-units/bits/fmt_hacks.h +++ b/src/core-fmt/include/mp-units/bits/fmt_hacks.h @@ -41,10 +41,24 @@ UNITS_DIAGNOSTIC_IGNORE_SHADOW #include UNITS_DIAGNOSTIC_POP -#define STD_FMT fmt -#define FMT_LOCALE(loc) (loc).template get() -#define FMT_TO_ARG_ID(arg) static_cast(arg) -#define FMT_FROM_ARG_ID(arg) static_cast(arg) +#define UNITS_STD_FMT fmt +#define UNITS_FMT_LOCALE(loc) (loc).template get() +#define UNITS_FMT_TO_ARG_ID(arg) static_cast(arg) +#define UNITS_FMT_FROM_ARG_ID(arg) static_cast(arg) + +// This re-uses code from fmt; +#if FMT_EXCEPTIONS +#if FMT_MSC_VERSION || defined(__NVCC__) +#define UNITS_THROW(x) ::fmt::detail::do_throw(x) +#else +#define UNITS_THROW(x) throw x +#endif +#else +#define UNITS_THROW(x) \ + do { \ + FMT_ASSERT(false, (x).what()); \ + } while (false) +#endif #else @@ -54,9 +68,10 @@ UNITS_DIAGNOSTIC_POP #include -#define STD_FMT std -#define FMT_LOCALE(loc) loc -#define FMT_TO_ARG_ID(arg) arg -#define FMT_FROM_ARG_ID(arg) arg +#define UNITS_STD_FMT std +#define UNITS_FMT_LOCALE(loc) loc +#define UNITS_FMT_TO_ARG_ID(arg) arg +#define UNITS_FMT_FROM_ARG_ID(arg) arg +#define UNITS_THROW(arg) throw arg #endif diff --git a/src/core-fmt/include/mp-units/format.h b/src/core-fmt/include/mp-units/format.h index 6c45d3c5..c5832340 100644 --- a/src/core-fmt/include/mp-units/format.h +++ b/src/core-fmt/include/mp-units/format.h @@ -96,7 +96,7 @@ constexpr It parse_units_rep(It begin, S end, Handler&& handler, bool treat_as_f if (treat_as_floating_point) { begin = parse_precision(begin, end, handler); } else - throw STD_FMT::format_error("precision not allowed for integral quantity representation"); + throw UNITS_STD_FMT::format_error("precision not allowed for integral quantity representation"); if (begin == end) return begin; } @@ -126,12 +126,12 @@ constexpr It parse_units_format(It begin, S end, Handler&& handler) } if (begin != ptr) handler.on_text(begin, ptr); begin = ++ptr; // consume '%' - if (ptr == end) throw STD_FMT::format_error("invalid format"); + if (ptr == end) throw UNITS_STD_FMT::format_error("invalid format"); c = *ptr++; constexpr auto units_types = std::string_view{"Qq"}; const auto new_end = find_first_of(begin, end, units_types.begin(), units_types.end()); - if (new_end == end) throw STD_FMT::format_error("invalid format"); + if (new_end == end) throw UNITS_STD_FMT::format_error("invalid format"); if (*new_end == 'Q') { handler.on_quantity_value(begin, new_end); // Edit `on_quantity_value` to add rep modifiers } else { @@ -152,43 +152,43 @@ template std::basic_string buffer; auto to_buffer = std::back_inserter(buffer); - STD_FMT::format_to(to_buffer, "{{:"); + UNITS_STD_FMT::format_to(to_buffer, "{{:"); switch (rep_specs.sign) { case fmt_sign::none: break; case fmt_sign::plus: - STD_FMT::format_to(to_buffer, "+"); + UNITS_STD_FMT::format_to(to_buffer, "+"); break; case fmt_sign::minus: - STD_FMT::format_to(to_buffer, "-"); + UNITS_STD_FMT::format_to(to_buffer, "-"); break; case fmt_sign::space: - STD_FMT::format_to(to_buffer, " "); + UNITS_STD_FMT::format_to(to_buffer, " "); break; } if (rep_specs.alt) { - STD_FMT::format_to(to_buffer, "#"); + UNITS_STD_FMT::format_to(to_buffer, "#"); } auto type = rep_specs.type; if (auto precision = rep_specs.precision; precision >= 0) { - STD_FMT::format_to(to_buffer, ".{}{}", precision, type == '\0' ? 'f' : type); + UNITS_STD_FMT::format_to(to_buffer, ".{}{}", precision, type == '\0' ? 'f' : type); } else if constexpr (treat_as_floating_point) { - STD_FMT::format_to(to_buffer, "{}", type == '\0' ? 'g' : type); + UNITS_STD_FMT::format_to(to_buffer, "{}", type == '\0' ? 'g' : type); } else { if (type != '\0') { - STD_FMT::format_to(to_buffer, "{}", type); + UNITS_STD_FMT::format_to(to_buffer, "{}", type); } } if (rep_specs.localized) { - STD_FMT::format_to(to_buffer, "L"); + UNITS_STD_FMT::format_to(to_buffer, "L"); } - STD_FMT::format_to(to_buffer, "}}"); + UNITS_STD_FMT::format_to(to_buffer, "}}"); if (rep_specs.localized) { - return STD_FMT::vformat_to(out, FMT_LOCALE(loc), buffer, STD_FMT::make_format_args(val)); + return UNITS_STD_FMT::vformat_to(out, UNITS_FMT_LOCALE(loc), buffer, UNITS_STD_FMT::make_format_args(val)); } - return STD_FMT::vformat_to(out, buffer, STD_FMT::make_format_args(val)); + return UNITS_STD_FMT::vformat_to(out, buffer, UNITS_STD_FMT::make_format_args(val)); } // Creates a global format string @@ -196,25 +196,25 @@ template template OutputIt format_global_buffer(OutputIt out, const quantity_global_format_specs& specs) { - STD_FMT::format_to(out, "{{:"); + UNITS_STD_FMT::format_to(out, "{{:"); if (specs.fill.size() != 1 || specs.fill[0] != ' ') { - STD_FMT::format_to(out, "{}", specs.fill.data()); + UNITS_STD_FMT::format_to(out, "{}", specs.fill.data()); } switch (specs.align) { case fmt_align::left: - STD_FMT::format_to(out, "<"); + UNITS_STD_FMT::format_to(out, "<"); break; case fmt_align::right: - STD_FMT::format_to(out, ">"); + UNITS_STD_FMT::format_to(out, ">"); break; case fmt_align::center: - STD_FMT::format_to(out, "^"); + UNITS_STD_FMT::format_to(out, "^"); break; default: break; } - if (specs.width >= 1) STD_FMT::format_to(out, "{}", specs.width); - return STD_FMT::format_to(out, "}}"); + if (specs.width >= 1) UNITS_STD_FMT::format_to(out, "{}", specs.width); + return UNITS_STD_FMT::format_to(out, "}}"); } template @@ -254,18 +254,18 @@ template S> { auto it = find_first_of(begin, end, modifiers.begin(), modifiers.end()); if (it != end && find_first_of(it + 1, end, modifiers.begin(), modifiers.end()) != end) - throw STD_FMT::format_error("only one of '" + std::string(modifiers) + - "' unit modifiers may be used in the format spec"); + throw UNITS_STD_FMT::format_error("only one of '" + std::string(modifiers) + + "' unit modifiers may be used in the format spec"); return it; } } // namespace mp_units::detail template -struct STD_FMT::formatter, CharT> { +struct UNITS_STD_FMT::formatter, CharT> { private: using quantity = mp_units::quantity; - using iterator = TYPENAME STD_FMT::basic_format_parse_context::iterator; + using iterator = TYPENAME UNITS_STD_FMT::basic_format_parse_context::iterator; bool quantity_value = false; bool quantity_unit = false; @@ -274,7 +274,7 @@ private: struct spec_handler { formatter& f; - STD_FMT::basic_format_parse_context& context; + UNITS_STD_FMT::basic_format_parse_context& context; constexpr void on_fill(std::basic_string_view fill) { f.specs.global.fill = fill; } constexpr void on_align(mp_units::detail::fmt_align align) { f.specs.global.align = align; } @@ -290,7 +290,7 @@ private: if (valid_rep_types.find(type) != std::string_view::npos) { f.specs.rep.type = type; } else { - throw STD_FMT::format_error("invalid quantity type specifier"); + throw UNITS_STD_FMT::format_error("invalid quantity type specifier"); } } @@ -326,7 +326,7 @@ private: constexpr auto valid_modifiers = std::string_view{"UAoansd"}; for (auto it = begin; it != end; ++it) { if (valid_modifiers.find(*it) == std::string_view::npos) - throw STD_FMT::format_error("invalid unit modifier specified"); + throw UNITS_STD_FMT::format_error("invalid unit modifier specified"); } if (auto it = mp_units::detail::at_most_one_of(begin, end, "UA"); it != end) { @@ -350,7 +350,7 @@ private: f.specs.unit.separator = mp_units::unit_symbol_separator::space; else { if (f.specs.unit.encoding == mp_units::text_encoding::ascii) - throw STD_FMT::format_error("dot unit separator allowed only for Unicode encoding"); + throw UNITS_STD_FMT::format_error("dot unit separator allowed only for Unicode encoding"); f.specs.unit.separator = mp_units::unit_symbol_separator::dot; } } @@ -359,7 +359,7 @@ private: } }; - [[nodiscard]] constexpr std::pair do_parse(STD_FMT::basic_format_parse_context& ctx) + [[nodiscard]] constexpr std::pair do_parse(UNITS_STD_FMT::basic_format_parse_context& ctx) { auto begin = ctx.begin(); auto end = ctx.end(); @@ -409,7 +409,7 @@ private: } public: - [[nodiscard]] constexpr auto parse(STD_FMT::basic_format_parse_context& ctx) + [[nodiscard]] constexpr auto parse(UNITS_STD_FMT::basic_format_parse_context& ctx) { auto range = do_parse(ctx); if (range.first != range.second) @@ -446,8 +446,9 @@ public: mp_units::detail::format_global_buffer(std::back_inserter(global_format_buffer), specs.global); // Format the `quantity buffer` using specs from `global_format_buffer` - // In the example, equivalent to STD_FMT::format("{:*^10}", "1.2_m") - return STD_FMT::vformat_to(ctx.out(), global_format_buffer, STD_FMT::make_format_args(quantity_buffer)); + // In the example, equivalent to UNITS_STD_FMT::format("{:*^10}", "1.2_m") + return UNITS_STD_FMT::vformat_to(ctx.out(), global_format_buffer, + UNITS_STD_FMT::make_format_args(quantity_buffer)); } } }; diff --git a/src/core-io/include/mp-units/iostream.h b/src/core-io/include/mp-units/iostream.h index 314bff50..3426a98d 100644 --- a/src/core-io/include/mp-units/iostream.h +++ b/src/core-io/include/mp-units/iostream.h @@ -49,12 +49,12 @@ std::basic_ostream& operator<<(std::basic_ostream& { if (os.width()) { // std::setw() applies to the whole quantity output so it has to be first put into std::string - std::basic_ostringstream s; - s.flags(os.flags()); - s.imbue(os.getloc()); - s.precision(os.precision()); - detail::to_stream(s, q); - return os << s.str(); + std::basic_ostringstream oss; + oss.flags(os.flags()); + oss.imbue(os.getloc()); + oss.precision(os.precision()); + detail::to_stream(oss, q); + return os << std::move(oss).str(); } detail::to_stream(os, q); diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7da20876..e7af7f73 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -23,7 +23,9 @@ cmake_minimum_required(VERSION 3.19) # find dependencies -find_package(gsl-lite CONFIG REQUIRED) +if(NOT TARGET gsl::gsl-lite) + find_package(gsl-lite CONFIG REQUIRED) +endif() # check if libc++ is being used include(CheckLibcxxInUse) @@ -81,7 +83,9 @@ target_include_directories( if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") if(${projectPrefix}LIBCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "14") - find_package(range-v3 CONFIG REQUIRED) + if(NOT TARGET range-v3::range-v3) + find_package(range-v3 CONFIG REQUIRED) + endif() target_link_libraries(mp-units-core INTERFACE range-v3::range-v3) endif() elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") diff --git a/src/core/include/mp-units/bits/external/hacks.h b/src/core/include/mp-units/bits/external/hacks.h index 467a1808..3eabdaed 100644 --- a/src/core/include/mp-units/bits/external/hacks.h +++ b/src/core/include/mp-units/bits/external/hacks.h @@ -96,7 +96,7 @@ #include #include -#if UNITS_COMP_MSVC || UNITS_COMP_CLANG +#if UNITS_COMP_MSVC || (UNITS_COMP_CLANG && UNITS_COMP_CLANG < 16) #define TYPENAME typename @@ -108,11 +108,21 @@ #if UNITS_COMP_MSVC -#define UNITS_MSVC_WORKAROUND(X) +#define UNITS_CONSTRAINED_AUTO_WORKAROUND(X) #else -#define UNITS_MSVC_WORKAROUND(X) X +#define UNITS_CONSTRAINED_AUTO_WORKAROUND(X) X + +#endif + +#if UNITS_COMP_MSVC || (UNITS_COMP_GCC && UNITS_COMP_GCC < 11) + +#define UNITS_CONSTRAINED_NTTP_WORKAROUND(X) + +#else + +#define UNITS_CONSTRAINED_NTTP_WORKAROUND(X) X #endif diff --git a/src/core/include/mp-units/bits/magnitude.h b/src/core/include/mp-units/bits/magnitude.h index 252f1c03..2d05ded0 100644 --- a/src/core/include/mp-units/bits/magnitude.h +++ b/src/core/include/mp-units/bits/magnitude.h @@ -36,7 +36,6 @@ #include #include #include -#include namespace mp_units { @@ -243,10 +242,10 @@ using widen_t = conditional, template [[nodiscard]] consteval T int_power(T base, std::integral auto exp) { - // As this function should only be called at compile time, the exceptions herein function as - // "parameter-compatible static_asserts", and should not result in exceptions at runtime. + // As this function should only be called at compile time, the terminations herein function as + // "parameter-compatible static_asserts", and should not result in terminations at runtime. if (exp < 0) { - throw std::invalid_argument{"int_power only supports positive integer powers"}; + std::terminate(); // int_power only supports positive integer powers } constexpr auto checked_multiply = [](auto a, auto b) { @@ -254,7 +253,7 @@ template UNITS_DIAGNOSTIC_PUSH UNITS_DIAGNOSTIC_IGNORE_FLOAT_EQUAL if (result / a != b) { - throw std::overflow_error{"Wraparound detected"}; + std::terminate(); // Wraparound detected } UNITS_DIAGNOSTIC_POP return result; @@ -284,15 +283,15 @@ template // need to write a custom function. // // Note that since this function should only be called at compile time, the point of these - // exceptions is to act as "static_assert substitutes", not to throw actual exceptions at runtime. + // terminations is to act as "static_assert substitutes", not to actually terminate at runtime. const auto exp = get_exponent(el); if (exp.den != 1) { - throw std::invalid_argument{"Rational powers not yet supported"}; + std::terminate(); // Rational powers not yet supported } if (exp.num < 0) { if constexpr (std::is_integral_v) { - throw std::invalid_argument{"Cannot represent reciprocal as integer"}; + std::terminate(); // Cannot represent reciprocal as integer } else { return T{1} / compute_base_power(inverse(el)); } @@ -311,11 +310,11 @@ template requires(!std::is_integral_v || std::is_integral_v) [[nodiscard]] consteval To checked_static_cast(From x) { - // This function should only ever be called at compile time. The purpose of these exceptions is + // This function should only ever be called at compile time. The purpose of these terminations is // to produce compiler errors, because we cannot `static_assert` on function arguments. if constexpr (std::is_integral_v) { if (!std::in_range(x)) { - throw std::invalid_argument{"Cannot represent magnitude in this type"}; + std::terminate(); // Cannot represent magnitude in this type } } @@ -473,7 +472,7 @@ void to_base_specialization_of_magnitude(const volatile magnitude*); template inline constexpr bool is_derived_from_specialization_of_magnitude = - requires(T * t) { to_base_specialization_of_magnitude(t); }; + requires(T* t) { to_base_specialization_of_magnitude(t); }; template requires is_derived_from_specialization_of_magnitude diff --git a/src/core/include/mp-units/unit.h b/src/core/include/mp-units/unit.h index 80b6d8f0..d0c52dee 100644 --- a/src/core/include/mp-units/unit.h +++ b/src/core/include/mp-units/unit.h @@ -50,7 +50,7 @@ namespace mp_units { */ template struct scaled_unit { - static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = M; + static constexpr UNITS_CONSTRAINED_AUTO_WORKAROUND(Magnitude) auto mag = M; static constexpr U reference_unit{}; }; diff --git a/test/unit_test/runtime/almost_equals.h b/test/unit_test/runtime/almost_equals.h index ded558fc..62df0e46 100644 --- a/test/unit_test/runtime/almost_equals.h +++ b/test/unit_test/runtime/almost_equals.h @@ -42,7 +42,7 @@ struct AlmostEqualsMatcher : Catch::Matchers::MatcherGenericBase { return abs(x - y) <= std::numeric_limits::epsilon() * maxXYOne; } - std::string describe() const override { return "almost equals: " + STD_FMT::format("{}", target_); } + std::string describe() const override { return "almost equals: " + UNITS_STD_FMT::format("{}", target_); } private: const T& target_; diff --git a/test/unit_test/runtime/fmt_test.cpp b/test/unit_test/runtime/fmt_test.cpp index d55378e6..2a295292 100644 --- a/test/unit_test/runtime/fmt_test.cpp +++ b/test/unit_test/runtime/fmt_test.cpp @@ -53,9 +53,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("iostream") { CHECK(os.str() == "60 W"); } - SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } + SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); } - SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); } + SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); } } SECTION("floating-point representation") @@ -65,9 +65,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("iostream") { CHECK(os.str() == "1023.5 Pa"); } - SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } + SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); } - SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); } + SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); } } } @@ -78,9 +78,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("iostream") { CHECK(os.str() == "125 µs"); } - SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } + SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); } - SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); } + SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); } } SECTION("quantity with a derived unit") @@ -94,9 +94,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("iostream") { CHECK(os.str() == "10 m/s²"); } - SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } + SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); } - SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); } + SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); } } SECTION("volume") @@ -106,9 +106,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("iostream") { CHECK(os.str() == "2 m³"); } - SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } + SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); } - SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); } + SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); } } SECTION("surface tension") @@ -118,9 +118,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("iostream") { CHECK(os.str() == "10 N/m"); } - SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } + SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); } - SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); } + SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); } } } @@ -133,9 +133,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("iostream") { CHECK(os.str() == "10 km/h"); } - SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } + SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); } - SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); } + SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); } } SECTION("angular impulse") @@ -145,9 +145,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("iostream") { CHECK(os.str() == "123 N m s"); } - SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } + SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); } - SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); } + SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); } } SECTION("compressibility") @@ -157,9 +157,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("iostream") { CHECK(os.str() == "123 1/Pa"); } - SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } + SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); } - SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); } + SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); } } SECTION("angular acceleration") @@ -169,9 +169,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("iostream") { CHECK(os.str() == "123 rad/s²"); } - SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } + SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); } - SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); } + SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); } } } } @@ -186,9 +186,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("iostream") { CHECK(os.str() == "2"); } - SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } + SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); } - SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == "2 "); } + SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == "2 "); } } SECTION("one with ratio.exp != 0") @@ -198,9 +198,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("iostream") { CHECK(os.str() == "2 km/m"); } - SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } + SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); } - SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == "2 km/m"); } + SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == "2 km/m"); } } SECTION("percents") @@ -210,9 +210,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("iostream") { CHECK(os.str() == "15 %"); } - SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } + SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); } - SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); } + SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); } } } } @@ -221,36 +221,39 @@ TEST_CASE("format string with only %Q should print quantity value only", "[text] { SECTION("integral representation") { - SECTION("positive value") { CHECK(STD_FMT::format("{:%Q}", 123 * isq::speed[km / h]) == "123"); } + SECTION("positive value") { CHECK(UNITS_STD_FMT::format("{:%Q}", 123 * isq::speed[km / h]) == "123"); } - SECTION("negative value") { CHECK(STD_FMT::format("{:%Q}", 5 * isq::length[m] - 10 * isq::length[m]) == "-5"); } + SECTION("negative value") + { + CHECK(UNITS_STD_FMT::format("{:%Q}", 5 * isq::length[m] - 10 * isq::length[m]) == "-5"); + } } SECTION("floating-point representation") { SECTION("positive value") { - CHECK(STD_FMT::format("{:%Q}", 221. * isq::length[km] / (2 * isq::time[h])) == "110.5"); + CHECK(UNITS_STD_FMT::format("{:%Q}", 221. * isq::length[km] / (2 * isq::time[h])) == "110.5"); } SECTION("negative value") { - CHECK(STD_FMT::format("{:%Q}", 3.14 * isq::length[m] - 10 * isq::length[m]) == "-6.86"); + CHECK(UNITS_STD_FMT::format("{:%Q}", 3.14 * isq::length[m] - 10 * isq::length[m]) == "-6.86"); } SECTION("nan") { - CHECK(STD_FMT::format("{:%Q}", std::numeric_limits::quiet_NaN() * isq::length[m]) == "nan"); + CHECK(UNITS_STD_FMT::format("{:%Q}", std::numeric_limits::quiet_NaN() * isq::length[m]) == "nan"); } SECTION("inf") { - CHECK(STD_FMT::format("{:%Q}", std::numeric_limits::infinity() * isq::length[m]) == "inf"); + CHECK(UNITS_STD_FMT::format("{:%Q}", std::numeric_limits::infinity() * isq::length[m]) == "inf"); } SECTION("-inf") { - CHECK(STD_FMT::format("{:%Q}", -std::numeric_limits::infinity() * isq::length[m]) == "-inf"); + CHECK(UNITS_STD_FMT::format("{:%Q}", -std::numeric_limits::infinity() * isq::length[m]) == "-inf"); } } } @@ -261,27 +264,27 @@ TEST_CASE("format string with only %q should print quantity unit symbol only", " { SECTION("Unicode text output") { - CHECK(STD_FMT::format("{:%Uq}", 123 * isq::speed[km / h]) == "km/h"); + CHECK(UNITS_STD_FMT::format("{:%Uq}", 123 * isq::speed[km / h]) == "km/h"); // TODO enable this when resistance is defined - // CHECK(STD_FMT::format("{:%Uq}", 123 * isq::resistance[kilo]) == "kΩ"); - CHECK(STD_FMT::format("{:%Uq}", 123 * isq::time[us]) == "µs"); - CHECK(STD_FMT::format("{:%Uq}", 123 * isq::acceleration[m / s2]) == "m/s²"); + // CHECK(UNITS_STD_FMT::format("{:%Uq}", 123 * isq::resistance[kilo]) == "kΩ"); + CHECK(UNITS_STD_FMT::format("{:%Uq}", 123 * isq::time[us]) == "µs"); + CHECK(UNITS_STD_FMT::format("{:%Uq}", 123 * isq::acceleration[m / s2]) == "m/s²"); } SECTION("Unicode text output is used by default") { - CHECK(STD_FMT::format("{:%q}", 123 * isq::speed[km / h]) == "km/h"); - // CHECK(STD_FMT::format("{:%q}", 123 * isq::resistance[kilo]) == "kΩ"); - CHECK(STD_FMT::format("{:%q}", 123 * isq::time[us]) == "µs"); - CHECK(STD_FMT::format("{:%q}", 123 * isq::acceleration[m / s2]) == "m/s²"); + CHECK(UNITS_STD_FMT::format("{:%q}", 123 * isq::speed[km / h]) == "km/h"); + // CHECK(UNITS_STD_FMT::format("{:%q}", 123 * isq::resistance[kilo]) == "kΩ"); + CHECK(UNITS_STD_FMT::format("{:%q}", 123 * isq::time[us]) == "µs"); + CHECK(UNITS_STD_FMT::format("{:%q}", 123 * isq::acceleration[m / s2]) == "m/s²"); } SECTION("ASCII text output") { - CHECK(STD_FMT::format("{:%Aq}", 123 * isq::speed[km / h]) == "km/h"); - // CHECK(STD_FMT::format("{:%Aq}", 123 * isq::resistance[kilo]) == "kohm"); - CHECK(STD_FMT::format("{:%Aq}", 123 * isq::time[us]) == "us"); - CHECK(STD_FMT::format("{:%Aq}", 123 * isq::acceleration[m / s2]) == "m/s^2"); + CHECK(UNITS_STD_FMT::format("{:%Aq}", 123 * isq::speed[km / h]) == "km/h"); + // CHECK(UNITS_STD_FMT::format("{:%Aq}", 123 * isq::resistance[kilo]) == "kohm"); + CHECK(UNITS_STD_FMT::format("{:%Aq}", 123 * isq::time[us]) == "us"); + CHECK(UNITS_STD_FMT::format("{:%Aq}", 123 * isq::acceleration[m / s2]) == "m/s^2"); } } @@ -289,30 +292,30 @@ TEST_CASE("format string with only %q should print quantity unit symbol only", " { SECTION("Solidus for only one element in denominator") { - CHECK(STD_FMT::format("{:%oq}", 123 * isq::speed[km / h]) == "km/h"); - CHECK(STD_FMT::format("{:%oq}", 123 * isq::acceleration[m / s2]) == "m/s²"); - CHECK(STD_FMT::format("{:%oq}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²"); + CHECK(UNITS_STD_FMT::format("{:%oq}", 123 * isq::speed[km / h]) == "km/h"); + CHECK(UNITS_STD_FMT::format("{:%oq}", 123 * isq::acceleration[m / s2]) == "m/s²"); + CHECK(UNITS_STD_FMT::format("{:%oq}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²"); } SECTION("Solidus for only one element in denominator is used by default") { - CHECK(STD_FMT::format("{:%q}", 123 * isq::speed[km / h]) == "km/h"); - CHECK(STD_FMT::format("{:%q}", 123 * isq::acceleration[m / s2]) == "m/s²"); - CHECK(STD_FMT::format("{:%q}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²"); + CHECK(UNITS_STD_FMT::format("{:%q}", 123 * isq::speed[km / h]) == "km/h"); + CHECK(UNITS_STD_FMT::format("{:%q}", 123 * isq::acceleration[m / s2]) == "m/s²"); + CHECK(UNITS_STD_FMT::format("{:%q}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²"); } SECTION("Always use solidus") { - CHECK(STD_FMT::format("{:%aq}", 123 * isq::speed[km / h]) == "km/h"); - CHECK(STD_FMT::format("{:%aq}", 123 * isq::acceleration[m / s2]) == "m/s²"); - CHECK(STD_FMT::format("{:%aq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m s²)"); + CHECK(UNITS_STD_FMT::format("{:%aq}", 123 * isq::speed[km / h]) == "km/h"); + CHECK(UNITS_STD_FMT::format("{:%aq}", 123 * isq::acceleration[m / s2]) == "m/s²"); + CHECK(UNITS_STD_FMT::format("{:%aq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m s²)"); } SECTION("Never use solidus") { - CHECK(STD_FMT::format("{:%nq}", 123 * isq::speed[km / h]) == "km h⁻¹"); - CHECK(STD_FMT::format("{:%nq}", 123 * isq::acceleration[m / s2]) == "m s⁻²"); - CHECK(STD_FMT::format("{:%nq}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²"); + CHECK(UNITS_STD_FMT::format("{:%nq}", 123 * isq::speed[km / h]) == "km h⁻¹"); + CHECK(UNITS_STD_FMT::format("{:%nq}", 123 * isq::acceleration[m / s2]) == "m s⁻²"); + CHECK(UNITS_STD_FMT::format("{:%nq}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²"); } } @@ -320,23 +323,23 @@ TEST_CASE("format string with only %q should print quantity unit symbol only", " { SECTION("Space") { - CHECK(STD_FMT::format("{:%sq}", 123 * isq::force[kg * m / s2]) == "kg m/s²"); - CHECK(STD_FMT::format("{:%sq}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²"); - CHECK(STD_FMT::format("{:%asq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m s²)"); + CHECK(UNITS_STD_FMT::format("{:%sq}", 123 * isq::force[kg * m / s2]) == "kg m/s²"); + CHECK(UNITS_STD_FMT::format("{:%sq}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²"); + CHECK(UNITS_STD_FMT::format("{:%asq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m s²)"); } SECTION("Space is used by default") { - CHECK(STD_FMT::format("{:%q}", 123 * isq::force[kg * m / s2]) == "kg m/s²"); - CHECK(STD_FMT::format("{:%q}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²"); - CHECK(STD_FMT::format("{:%aq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m s²)"); + CHECK(UNITS_STD_FMT::format("{:%q}", 123 * isq::force[kg * m / s2]) == "kg m/s²"); + CHECK(UNITS_STD_FMT::format("{:%q}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²"); + CHECK(UNITS_STD_FMT::format("{:%aq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m s²)"); } SECTION("Dot") { - CHECK(STD_FMT::format("{:%dq}", 123 * isq::force[kg * m / s2]) == "kg⋅m/s²"); - CHECK(STD_FMT::format("{:%dq}", 123 * isq::pressure[kg / m / s2]) == "kg⋅m⁻¹⋅s⁻²"); - CHECK(STD_FMT::format("{:%adq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m⋅s²)"); + CHECK(UNITS_STD_FMT::format("{:%dq}", 123 * isq::force[kg * m / s2]) == "kg⋅m/s²"); + CHECK(UNITS_STD_FMT::format("{:%dq}", 123 * isq::pressure[kg / m / s2]) == "kg⋅m⁻¹⋅s⁻²"); + CHECK(UNITS_STD_FMT::format("{:%adq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m⋅s²)"); } } } @@ -345,26 +348,26 @@ TEST_CASE("unknown unit modifiers should throw", "[text][fmt][exception]") { SECTION("only the invalid modifier") { - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%xq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified")); + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%xq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified")); } SECTION("invalid modifier in the front") { - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%xUdaq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified")); + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%xUdaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified")); } SECTION("invalid modifier in the end") { - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%Udaxq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified")); + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%Udaxq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified")); } SECTION("invalid modifier in the middle") { - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%Udxaq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified")); + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%Udxaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified")); } } @@ -372,40 +375,40 @@ TEST_CASE("repeated unit modifiers should throw", "[text][fmt][exception]") { SECTION("text encoding") { - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%UdaUq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%UdaUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec")); - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%dUaUq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dUaUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec")); - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%dUUaq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dUUaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec")); } SECTION("solidus") { - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%aUdaq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%aUdaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec")); - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%daUaq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%daUaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec")); - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%daaUq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%daaUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec")); } SECTION("separator") { - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%dUadq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dUadq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec")); - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%dadUq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dadUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec")); - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%addUq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%addUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec")); } } @@ -414,58 +417,58 @@ TEST_CASE("more then one modifier of the same kind should throw", "[text][fmt][e { SECTION("text encoding") { - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%UdaAq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%UdaAq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec")); - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%dAaUq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dAaUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec")); - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%dAUaq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dAUaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec")); } SECTION("solidus") { - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%aUdnq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%aUdnq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec")); - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%dnUaq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dnUaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec")); - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%daoUq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%daoUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec")); } SECTION("separator") { - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%dUasq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dUasq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec")); - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%sadUq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%sadUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec")); - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%adsUq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%adsUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec")); } } TEST_CASE("dot separator requested for ASCII encoding should throw", "[text][fmt][exception]") { - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%dAaq}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dAaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("dot unit separator allowed only for Unicode encoding")); } TEST_CASE("%q and %Q can be put anywhere in a format string", "[text][fmt]") { - SECTION("no space") { CHECK(STD_FMT::format("{:%Q%q}", 123 * isq::speed[km / h]) == "123km/h"); } + SECTION("no space") { CHECK(UNITS_STD_FMT::format("{:%Q%q}", 123 * isq::speed[km / h]) == "123km/h"); } - SECTION("separator") { CHECK(STD_FMT::format("{:%Q###%q}", 123 * isq::speed[km / h]) == "123###km/h"); } + SECTION("separator") { CHECK(UNITS_STD_FMT::format("{:%Q###%q}", 123 * isq::speed[km / h]) == "123###km/h"); } - SECTION("opposite order") { CHECK(STD_FMT::format("{:%q %Q}", 123 * isq::speed[km / h]) == "km/h 123"); } + SECTION("opposite order") { CHECK(UNITS_STD_FMT::format("{:%q %Q}", 123 * isq::speed[km / h]) == "km/h 123"); } } TEST_CASE("fill and align specification", "[text][fmt][ostream]") @@ -513,50 +516,50 @@ TEST_CASE("fill and align specification", "[text][fmt][ostream]") SECTION("default format {} on a quantity") { - CHECK(STD_FMT::format("|{:0}|", 123 * isq::length[m]) == "|123 m|"); - CHECK(STD_FMT::format("|{:10}|", 123 * isq::length[m]) == "| 123 m|"); - CHECK(STD_FMT::format("|{:<10}|", 123 * isq::length[m]) == "|123 m |"); - CHECK(STD_FMT::format("|{:>10}|", 123 * isq::length[m]) == "| 123 m|"); - CHECK(STD_FMT::format("|{:^10}|", 123 * isq::length[m]) == "| 123 m |"); - CHECK(STD_FMT::format("|{:*<10}|", 123 * isq::length[m]) == "|123 m*****|"); - CHECK(STD_FMT::format("|{:*>10}|", 123 * isq::length[m]) == "|*****123 m|"); - CHECK(STD_FMT::format("|{:*^10}|", 123 * isq::length[m]) == "|**123 m***|"); + CHECK(UNITS_STD_FMT::format("|{:0}|", 123 * isq::length[m]) == "|123 m|"); + CHECK(UNITS_STD_FMT::format("|{:10}|", 123 * isq::length[m]) == "| 123 m|"); + CHECK(UNITS_STD_FMT::format("|{:<10}|", 123 * isq::length[m]) == "|123 m |"); + CHECK(UNITS_STD_FMT::format("|{:>10}|", 123 * isq::length[m]) == "| 123 m|"); + CHECK(UNITS_STD_FMT::format("|{:^10}|", 123 * isq::length[m]) == "| 123 m |"); + CHECK(UNITS_STD_FMT::format("|{:*<10}|", 123 * isq::length[m]) == "|123 m*****|"); + CHECK(UNITS_STD_FMT::format("|{:*>10}|", 123 * isq::length[m]) == "|*****123 m|"); + CHECK(UNITS_STD_FMT::format("|{:*^10}|", 123 * isq::length[m]) == "|**123 m***|"); } SECTION("full format {:%Q %q} on a quantity") { - CHECK(STD_FMT::format("|{:0%Q%q}|", 123 * isq::length[m]) == "|123m|"); - CHECK(STD_FMT::format("|{:10%Q%q}|", 123 * isq::length[m]) == "| 123m|"); - CHECK(STD_FMT::format("|{:<10%Q%q}|", 123 * isq::length[m]) == "|123m |"); - CHECK(STD_FMT::format("|{:>10%Q%q}|", 123 * isq::length[m]) == "| 123m|"); - CHECK(STD_FMT::format("|{:^10%Q%q}|", 123 * isq::length[m]) == "| 123m |"); - CHECK(STD_FMT::format("|{:*<10%Q%q}|", 123 * isq::length[m]) == "|123m******|"); - CHECK(STD_FMT::format("|{:*>10%Q%q}|", 123 * isq::length[m]) == "|******123m|"); - CHECK(STD_FMT::format("|{:*^10%Q%q}|", 123 * isq::length[m]) == "|***123m***|"); + CHECK(UNITS_STD_FMT::format("|{:0%Q%q}|", 123 * isq::length[m]) == "|123m|"); + CHECK(UNITS_STD_FMT::format("|{:10%Q%q}|", 123 * isq::length[m]) == "| 123m|"); + CHECK(UNITS_STD_FMT::format("|{:<10%Q%q}|", 123 * isq::length[m]) == "|123m |"); + CHECK(UNITS_STD_FMT::format("|{:>10%Q%q}|", 123 * isq::length[m]) == "| 123m|"); + CHECK(UNITS_STD_FMT::format("|{:^10%Q%q}|", 123 * isq::length[m]) == "| 123m |"); + CHECK(UNITS_STD_FMT::format("|{:*<10%Q%q}|", 123 * isq::length[m]) == "|123m******|"); + CHECK(UNITS_STD_FMT::format("|{:*>10%Q%q}|", 123 * isq::length[m]) == "|******123m|"); + CHECK(UNITS_STD_FMT::format("|{:*^10%Q%q}|", 123 * isq::length[m]) == "|***123m***|"); } SECTION("value only format {:%Q} on a quantity") { - CHECK(STD_FMT::format("|{:0%Q}|", 123 * isq::length[m]) == "|123|"); - CHECK(STD_FMT::format("|{:10%Q}|", 123 * isq::length[m]) == "| 123|"); - CHECK(STD_FMT::format("|{:<10%Q}|", 123 * isq::length[m]) == "|123 |"); - CHECK(STD_FMT::format("|{:>10%Q}|", 123 * isq::length[m]) == "| 123|"); - CHECK(STD_FMT::format("|{:^10%Q}|", 123 * isq::length[m]) == "| 123 |"); - CHECK(STD_FMT::format("|{:*<10%Q}|", 123 * isq::length[m]) == "|123*******|"); - CHECK(STD_FMT::format("|{:*>10%Q}|", 123 * isq::length[m]) == "|*******123|"); - CHECK(STD_FMT::format("|{:*^10%Q}|", 123 * isq::length[m]) == "|***123****|"); + CHECK(UNITS_STD_FMT::format("|{:0%Q}|", 123 * isq::length[m]) == "|123|"); + CHECK(UNITS_STD_FMT::format("|{:10%Q}|", 123 * isq::length[m]) == "| 123|"); + CHECK(UNITS_STD_FMT::format("|{:<10%Q}|", 123 * isq::length[m]) == "|123 |"); + CHECK(UNITS_STD_FMT::format("|{:>10%Q}|", 123 * isq::length[m]) == "| 123|"); + CHECK(UNITS_STD_FMT::format("|{:^10%Q}|", 123 * isq::length[m]) == "| 123 |"); + CHECK(UNITS_STD_FMT::format("|{:*<10%Q}|", 123 * isq::length[m]) == "|123*******|"); + CHECK(UNITS_STD_FMT::format("|{:*>10%Q}|", 123 * isq::length[m]) == "|*******123|"); + CHECK(UNITS_STD_FMT::format("|{:*^10%Q}|", 123 * isq::length[m]) == "|***123****|"); } SECTION("symbol only format {:%q} on a quantity") { - CHECK(STD_FMT::format("|{:0%q}|", 123 * isq::length[m]) == "|m|"); - CHECK(STD_FMT::format("|{:10%q}|", 123 * isq::length[m]) == "| m|"); - CHECK(STD_FMT::format("|{:<10%q}|", 123 * isq::length[m]) == "|m |"); - CHECK(STD_FMT::format("|{:>10%q}|", 123 * isq::length[m]) == "| m|"); - CHECK(STD_FMT::format("|{:^10%q}|", 123 * isq::length[m]) == "| m |"); - CHECK(STD_FMT::format("|{:*<10%q}|", 123 * isq::length[m]) == "|m*********|"); - CHECK(STD_FMT::format("|{:*>10%q}|", 123 * isq::length[m]) == "|*********m|"); - CHECK(STD_FMT::format("|{:*^10%q}|", 123 * isq::length[m]) == "|****m*****|"); + CHECK(UNITS_STD_FMT::format("|{:0%q}|", 123 * isq::length[m]) == "|m|"); + CHECK(UNITS_STD_FMT::format("|{:10%q}|", 123 * isq::length[m]) == "| m|"); + CHECK(UNITS_STD_FMT::format("|{:<10%q}|", 123 * isq::length[m]) == "|m |"); + CHECK(UNITS_STD_FMT::format("|{:>10%q}|", 123 * isq::length[m]) == "| m|"); + CHECK(UNITS_STD_FMT::format("|{:^10%q}|", 123 * isq::length[m]) == "| m |"); + CHECK(UNITS_STD_FMT::format("|{:*<10%q}|", 123 * isq::length[m]) == "|m*********|"); + CHECK(UNITS_STD_FMT::format("|{:*>10%q}|", 123 * isq::length[m]) == "|*********m|"); + CHECK(UNITS_STD_FMT::format("|{:*^10%q}|", 123 * isq::length[m]) == "|****m*****|"); } } @@ -567,18 +570,18 @@ TEST_CASE("sign specification", "[text][fmt]") SECTION("full format {:%Q %q} on a quantity") { - CHECK(STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", 1 * isq::length[m]) == "1m,+1m,1m, 1m"); - CHECK(STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", -1 * isq::length[m]) == "-1m,-1m,-1m,-1m"); - CHECK(STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", inf) == "infm,+infm,infm, infm"); - CHECK(STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", nan) == "nanm,+nanm,nanm, nanm"); + CHECK(UNITS_STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", 1 * isq::length[m]) == "1m,+1m,1m, 1m"); + CHECK(UNITS_STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", -1 * isq::length[m]) == "-1m,-1m,-1m,-1m"); + CHECK(UNITS_STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", inf) == "infm,+infm,infm, infm"); + CHECK(UNITS_STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", nan) == "nanm,+nanm,nanm, nanm"); } SECTION("value only format {:%Q} on a quantity") { - CHECK(STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", 1 * isq::length[m]) == "1,+1,1, 1"); - CHECK(STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", -1 * isq::length[m]) == "-1,-1,-1,-1"); - CHECK(STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", inf) == "inf,+inf,inf, inf"); - CHECK(STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", nan) == "nan,+nan,nan, nan"); + CHECK(UNITS_STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", 1 * isq::length[m]) == "1,+1,1, 1"); + CHECK(UNITS_STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", -1 * isq::length[m]) == "-1,-1,-1,-1"); + CHECK(UNITS_STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", inf) == "inf,+inf,inf, inf"); + CHECK(UNITS_STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", nan) == "nan,+nan,nan, nan"); } } @@ -586,24 +589,24 @@ TEST_CASE("precision specification", "[text][fmt]") { SECTION("full format {:%Q %q} on a quantity") { - CHECK(STD_FMT::format("{:%.0Q %q}", 1.2345 * isq::length[m]) == "1 m"); - CHECK(STD_FMT::format("{:%.1Q %q}", 1.2345 * isq::length[m]) == "1.2 m"); - CHECK(STD_FMT::format("{:%.2Q %q}", 1.2345 * isq::length[m]) == "1.23 m"); - CHECK(STD_FMT::format("{:%.3Q %q}", 1.2345 * isq::length[m]) == "1.234 m"); - CHECK(STD_FMT::format("{:%.4Q %q}", 1.2345 * isq::length[m]) == "1.2345 m"); - CHECK(STD_FMT::format("{:%.5Q %q}", 1.2345 * isq::length[m]) == "1.23450 m"); - CHECK(STD_FMT::format("{:%.10Q %q}", 1.2345 * isq::length[m]) == "1.2345000000 m"); + CHECK(UNITS_STD_FMT::format("{:%.0Q %q}", 1.2345 * isq::length[m]) == "1 m"); + CHECK(UNITS_STD_FMT::format("{:%.1Q %q}", 1.2345 * isq::length[m]) == "1.2 m"); + CHECK(UNITS_STD_FMT::format("{:%.2Q %q}", 1.2345 * isq::length[m]) == "1.23 m"); + CHECK(UNITS_STD_FMT::format("{:%.3Q %q}", 1.2345 * isq::length[m]) == "1.234 m"); + CHECK(UNITS_STD_FMT::format("{:%.4Q %q}", 1.2345 * isq::length[m]) == "1.2345 m"); + CHECK(UNITS_STD_FMT::format("{:%.5Q %q}", 1.2345 * isq::length[m]) == "1.23450 m"); + CHECK(UNITS_STD_FMT::format("{:%.10Q %q}", 1.2345 * isq::length[m]) == "1.2345000000 m"); } SECTION("value only format {:%Q} on a quantity") { - CHECK(STD_FMT::format("{:%.0Q}", 1.2345 * isq::length[m]) == "1"); - CHECK(STD_FMT::format("{:%.1Q}", 1.2345 * isq::length[m]) == "1.2"); - CHECK(STD_FMT::format("{:%.2Q}", 1.2345 * isq::length[m]) == "1.23"); - CHECK(STD_FMT::format("{:%.3Q}", 1.2345 * isq::length[m]) == "1.234"); - CHECK(STD_FMT::format("{:%.4Q}", 1.2345 * isq::length[m]) == "1.2345"); - CHECK(STD_FMT::format("{:%.5Q}", 1.2345 * isq::length[m]) == "1.23450"); - CHECK(STD_FMT::format("{:%.10Q}", 1.2345 * isq::length[m]) == "1.2345000000"); + CHECK(UNITS_STD_FMT::format("{:%.0Q}", 1.2345 * isq::length[m]) == "1"); + CHECK(UNITS_STD_FMT::format("{:%.1Q}", 1.2345 * isq::length[m]) == "1.2"); + CHECK(UNITS_STD_FMT::format("{:%.2Q}", 1.2345 * isq::length[m]) == "1.23"); + CHECK(UNITS_STD_FMT::format("{:%.3Q}", 1.2345 * isq::length[m]) == "1.234"); + CHECK(UNITS_STD_FMT::format("{:%.4Q}", 1.2345 * isq::length[m]) == "1.2345"); + CHECK(UNITS_STD_FMT::format("{:%.5Q}", 1.2345 * isq::length[m]) == "1.23450"); + CHECK(UNITS_STD_FMT::format("{:%.10Q}", 1.2345 * isq::length[m]) == "1.2345000000"); } } @@ -611,15 +614,15 @@ TEST_CASE("precision specification for integral representation should throw", "[ { SECTION("full format {:%Q %q} on a quantity") { - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%.1Q %q}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%.1Q %q}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("precision not allowed for integral quantity representation")); } SECTION("value only format {:%Q} on a quantity") { - REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%.1Q}", STD_FMT::make_format_args(1 * isq::length[m])), - STD_FMT::format_error, + REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%.1Q}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])), + UNITS_STD_FMT::format_error, Catch::Matchers::Message("precision not allowed for integral quantity representation")); } } @@ -628,56 +631,56 @@ TEST_CASE("type specification", "[text][fmt]") { SECTION("full format {:%Q %q} on a quantity") { - CHECK(STD_FMT::format("{:%bQ %q}", 42 * isq::length[m]) == "101010 m"); - CHECK(STD_FMT::format("{:%BQ %q}", 42 * isq::length[m]) == "101010 m"); - CHECK(STD_FMT::format("{:%dQ %q}", 42 * isq::length[m]) == "42 m"); - CHECK(STD_FMT::format("{:%oQ %q}", 42 * isq::length[m]) == "52 m"); - CHECK(STD_FMT::format("{:%xQ %q}", 42 * isq::length[m]) == "2a m"); - CHECK(STD_FMT::format("{:%XQ %q}", 42 * isq::length[m]) == "2A m"); + CHECK(UNITS_STD_FMT::format("{:%bQ %q}", 42 * isq::length[m]) == "101010 m"); + CHECK(UNITS_STD_FMT::format("{:%BQ %q}", 42 * isq::length[m]) == "101010 m"); + CHECK(UNITS_STD_FMT::format("{:%dQ %q}", 42 * isq::length[m]) == "42 m"); + CHECK(UNITS_STD_FMT::format("{:%oQ %q}", 42 * isq::length[m]) == "52 m"); + CHECK(UNITS_STD_FMT::format("{:%xQ %q}", 42 * isq::length[m]) == "2a m"); + CHECK(UNITS_STD_FMT::format("{:%XQ %q}", 42 * isq::length[m]) == "2A m"); - CHECK(STD_FMT::format("{:%aQ %q}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0 m"); - CHECK(STD_FMT::format("{:%.3aQ %q}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0 m"); - CHECK(STD_FMT::format("{:%AQ %q}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0 m"); - CHECK(STD_FMT::format("{:%.3AQ %q}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0 m"); - CHECK(STD_FMT::format("{:%eQ %q}", 1.2345678 * isq::length[m]) == "1.234568e+00 m"); - CHECK(STD_FMT::format("{:%.3eQ %q}", 1.2345678 * isq::length[m]) == "1.235e+00 m"); - CHECK(STD_FMT::format("{:%EQ %q}", 1.2345678 * isq::length[m]) == "1.234568E+00 m"); - CHECK(STD_FMT::format("{:%.3EQ %q}", 1.2345678 * isq::length[m]) == "1.235E+00 m"); - CHECK(STD_FMT::format("{:%gQ %q}", 1.2345678 * isq::length[m]) == "1.23457 m"); - CHECK(STD_FMT::format("{:%gQ %q}", 1.2345678e8 * isq::length[m]) == "1.23457e+08 m"); - CHECK(STD_FMT::format("{:%.3gQ %q}", 1.2345678 * isq::length[m]) == "1.23 m"); - CHECK(STD_FMT::format("{:%.3gQ %q}", 1.2345678e8 * isq::length[m]) == "1.23e+08 m"); - CHECK(STD_FMT::format("{:%GQ %q}", 1.2345678 * isq::length[m]) == "1.23457 m"); - CHECK(STD_FMT::format("{:%GQ %q}", 1.2345678e8 * isq::length[m]) == "1.23457E+08 m"); - CHECK(STD_FMT::format("{:%.3GQ %q}", 1.2345678 * isq::length[m]) == "1.23 m"); - CHECK(STD_FMT::format("{:%.3GQ %q}", 1.2345678e8 * isq::length[m]) == "1.23E+08 m"); + CHECK(UNITS_STD_FMT::format("{:%aQ %q}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0 m"); + CHECK(UNITS_STD_FMT::format("{:%.3aQ %q}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0 m"); + CHECK(UNITS_STD_FMT::format("{:%AQ %q}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0 m"); + CHECK(UNITS_STD_FMT::format("{:%.3AQ %q}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0 m"); + CHECK(UNITS_STD_FMT::format("{:%eQ %q}", 1.2345678 * isq::length[m]) == "1.234568e+00 m"); + CHECK(UNITS_STD_FMT::format("{:%.3eQ %q}", 1.2345678 * isq::length[m]) == "1.235e+00 m"); + CHECK(UNITS_STD_FMT::format("{:%EQ %q}", 1.2345678 * isq::length[m]) == "1.234568E+00 m"); + CHECK(UNITS_STD_FMT::format("{:%.3EQ %q}", 1.2345678 * isq::length[m]) == "1.235E+00 m"); + CHECK(UNITS_STD_FMT::format("{:%gQ %q}", 1.2345678 * isq::length[m]) == "1.23457 m"); + CHECK(UNITS_STD_FMT::format("{:%gQ %q}", 1.2345678e8 * isq::length[m]) == "1.23457e+08 m"); + CHECK(UNITS_STD_FMT::format("{:%.3gQ %q}", 1.2345678 * isq::length[m]) == "1.23 m"); + CHECK(UNITS_STD_FMT::format("{:%.3gQ %q}", 1.2345678e8 * isq::length[m]) == "1.23e+08 m"); + CHECK(UNITS_STD_FMT::format("{:%GQ %q}", 1.2345678 * isq::length[m]) == "1.23457 m"); + CHECK(UNITS_STD_FMT::format("{:%GQ %q}", 1.2345678e8 * isq::length[m]) == "1.23457E+08 m"); + CHECK(UNITS_STD_FMT::format("{:%.3GQ %q}", 1.2345678 * isq::length[m]) == "1.23 m"); + CHECK(UNITS_STD_FMT::format("{:%.3GQ %q}", 1.2345678e8 * isq::length[m]) == "1.23E+08 m"); } SECTION("value only format {:%Q} on a quantity") { - CHECK(STD_FMT::format("{:%bQ}", 42 * isq::length[m]) == "101010"); - CHECK(STD_FMT::format("{:%BQ}", 42 * isq::length[m]) == "101010"); - CHECK(STD_FMT::format("{:%dQ}", 42 * isq::length[m]) == "42"); - CHECK(STD_FMT::format("{:%oQ}", 42 * isq::length[m]) == "52"); - CHECK(STD_FMT::format("{:%xQ}", 42 * isq::length[m]) == "2a"); - CHECK(STD_FMT::format("{:%XQ}", 42 * isq::length[m]) == "2A"); + CHECK(UNITS_STD_FMT::format("{:%bQ}", 42 * isq::length[m]) == "101010"); + CHECK(UNITS_STD_FMT::format("{:%BQ}", 42 * isq::length[m]) == "101010"); + CHECK(UNITS_STD_FMT::format("{:%dQ}", 42 * isq::length[m]) == "42"); + CHECK(UNITS_STD_FMT::format("{:%oQ}", 42 * isq::length[m]) == "52"); + CHECK(UNITS_STD_FMT::format("{:%xQ}", 42 * isq::length[m]) == "2a"); + CHECK(UNITS_STD_FMT::format("{:%XQ}", 42 * isq::length[m]) == "2A"); - CHECK(STD_FMT::format("{:%aQ}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0"); - CHECK(STD_FMT::format("{:%.3aQ}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0"); - CHECK(STD_FMT::format("{:%AQ}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0"); - CHECK(STD_FMT::format("{:%.3AQ}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0"); - CHECK(STD_FMT::format("{:%eQ}", 1.2345678 * isq::length[m]) == "1.234568e+00"); - CHECK(STD_FMT::format("{:%.3eQ}", 1.2345678 * isq::length[m]) == "1.235e+00"); - CHECK(STD_FMT::format("{:%EQ}", 1.2345678 * isq::length[m]) == "1.234568E+00"); - CHECK(STD_FMT::format("{:%.3EQ}", 1.2345678 * isq::length[m]) == "1.235E+00"); - CHECK(STD_FMT::format("{:%gQ}", 1.2345678 * isq::length[m]) == "1.23457"); - CHECK(STD_FMT::format("{:%gQ}", 1.2345678e8 * isq::length[m]) == "1.23457e+08"); - CHECK(STD_FMT::format("{:%.3gQ}", 1.2345678 * isq::length[m]) == "1.23"); - CHECK(STD_FMT::format("{:%.3gQ}", 1.2345678e8 * isq::length[m]) == "1.23e+08"); - CHECK(STD_FMT::format("{:%GQ}", 1.2345678 * isq::length[m]) == "1.23457"); - CHECK(STD_FMT::format("{:%GQ}", 1.2345678e8 * isq::length[m]) == "1.23457E+08"); - CHECK(STD_FMT::format("{:%.3GQ}", 1.2345678 * isq::length[m]) == "1.23"); - CHECK(STD_FMT::format("{:%.3GQ}", 1.2345678e8 * isq::length[m]) == "1.23E+08"); + CHECK(UNITS_STD_FMT::format("{:%aQ}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0"); + CHECK(UNITS_STD_FMT::format("{:%.3aQ}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0"); + CHECK(UNITS_STD_FMT::format("{:%AQ}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0"); + CHECK(UNITS_STD_FMT::format("{:%.3AQ}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0"); + CHECK(UNITS_STD_FMT::format("{:%eQ}", 1.2345678 * isq::length[m]) == "1.234568e+00"); + CHECK(UNITS_STD_FMT::format("{:%.3eQ}", 1.2345678 * isq::length[m]) == "1.235e+00"); + CHECK(UNITS_STD_FMT::format("{:%EQ}", 1.2345678 * isq::length[m]) == "1.234568E+00"); + CHECK(UNITS_STD_FMT::format("{:%.3EQ}", 1.2345678 * isq::length[m]) == "1.235E+00"); + CHECK(UNITS_STD_FMT::format("{:%gQ}", 1.2345678 * isq::length[m]) == "1.23457"); + CHECK(UNITS_STD_FMT::format("{:%gQ}", 1.2345678e8 * isq::length[m]) == "1.23457e+08"); + CHECK(UNITS_STD_FMT::format("{:%.3gQ}", 1.2345678 * isq::length[m]) == "1.23"); + CHECK(UNITS_STD_FMT::format("{:%.3gQ}", 1.2345678e8 * isq::length[m]) == "1.23e+08"); + CHECK(UNITS_STD_FMT::format("{:%GQ}", 1.2345678 * isq::length[m]) == "1.23457"); + CHECK(UNITS_STD_FMT::format("{:%GQ}", 1.2345678e8 * isq::length[m]) == "1.23457E+08"); + CHECK(UNITS_STD_FMT::format("{:%.3GQ}", 1.2345678 * isq::length[m]) == "1.23"); + CHECK(UNITS_STD_FMT::format("{:%.3GQ}", 1.2345678e8 * isq::length[m]) == "1.23E+08"); } } @@ -685,20 +688,20 @@ TEST_CASE("different base types with the # specifier", "[text][fmt]") { SECTION("full format {:%Q %q} on a quantity") { - CHECK(STD_FMT::format("{:%#bQ %q}", 42 * isq::length[m]) == "0b101010 m"); - CHECK(STD_FMT::format("{:%#BQ %q}", 42 * isq::length[m]) == "0B101010 m"); - CHECK(STD_FMT::format("{:%#oQ %q}", 42 * isq::length[m]) == "052 m"); - CHECK(STD_FMT::format("{:%#xQ %q}", 42 * isq::length[m]) == "0x2a m"); - CHECK(STD_FMT::format("{:%#XQ %q}", 42 * isq::length[m]) == "0X2A m"); + CHECK(UNITS_STD_FMT::format("{:%#bQ %q}", 42 * isq::length[m]) == "0b101010 m"); + CHECK(UNITS_STD_FMT::format("{:%#BQ %q}", 42 * isq::length[m]) == "0B101010 m"); + CHECK(UNITS_STD_FMT::format("{:%#oQ %q}", 42 * isq::length[m]) == "052 m"); + CHECK(UNITS_STD_FMT::format("{:%#xQ %q}", 42 * isq::length[m]) == "0x2a m"); + CHECK(UNITS_STD_FMT::format("{:%#XQ %q}", 42 * isq::length[m]) == "0X2A m"); } SECTION("value only format {:%Q} on a quantity") { - CHECK(STD_FMT::format("{:%#bQ}", 42 * isq::length[m]) == "0b101010"); - CHECK(STD_FMT::format("{:%#BQ}", 42 * isq::length[m]) == "0B101010"); - CHECK(STD_FMT::format("{:%#oQ}", 42 * isq::length[m]) == "052"); - CHECK(STD_FMT::format("{:%#xQ}", 42 * isq::length[m]) == "0x2a"); - CHECK(STD_FMT::format("{:%#XQ}", 42 * isq::length[m]) == "0X2A"); + CHECK(UNITS_STD_FMT::format("{:%#bQ}", 42 * isq::length[m]) == "0b101010"); + CHECK(UNITS_STD_FMT::format("{:%#BQ}", 42 * isq::length[m]) == "0B101010"); + CHECK(UNITS_STD_FMT::format("{:%#oQ}", 42 * isq::length[m]) == "052"); + CHECK(UNITS_STD_FMT::format("{:%#xQ}", 42 * isq::length[m]) == "0x2a"); + CHECK(UNITS_STD_FMT::format("{:%#XQ}", 42 * isq::length[m]) == "0X2A"); } } @@ -719,8 +722,8 @@ TEST_CASE("localization with the 'L' specifier", "[text][fmt][localization]") SECTION("full format {:%LQ %q} on a quantity") { - CHECK(STD_FMT::format(grp2, "{:%LQ %q}", 299792458 * isq::speed[m / s]) == "2_99_79_24_58 m/s"); - CHECK(STD_FMT::format(grp3, "{:%LQ %q}", 299792458 * isq::speed[m / s]) == "299'792'458 m/s"); + CHECK(UNITS_STD_FMT::format(grp2, "{:%LQ %q}", 299792458 * isq::speed[m / s]) == "2_99_79_24_58 m/s"); + CHECK(UNITS_STD_FMT::format(grp3, "{:%LQ %q}", 299792458 * isq::speed[m / s]) == "299'792'458 m/s"); } } diff --git a/test_package/conanfile.py b/test_package/conanfile.py index e71afd55..048827ad 100644 --- a/test_package/conanfile.py +++ b/test_package/conanfile.py @@ -23,28 +23,26 @@ import os from conan import ConanFile -from conan.tools.build import cross_building +from conan.tools.build import can_run from conan.tools.cmake import CMake, cmake_layout class TestPackageConan(ConanFile): - settings = "os", "compiler", "build_type", "arch" - generators = "CMakeDeps", "CMakeToolchain", "VirtualBuildEnv", "VirtualRunEnv" - apply_env = False - test_type = "explicit" # TODO Remove for Conan 2.0 + settings = "os", "arch", "compiler", "build_type" + generators = "CMakeDeps", "CMakeToolchain", "VirtualRunEnv" def requirements(self): self.requires(self.tested_reference_str) + def layout(self): + cmake_layout(self) + def build(self): cmake = CMake(self) cmake.configure() cmake.build() - def layout(self): - cmake_layout(self) - def test(self): - if not cross_building(self): - cmd = os.path.join(self.cpp.build.bindirs[0], "test_package") - self.run(cmd, env="conanrun") + if can_run(self): + bin_path = os.path.join(self.cpp.build.bindirs[0], "test_package") + self.run(bin_path, env="conanrun")