merge branch master

This commit is contained in:
Mateusz Pusz
2023-05-26 13:53:52 +02:00
parent b7e467ff42
commit 561fbf7c93
68 changed files with 1426 additions and 812 deletions

View File

@ -26,17 +26,17 @@ on: [push, pull_request]
jobs: jobs:
check: check:
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Set up Python 3.8 - name: Set up Python 3.8
uses: actions/setup-python@v2 uses: actions/setup-python@v4
with: with:
python-version: 3.8 python-version: 3.8
- name: Install dependencies - name: Install dependencies
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
pip install -r requirements.txt pip install -r requirements.txt
- name: Check - name: Check
run: | run: |
pre-commit run --all-files pre-commit run --all-files

View File

@ -30,6 +30,9 @@ on:
paths-ignore: paths-ignore:
- "docs/**" - "docs/**"
env:
CHANNEL: ${{ fromJSON('["testing", "stable"]')[github.ref_type == 'tag' && startsWith(github.ref_name, 'v')] }}
jobs: jobs:
build: build:
name: ${{ matrix.config.name }} ${{ matrix.build_type }} [downcast=${{ matrix.downcast_mode }}] name: ${{ matrix.config.name }} ${{ matrix.build_type }} [downcast=${{ matrix.downcast_mode }}]
@ -42,15 +45,17 @@ jobs:
name: "Windows MSVC 14.2", name: "Windows MSVC 14.2",
os: windows-2019, os: windows-2019,
compiler: { type: VISUAL, version: 16, cc: "", cxx: "", std: 20 }, compiler: { type: VISUAL, version: 16, cc: "", cxx: "", std: 20 },
conan-config: "-c user.build:skip_la=True",
} }
- { - {
name: "Windows MSVC 14.3", name: "Windows MSVC 14.3",
os: windows-2022, os: windows-2022,
compiler: { type: MSVC, version: 193, cc: "", cxx: "", std: 23 }, compiler: { type: MSVC, version: 193, cc: "", cxx: "", std: 23 },
conan-config: "",
} }
- { - {
name: "Ubuntu GCC-10", name: "Ubuntu GCC-10",
os: ubuntu-20.04, os: ubuntu-22.04,
compiler: compiler:
{ {
type: GCC, type: GCC,
@ -59,11 +64,11 @@ jobs:
cxx: "g++-10", cxx: "g++-10",
std: 20, std: 20,
}, },
lib: "libstdc++11", conan-config: "",
} }
- { - {
name: "Ubuntu GCC-11", name: "Ubuntu GCC-11",
os: ubuntu-20.04, os: ubuntu-22.04,
compiler: compiler:
{ {
type: GCC, type: GCC,
@ -72,7 +77,20 @@ jobs:
cxx: "g++-11", cxx: "g++-11",
std: 20, 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", name: "Ubuntu Clang-12 + libstdc++11",
@ -86,6 +104,7 @@ jobs:
std: 20, std: 20,
}, },
lib: "libstdc++11", lib: "libstdc++11",
conan-config: "",
} }
- { - {
name: "Ubuntu Clang-12 + libc++", name: "Ubuntu Clang-12 + libc++",
@ -99,10 +118,11 @@ jobs:
std: 20, std: 20,
}, },
lib: "libc++", lib: "libc++",
conan-config: "",
} }
- { - {
name: "Ubuntu Clang-13 + libc++", name: "Ubuntu Clang-13 + libc++",
os: ubuntu-20.04, os: ubuntu-22.04,
compiler: compiler:
{ {
type: CLANG, type: CLANG,
@ -112,10 +132,11 @@ jobs:
std: 20, std: 20,
}, },
lib: "libc++", lib: "libc++",
conan-config: "",
} }
- { - {
name: "Ubuntu Clang-14 + libc++", name: "Ubuntu Clang-14 + libc++",
os: ubuntu-20.04, os: ubuntu-22.04,
compiler: compiler:
{ {
type: CLANG, type: CLANG,
@ -125,6 +146,35 @@ jobs:
std: 20, std: 20,
}, },
lib: "libc++", 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", name: "MacOS Apple Clang 13",
@ -137,6 +187,7 @@ jobs:
cxx: "clang++", cxx: "clang++",
std: 20, std: 20,
}, },
conan-config: "",
} }
build_type: ["Release", "Debug"] build_type: ["Release", "Debug"]
downcast_mode: ["on", "auto"] downcast_mode: ["on", "auto"]
@ -146,25 +197,27 @@ jobs:
CXX: ${{ matrix.config.compiler.cxx }} CXX: ${{ matrix.config.compiler.cxx }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Cache Conan data - name: Cache Conan data
uses: actions/cache@v2 uses: actions/cache@v3
env: env:
cache-name: cache-conan-data cache-name: cache-conan-data
with: with:
path: ~/.conan/data path: ~/.conan2/p
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/metadata.json') }} key: build-${{ matrix.config.os }}-${{ matrix.build_type }}-${{ matrix.config.compiler.type }}-${{ matrix.config.compiler.version }}-${{ matrix.config.lib }}
restore-keys: | restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}- build-${{ matrix.config.os }}-${{ matrix.build_type }}-${{ matrix.config.compiler.type }}-${{ matrix.config.compiler.version }}-${{ matrix.config.lib }}
${{ runner.os }}-build- build-${{ matrix.config.os }}-${{ matrix.build_type }}-${{ matrix.config.compiler.type }}-${{ matrix.config.compiler.version }}-
${{ runner.os }}- build-${{ matrix.config.os }}-${{ matrix.build_type }}-${{ matrix.config.compiler.type }}-
- uses: hendrikmuhs/ccache-action@v1 build-${{ matrix.config.os }}-${{ matrix.build_type }}-
build-${{ matrix.config.os }}-
- uses: hendrikmuhs/ccache-action@v1.2
if: runner.os == 'Linux' if: runner.os == 'Linux'
with: with:
key: ${{ matrix.config.os }}-${{ matrix.config.compiler.type }}-${{ matrix.config.compiler.version }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.downcast_mode }} key: ${{ matrix.config.os }}-${{ matrix.config.compiler.type }}-${{ matrix.config.compiler.version }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.downcast_mode }}
max-size: 50M max-size: 50M
- name: Install gcc-11 - name: Install gcc-12
if: matrix.config.compiler.type == 'GCC' && matrix.config.compiler.version == '11' if: matrix.config.compiler.type == 'GCC' && matrix.config.compiler.version == '12'
shell: bash shell: bash
run: | run: |
sudo apt install -y g++-${{ matrix.config.compiler.version }} sudo apt install -y g++-${{ matrix.config.compiler.version }}
@ -201,7 +254,7 @@ jobs:
exit 1 exit 1
fi fi
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v2 uses: actions/setup-python@v4
with: with:
python-version: "3.8" python-version: "3.8"
- name: Install Conan - name: Install Conan
@ -211,37 +264,76 @@ jobs:
- name: Configure Conan - name: Configure Conan
shell: bash shell: bash
run: | 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 }}" == "CLANG" ]]; then
if [[ "${{ matrix.config.compiler.type }}" == "GCC" || "${{ matrix.config.compiler.type }}" == "CLANG" ]]; then sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.libcxx=.*/compiler.libcxx=${{ matrix.config.lib }}/' ~/.conan2/profiles/default
conan profile update settings.compiler.libcxx=${{ matrix.config.lib }} default
fi fi
conan profile update settings.compiler.cppstd=${{ matrix.config.compiler.std }} default sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.cppstd=.*/compiler.cppstd=${{ matrix.config.compiler.std }}/' ~/.conan2/profiles/default
conan profile update settings.build_type=${{ matrix.build_type }} default sed -i.backup '/^\[settings\]$/,/^\[/ s/^build_type=.*/build_type=${{ matrix.build_type }}/' ~/.conan2/profiles/default
conan profile update conf.tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" default conan profile show -pr default
conan profile show default
# - name: Add support for clang-13 to Conan's settings.yml # - name: Add support for clang-13 to Conan's settings.yml
# # TODO Remove when Conan will support clang-13 # # TODO Remove when Conan will support clang-13
# if: matrix.config.compiler.type == 'CLANG' # if: matrix.config.compiler.type == 'CLANG'
# shell: bash # shell: bash
# run: | # run: |
# sed -i -e 's/"8", "9", "10", "11", "12"]/"8", "9", "10", "11", "12", "13"]/' ~/.conan/settings.yml # 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 - name: Create Conan package
shell: bash shell: bash
run: | 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 conan create . --user mpusz --channel ${CHANNEL} --lockfile-out=package.lock \
- name: Upload mp-units Conan package -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' if: github.ref == 'refs/heads/master' || env.CHANNEL == 'stable'
shell: bash shell: bash
run: | run: |
conan user ${{ secrets.CONAN_LOGIN_USERNAME }} -r artifactory -p ${{ secrets.CONAN_PASSWORD }} conan remote add conan-mpusz-ci https://mpusz.jfrog.io/artifactory/api/conan/conan-ci
conan upload "mp-units*" --all -r artifactory --confirm conan remote login conan-mpusz-ci ${{ secrets.CONAN_LOGIN_USERNAME }} -p ${{ secrets.CONAN_CI_PASSWORD }}
- name: Remove mp-units package from Conan local cache conan upload ${{ steps.get-package-ref.outputs.package_ref }} -r conan-mpusz-ci --confirm
- name: Clean Conan cache before backup
shell: bash shell: bash
run: | 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

View File

@ -54,7 +54,7 @@ jobs:
} }
- { - {
name: "Ubuntu GCC-10", name: "Ubuntu GCC-10",
os: ubuntu-20.04, os: ubuntu-22.04,
compiler: compiler:
{ {
type: GCC, type: GCC,
@ -63,11 +63,10 @@ jobs:
cxx: "g++-10", cxx: "g++-10",
std: 20, std: 20,
}, },
lib: "libstdc++11",
} }
- { - {
name: "Ubuntu GCC-11", name: "Ubuntu GCC-11",
os: ubuntu-20.04, os: ubuntu-22.04,
compiler: compiler:
{ {
type: GCC, type: GCC,
@ -76,7 +75,18 @@ jobs:
cxx: "g++-11", cxx: "g++-11",
std: 20, 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", name: "Ubuntu Clang-12 + libstdc++11",
@ -106,7 +116,7 @@ jobs:
} }
- { - {
name: "Ubuntu Clang-13 + libc++", name: "Ubuntu Clang-13 + libc++",
os: ubuntu-20.04, os: ubuntu-22.04,
compiler: compiler:
{ {
type: CLANG, type: CLANG,
@ -119,7 +129,7 @@ jobs:
} }
- { - {
name: "Ubuntu Clang-14 + libc++", name: "Ubuntu Clang-14 + libc++",
os: ubuntu-20.04, os: ubuntu-22.04,
compiler: compiler:
{ {
type: CLANG, type: CLANG,
@ -130,6 +140,32 @@ jobs:
}, },
lib: "libc++", 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", name: "MacOS Apple Clang 13",
os: macos-11, os: macos-11,
@ -154,18 +190,20 @@ jobs:
uses: ASzc/change-string-case-action@v2 uses: ASzc/change-string-case-action@v2
with: with:
string: ${{ matrix.build_type }} string: ${{ matrix.build_type }}
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Cache Conan data - name: Cache Conan data
uses: actions/cache@v2 uses: actions/cache@v3
env: env:
cache-name: cache-conan-data cache-name: cache-conan-data
with: with:
path: ~/.conan/data path: ~/.conan2/p
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/metadata.json') }} key: build-${{ matrix.config.os }}-${{ matrix.build_type }}-${{ matrix.config.compiler.type }}-${{ matrix.config.compiler.version }}-${{ matrix.config.lib }}
restore-keys: | restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}- build-${{ matrix.config.os }}-${{ matrix.build_type }}-${{ matrix.config.compiler.type }}-${{ matrix.config.compiler.version }}-${{ matrix.config.lib }}
${{ runner.os }}-build- build-${{ matrix.config.os }}-${{ matrix.build_type }}-${{ matrix.config.compiler.type }}-${{ matrix.config.compiler.version }}-
${{ runner.os }}- 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 - name: Install gcc-11
if: matrix.config.compiler.type == 'GCC' && matrix.config.compiler.version == '11' if: matrix.config.compiler.type == 'GCC' && matrix.config.compiler.version == '11'
shell: bash shell: bash
@ -204,7 +242,7 @@ jobs:
exit 1 exit 1
fi fi
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v2 uses: actions/setup-python@v4
with: with:
python-version: "3.8" python-version: "3.8"
- name: Install Conan - name: Install Conan
@ -214,15 +252,14 @@ jobs:
- name: Configure Conan - name: Configure Conan
shell: bash shell: bash
run: | run: |
conan config init conan profile detect --force
conan remote add artifactory https://mpusz.jfrog.io/artifactory/api/conan/conan-oss 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 if [[ "${{ matrix.config.compiler.type }}" == "CLANG" ]]; then
conan profile update settings.compiler.libcxx=${{ matrix.config.lib }} default sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.libcxx=.*/compiler.libcxx=${{ matrix.config.lib }}/' ~/.conan2/profiles/default
fi fi
conan profile update settings.compiler.cppstd=${{ matrix.config.compiler.std }} default sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.cppstd=.*/compiler.cppstd=${{ matrix.config.compiler.std }}/' ~/.conan2/profiles/default
conan profile update settings.build_type=${{ matrix.build_type }} default sed -i.backup '/^\[settings\]$/,/^\[/ s/^build_type=.*/build_type=${{ matrix.build_type }}/' ~/.conan2/profiles/default
conan profile update conf.tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" default conan profile show -pr default
conan profile show default
# - name: Add support for clang-13 to Conan's settings.yml # - name: Add support for clang-13 to Conan's settings.yml
# # TODO Remove when Conan will support clang-13 # # TODO Remove when Conan will support clang-13
# if: matrix.config.compiler.type == 'CLANG' # if: matrix.config.compiler.type == 'CLANG'
@ -232,7 +269,7 @@ jobs:
- name: Install Conan dependencies - name: Install Conan dependencies
shell: bash shell: bash
run: | 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 mv CMakeUserPresets.json src
- name: Configure mp-units CMake - name: Configure mp-units CMake
if: matrix.config.compiler.type == 'VISUAL' || matrix.config.compiler.type == 'MSVC' if: matrix.config.compiler.type == 'VISUAL' || matrix.config.compiler.type == 'MSVC'
@ -241,19 +278,19 @@ jobs:
run: | run: |
cmake --version cmake --version
call ..\build\generators\conanvcvars.bat 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 - name: Configure mp-units CMake
if: matrix.config.compiler.type != 'VISUAL' && matrix.config.compiler.type != 'MSVC' if: matrix.config.compiler.type != 'VISUAL' && matrix.config.compiler.type != 'MSVC'
shell: bash shell: bash
working-directory: src working-directory: src
run: | run: |
cmake --version cmake --version
cmake --preset default -DCMAKE_INSTALL_PREFIX=../out cmake --preset conan-default -DCMAKE_INSTALL_PREFIX=../out
- name: Install mp-units - name: Install mp-units
shell: bash shell: bash
working-directory: src working-directory: src
run: | 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 - name: Provide dependencies for test_package
shell: bash shell: bash
working-directory: test_package working-directory: test_package
@ -265,14 +302,14 @@ jobs:
working-directory: test_package working-directory: test_package
run: | run: |
call ..\build\generators\conanvcvars.bat 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 }} cmake --build build/local --config ${{ matrix.build_type }}
- name: Build test_package CMake (local build) - name: Build test_package CMake (local build)
if: matrix.config.compiler.type != 'VISUAL' && matrix.config.compiler.type != 'MSVC' if: matrix.config.compiler.type != 'VISUAL' && matrix.config.compiler.type != 'MSVC'
shell: bash shell: bash
working-directory: test_package working-directory: test_package
run: | 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 }} cmake --build build/local --config ${{ matrix.build_type }}
- name: Run test_package (local build) - name: Run test_package (local build)
shell: bash shell: bash
@ -285,14 +322,14 @@ jobs:
working-directory: test_package working-directory: test_package
run: | run: |
call ..\build\generators\conanvcvars.bat 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 }} cmake --build build/install --config ${{ matrix.build_type }}
- name: Build test_package CMake (installation) - name: Build test_package CMake (installation)
if: matrix.config.compiler.type != 'VISUAL' && matrix.config.compiler.type != 'MSVC' if: matrix.config.compiler.type != 'VISUAL' && matrix.config.compiler.type != 'MSVC'
shell: bash shell: bash
working-directory: test_package working-directory: test_package
run: | 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 }} cmake --build build/install --config ${{ matrix.build_type }}
- name: Run test_package (installation) - name: Run test_package (installation)
shell: bash shell: bash

View File

@ -15,7 +15,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
# This is needed for workflows running on # This is needed for workflows running on
# ubuntu-20.04 or later # ubuntu-20.04 or later

View File

@ -25,22 +25,26 @@ on:
jobs: jobs:
analyze: analyze:
name: Analyze name: Analyze
runs-on: ubuntu-20.04 runs-on: ${{ matrix.os }}
env: env:
CC: gcc-10 CC: gcc-10
CXX: g++-10 CXX: g++-10
BUILD_TYPE: Debug
COMPILER_TYPE: GCC
COMPILER_VERSION: 10
STDLIB: libstdc++11
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
language: ["cpp", "python"] language: ["cpp", "python"]
os: ["ubuntu-latest"]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more... # Learn more...
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
@ -63,29 +67,29 @@ jobs:
- name: Cache Conan data - name: Cache Conan data
if: matrix.language == 'cpp' if: matrix.language == 'cpp'
uses: actions/cache@v2 uses: actions/cache@v3
env: env:
cache-name: cache-conan-data cache-name: cache-conan-data
with: with:
path: ~/.conan/data path: ~/.conan2/p
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/metadata.json') }} key: build-${{ matrix.os }}-$BUILD_TYPE-$COMPILER_TYPE-$COMPILER_VERSION-$STDLIB
restore-keys: | restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}- build-${{ matrix.os }}-$BUILD_TYPE-$COMPILER_TYPE-$COMPILER_VERSION-
${{ runner.os }}-build- build-${{ matrix.os }}-$BUILD_TYPE-$COMPILER_TYPE-
${{ runner.os }}- build-${{ matrix.os }}-$BUILD_TYPE-
build-${{ matrix.os }}-
- name: Set up Python - name: Set up Python
if: matrix.language == 'cpp' if: matrix.language == 'cpp'
uses: actions/setup-python@v2 uses: actions/setup-python@v4
with: with:
python-version: "3.8" python-version: "3.8"
- name: Conan build - name: Conan build
if: matrix.language == 'cpp' if: matrix.language == 'cpp'
run: | run: |
pip install -U conan pip install -U conan
conan config init conan profile detect --force
conan remote add upload https://mpusz.jfrog.io/artifactory/api/conan/conan-oss conan remote add artifactory https://mpusz.jfrog.io/artifactory/api/conan/conan-oss
mkdir _lgtm_build_dir && cd _lgtm_build_dir 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 .. -s compiler.cppstd=20 -s compiler.libcxx=$STDLIB -c user.build:all=True -c user.build:skip_docs=True -b missing
conan build ..
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1 uses: github/codeql-action/analyze@v1

View File

@ -35,35 +35,44 @@ on:
- "docs/**" - "docs/**"
- "src/**" - "src/**"
- "example/**" - "example/**"
env:
CC: gcc-10
CXX: g++-10
CMAKE_GENERATOR: Ninja
CONAN_CMAKE_GENERATOR: Ninja
jobs: jobs:
docs: docs:
name: Generate documentation 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: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Cache Conan data - name: Cache Conan data
uses: actions/cache@v2 uses: actions/cache@v3
env: env:
cache-name: cache-conan-data cache-name: cache-conan-data
with: with:
path: ~/.conan/data path: ~/.conan2/p
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/metadata.json') }} key: build-${{ matrix.os }}-$BUILD_TYPE-$COMPILER_TYPE-$COMPILER_VERSION-$STDLIB-docs
restore-keys: | restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}- build-${{ matrix.os }}-$BUILD_TYPE-$COMPILER_TYPE-$COMPILER_VERSION-$STDLIB
${{ runner.os }}-build- build-${{ matrix.os }}-$BUILD_TYPE-$COMPILER_TYPE-$COMPILER_VERSION-
${{ runner.os }}- build-${{ matrix.os }}-$BUILD_TYPE-$COMPILER_TYPE-
build-${{ matrix.os }}-$BUILD_TYPE-
build-${{ matrix.os }}-
- name: Install Ninja - name: Install Ninja
run: | run: |
sudo apt install -y ninja-build sudo apt install -y ninja-build
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v2 uses: actions/setup-python@v4
with: with:
python-version: "3.8" python-version: "3.8"
- name: Install Python dependencies - name: Install Python dependencies
@ -74,17 +83,17 @@ jobs:
pip install -U conan pip install -U conan
- name: Configure Conan - name: Configure Conan
run: | run: |
conan config init conan profile detect --force
conan remote add -i 0 upload https://mpusz.jfrog.io/artifactory/api/conan/conan-oss conan remote add artifactory https://mpusz.jfrog.io/artifactory/api/conan/conan-oss
- name: Install Conan dependencies - name: Install Conan dependencies
run: | 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 - name: Configure CMake
run: | run: |
cmake --preset default cmake --preset conan-default
- name: Generate documentation - name: Generate documentation
run: | run: |
cmake --build --preset release --target documentation cmake --build --preset conan-release --target documentation
- name: Deploy documentation - name: Deploy documentation
if: github.ref == 'refs/heads/master' if: github.ref == 'refs/heads/master'
uses: peaceiris/actions-gh-pages@v3 uses: peaceiris/actions-gh-pages@v3

6
.gitpod.Dockerfile vendored
View File

@ -2,13 +2,13 @@ FROM trainiteu/gitpod-cpp
# Add clang-12 and clang-15 apt repositories # Add clang-12 and clang-15 apt repositories
RUN lsb_rel=`lsb_release -cs` \ 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}-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" && 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 # Install older compilers supported by the project as well as clang-format-15 for code formatting
RUN sudo install-packages \ RUN sudo install-packages \
g++-10 \ g++-10 \
clang-12 \ g++-11 \
clang-13 \ clang-13 \
clang-format-15 clang-14

View File

@ -36,8 +36,7 @@ vscode:
- vivaxy.vscode-conventional-commits - vivaxy.vscode-conventional-commits
- hbenl.vscode-test-explorer - hbenl.vscode-test-explorer
- matepek.vscode-catch2-test-adapter - matepek.vscode-catch2-test-adapter
- trond-snekvik.simple-rst - redhat.vscode-yaml
- lextudio.restructuredtext
- ritwickdey.liveserver - ritwickdey.liveserver
- ms-python.python - ms-python.python
@ -49,24 +48,28 @@ tasks:
mkdir -p "$PWD/.vscode"; mkdir -p "$PWD/.vscode";
cat << 'EOF' > "$PWD/.vscode/settings.json" cat << 'EOF' > "$PWD/.vscode/settings.json"
{ {
"cmake.buildDirectory": "${workspaceFolder}/build/${buildKitVendor}-${buildKitVersionMajor}",
"cmake.configureArgs": [
"--toolchain conan_toolchain.cmake"
],
"cmake.generator": "Ninja Multi-Config", "cmake.generator": "Ninja Multi-Config",
"cmake.configureOnOpen": true, "cmake.configureOnOpen": true,
"clang-format.executable": "/usr/bin/clang-format-15", "clang-format.executable": "/usr/bin/clang-format-15",
"editor.tabSize": 2, "editor.tabSize": 2,
"editor.formatOnPaste": true,
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.formatOnType": true,
"editor.bracketPairColorization.enabled": true, "editor.bracketPairColorization.enabled": true,
"editor.guides.bracketPairs": "active", "editor.guides.bracketPairs": "active",
"restructuredtext.preview.scrollEditorWithPreview": false, "restructuredtext.preview.scrollEditorWithPreview": false,
"restructuredtext.preview.scrollPreviewWithEditor": false, "restructuredtext.preview.scrollPreviewWithEditor": false,
"liveServer.settings.root": "/build/docs/docs/sphinx/", "liveServer.settings.root": "/build/docs/docs/sphinx/",
"esbonio.sphinx.confDir": "${workspaceFolder}/docs", "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 EOF
@ -77,96 +80,103 @@ tasks:
conan config init conan config init
conan profile update settings.compiler.libcxx=libstdc++11 default conan profile update settings.compiler.libcxx=libstdc++11 default
conan profile update settings.compiler.cppstd=20 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 conan remote add -i 0 conan-mpusz https://mpusz.jfrog.io/artifactory/api/conan/conan-oss
pushd /workspace/.conan/profiles pushd /workspace/.conan/profiles
cp default gcc10 cp default gcc10
cp default gcc11 cp default gcc11
cp default clang12 cp default gcc12
cp default clang13 cp default clang13
cp default clang14 cp default clang14
cp default clang15
popd popd
conan profile update settings.compiler.version=10 gcc10 conan profile update settings.compiler.version=10 gcc10
conan profile update env.CXX=/usr/bin/g++-10 gcc10 conan profile update 'conf.tools.build.compiler_executables={"c": "gcc-10", "cpp": "g++-10"}' gcc10
conan profile update env.CC=/usr/bin/gcc-10 gcc10 conan profile update settings.compiler.version=11 gcc11
conan profile update settings.compiler=clang clang12 conan profile update 'conf.tools.build.compiler_executables={"c": "gcc-11", "cpp": "g++-11"}' gcc11
conan profile update settings.compiler.version=12 clang12 conan profile update settings.compiler.version=11 gcc12
conan profile update settings.compiler.libcxx=libstdc++11 clang12 conan profile update 'conf.tools.build.compiler_executables={"c": "gcc-12", "cpp": "g++-12"}' gcc12
conan profile update env.CXX=/usr/bin/clang++-12 clang12
conan profile update env.CC=/usr/bin/clang-12 clang12
conan profile update settings.compiler=clang clang13 conan profile update settings.compiler=clang clang13
conan profile update settings.compiler.version=13 clang13 conan profile update settings.compiler.version=13 clang13
conan profile update settings.compiler.libcxx=libstdc++11 clang13 conan profile update settings.compiler.libcxx=libstdc++11 clang13
conan profile update env.CXX=/usr/bin/clang++-13 clang13 conan profile update 'conf.tools.build.compiler_executables={"c": "clang-13", "cpp": "clang++-13"}' clang13
conan profile update env.CC=/usr/bin/clang-13 clang13
conan profile update settings.compiler=clang clang14 conan profile update settings.compiler=clang clang14
conan profile update settings.compiler.version=14 clang14 conan profile update settings.compiler.version=14 clang14
conan profile update settings.compiler.libcxx=libc++ clang14 conan profile update settings.compiler.libcxx=libstdc++11 clang14
conan profile update env.CXX=/usr/bin/clang++-14 clang14 conan profile update 'conf.tools.build.compiler_executables={"c": "clang-14", "cpp": "clang++-14"}' clang14
conan profile update env.CC=/usr/bin/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 gp sync-done conan-init
exit exit
- name: gcc-10 - name: gcc-10
init: | init: |
gp sync-await conan-init gp sync-await conan-init
mkdir -p build/GCC-10 && cd build/GCC-10 conan install . -pr gcc10 -c user.build:all=True -c user.build:skip_docs=True -b outdated
conan install ../.. -pr gcc10 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated conan install . -pr gcc10 -c user.build:all=True -c user.build:skip_docs=True -b outdated -s build_type=Debug
conan install ../.. -pr gcc10 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated -s build_type=Debug cmake --preset gcc-10
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 --preset gcc-10-release -j
cmake --build . --config Release -j cmake --build --preset gcc-10-debug -j
cmake --build . --config Debug -j
ctest -C Release ctest -C Release
ctest -C Debug 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. 🛠️" 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 - name: gcc-11
init: | init: |
gp sync-await conan-init gp sync-await conan-init
mkdir -p build/GCC-11 && cd build/GCC-11 conan install . -pr gcc11 -c user.build:all=True -c user.build:skip_docs=True -b outdated
conan install ../.. -pr gcc11 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated conan install . -pr gcc11 -c user.build:all=True -c user.build:skip_docs=True -b outdated -s build_type=Debug
gp sync-done conan-gcc-install cmake --preset gcc-11
conan install ../.. -pr gcc11 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated -s build_type=Debug cmake --build --preset gcc-11-release -j
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 --preset gcc-11-debug -j
cmake --build . --config Release -j
cmake --build . --config Debug -j
ctest -C Release ctest -C Release
ctest -C Debug 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. 🛠️" 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: | init: |
gp sync-await conan-init gp sync-await conan-init
mkdir -p build/Clang-12 && cd build/Clang-12 conan install . -pr gcc12 -c user.build:all=True -c user.build:skip_docs=True -b outdated
conan install ../.. -pr clang12 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated conan install . -pr gcc12 -c user.build:all=True -c user.build:skip_docs=True -b outdated -s build_type=Debug
conan install ../.. -pr clang12 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated -s build_type=Debug cmake --preset gcc-12
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 --preset gcc-12-release -j
cmake --build . --config Release -j cmake --build --preset gcc-12-debug -j
cmake --build . --config Debug -j
ctest -C Release ctest -C Release
ctest -C Debug 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 - name: clang-13
init: | init: |
gp sync-await conan-init gp sync-await conan-init
mkdir -p build/Clang-13 && cd build/Clang-13 conan install . -pr clang13 -c user.build:all=True -c user.build:skip_docs=True -b outdated
conan install ../.. -pr clang13 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated conan install . -pr clang13 -c user.build:all=True -c user.build:skip_docs=True -b outdated -s build_type=Debug
conan install ../.. -pr clang13 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated -s build_type=Debug cmake --preset clang-13
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 --preset clang-13-release -j
cmake --build . --config Release -j cmake --build --preset clang-13-debug -j
cmake --build . --config Debug -j
ctest -C Release ctest -C Release
ctest -C Debug 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. 🛠️" 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 - name: clang-14
init: | init: |
gp sync-await conan-init gp sync-await conan-init
mkdir -p build/Clang-14 && cd build/Clang-14 conan install . -pr clang14 -c user.build:all=True -c user.build:skip_docs=True -b outdated
conan install ../.. -pr clang4 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated conan install . -pr clang14 -c user.build:all=True -c user.build:skip_docs=True -b outdated -s build_type=Debug
conan install ../.. -pr clang14 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated -s build_type=Debug cmake --preset clang-14
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 --preset clang-14-release -j
cmake --build . --config Release -j cmake --build --preset clang-14-debug -j
cmake --build . --config Debug -j
ctest -C Release ctest -C Release
ctest -C Debug 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. 🛠️" 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 - name: documentation
init: | init: |
gp sync-await conan-init gp sync-await conan-init

View File

@ -1,15 +1,19 @@
default_stages: [commit] default_stages: [commit]
repos: repos:
- repo: meta
hooks:
- id: check-hooks-apply
- id: check-useless-excludes
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.2.0 rev: v4.3.0
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
- id: end-of-file-fixer - id: end-of-file-fixer
# - repo: https://github.com/pocc/pre-commit-hooks - repo: https://github.com/pre-commit/mirrors-clang-format
# rev: v1.3.5 rev: v16.0.0
# hooks: hooks:
# - id: clang-format - id: clang-format
- repo: https://github.com/cheshirekow/cmake-format-precommit - repo: https://github.com/cheshirekow/cmake-format-precommit
rev: v0.6.13 rev: v0.6.13
hooks: hooks:
@ -19,16 +23,16 @@ repos:
# additional_dependencies: ["cmakelang"] # additional_dependencies: ["cmakelang"]
# exclude: "cmake/.*" # exclude: "cmake/.*"
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: 22.3.0 rev: 22.10.0
hooks: hooks:
- id: black - id: black
language_version: python3 language_version: python3
- repo: https://github.com/PyCQA/isort - repo: https://github.com/PyCQA/isort
rev: 5.10.1 rev: 5.12.0
hooks: hooks:
- id: isort - id: isort
args: [--profile, black, --multi-line, "3"] args: [--profile, black, --multi-line, "3"]
- repo: https://github.com/PyCQA/flake8 - repo: https://github.com/PyCQA/flake8
rev: 4.0.1 rev: 5.0.4
hooks: hooks:
- id: flake8 - id: flake8

View File

@ -27,6 +27,8 @@ authors:
orcid: https://orcid.org/0000-0003-0680-0765 orcid: https://orcid.org/0000-0003-0680-0765
- given-names: "Johel Ernesto" - given-names: "Johel Ernesto"
family-names: "Guerrero Peña" family-names: "Guerrero Peña"
- given-names: "Chip"
family-names: "Hogg"
- name: "The mp-units project team" - name: "The mp-units project team"
contact: contact:
@ -34,9 +36,9 @@ contact:
given-names: Mateusz given-names: Mateusz
family-names: Pusz family-names: Pusz
repository-code: 'https://github.com/mpusz/units' repository-code: "https://github.com/mpusz/units"
url: 'https://mpusz.github.io/units' url: "https://mpusz.github.io/units"
repository-artifact: 'https://conan.io/center/mp-units' repository-artifact: "https://conan.io/center/mp-units"
version: 0.7.0 version: 0.7.0
date-released: "2021-05-11" date-released: "2021-05-11"

View File

@ -12,11 +12,11 @@ below or prefix any `mp-units` URL (main branch, other branches, issues, PRs, ..
The above environment provides you with: 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 Conan dependencies preinstalled on the machine
- all documentation generation tools ready to use - all documentation generation tools ready to use
- completed prebuilds for all targets (each compiler as well as a documentation) - completed prebuilds for all targets (Debug and Release builds for each compiler as well as a documentation)
- `clang-format-15` for source code formatting
- VSCode preconfigured to benefit from all the above - VSCode preconfigured to benefit from all the above
## Download, Build, Install ## 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) 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. 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 ## Naming Conventions
@ -40,18 +46,6 @@ Here are the main rules for naming things in this repo:
- template parameters in a `PascalCase` - template parameters in a `PascalCase`
- C++20 concepts names for now in a `PascalCase` but we plan to change it (see <https://github.com/mpusz/units/issues/93> for more details) - C++20 concepts names for now in a `PascalCase` but we plan to change it (see <https://github.com/mpusz/units/issues/93> 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 ## 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. 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 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). 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.

View File

@ -1,11 +1,15 @@
[![GitHub license](https://img.shields.io/github/license/mpusz/units?cacheSeconds=3600&color=informational&label=License)](./LICENSE.md) [![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) [![GitHub license](https://img.shields.io/badge/C%2B%2B-20-blue)](https://en.cppreference.com/w/cpp/compiler_support#cpp20)
[![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) [![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)
[![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) [![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)
[![Conan stable](https://img.shields.io/badge/ConanCenter-0.7.0%3Astable-blue)](https://conan.io/center/mp-units) [![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) [![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++ # `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 **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). 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 ## TL;DR
`mp-units` is a compile-time enabled Modern C++ library that provides compile-time dimensional `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); 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 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 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)._

View File

@ -24,19 +24,19 @@ import os
import re import re
from conan import ConanFile from conan import ConanFile
from conan.errors import ConanInvalidConfiguration
from conan.tools.build import check_min_cppstd from conan.tools.build import check_min_cppstd
from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout
from conan.tools.files import copy, load, rmdir from conan.tools.files import copy, load, rmdir
from conan.tools.scm import Version 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): class MPUnitsConan(ConanFile):
name = "mp-units" name = "mp-units"
homepage = "https://github.com/mpusz/units" homepage = "https://github.com/mpusz/units"
description = "Physical Units library for C++" description = "Physical Quantities and Units library for C++"
topics = ( topics = (
"units", "units",
"dimensions", "dimensions",
@ -45,15 +45,15 @@ class MPUnitsConan(ConanFile):
"physical-quantities", "physical-quantities",
"physical-units", "physical-units",
"system-of-units", "system-of-units",
"cpp23", "system-of-quantities",
"cpp20", "isq",
"si",
"library", "library",
"quantity-manipulation", "quantity-manipulation",
) )
license = "MIT" license = "MIT"
url = "https://github.com/mpusz/units" url = "https://github.com/mpusz/units"
settings = "os", "compiler", "build_type", "arch" settings = "os", "arch", "compiler", "build_type"
requires = "gsl-lite/0.40.0"
exports = ["LICENSE.md"] exports = ["LICENSE.md"]
exports_sources = [ exports_sources = [
"docs/*", "docs/*",
@ -63,24 +63,34 @@ class MPUnitsConan(ConanFile):
"example/*", "example/*",
"CMakeLists.txt", "CMakeLists.txt",
] ]
package_type = "header-library"
no_copy_source = True 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 @property
def _build_all(self): 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 @property
def _skip_docs(self): def _skip_docs(self):
return bool(self.conf["user.build:skip_docs"]) return bool(self.conf.get("user.build:skip_docs", default=True))
@property @property
def _use_libfmt(self): def _use_libfmt(self):
compiler = self.settings.compiler compiler = self.settings.compiler
version = Version(self.settings.compiler.version) version = Version(self.settings.compiler.version)
std_support = ( std_support = compiler == "msvc" and version >= 193 and compiler.cppstd == 23
compiler == "Visual Studio" and version >= 17 and compiler.cppstd == 23
) or (compiler == "msvc" and version >= 193 and compiler.cppstd == 23)
return not std_support return not std_support
@property @property
@ -89,14 +99,6 @@ class MPUnitsConan(ConanFile):
version = Version(self.settings.compiler.version) version = Version(self.settings.compiler.version)
return "clang" in compiler and compiler.libcxx == "libc++" and version < 14 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): def set_version(self):
content = load(self, os.path.join(self.recipe_folder, "src/CMakeLists.txt")) content = load(self, os.path.join(self.recipe_folder, "src/CMakeLists.txt"))
version = re.search( version = re.search(
@ -105,51 +107,42 @@ class MPUnitsConan(ConanFile):
self.version = version.strip() self.version = version.strip()
def requirements(self): def requirements(self):
self.requires("gsl-lite/0.40.0")
if self._use_libfmt: if self._use_libfmt:
self.requires("fmt/8.1.1") self.requires("fmt/8.1.1")
if self._use_range_v3: if self._use_range_v3:
self.requires("range-v3/0.11.0") self.requires("range-v3/0.11.0")
def build_requirements(self): def build_requirements(self):
if self._build_all: if self._build_all:
self.test_requires("catch2/3.1.0") 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: if not self._skip_docs:
self.tool_requires("doxygen/1.9.4") self.tool_requires("doxygen/1.9.4")
# TODO Replace with `valdate()` for Conan 2.0 (https://github.com/conan-io/conan/issues/10723) def validate(self):
def configure(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 compiler = self.settings.compiler
version = Version(self.settings.compiler.version) min_version = self._minimum_compilers_version.get(str(compiler))
if compiler == "gcc": if min_version and loose_lt_semver(str(compiler.version), min_version):
if version < 10: raise ConanInvalidConfiguration(
raise ConanInvalidConfiguration("mp-units requires at least g++-10") f"{self.ref} requires at least {compiler} {min_version} ({compiler.version} in use)"
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)
def layout(self): def layout(self):
cmake_layout(self) cmake_layout(self)
def generate(self): def generate(self):
tc = CMakeToolchain(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_BUILD_DOCS"] = self._build_all and not self._skip_docs
tc.variables["UNITS_USE_LIBFMT"] = self._use_libfmt tc.variables["UNITS_USE_LIBFMT"] = self._use_libfmt
tc.generate() tc.generate()
@ -182,7 +175,7 @@ class MPUnitsConan(ConanFile):
# core # core
self.cpp_info.components["core"].requires = ["gsl-lite::gsl-lite"] 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"] self.cpp_info.components["core"].cxxflags = ["/utf-8"]
if self._use_range_v3: if self._use_range_v3:
self.cpp_info.components["core"].requires.append("range-v3::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"] self.cpp_info.components["core-fmt"].requires = ["core"]
if self._use_libfmt: if self._use_libfmt:
self.cpp_info.components["core-fmt"].requires.append("fmt::fmt") 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"].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"].requires = ["isq"]
self.cpp_info.components["si-cgs"].requires = ["si"] self.cpp_info.components["cgs"].requires = ["si"]
self.cpp_info.components["si-fps"].requires = ["si-international"] self.cpp_info.components["hep"].requires = ["si"]
self.cpp_info.components["si-hep"].requires = ["si"] self.cpp_info.components["iau"].requires = ["si"]
self.cpp_info.components["si-iau"].requires = ["si"] self.cpp_info.components["imperial"].requires = ["si"]
self.cpp_info.components["si-imperial"].requires = ["si"] self.cpp_info.components["international"].requires = ["si"]
self.cpp_info.components["si-international"].requires = ["si"] self.cpp_info.components["typographic"].requires = ["usc"]
self.cpp_info.components["si-typographic"].requires = ["si"] self.cpp_info.components["usc"].requires = ["international"]
self.cpp_info.components["si-uscs"].requires = ["si"] self.cpp_info.components["iec80000"].requires = ["isq", "si"]
self.cpp_info.components["isq-iec80000"].requires = ["si"]
self.cpp_info.components["systems"].requires = [ self.cpp_info.components["systems"].requires = [
"isq", "isq",
"isq-natural", "angular",
"isq_angular",
"natural",
"si", "si",
"si-cgs", "cgs",
"si-fps", "hep",
"si-hep", "iau",
"si-iau", "imperial",
"si-imperial", "international",
"si-international", "typographic",
"si-typographic", "usc",
"si-uscs", "iec80000",
"isq-iec80000",
] ]

View File

@ -46,6 +46,10 @@ set(unitsSphinxDocs
"${CMAKE_CURRENT_SOURCE_DIR}/_static/img/units.svg" "${CMAKE_CURRENT_SOURCE_DIR}/_static/img/units.svg"
"${CMAKE_CURRENT_SOURCE_DIR}/_static/img/quantity_like.svg" "${CMAKE_CURRENT_SOURCE_DIR}/_static/img/quantity_like.svg"
"${CMAKE_CURRENT_SOURCE_DIR}/CHANGELOG.md" "${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.rst"
"${CMAKE_CURRENT_SOURCE_DIR}/design/directories.rst" "${CMAKE_CURRENT_SOURCE_DIR}/design/directories.rst"
"${CMAKE_CURRENT_SOURCE_DIR}/design/downcasting.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}/examples/kalman_filter/kalman.rst"
"${CMAKE_CURRENT_SOURCE_DIR}/faq.rst" "${CMAKE_CURRENT_SOURCE_DIR}/faq.rst"
"${CMAKE_CURRENT_SOURCE_DIR}/framework.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/arithmetics.rst"
"${CMAKE_CURRENT_SOURCE_DIR}/framework/basic_concepts.rst" "${CMAKE_CURRENT_SOURCE_DIR}/framework/basic_concepts.rst"
"${CMAKE_CURRENT_SOURCE_DIR}/framework/constants.rst" "${CMAKE_CURRENT_SOURCE_DIR}/framework/constants.rst"

View File

@ -0,0 +1,9 @@
Defining Systems
================
.. toctree::
:maxdepth: 2
defining_systems/isq
defining_systems/si
defining_systems/angular_units

View File

@ -59,6 +59,36 @@ Paul Quincey summarizes that with the above in action:
can be measured in terms of the Planck constant. 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 Angular quantities in the library
--------------------------------- ---------------------------------

View File

@ -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<Unit U> struct dim_time : base_dimension<"T", U> {};
template<Unit U> struct dim_length : base_dimension<"L", U> {};
template<Unit U> struct dim_mass : base_dimension<"M", U> {};
template<Unit U> struct dim_electric_current : base_dimension<"I", U> {};
template<Unit U> struct dim_thermodynamic_temperature : base_dimension<"Θ", U> {};
template<Unit U> struct dim_amount_of_substance : base_dimension<"N", U> {};
template<Unit U> 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.

View File

@ -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<second, "s"> {};
struct dim_time : isq::dim_time<second> {};
template<UnitOf<dim_time> U, Representation Rep = double>
using time = quantity<dim_time, U, Rep>;
struct metre : named_unit<metre, "m"> {};
struct dim_length : isq::dim_length<metre> {};
template<UnitOf<dim_length> U, Representation Rep = double>
using length = quantity<dim_length, U, Rep>;
struct gram : named_unit<gram, "g"> {};
struct kilogram : prefixed_unit<kilogram, kilo, gram> {};
struct dim_mass : isq::dim_mass<kilogram> {};
template<UnitOf<dim_mass> U, Representation Rep = double>
using mass = quantity<dim_mass, U, Rep>;
struct ampere : named_unit<ampere, "A"> {};
struct dim_electric_current : isq::dim_electric_current<ampere> {};
template<UnitOf<dim_electric_current> U, Representation Rep = double>
using electric_current = quantity<dim_electric_current, U, Rep>;
struct kelvin : named_unit<kelvin, "K"> {};
struct dim_thermodynamic_temperature : isq::dim_thermodynamic_temperature<kelvin> {};
template<UnitOf<dim_thermodynamic_temperature> U, Representation Rep = double>
using thermodynamic_temperature = quantity<dim_thermodynamic_temperature, U, Rep>;
struct mole : named_unit<mole, "mol"> {};
struct dim_amount_of_substance : isq::dim_amount_of_substance<mole> {};
template<UnitOf<dim_amount_of_substance> U, Representation Rep = double>
using amount_of_substance = quantity<dim_amount_of_substance, U, Rep>;
struct candela : named_unit<candela, "cd"> {};
struct dim_luminous_intensity : isq::dim_luminous_intensity<candela> {};
template<UnitOf<dim_luminous_intensity> U, Representation Rep = double>
using luminous_intensity = quantity<dim_luminous_intensity, U, Rep>;
}
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<yocto, "y", mag_power<10, -24>()> {};
struct zepto : prefix<zepto, "z", mag_power<10, -21>()> {};
struct atto : prefix<atto, "a", mag_power<10, -18>()> {};
struct femto : prefix<femto, "f", mag_power<10, -15>()> {};
struct pico : prefix<pico, "p", mag_power<10, -12>()> {};
struct nano : prefix<nano, "n", mag_power<10, -9>()> {};
struct micro : prefix<micro, basic_symbol_text{"\u00b5", "u"},
mag_power<10, -6>()> {};
struct milli : prefix<milli, "m", mag_power<10, -3>()> {};
struct centi : prefix<centi, "c", mag_power<10, -2>()> {};
struct deci : prefix<deci, "d", mag_power<10, -1>()> {};
struct deca : prefix<deca, "da", mag_power<10, 1>()> {};
struct hecto : prefix<hecto, "h", mag_power<10, 2>()> {};
struct kilo : prefix<kilo, "k", mag_power<10, 3>()> {};
struct mega : prefix<mega, "M", mag_power<10, 6>()> {};
struct giga : prefix<giga, "G", mag_power<10, 9>()> {};
struct tera : prefix<tera, "T", mag_power<10, 12>()> {};
struct peta : prefix<peta, "P", mag_power<10, 15>()> {};
struct exa : prefix<exa, "E", mag_power<10, 18>()> {};
struct zetta : prefix<zetta, "Z", mag_power<10, 21>()> {};
struct yotta : prefix<yotta, "Y", mag_power<10, 24>()> {};
}
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<millisecond, milli, second> {};
}
- :ref:`framework/quantities:Quantity References (Experimental)`::
namespace units::isq::si {
namespace time_references {
inline constexpr auto s = reference<dim_time, second>{};
}
namespace references {
using namespace time_references;
}
- :ref:`framework/quantities:Unit-Specific Aliases (Experimental)`::
namespace units::aliases::isq::si::inline time {
template<Representation Rep = double>
using s = units::isq::si::time<units::isq::si::second, Rep>;
}
- :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<std::int64_t>(l));
return time<second, std::int64_t>(static_cast<std::int64_t>(l));
}
constexpr auto operator"" _q_s(long double l) { return time<second, long double>(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<minute, "min", mag<60>(), second> {};
struct hour : named_scaled_unit<hour, "h", mag<60>(), minute> {};
struct day : named_scaled_unit<day, "d", mag<24>(), 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<Representation Rep = double>
inline constexpr auto hyperfine_structure_transition_frequency = frequency<hertz, Rep>(Rep{9'192'631'770});
template<Representation Rep = double>
inline constexpr auto speed_of_light = speed<metre_per_second, Rep>(299'792'458);
template<Representation Rep = double>
inline constexpr auto planck_constant = energy<joule, Rep>(6.62607015e-34) * time<second, Rep>(1);
template<Representation Rep = double>
inline constexpr auto elementary_charge = electric_charge<coulomb, Rep>(1.602176634e-19);
template<Representation Rep = double>
inline constexpr auto boltzmann_constant = energy<joule, Rep>(1.380649e-23) / thermodynamic_temperature<kelvin, Rep>(1);
template<Representation Rep = double>
inline constexpr auto avogadro_constant = Rep(6.02214076e23) / amount_of_substance<mole, Rep>(1);
template<Representation Rep = double>
inline constexpr auto luminous_efficacy = luminous_flux<lumen, Rep>(683) / power<watt, Rep>(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.

View File

@ -22,5 +22,4 @@ Framework Basics
framework/arithmetics framework/arithmetics
framework/constants framework/constants
framework/conversions_and_casting framework/conversions_and_casting
framework/angular_units
framework/text_output framework/text_output

View File

@ -50,6 +50,20 @@ The quantities of derived dimensions are called
quantities. This means that they are created by multiplying or dividing quantities. This means that they are created by multiplying or dividing
quantities of other dimensions. 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 Looking at the previous code snippets the area, speed, or frequency are
the examples of such quantities. Each derived quantity can be represented the examples of such quantities. Each derived quantity can be represented
as a unique list of exponents of base quantities. For example: as a unique list of exponents of base quantities. For example:

View File

@ -313,7 +313,7 @@ Quantity References vs Unit-specific Aliases
- Unit-specific Aliases - Unit-specific Aliases
As aliases are defined in terms of types rather variables no major shadowing issues were found 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. namespaces prefixed in front of the alias.
2. Adjustable verbosity 2. Adjustable verbosity
@ -385,7 +385,7 @@ Quantity References vs Unit-specific Aliases
- Quantity References - Quantity References
The syntax for references uses ``*`` operator which has some predefined precedence. This operator 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 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:: 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 - 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 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 can be constructed using the provided ones already even if they do not correspond to any predefined
dimension:: dimension::

View File

@ -49,4 +49,4 @@ Unlike `quantity`, the library provides:
- no dimension-specific concepts, such as ``LengthPoint`` - no dimension-specific concepts, such as ``LengthPoint``
(there's the dimension-agnostic `QuantityPoint`), (there's the dimension-agnostic `QuantityPoint`),
- a more limited set of operations on quantity points - 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)

View File

@ -203,27 +203,27 @@ complete list of all the :term:`SI` prefixes supported by the library::
namespace si { namespace si {
struct yocto : prefix<yocto, "y", pow<-24>(mag<10>())> {}; struct yocto : prefix<yocto, "y", mag_power<10, -24>()> {};
struct zepto : prefix<zepto, "z", pow<-21>(mag<10>())> {}; struct zepto : prefix<zepto, "z", mag_power<10, -21>()> {};
struct atto : prefix<atto, "a", pow<-18>(mag<10>())> {}; struct atto : prefix<atto, "a", mag_power<10, -18>()> {};
struct femto : prefix<femto, "f", pow<-15>(mag<10>())> {}; struct femto : prefix<femto, "f", mag_power<10, -15>()> {};
struct pico : prefix<pico, "p", pow<-12>(mag<10>())> {}; struct pico : prefix<pico, "p", mag_power<10, -12>()> {};
struct nano : prefix<nano, "n", pow<-9>(mag<10>())> {}; struct nano : prefix<nano, "n", mag_power<10, -9>()> {};
struct micro : prefix<micro, basic_symbol_text{"\u00b5", "u"}, struct micro : prefix<micro, basic_symbol_text{"\u00b5", "u"},
pow<-6>(mag<10>())> {}; mag_power<10, -6>()> {};
struct milli : prefix<milli, "m", pow<-3>(mag<10>())> {}; struct milli : prefix<milli, "m", mag_power<10, -3>()> {};
struct centi : prefix<centi, "c", pow<-2>(mag<10>())> {}; struct centi : prefix<centi, "c", mag_power<10, -2>()> {};
struct deci : prefix<deci, "d", pow<-1>(mag<10>())> {}; struct deci : prefix<deci, "d", mag_power<10, -1>()> {};
struct deca : prefix<deca, "da", pow<1>(mag<10>())> {}; struct deca : prefix<deca, "da", mag_power<10, 1>()> {};
struct hecto : prefix<hecto, "h", pow<2>(mag<10>())> {}; struct hecto : prefix<hecto, "h", mag_power<10, 2>()> {};
struct kilo : prefix<kilo, "k", pow<3>(mag<10>())> {}; struct kilo : prefix<kilo, "k", mag_power<10, 3>()> {};
struct mega : prefix<mega, "M", pow<6>(mag<10>())> {}; struct mega : prefix<mega, "M", mag_power<10, 6>()> {};
struct giga : prefix<giga, "G", pow<9>(mag<10>())> {}; struct giga : prefix<giga, "G", mag_power<10, 9>()> {};
struct tera : prefix<tera, "T", pow<12>(mag<10>())> {}; struct tera : prefix<tera, "T", mag_power<10, 12>()> {};
struct peta : prefix<peta, "P", pow<15>(mag<10>())> {}; struct peta : prefix<peta, "P", mag_power<10, 15>()> {};
struct exa : prefix<exa, "E", pow<18>(mag<10>())> {}; struct exa : prefix<exa, "E", mag_power<10, 18>()> {};
struct zetta : prefix<zetta, "Z", pow<21>(mag<10>())> {}; struct zetta : prefix<zetta, "Z", mag_power<10, 21>()> {};
struct yotta : prefix<yotta, "Y", pow<24>(mag<10>())> {}; struct yotta : prefix<yotta, "Y", mag_power<10, 24>()> {};
} }
@ -232,14 +232,14 @@ domain::
namespace iec80000 { namespace iec80000 {
struct kibi : prefix<kibi, "Ki", pow<10>(mag<2>())> {}; struct kibi : prefix<kibi, "Ki", mag_power<2, 10>()> {};
struct mebi : prefix<mebi, "Mi", pow<20>(mag<2>())> {}; struct mebi : prefix<mebi, "Mi", mag_power<2, 20>()> {};
struct gibi : prefix<gibi, "Gi", pow<30>(mag<2>())> {}; struct gibi : prefix<gibi, "Gi", mag_power<2, 30>()> {};
struct tebi : prefix<tebi, "Ti", pow<40>(mag<2>())> {}; struct tebi : prefix<tebi, "Ti", mag_power<2, 40>()> {};
struct pebi : prefix<pebi, "Pi", pow<50>(mag<2>())> {}; struct pebi : prefix<pebi, "Pi", mag_power<2, 50>()> {};
struct exbi : prefix<exbi, "Ei", pow<60>(mag<2>())> {}; struct exbi : prefix<exbi, "Ei", mag_power<2, 60>()> {};
struct zebi : prefix<zebi, "Zi", pow<70>(mag<2>())> {}; struct zebi : prefix<zebi, "Zi", mag_power<2, 70>()> {};
struct yobi : prefix<yobi, "Yi", pow<80>(mag<2>())> {}; struct yobi : prefix<yobi, "Yi", mag_power<2, 80>()> {};
} }
@ -260,6 +260,14 @@ example we can define ``si::kilometre`` as::
as ``km²`` would be invalid). 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 Derived Units
------------- -------------
@ -268,10 +276,21 @@ Derived Units
names (i.e. ``N`` (newton)) or can be composed from the names of units of quantities 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``). 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 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`` 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 (pascal)) and they are defined in the same way as base units (which
always have to be a named unit):: always have to be a named unit)::

View File

@ -73,9 +73,9 @@ ISO 80000 [1]_ definitions
tensor character. tensor character.
- In a given `system of quantities`: - In a given `system of quantities`:
- `quantities <quantity>` of the same `kind` have the same quantity dimension, - `quantities <quantity>` of the same `kind` have the same quantity dimension,
- `quantities <quantity>` of different quantity dimensions are always of different `kinds <kind>`, - `quantities <quantity>` of different quantity dimensions are always of different `kinds <kind>`,
- `quantities <quantity>` having the same quantity dimension are not necessarily of the same `kind`. - `quantities <quantity>` having the same quantity dimension are not necessarily of the same `kind`.
quantity of dimension one quantity of dimension one
dimensionless quantity dimensionless quantity
@ -168,7 +168,15 @@ ISO 80000 [1]_ definitions
value of a quantity value of a quantity
value value
- Number and reference together expressing magnitude of a `quantity`. - 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. - 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 Other definitions

View File

@ -30,6 +30,7 @@ with a permissive `MIT license <https://github.com/mpusz/units/blob/master/LICEN
introduction introduction
quick_start quick_start
framework framework
defining_systems
use_cases use_cases
design design
examples examples

View File

@ -32,7 +32,7 @@ Here is a small example of possible operations::
.. admonition:: Try it on Compiler Explorer .. admonition:: Try it on Compiler Explorer
`Example #1 <https://godbolt.org/z/5dvY8Woh1>`_ `Example #1 <https://godbolt.org/z/qbbbnfK3s>`_
This library requires some C++20 features (concepts, classes as This library requires some C++20 features (concepts, classes as
:abbr:`NTTP (Non-Type Template Parameter)`, ...). Thanks to them the user gets a powerful :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 .. admonition:: Try it on Compiler Explorer
`Example #2 <https://godbolt.org/z/bcb87Kvea>`_ `Example #2 <https://godbolt.org/z/b4a3Ya6dY>`_
.. seealso:: .. seealso::

View File

@ -1,4 +1,4 @@
Sotrage Capacity Storage Capacity
================ ================
.. doxygenfile:: iec80000/storage_capacity.h .. doxygenfile:: iec80000/storage_capacity.h

View File

@ -0,0 +1,4 @@
Pressure
========
.. doxygenfile:: si/uscs/pressure.h

View File

@ -5,3 +5,4 @@ U.S. Customary Units (based on SI)
:maxdepth: 2 :maxdepth: 2
si/uscs/length si/uscs/length
si/uscs/pressure

View File

@ -2,4 +2,5 @@ References
========== ==========
.. [ISO80000] `ISO 80000-1:2009(E) "Quantities and units — Part 1: General" <https://www.iso.org/standard/30669.html>`_, International Organization for Standardization. .. [ISO80000] `ISO 80000-1:2009(E) "Quantities and units — Part 1: General" <https://www.iso.org/standard/30669.html>`_, International Organization for Standardization.
.. [Quincey] `"Angles in the SI: a detailed proposal for solving the problem" <https://arxiv.org/pdf/2108.05704.pdf>`_, Quincey, Paul (1 October 2021) .. [SIBrochure] `The International System of Units (SI) <https://www.bipm.org/documents/20126/41483022/SI-Brochure-9-EN.pdf>`_, 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" <https://arxiv.org/pdf/2108.05704.pdf>`_, Quincey, Paul (1 October 2021).

View File

@ -111,7 +111,7 @@ generators. For example:
.. note:: .. 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 `makes working with several compilers or build configurations easier
<https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmake_layout.html#multi-setting-option-cmake-layout>`_. <https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmake_layout.html#multi-setting-option-cmake-layout>`_.
For example the below line will force Conan to generate separate CMake presets and folders for each compiler: 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 Conan Configuration Properties
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
build_all user.build:all
+++++++++ ++++++++++++++
**Values**: ``True``/``False`` **Values**: ``True``/``False``
@ -158,14 +158,24 @@ To support this it requires some additional Conan build dependencies described i
`Repository Structure and Dependencies`_. `Repository Structure and Dependencies`_.
It also runs unit tests during Conan build (unless ``tools.build:skip_test`` configuration property is set to ``True``) 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`` **Values**: ``True``/``False``
**Defaulted to**: ``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 <https://conan.io/center/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. turns on the project documentation generation. Such behavior can be disabled with this option.
CMake Options CMake Options
@ -181,6 +191,16 @@ UNITS_AS_SYSTEM_HEADERS
Exports library 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 UNITS_BUILD_DOCS
++++++++++++++++ ++++++++++++++++
@ -387,8 +407,8 @@ differences:
.. code-block:: shell .. code-block:: shell
conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -b=outdated -u conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -b=outdated -u
cmake --preset default cmake --preset conan-default
cmake --build --preset release cmake --build --preset conan-release
Install Install
@ -403,8 +423,8 @@ to find it, it is enough to perform the following steps:
conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -b=missing conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -b=missing
mv CMakeUserPresets.json src mv CMakeUserPresets.json src
cd src cd src
cmake --preset default -DCMAKE_INSTALL_PREFIX=<your_installation_path> cmake --preset conan-default -DCMAKE_INSTALL_PREFIX=<your_installation_path>
cmake --build --preset release --target install cmake --build --preset conan-release --target install
Contributing (or just building all the tests and examples) 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: you should:
1. Use the *CMakeLists.txt* from the top-level directory. 1. Use the *CMakeLists.txt* from the top-level directory.
2. Run Conan with `build_all`_ = ``True`` 2. Run Conan with `user.build:all`_ = ``True``
(use ``-o build_docs=False`` if you want to skip the documentation generation). (use ``-c user.build:skip_docs=True`` if you want to skip the documentation generation).
.. code-block:: shell .. code-block:: shell
git clone https://github.com/mpusz/units.git && cd units git clone https://github.com/mpusz/units.git && cd units
conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -c user.build:all=True -c user.build:skip_docs=True -b outdated -u conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -c user.build:all=True -c user.build:skip_docs=True -b missing
conan build . conan build .
The above will download and install all of the dependencies needed for the development of the library, 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 .. code-block:: shell
cmake --preset default cmake --preset conan-default
cmake --build --preset release cmake --build --preset conan-release
cmake --build --preset release --target test cmake --build --preset conan-release --target test
Building documentation 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. 1. Use the *CMakeLists.txt* from the top-level directory.
2. Obtain Python dependencies. 2. Obtain Python dependencies.
3. Run Conan with `build_all`_ = ``True``. 3. Run Conan with `user.build:all`_ = ``True``.
.. code-block:: shell .. code-block:: shell
git clone https://github.com/mpusz/units.git && cd units git clone https://github.com/mpusz/units.git && cd units
pip3 install -r docs/requirements.txt pip3 install -r docs/requirements.txt
conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -c user.build:all=True -b missing conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -c user.build:all=True -b missing
cmake --preset default cmake --preset conan-default
cmake --build --preset release --target documentation cmake --build --preset conan-release --target documentation
The above will download and install all of the dependencies needed and build the 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 .. code-block:: shell
conan create . <username>/<channel> -pr <your_conan_profile> -s compiler.cppstd=20 -c user.build:all=True -c user.build:skip_docs=True -b outdated -u conan create . <username>/<channel> -pr <your_conan_profile> -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. The above will create a Conan package and run tests provided in *./test_package* directory.

View File

@ -14,7 +14,7 @@ enough to be used with other Linear Algebra libraries existing on the market.
or `Conan <https://conan.io/center/wg21-linear_algebra>`_. or `Conan <https://conan.io/center/wg21-linear_algebra>`_.
Also, to simplify the examples all of them assume:: Also, to simplify the examples all of them assume::
using namespace std::math; using namespace STD_LA;
Linear Algebra of Quantities 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:: So the most common use case would be to create a vector or matrix of quantities::
fs_vector<si::length<si::metre>, 3> v = { 1 * m, 2 * m, 3 * m }; fixed_size_column_vector<si::length<si::metre>, 3> v = { 1 * m, 2 * m, 3 * m };
fs_vector<si::length<si::metre>, 3> u = { 3 * m, 2 * m, 1 * m }; fixed_size_column_vector<si::length<si::metre>, 3> u = { 3 * m, 2 * m, 1 * m };
fs_vector<si::length<si::kilometre>, 3> t = { 3 * km, 2 * km, 1 * km }; fixed_size_column_vector<si::length<si::kilometre>, 3> t = { 3 * km, 2 * km, 1 * km };
Having such definitions we can perform full dimensional analysis operations for the operations Having such definitions we can perform full dimensional analysis operations for the operations
allowed by the Linear Algebra rules. For example:: allowed by the Linear Algebra rules. For example::
std::cout << "v + u = " << v + u << "\n"; std::cout << "v + u = " << v + u << "\n";
std::cout << "v + t = " << v + t << "\n"; std::cout << "v + t = " << v + t << "\n";
std::cout << "t[m] = " << fs_vector<si::length<si::metre>, 3>(t) << "\n"; std::cout << "t[m] = " << fixed_size_column_vector<si::length<si::metre>, 3>(t) << "\n";
std::cout << "v * u = " << v * u << "\n"; std::cout << "v * u = " << v * u << "\n";
std::cout << "2 * m * v = " << 2 * m * v << "\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:: With this the above vector definitions can be rewritten as follows::
si::length<si::metre, fs_vector<int, 3>> v(fs_vector<int, 3>{ 1, 2, 3 }); si::length<si::metre, fixed_size_column_vector<int, 3>> v(fixed_size_column_vector<int, 3>{ 1, 2, 3 });
si::length<si::metre, fs_vector<int, 3>> u(fs_vector<int, 3>{ 3, 2, 1 }); si::length<si::metre, fixed_size_column_vector<int, 3>> u(fixed_size_column_vector<int, 3>{ 3, 2, 1 });
si::length<si::kilometre, fs_vector<int, 3>> t(fs_vector<int, 3>{ 3, 2, 1 }); si::length<si::kilometre, fixed_size_column_vector<int, 3>> t(fixed_size_column_vector<int, 3>{ 3, 2, 1 });
Now the same code doing basic Linear Algebra operations will provide the following Now the same code doing basic Linear Algebra operations will provide the following
output: output:

View File

@ -1,10 +1,10 @@
Math 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

View File

@ -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(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) 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(glide_computer)
add_subdirectory(kalman_filter) add_subdirectory(kalman_filter)

View File

@ -113,7 +113,7 @@ void calcs_comparison()
const auto L1A = 2.f * fm; const auto L1A = 2.f * fm;
const auto L2A = 3.f * fm; const auto L2A = 3.f * fm;
const auto LrA = L1A + L2A; 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" std::cout << "The single unit method must convert large\n"
"or small values in other units to the base unit.\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 L1B = L1A[m];
const auto L2B = L2A[m]; const auto L2B = L2A[m];
const auto LrB = L1B + L2B; 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"; std::cout << "In multiplication and division:\n\n";
const quantity<isq::area[square<fm>], float> ArA = L1A * L2A; const quantity<isq::area[square<fm>], 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"; std::cout << "similar problems arise\n\n";
const quantity<isq::area[m2], float> ArB = L1B * L2B; const quantity<isq::area[m2], float> 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 } // namespace

View File

@ -48,13 +48,13 @@ int main()
constexpr auto lengthA = 2.0 * m; constexpr auto lengthA = 2.0 * m;
constexpr auto lengthB = lengthA[mm]; 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"; << "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, std::cout << UNITS_STD_FMT::format("conversion factor from lengthA::unit of {:%q} to lengthB::unit of {:%q}:\n\n",
lengthB) lengthA, lengthB)
<< STD_FMT::format("lengthB.number( {} ) == lengthA.number( {} ) * conversion_factor( {} )\n", << UNITS_STD_FMT::format("lengthB.number( {} ) == lengthA.number( {} ) * conversion_factor( {} )\n",
lengthB.number(), lengthA.number(), conversion_factor(lengthB, lengthA)); lengthB.number(), lengthA.number(), conversion_factor(lengthB, lengthA));
} }

View File

@ -52,24 +52,25 @@ struct Ship {
template<Unit auto... Us, Quantity Q> template<Unit auto... Us, Quantity Q>
auto fmt_line(const Q& q) auto fmt_line(const Q& q)
{ {
return STD_FMT::format("{:22}", q) + (STD_FMT::format(",{:20}", value_cast<Us>(q)) + ...); return UNITS_STD_FMT::format("{:22}", q) + (UNITS_STD_FMT::format(",{:20}", value_cast<Us>(q)) + ...);
} }
// Print the ship details in the units as defined in the Ship struct, in other si::imperial units, and in SI // 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) void print_details(std::string_view description, const Ship& ship)
{ {
const auto waterDensity = 62.4 * isq::density[lb / cubic<ft>]; const auto waterDensity = 62.4 * isq::density[lb / cubic<ft>];
std::cout << STD_FMT::format("{}\n", description); std::cout << UNITS_STD_FMT::format("{}\n", description);
std::cout << STD_FMT::format("{:20} : {}\n", "length", fmt_line<yd, m>(ship.length)) std::cout << UNITS_STD_FMT::format("{:20} : {}\n", "length", fmt_line<yd, m>(ship.length))
<< STD_FMT::format("{:20} : {}\n", "draft", fmt_line<yd, m>(ship.draft)) << UNITS_STD_FMT::format("{:20} : {}\n", "draft", fmt_line<yd, m>(ship.draft))
<< STD_FMT::format("{:20} : {}\n", "beam", fmt_line<yd, m>(ship.beam)) << UNITS_STD_FMT::format("{:20} : {}\n", "beam", fmt_line<yd, m>(ship.beam))
<< STD_FMT::format("{:20} : {}\n", "mass", fmt_line<imperial::long_ton, t>(ship.mass)) << UNITS_STD_FMT::format("{:20} : {}\n", "mass", fmt_line<imperial::long_ton, t>(ship.mass))
<< STD_FMT::format("{:20} : {}\n", "speed", fmt_line<kt, km / h>(ship.speed)) << UNITS_STD_FMT::format("{:20} : {}\n", "speed", fmt_line<kt, km / h>(ship.speed))
<< STD_FMT::format("{:20} : {}\n", "power", fmt_line<hp, kW>(ship.power)) << UNITS_STD_FMT::format("{:20} : {}\n", "power", fmt_line<hp, kW>(ship.power))
<< STD_FMT::format("{:20} : {}\n", "main guns", fmt_line<in, mm>(ship.mainGuns)) << UNITS_STD_FMT::format("{:20} : {}\n", "main guns", fmt_line<in, mm>(ship.mainGuns))
<< STD_FMT::format("{:20} : {}\n", "fire shells weighing", fmt_line<imperial::long_ton, kg>(ship.shellMass)) << UNITS_STD_FMT::format("{:20} : {}\n", "fire shells weighing",
<< STD_FMT::format("{:20} : {}\n", "fire shells at", fmt_line<mph, km / h>(ship.shellSpeed)) fmt_line<imperial::long_ton, kg>(ship.shellMass))
<< STD_FMT::format("{:20} : {}\n", "volume underwater", fmt_line<m3, l>(ship.mass / waterDensity)); << UNITS_STD_FMT::format("{:20} : {}\n", "fire shells at", fmt_line<mph, km / h>(ship.shellSpeed))
<< UNITS_STD_FMT::format("{:20} : {}\n", "volume underwater", fmt_line<m3, l>(ship.mass / waterDensity));
} }
int main() int main()

View File

@ -76,7 +76,7 @@ using namespace glide_computer;
void print(std::string_view phase_name, timestamp start_ts, const glide_computer::flight_point& point, void print(std::string_view phase_name, timestamp start_ts, const glide_computer::flight_point& point,
const glide_computer::flight_point& new_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}) " "| {:<12} | {:>9%.1Q %q} (Total: {:>9%.1Q %q}) | {:>8%.1Q %q} (Total: {:>8%.1Q %q}) | {:>7%.0Q %q} ({:>6%.0Q %q}) "
"|\n", "|\n",
phase_name, value_cast<si::minute>(new_point.ts - point.ts), value_cast<si::minute>(new_point.ts - start_ts), phase_name, value_cast<si::minute>(new_point.ts - point.ts), value_cast<si::minute>(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, void estimate(timestamp start_ts, const glider& g, const weather& w, const task& t, const safety& s,
const aircraft_tow& at) const aircraft_tow& at)
{ {
std::cout << STD_FMT::format("| {:<12} | {:^28} | {:^26} | {:^21} |\n", "Flight phase", "Duration", "Distance", std::cout << UNITS_STD_FMT::format("| {:<12} | {:^28} | {:^26} | {:^21} |\n", "Flight phase", "Duration", "Distance",
"Height"); "Height");
std::cout << STD_FMT::format("|{0:-^14}|{0:-^30}|{0:-^28}|{0:-^23}|\n", ""); std::cout << UNITS_STD_FMT::format("|{0:-^14}|{0:-^30}|{0:-^28}|{0:-^23}|\n", "");
// ready to takeoff // ready to takeoff
flight_point pos = takeoff(start_ts, t); flight_point pos = takeoff(start_ts, t);

View File

@ -84,7 +84,7 @@ void print(const R& gliders)
std::cout << "- Polar:\n"; std::cout << "- Polar:\n";
for (const auto& p : g.polar) { for (const auto& p : g.polar) {
const auto ratio = value_cast<one>(glide_ratio(g.polar[0])); const auto ratio = value_cast<one>(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 // TODO is it possible to make ADL work below (we need another set of trig functions
// for strong angle in a different namespace) // for strong angle in a different namespace)
value_cast<si::degree>(isq::asin(1 / ratio))); value_cast<si::degree>(isq::asin(1 / ratio)));
@ -102,8 +102,8 @@ void print(const R& conditions)
for (const auto& c : conditions) { for (const auto& c : conditions) {
std::cout << "- " << c.first << "\n"; std::cout << "- " << c.first << "\n";
const auto& w = c.second; const auto& w = c.second;
std::cout << " * Cloud base: " << STD_FMT::format("{:%.0Q %q}", w.cloud_base) << " AGL\n"; std::cout << " * Cloud base: " << UNITS_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 << " * Thermals strength: " << UNITS_STD_FMT::format("{:%.1Q %q}", w.thermal_strength) << "\n";
std::cout << "\n"; std::cout << "\n";
} }
} }
@ -115,7 +115,7 @@ void print(const R& waypoints)
std::cout << "Waypoints:\n"; std::cout << "Waypoints:\n";
std::cout << "==========\n"; std::cout << "==========\n";
for (const auto& w : waypoints) 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"; std::cout << "\n";
} }
@ -126,12 +126,12 @@ void print(const task& t)
std::cout << "- Start: " << t.get_start().name << "\n"; std::cout << "- Start: " << t.get_start().name << "\n";
std::cout << "- Finish: " << t.get_finish().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: " std::cout << "- Legs: "
<< "\n"; << "\n";
for (const auto& l : t.get_legs()) 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"; std::cout << "\n";
} }
@ -139,7 +139,7 @@ void print(const safety& s)
{ {
std::cout << "Safety:\n"; std::cout << "Safety:\n";
std::cout << "=======\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"; std::cout << "\n";
} }
@ -148,8 +148,8 @@ void print(const aircraft_tow& tow)
std::cout << "Tow:\n"; std::cout << "Tow:\n";
std::cout << "====\n"; std::cout << "====\n";
std::cout << "- Type: aircraft\n"; std::cout << "- Type: aircraft\n";
std::cout << "- Height: " << STD_FMT::format("{:%.0Q %q}", tow.height_agl) << "\n"; std::cout << "- Height: " << UNITS_STD_FMT::format("{:%.0Q %q}", tow.height_agl) << "\n";
std::cout << "- Performance: " << STD_FMT::format("{:%.1Q %q}", tow.performance) << "\n"; std::cout << "- Performance: " << UNITS_STD_FMT::format("{:%.1Q %q}", tow.performance) << "\n";
std::cout << "\n"; std::cout << "\n";
} }
@ -178,7 +178,7 @@ void example()
for (const auto& c : weather_conditions) { for (const auto& c : weather_conditions) {
std::string txt = "Scenario: Glider = " + g.name + ", Weather = " + c.first; std::string txt = "Scenario: Glider = " + g.name + ", Weather = " + c.first;
std::cout << txt << "\n"; 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); estimate(start_time, g, c.second, t, sfty, tow);

View File

@ -47,11 +47,11 @@ int main()
constexpr auto v6 = value_cast<m / s>(v4); constexpr auto v6 = value_cast<m / s>(v4);
constexpr auto v7 = value_cast<int>(v6); constexpr auto v7 = value_cast<int>(v6);
std::cout << v1 << '\n'; // 110 km/h std::cout << v1 << '\n'; // 110 km/h
std::cout << v2 << '\n'; // 70 mi/h std::cout << v2 << '\n'; // 70 mi/h
std::cout << STD_FMT::format("{}", v3) << '\n'; // 110 km/h std::cout << UNITS_STD_FMT::format("{}", v3) << '\n'; // 110 km/h
std::cout << STD_FMT::format("{:*^14}", v4) << '\n'; // ***70 mi/h**** std::cout << UNITS_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 << UNITS_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 << UNITS_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 << UNITS_STD_FMT::format("{:%Q}", v7) << '\n'; // 31
} }

View File

@ -53,12 +53,12 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
} // namespace geographic } // namespace geographic
template<> template<>
struct STD_FMT::formatter<geographic::msl_altitude> : formatter<geographic::msl_altitude::quantity_type> { struct UNITS_STD_FMT::formatter<geographic::msl_altitude> : formatter<geographic::msl_altitude::quantity_type> {
template<typename FormatContext> template<typename FormatContext>
auto format(const geographic::msl_altitude& a, FormatContext& ctx) auto format(const geographic::msl_altitude& a, FormatContext& ctx)
{ {
formatter<geographic::msl_altitude::quantity_type>::format(a.absolute(), ctx); formatter<geographic::msl_altitude::quantity_type>::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<geographic::longitude<T>> : public numeric_limits<T> {
}; };
template<typename T> template<typename T>
struct STD_FMT::formatter<geographic::latitude<T>> : formatter<T> { struct UNITS_STD_FMT::formatter<geographic::latitude<T>> : formatter<T> {
template<typename FormatContext> template<typename FormatContext>
auto format(geographic::latitude<T> lat, FormatContext& ctx) auto format(geographic::latitude<T> lat, FormatContext& ctx)
{ {
STD_FMT::format_to(ctx.out(), "{}", lat > geographic::latitude<T>::zero() ? 'N' : 'S'); UNITS_STD_FMT::format_to(ctx.out(), "{}", lat > geographic::latitude<T>::zero() ? 'N' : 'S');
return formatter<T>::format(lat > geographic::latitude<T>::zero() ? lat.number() : -lat.number(), ctx); return formatter<T>::format(lat > geographic::latitude<T>::zero() ? lat.number() : -lat.number(), ctx);
} }
}; };
template<typename T> template<typename T>
struct STD_FMT::formatter<geographic::longitude<T>> : formatter<T> { struct UNITS_STD_FMT::formatter<geographic::longitude<T>> : formatter<T> {
template<typename FormatContext> template<typename FormatContext>
auto format(geographic::longitude<T> lon, FormatContext& ctx) auto format(geographic::longitude<T> lon, FormatContext& ctx)
{ {
STD_FMT::format_to(ctx.out(), "{}", lon > geographic::longitude<T>::zero() ? 'E' : 'W'); UNITS_STD_FMT::format_to(ctx.out(), "{}", lon > geographic::longitude<T>::zero() ? 'E' : 'W');
return formatter<T>::format(lon > geographic::longitude<T>::zero() ? lon.number() : -lon.number(), ctx); return formatter<T>::format(lon > geographic::longitude<T>::zero() ? lon.number() : -lon.number(), ctx);
} }
}; };

View File

@ -26,21 +26,29 @@
#include <mp-units/bits/external/hacks.h> #include <mp-units/bits/external/hacks.h>
#include <mp-units/customization_points.h> #include <mp-units/customization_points.h>
#include <algorithm> #include <algorithm>
#include <concepts>
#include <type_traits> #include <type_traits>
template<typename T, auto Min, auto Max> template<std::movable T, UNITS_CONSTRAINED_NTTP_WORKAROUND(std::convertible_to<T>) auto Min,
UNITS_CONSTRAINED_NTTP_WORKAROUND(std::convertible_to<T>) auto Max>
inline constexpr auto is_in_range = [](const auto& v) { return std::clamp(v, T{Min}, T{Max}) == v; }; inline constexpr auto is_in_range = [](const auto& v) { return std::clamp(v, T{Min}, T{Max}) == v; };
template<typename T, auto Min, auto Max> template<std::movable T, UNITS_CONSTRAINED_NTTP_WORKAROUND(std::convertible_to<T>) auto Min,
UNITS_CONSTRAINED_NTTP_WORKAROUND(std::convertible_to<T>) auto Max>
using is_in_range_t = decltype(is_in_range<T, Min, Max>); using is_in_range_t = decltype(is_in_range<T, Min, Max>);
template<typename T, auto Min, auto Max> template<std::movable T, UNITS_CONSTRAINED_NTTP_WORKAROUND(std::convertible_to<T>) auto Min,
UNITS_CONSTRAINED_NTTP_WORKAROUND(std::convertible_to<T>) auto Max>
class ranged_representation : public validated_type<T, is_in_range_t<T, Min, Max>> { class ranged_representation : public validated_type<T, is_in_range_t<T, Min, Max>> {
public: public:
using validated_type<T, is_in_range_t<T, Min, Max>>::validated_type; using validated_type<T, is_in_range_t<T, Min, Max>>::validated_type;
constexpr ranged_representation() : validated_type<T, is_in_range_t<T, Min, Max>>(T{}) {} constexpr ranged_representation() : validated_type<T, is_in_range_t<T, Min, Max>>(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<typename T, auto Min, auto Max> template<typename T, auto Min, auto Max>

View File

@ -39,7 +39,7 @@ public:
constexpr explicit validated_type(const T& value) noexcept(std::is_nothrow_copy_constructible_v<T>) constexpr explicit validated_type(const T& value) noexcept(std::is_nothrow_copy_constructible_v<T>)
requires std::copyable<T> requires std::copyable<T>
: value_(value) : value_(value)
{ {
gsl_Expects(validate(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<T>) constexpr validated_type(const T& value, validated_tag) noexcept(std::is_nothrow_copy_constructible_v<T>)
requires std::copyable<T> requires std::copyable<T>
: 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<T>) constexpr explicit(false) operator T() const noexcept(std::is_nothrow_copy_constructible_v<T>)
requires std::copyable<T> requires std::copyable<T>
{ {
return value_; return value_;
} }
#else
constexpr explicit(false) operator T() const& noexcept(std::is_nothrow_copy_constructible_v<T>)
requires std::copyable<T>
{
return value_;
}
constexpr explicit(false) operator T() && noexcept(std::is_nothrow_move_constructible_v<T>)
{
return std::move(value_);
}
#endif
constexpr T& value() & noexcept = delete; constexpr T& value() & noexcept = delete;
constexpr const T& value() const& noexcept { return value_; } constexpr const T& value() const& noexcept { return value_; }
constexpr T&& value() && noexcept { return std::move(value_); } constexpr T&& value() && noexcept { return std::move(value_); }

View File

@ -79,13 +79,9 @@ public:
uncertainty_type uncertainty; uncertainty_type uncertainty;
}; };
#if UNITS_COMP_CLANG <= 14
template<QuantityOrQuantityPoint QQP, mp_units::Quantity U> template<QuantityOrQuantityPoint QQP, mp_units::Quantity U>
estimation(state<QQP>, U) -> estimation<QQP>; estimation(state<QQP>, U) -> estimation<QQP>;
#endif
// kalman gain // kalman gain
template<mp_units::Quantity Q> template<mp_units::Quantity Q>
constexpr mp_units::quantity<mp_units::dimensionless[mp_units::one]> kalman_gain(Q estimate_uncertainty, constexpr mp_units::quantity<mp_units::dimensionless[mp_units::one]> kalman_gain(Q estimate_uncertainty,
@ -159,7 +155,7 @@ constexpr Q covariance_extrapolation(Q uncertainty, Q process_noise_variance)
} // namespace kalman } // namespace kalman
template<typename... Qs> template<typename... Qs>
struct STD_FMT::formatter<kalman::state<Qs...>> { struct UNITS_STD_FMT::formatter<kalman::state<Qs...>> {
constexpr auto parse(format_parse_context& ctx) constexpr auto parse(format_parse_context& ctx)
{ {
mp_units::detail::dynamic_specs_handler handler(specs, ctx); mp_units::detail::dynamic_specs_handler handler(specs, ctx);
@ -173,35 +169,35 @@ struct STD_FMT::formatter<kalman::state<Qs...>> {
auto to_value_buffer = std::back_inserter(value_buffer); auto to_value_buffer = std::back_inserter(value_buffer);
if (specs.precision != -1) { if (specs.precision != -1) {
if constexpr (sizeof...(Qs) == 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) 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), UNITS_STD_FMT::format_to(to_value_buffer, "{{ {1:%.{0}Q %q}, {2:%.{0}Q %q} }}", specs.precision,
kalman::get<1>(s)); kalman::get<0>(s), kalman::get<1>(s));
else else
STD_FMT::format_to(to_value_buffer, "{{ {1:%.{0}Q %q}, {2:%.{0}Q %q}, {3:%.{0}Q %q} }}", specs.precision, 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)); kalman::get<0>(s), kalman::get<1>(s), kalman::get<2>(s));
} else { } else {
if constexpr (sizeof...(Qs) == 1) 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) 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 else
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),
kalman::get<2>(s)); kalman::get<2>(s));
} }
std::string global_format_buffer; std::string global_format_buffer;
mp_units::detail::quantity_global_format_specs<char> global_specs = {specs.fill, specs.align, specs.width}; mp_units::detail::quantity_global_format_specs<char> global_specs = {specs.fill, specs.align, specs.width};
mp_units::detail::format_global_buffer(std::back_inserter(global_format_buffer), global_specs); 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: private:
mp_units::detail::dynamic_format_specs<char> specs; mp_units::detail::dynamic_format_specs<char> specs;
}; };
template<typename Q> template<typename Q>
struct STD_FMT::formatter<kalman::estimation<Q>> { struct UNITS_STD_FMT::formatter<kalman::estimation<Q>> {
constexpr auto parse(format_parse_context& ctx) constexpr auto parse(format_parse_context& ctx)
{ {
mp_units::detail::dynamic_specs_handler handler(specs, ctx); mp_units::detail::dynamic_specs_handler handler(specs, ctx);
@ -221,16 +217,17 @@ struct STD_FMT::formatter<kalman::estimation<Q>> {
std::string value_buffer; std::string value_buffer;
auto to_value_buffer = std::back_inserter(value_buffer); auto to_value_buffer = std::back_inserter(value_buffer);
if (specs.precision != -1) { 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 { } 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; std::string global_format_buffer;
mp_units::detail::quantity_global_format_specs<char> global_specs = {specs.fill, specs.align, specs.width}; mp_units::detail::quantity_global_format_specs<char> global_specs = {specs.fill, specs.align, specs.width};
mp_units::detail::format_global_buffer(std::back_inserter(global_format_buffer), global_specs); 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: private:
mp_units::detail::dynamic_format_specs<char> specs; mp_units::detail::dynamic_format_specs<char> specs;

View File

@ -33,15 +33,15 @@ using namespace mp_units;
void print_header(const kalman::State auto& initial) void print_header(const kalman::State auto& initial)
{ {
std::cout << STD_FMT::format("Initial: {}\n", initial); std::cout << UNITS_STD_FMT::format("Initial: {}\n", initial);
std::cout << STD_FMT::format("{:>2} | {:>9} | {:>8} | {:>14} | {:>14}\n", "N", "Gain", "Measured", "Curr. Estimate", std::cout << UNITS_STD_FMT::format("{:>2} | {:>9} | {:>8} | {:>14} | {:>14}\n", "N", "Gain", "Measured",
"Next Estimate"); "Curr. Estimate", "Next Estimate");
} }
void print(auto iteration, QuantityOf<dimensionless> auto gain, Quantity auto measured, void print(auto iteration, QuantityOf<dimensionless> auto gain, Quantity auto measured,
const kalman::State auto& current, const kalman::State auto& next) 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() int main()

View File

@ -37,13 +37,14 @@ using namespace mp_units;
void print_header(const kalman::State auto& initial) void print_header(const kalman::State auto& initial)
{ {
std::cout << STD_FMT::format("Initial: {}\n", initial); std::cout << UNITS_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("{:>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) 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() int main()

View File

@ -37,13 +37,14 @@ using namespace mp_units;
void print_header(const kalman::State auto& initial) void print_header(const kalman::State auto& initial)
{ {
std::cout << STD_FMT::format("Initial: {}\n", initial); std::cout << UNITS_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("{:>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) 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() int main()

View File

@ -37,13 +37,14 @@ using namespace mp_units;
void print_header(const kalman::State auto& initial) void print_header(const kalman::State auto& initial)
{ {
std::cout << STD_FMT::format("Initial: {}\n", initial); std::cout << UNITS_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("{:>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) 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() int main()

View File

@ -35,16 +35,16 @@ using namespace mp_units;
template<Quantity Q> template<Quantity Q>
void print_header(kalman::estimation<Q> initial) void print_header(kalman::estimation<Q> initial)
{ {
std::cout << STD_FMT::format("Initial: {}\n", initial); std::cout << UNITS_STD_FMT::format("Initial: {}\n", initial);
std::cout << STD_FMT::format("{:>2} | {:>5} | {:>8} | {:>16} | {:>16}\n", "N", "Gain", "Measured", "Curr. Estimate", std::cout << UNITS_STD_FMT::format("{:>2} | {:>5} | {:>8} | {:>16} | {:>16}\n", "N", "Gain", "Measured",
"Next Estimate"); "Curr. Estimate", "Next Estimate");
} }
template<Quantity Q, QuantityOf<dimensionless> K> template<Quantity Q, QuantityOf<dimensionless> K>
void print(auto iteration, K gain, Q measured, kalman::estimation<Q> current, kalman::estimation<Q> next) void print(auto iteration, K gain, Q measured, kalman::estimation<Q> current, kalman::estimation<Q> next)
{ {
std::cout << STD_FMT::format("{:2} | {:5%.2Q} | {:8} | {:>16.2} | {:>16.2}\n", iteration, gain, measured, current, std::cout << UNITS_STD_FMT::format("{:2} | {:5%.2Q} | {:8} | {:>16.2} | {:>16.2}\n", iteration, gain, measured,
next); current, next);
} }
int main() int main()

View File

@ -36,16 +36,16 @@ using namespace mp_units;
template<QuantityPoint QP> template<QuantityPoint QP>
void print_header(kalman::estimation<QP> initial) void print_header(kalman::estimation<QP> initial)
{ {
std::cout << STD_FMT::format("Initial: {}\n", initial); std::cout << UNITS_STD_FMT::format("Initial: {}\n", initial);
std::cout << STD_FMT::format("{:>2} | {:>7} | {:>10} | {:>18} | {:>18}\n", "N", "Gain", "Measured", "Curr. Estimate", std::cout << UNITS_STD_FMT::format("{:>2} | {:>7} | {:>10} | {:>18} | {:>18}\n", "N", "Gain", "Measured",
"Next Estimate"); "Curr. Estimate", "Next Estimate");
} }
template<QuantityPoint QP, QuantityOf<dimensionless> K> template<QuantityPoint QP, QuantityOf<dimensionless> K>
void print(auto iteration, K gain, QP measured, kalman::estimation<QP> current, kalman::estimation<QP> next) void print(auto iteration, K gain, QP measured, kalman::estimation<QP> current, kalman::estimation<QP> next)
{ {
std::cout << STD_FMT::format("{:2} | {:7%.4Q} | {:10%.3Q %q} | {:>18.3} | {:>18.3}\n", iteration, gain, std::cout << UNITS_STD_FMT::format("{:2} | {:7%.4Q} | {:10%.3Q %q} | {:>18.3} | {:>18.3}\n", iteration, gain,
measured.relative(), current, next); measured.relative(), current, next);
} }
int main() int main()

View File

@ -36,16 +36,16 @@ using namespace mp_units;
template<QuantityPoint QP> template<QuantityPoint QP>
void print_header(kalman::estimation<QP> initial) void print_header(kalman::estimation<QP> initial)
{ {
std::cout << STD_FMT::format("Initial: {}\n", initial); std::cout << UNITS_STD_FMT::format("Initial: {}\n", initial);
std::cout << STD_FMT::format("{:>2} | {:>7} | {:>10} | {:>18} | {:>18}\n", "N", "Gain", "Measured", "Curr. Estimate", std::cout << UNITS_STD_FMT::format("{:>2} | {:>7} | {:>10} | {:>18} | {:>18}\n", "N", "Gain", "Measured",
"Next Estimate"); "Curr. Estimate", "Next Estimate");
} }
template<QuantityPoint QP, QuantityOf<dimensionless> K> template<QuantityPoint QP, QuantityOf<dimensionless> K>
void print(auto iteration, K gain, QP measured, kalman::estimation<QP> current, kalman::estimation<QP> next) void print(auto iteration, K gain, QP measured, kalman::estimation<QP> current, kalman::estimation<QP> next)
{ {
std::cout << STD_FMT::format("{:2} | {:7%.4Q} | {:10%.3Q %q} | {:>18.3} | {:>18.3}\n", iteration, gain, std::cout << UNITS_STD_FMT::format("{:2} | {:7%.4Q} | {:10%.3Q %q} | {:>18.3} | {:>18.3}\n", iteration, gain,
measured.relative(), current, next); measured.relative(), current, next);
} }
int main() int main()

View File

@ -36,16 +36,16 @@ using namespace mp_units;
template<QuantityPoint QP> template<QuantityPoint QP>
void print_header(kalman::estimation<QP> initial) void print_header(kalman::estimation<QP> initial)
{ {
std::cout << STD_FMT::format("Initial: {}\n", initial); std::cout << UNITS_STD_FMT::format("Initial: {}\n", initial);
std::cout << STD_FMT::format("{:>2} | {:>7} | {:>10} | {:>16} | {:>16}\n", "N", "Gain", "Measured", "Curr. Estimate", std::cout << UNITS_STD_FMT::format("{:>2} | {:>7} | {:>10} | {:>16} | {:>16}\n", "N", "Gain", "Measured",
"Next Estimate"); "Curr. Estimate", "Next Estimate");
} }
template<QuantityPoint QP, QuantityOf<dimensionless> K> template<QuantityPoint QP, QuantityOf<dimensionless> K>
void print(auto iteration, K gain, QP measured, kalman::estimation<QP> current, kalman::estimation<QP> next) void print(auto iteration, K gain, QP measured, kalman::estimation<QP> current, kalman::estimation<QP> next)
{ {
std::cout << STD_FMT::format("{:2} | {:7%.3Q} | {:10%.3Q %q} | {:>16.2} | {:>16.2}\n", iteration, gain, std::cout << UNITS_STD_FMT::format("{:2} | {:7%.3Q} | {:10%.3Q %q} | {:>16.2} | {:>16.2}\n", iteration, gain,
measured.relative(), current, next); measured.relative(), current, next);
} }
int main() int main()

View File

@ -35,20 +35,20 @@ int main()
using namespace mp_units::si::unit_symbols; 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 << "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,
(1. * si2019::hyperfine_structure_transition_frequency_of_cs)[Hz]); (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]); 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]); 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]); 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]); 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]); 1. * si2019::avogadro_constant, (1. * si2019::avogadro_constant)[1 / mol]);
// TODO uncomment the below when ISQ is done // 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]); // si2019::luminous_efficacy(1.)[lm / W]);
} }

View File

@ -119,10 +119,10 @@ int main()
const auto fill_ratio = fill_level / height; 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 << UNITS_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 << UNITS_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 << UNITS_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 << UNITS_STD_FMT::format("input flow rate = {}\n", input_flow_rate);
std::cout << STD_FMT::format("float rise rate = {}\n", float_rise_rate); std::cout << UNITS_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("tank full E.T.A. at current flow rate = {}\n", fill_time_left[s]);
} }

View File

@ -75,12 +75,12 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
template<QuantityPoint QP> template<QuantityPoint QP>
requires(is_hae(QP::absolute_point_origin)) requires(is_hae(QP::absolute_point_origin))
struct STD_FMT::formatter<QP> : formatter<typename QP::quantity_type> { struct UNITS_STD_FMT::formatter<QP> : formatter<typename QP::quantity_type> {
template<typename FormatContext> template<typename FormatContext>
auto format(const QP& a, FormatContext& ctx) auto format(const QP& a, FormatContext& ctx)
{ {
formatter<typename QP::quantity_type>::format(a.absolute(), ctx); formatter<typename QP::quantity_type>::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<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
} }
template<> template<>
struct STD_FMT::formatter<hal_altitude> : formatter<hal_altitude::quantity_type> { struct UNITS_STD_FMT::formatter<hal_altitude> : formatter<hal_altitude::quantity_type> {
template<typename FormatContext> template<typename FormatContext>
auto format(const hal_altitude& a, FormatContext& ctx) auto format(const hal_altitude& a, FormatContext& ctx)
{ {
formatter<hal_altitude::quantity_type>::format(a.absolute(), ctx); formatter<hal_altitude::quantity_type>::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; unmanned_aerial_vehicle uav;
uav.take_off(msl_altitude{6'000 * ft}); uav.take_off(msl_altitude{6'000 * ft});
uav.current(msl_altitude{10'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}; 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 { struct waypoint {
std::string name; std::string name;
@ -162,6 +162,6 @@ int main()
}; };
waypoint wpt = {"EPPR", {54.24772_N, 18.6745_E}, msl_altitude{16. * ft}}; 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<earth_gravity_model::egm2008_1>(wpt.msl_alt, wpt.pos)); to_hae<earth_gravity_model::egm2008_1>(wpt.msl_alt, wpt.pos));
} }

View File

@ -1,2 +1,3 @@
conan conan
pre-commit pre-commit
identify

View File

@ -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=$<BOOL:${${projectPrefix}USE_LIBFMT}>) target_compile_definitions(mp-units-core-fmt INTERFACE ${projectPrefix}USE_LIBFMT=$<BOOL:${${projectPrefix}USE_LIBFMT}>)
if(${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) target_link_libraries(mp-units-core-fmt INTERFACE fmt::fmt)
endif() endif()

View File

@ -55,7 +55,7 @@ public:
constexpr fill_t& operator=(std::basic_string_view<Char> str) constexpr fill_t& operator=(std::basic_string_view<Char> str)
{ {
auto size = str.size(); 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]; for (size_t i = 0; i < size; ++i) data_[i] = str[i];
size_ = static_cast<unsigned char>(size); size_ = static_cast<unsigned char>(size);
return *this; return *this;
@ -98,11 +98,11 @@ struct width_checker {
{ {
if constexpr (is_integer<T>) { if constexpr (is_integer<T>) {
if constexpr (std::numeric_limits<T>::is_signed) { if constexpr (std::numeric_limits<T>::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<unsigned long long>(value); return static_cast<unsigned long long>(value);
} else { } 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<T>) { if constexpr (is_integer<T>) {
if constexpr (std::numeric_limits<T>::is_signed) { if constexpr (std::numeric_limits<T>::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<unsigned long long>(value); return static_cast<unsigned long long>(value);
} else { } 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<Char> {
[[nodiscard]] constexpr int verify_dynamic_arg_index_in_range(size_t idx) [[nodiscard]] constexpr int verify_dynamic_arg_index_in_range(size_t idx)
{ {
if (idx > static_cast<size_t>(std::numeric_limits<int>::max())) { if (idx > static_cast<size_t>(std::numeric_limits<int>::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<int>(idx); return static_cast<int>(idx);
} }
template<typename CharT> template<typename CharT>
[[nodiscard]] constexpr int on_dynamic_arg(size_t arg_id, STD_FMT::basic_format_parse_context<CharT>& context) [[nodiscard]] constexpr int on_dynamic_arg(size_t arg_id, UNITS_STD_FMT::basic_format_parse_context<CharT>& 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); return verify_dynamic_arg_index_in_range(arg_id);
} }
template<typename CharT> template<typename CharT>
[[nodiscard]] constexpr int on_dynamic_arg(auto_id, STD_FMT::basic_format_parse_context<CharT>& context) [[nodiscard]] constexpr int on_dynamic_arg(auto_id, UNITS_STD_FMT::basic_format_parse_context<CharT>& 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<class Handler, typename FormatContext> template<class Handler, typename FormatContext>
[[nodiscard]] constexpr int get_dynamic_spec(int index, FormatContext& ctx) [[nodiscard]] constexpr int get_dynamic_spec(int index, FormatContext& ctx)
{ {
const unsigned long long value = const unsigned long long value =
STD_FMT::visit_format_arg(Handler{}, ctx.arg(FMT_TO_ARG_ID(static_cast<size_t>(index)))); UNITS_STD_FMT::visit_format_arg(Handler{}, ctx.arg(UNITS_FMT_TO_ARG_ID(static_cast<size_t>(index))));
if (value > static_cast<unsigned long long>(std::numeric_limits<int>::max())) { if (value > static_cast<unsigned long long>(std::numeric_limits<int>::max())) {
throw STD_FMT::format_error("number is too big"); UNITS_THROW(UNITS_STD_FMT::format_error("number is too big"));
} }
return static_cast<int>(value); return static_cast<int>(value);
} }
@ -195,7 +195,7 @@ template<std::input_iterator It, std::sentinel_for<It> S>
++begin; ++begin;
} while (begin != end && '0' <= *begin && *begin <= '9'); } 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; return begin;
} }
@ -222,12 +222,12 @@ template<std::input_iterator It, std::sentinel_for<It> S, typename IDHandler>
else else
++begin; ++begin;
if (begin == end || (*begin != '}' && *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 else
handler(index); handler(index);
return begin; return begin;
} }
throw STD_FMT::format_error("invalid format string"); UNITS_THROW(UNITS_STD_FMT::format_error("invalid format string"));
} }
template<std::input_iterator It, std::sentinel_for<It> S, typename IDHandler> template<std::input_iterator It, std::sentinel_for<It> S, typename IDHandler>
@ -278,11 +278,11 @@ template<std::input_iterator It, std::sentinel_for<It> S, typename Handler>
if (width != -1) if (width != -1)
handler.on_width(width); handler.on_width(width);
else else
throw STD_FMT::format_error("number is too big"); UNITS_THROW(UNITS_STD_FMT::format_error("number is too big"));
} else if (*begin == '{') { } else if (*begin == '{') {
++begin; ++begin;
if (begin != end) begin = parse_arg_id(begin, end, width_adapter{handler}); 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; ++begin;
} }
return begin; return begin;
@ -305,13 +305,13 @@ template<std::input_iterator It, std::sentinel_for<It> S, typename Handler>
if (precision != -1) if (precision != -1)
handler.on_precision(precision); handler.on_precision(precision);
else else
throw STD_FMT::format_error("number is too big"); UNITS_THROW(UNITS_STD_FMT::format_error("number is too big"));
} else if (c == '{') { } else if (c == '{') {
++begin; ++begin;
if (begin != end) begin = parse_arg_id(begin, end, precision_adapter{handler}); 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 { } else {
throw STD_FMT::format_error("missing precision specifier"); UNITS_THROW(UNITS_STD_FMT::format_error("missing precision specifier"));
} }
return begin; return begin;
} }
@ -355,7 +355,7 @@ template<std::input_iterator It, std::sentinel_for<It> S, typename Handler>
if (align != fmt_align::none) { if (align != fmt_align::none) {
if (p != begin) { if (p != begin) {
auto c = *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<std::iter_value_t<It>>(&*begin, static_cast<size_t>(p - begin))); handler.on_fill(std::basic_string_view<std::iter_value_t<It>>(&*begin, static_cast<size_t>(p - begin)));
begin = p + 1; begin = p + 1;
} else } else

View File

@ -41,10 +41,24 @@ UNITS_DIAGNOSTIC_IGNORE_SHADOW
#include <fmt/format.h> #include <fmt/format.h>
UNITS_DIAGNOSTIC_POP UNITS_DIAGNOSTIC_POP
#define STD_FMT fmt #define UNITS_STD_FMT fmt
#define FMT_LOCALE(loc) (loc).template get<std::locale>() #define UNITS_FMT_LOCALE(loc) (loc).template get<std::locale>()
#define FMT_TO_ARG_ID(arg) static_cast<int>(arg) #define UNITS_FMT_TO_ARG_ID(arg) static_cast<int>(arg)
#define FMT_FROM_ARG_ID(arg) static_cast<size_t>(arg) #define UNITS_FMT_FROM_ARG_ID(arg) static_cast<size_t>(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 #else
@ -54,9 +68,10 @@ UNITS_DIAGNOSTIC_POP
#include <format> #include <format>
#define STD_FMT std #define UNITS_STD_FMT std
#define FMT_LOCALE(loc) loc #define UNITS_FMT_LOCALE(loc) loc
#define FMT_TO_ARG_ID(arg) arg #define UNITS_FMT_TO_ARG_ID(arg) arg
#define FMT_FROM_ARG_ID(arg) arg #define UNITS_FMT_FROM_ARG_ID(arg) arg
#define UNITS_THROW(arg) throw arg
#endif #endif

View File

@ -96,7 +96,7 @@ constexpr It parse_units_rep(It begin, S end, Handler&& handler, bool treat_as_f
if (treat_as_floating_point) { if (treat_as_floating_point) {
begin = parse_precision(begin, end, handler); begin = parse_precision(begin, end, handler);
} else } 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; 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); if (begin != ptr) handler.on_text(begin, ptr);
begin = ++ptr; // consume '%' 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++; c = *ptr++;
constexpr auto units_types = std::string_view{"Qq"}; constexpr auto units_types = std::string_view{"Qq"};
const auto new_end = find_first_of(begin, end, units_types.begin(), units_types.end()); 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') { if (*new_end == 'Q') {
handler.on_quantity_value(begin, new_end); // Edit `on_quantity_value` to add rep modifiers handler.on_quantity_value(begin, new_end); // Edit `on_quantity_value` to add rep modifiers
} else { } else {
@ -152,43 +152,43 @@ template<typename CharT, typename Rep, typename OutputIt, typename Locale>
std::basic_string<CharT> buffer; std::basic_string<CharT> buffer;
auto to_buffer = std::back_inserter(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) { switch (rep_specs.sign) {
case fmt_sign::none: case fmt_sign::none:
break; break;
case fmt_sign::plus: case fmt_sign::plus:
STD_FMT::format_to(to_buffer, "+"); UNITS_STD_FMT::format_to(to_buffer, "+");
break; break;
case fmt_sign::minus: case fmt_sign::minus:
STD_FMT::format_to(to_buffer, "-"); UNITS_STD_FMT::format_to(to_buffer, "-");
break; break;
case fmt_sign::space: case fmt_sign::space:
STD_FMT::format_to(to_buffer, " "); UNITS_STD_FMT::format_to(to_buffer, " ");
break; break;
} }
if (rep_specs.alt) { if (rep_specs.alt) {
STD_FMT::format_to(to_buffer, "#"); UNITS_STD_FMT::format_to(to_buffer, "#");
} }
auto type = rep_specs.type; auto type = rep_specs.type;
if (auto precision = rep_specs.precision; precision >= 0) { 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<Rep>) { } else if constexpr (treat_as_floating_point<Rep>) {
STD_FMT::format_to(to_buffer, "{}", type == '\0' ? 'g' : type); UNITS_STD_FMT::format_to(to_buffer, "{}", type == '\0' ? 'g' : type);
} else { } else {
if (type != '\0') { if (type != '\0') {
STD_FMT::format_to(to_buffer, "{}", type); UNITS_STD_FMT::format_to(to_buffer, "{}", type);
} }
} }
if (rep_specs.localized) { 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) { 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 // Creates a global format string
@ -196,25 +196,25 @@ template<typename CharT, typename Rep, typename OutputIt, typename Locale>
template<typename CharT, typename OutputIt> template<typename CharT, typename OutputIt>
OutputIt format_global_buffer(OutputIt out, const quantity_global_format_specs<CharT>& specs) OutputIt format_global_buffer(OutputIt out, const quantity_global_format_specs<CharT>& specs)
{ {
STD_FMT::format_to(out, "{{:"); UNITS_STD_FMT::format_to(out, "{{:");
if (specs.fill.size() != 1 || specs.fill[0] != ' ') { 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) { switch (specs.align) {
case fmt_align::left: case fmt_align::left:
STD_FMT::format_to(out, "<"); UNITS_STD_FMT::format_to(out, "<");
break; break;
case fmt_align::right: case fmt_align::right:
STD_FMT::format_to(out, ">"); UNITS_STD_FMT::format_to(out, ">");
break; break;
case fmt_align::center: case fmt_align::center:
STD_FMT::format_to(out, "^"); UNITS_STD_FMT::format_to(out, "^");
break; break;
default: default:
break; break;
} }
if (specs.width >= 1) STD_FMT::format_to(out, "{}", specs.width); if (specs.width >= 1) UNITS_STD_FMT::format_to(out, "{}", specs.width);
return STD_FMT::format_to(out, "}}"); return UNITS_STD_FMT::format_to(out, "}}");
} }
template<auto Reference, typename Rep, typename Locale, typename CharT, typename OutputIt> template<auto Reference, typename Rep, typename Locale, typename CharT, typename OutputIt>
@ -254,18 +254,18 @@ template<std::input_iterator It, std::sentinel_for<It> S>
{ {
auto it = find_first_of(begin, end, modifiers.begin(), modifiers.end()); 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) 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) + throw UNITS_STD_FMT::format_error("only one of '" + std::string(modifiers) +
"' unit modifiers may be used in the format spec"); "' unit modifiers may be used in the format spec");
return it; return it;
} }
} // namespace mp_units::detail } // namespace mp_units::detail
template<auto Reference, typename Rep, typename CharT> template<auto Reference, typename Rep, typename CharT>
struct STD_FMT::formatter<mp_units::quantity<Reference, Rep>, CharT> { struct UNITS_STD_FMT::formatter<mp_units::quantity<Reference, Rep>, CharT> {
private: private:
using quantity = mp_units::quantity<Reference, Rep>; using quantity = mp_units::quantity<Reference, Rep>;
using iterator = TYPENAME STD_FMT::basic_format_parse_context<CharT>::iterator; using iterator = TYPENAME UNITS_STD_FMT::basic_format_parse_context<CharT>::iterator;
bool quantity_value = false; bool quantity_value = false;
bool quantity_unit = false; bool quantity_unit = false;
@ -274,7 +274,7 @@ private:
struct spec_handler { struct spec_handler {
formatter& f; formatter& f;
STD_FMT::basic_format_parse_context<CharT>& context; UNITS_STD_FMT::basic_format_parse_context<CharT>& context;
constexpr void on_fill(std::basic_string_view<CharT> fill) { f.specs.global.fill = fill; } constexpr void on_fill(std::basic_string_view<CharT> fill) { f.specs.global.fill = fill; }
constexpr void on_align(mp_units::detail::fmt_align align) { f.specs.global.align = align; } 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) { if (valid_rep_types.find(type) != std::string_view::npos) {
f.specs.rep.type = type; f.specs.rep.type = type;
} else { } 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"}; constexpr auto valid_modifiers = std::string_view{"UAoansd"};
for (auto it = begin; it != end; ++it) { for (auto it = begin; it != end; ++it) {
if (valid_modifiers.find(*it) == std::string_view::npos) 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) { 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; f.specs.unit.separator = mp_units::unit_symbol_separator::space;
else { else {
if (f.specs.unit.encoding == mp_units::text_encoding::ascii) 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; f.specs.unit.separator = mp_units::unit_symbol_separator::dot;
} }
} }
@ -359,7 +359,7 @@ private:
} }
}; };
[[nodiscard]] constexpr std::pair<iterator, iterator> do_parse(STD_FMT::basic_format_parse_context<CharT>& ctx) [[nodiscard]] constexpr std::pair<iterator, iterator> do_parse(UNITS_STD_FMT::basic_format_parse_context<CharT>& ctx)
{ {
auto begin = ctx.begin(); auto begin = ctx.begin();
auto end = ctx.end(); auto end = ctx.end();
@ -409,7 +409,7 @@ private:
} }
public: public:
[[nodiscard]] constexpr auto parse(STD_FMT::basic_format_parse_context<CharT>& ctx) [[nodiscard]] constexpr auto parse(UNITS_STD_FMT::basic_format_parse_context<CharT>& ctx)
{ {
auto range = do_parse(ctx); auto range = do_parse(ctx);
if (range.first != range.second) if (range.first != range.second)
@ -446,8 +446,9 @@ public:
mp_units::detail::format_global_buffer<CharT>(std::back_inserter(global_format_buffer), specs.global); mp_units::detail::format_global_buffer<CharT>(std::back_inserter(global_format_buffer), specs.global);
// Format the `quantity buffer` using specs from `global_format_buffer` // Format the `quantity buffer` using specs from `global_format_buffer`
// In the example, equivalent to STD_FMT::format("{:*^10}", "1.2_m") // In the example, equivalent to UNITS_STD_FMT::format("{:*^10}", "1.2_m")
return STD_FMT::vformat_to(ctx.out(), global_format_buffer, STD_FMT::make_format_args(quantity_buffer)); return UNITS_STD_FMT::vformat_to(ctx.out(), global_format_buffer,
UNITS_STD_FMT::make_format_args(quantity_buffer));
} }
} }
}; };

View File

@ -49,12 +49,12 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
{ {
if (os.width()) { if (os.width()) {
// std::setw() applies to the whole quantity output so it has to be first put into std::string // std::setw() applies to the whole quantity output so it has to be first put into std::string
std::basic_ostringstream<CharT, Traits> s; std::basic_ostringstream<CharT, Traits> oss;
s.flags(os.flags()); oss.flags(os.flags());
s.imbue(os.getloc()); oss.imbue(os.getloc());
s.precision(os.precision()); oss.precision(os.precision());
detail::to_stream(s, q); detail::to_stream(oss, q);
return os << s.str(); return os << std::move(oss).str();
} }
detail::to_stream(os, q); detail::to_stream(os, q);

View File

@ -23,7 +23,9 @@
cmake_minimum_required(VERSION 3.19) cmake_minimum_required(VERSION 3.19)
# find dependencies # 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 # check if libc++ is being used
include(CheckLibcxxInUse) include(CheckLibcxxInUse)
@ -81,7 +83,9 @@ target_include_directories(
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
if(${projectPrefix}LIBCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "14") 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) target_link_libraries(mp-units-core INTERFACE range-v3::range-v3)
endif() endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")

View File

@ -96,7 +96,7 @@
#include <compare> #include <compare>
#include <concepts> #include <concepts>
#if UNITS_COMP_MSVC || UNITS_COMP_CLANG #if UNITS_COMP_MSVC || (UNITS_COMP_CLANG && UNITS_COMP_CLANG < 16)
#define TYPENAME typename #define TYPENAME typename
@ -108,11 +108,21 @@
#if UNITS_COMP_MSVC #if UNITS_COMP_MSVC
#define UNITS_MSVC_WORKAROUND(X) #define UNITS_CONSTRAINED_AUTO_WORKAROUND(X)
#else #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 #endif

View File

@ -36,7 +36,6 @@
#include <cstdint> #include <cstdint>
#include <numbers> #include <numbers>
#include <optional> #include <optional>
#include <stdexcept>
namespace mp_units { namespace mp_units {
@ -243,10 +242,10 @@ using widen_t = conditional<std::is_arithmetic_v<T>,
template<typename T> template<typename T>
[[nodiscard]] consteval T int_power(T base, std::integral auto exp) [[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 // As this function should only be called at compile time, the terminations herein function as
// "parameter-compatible static_asserts", and should not result in exceptions at runtime. // "parameter-compatible static_asserts", and should not result in terminations at runtime.
if (exp < 0) { 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) { constexpr auto checked_multiply = [](auto a, auto b) {
@ -254,7 +253,7 @@ template<typename T>
UNITS_DIAGNOSTIC_PUSH UNITS_DIAGNOSTIC_PUSH
UNITS_DIAGNOSTIC_IGNORE_FLOAT_EQUAL UNITS_DIAGNOSTIC_IGNORE_FLOAT_EQUAL
if (result / a != b) { if (result / a != b) {
throw std::overflow_error{"Wraparound detected"}; std::terminate(); // Wraparound detected
} }
UNITS_DIAGNOSTIC_POP UNITS_DIAGNOSTIC_POP
return result; return result;
@ -284,15 +283,15 @@ template<typename T>
// need to write a custom function. // need to write a custom function.
// //
// Note that since this function should only be called at compile time, the point of these // 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); const auto exp = get_exponent(el);
if (exp.den != 1) { 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 (exp.num < 0) {
if constexpr (std::is_integral_v<T>) { if constexpr (std::is_integral_v<T>) {
throw std::invalid_argument{"Cannot represent reciprocal as integer"}; std::terminate(); // Cannot represent reciprocal as integer
} else { } else {
return T{1} / compute_base_power<T>(inverse(el)); return T{1} / compute_base_power<T>(inverse(el));
} }
@ -311,11 +310,11 @@ template<typename To, typename From>
requires(!std::is_integral_v<To> || std::is_integral_v<From>) requires(!std::is_integral_v<To> || std::is_integral_v<From>)
[[nodiscard]] consteval To checked_static_cast(From x) [[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. // to produce compiler errors, because we cannot `static_assert` on function arguments.
if constexpr (std::is_integral_v<To>) { if constexpr (std::is_integral_v<To>) {
if (!std::in_range<To>(x)) { if (!std::in_range<To>(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<Ms...>*);
template<typename T> template<typename T>
inline constexpr bool is_derived_from_specialization_of_magnitude = 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<typename T> template<typename T>
requires is_derived_from_specialization_of_magnitude<T> requires is_derived_from_specialization_of_magnitude<T>

View File

@ -50,7 +50,7 @@ namespace mp_units {
*/ */
template<Magnitude auto M, Unit U> template<Magnitude auto M, Unit U>
struct scaled_unit { 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{}; static constexpr U reference_unit{};
}; };

View File

@ -42,7 +42,7 @@ struct AlmostEqualsMatcher : Catch::Matchers::MatcherGenericBase {
return abs(x - y) <= std::numeric_limits<typename T::rep>::epsilon() * maxXYOne; return abs(x - y) <= std::numeric_limits<typename T::rep>::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: private:
const T& target_; const T& target_;

View File

@ -53,9 +53,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
SECTION("iostream") { CHECK(os.str() == "60 W"); } 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") 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("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("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") 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("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") SECTION("volume")
@ -106,9 +106,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
SECTION("iostream") { CHECK(os.str() == "2 m³"); } 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") 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("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("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") 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("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") SECTION("compressibility")
@ -157,9 +157,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
SECTION("iostream") { CHECK(os.str() == "123 1/Pa"); } 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") 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("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("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") 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("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") SECTION("percents")
@ -210,9 +210,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
SECTION("iostream") { CHECK(os.str() == "15 %"); } 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("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("floating-point representation")
{ {
SECTION("positive value") 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") 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") SECTION("nan")
{ {
CHECK(STD_FMT::format("{:%Q}", std::numeric_limits<double>::quiet_NaN() * isq::length[m]) == "nan"); CHECK(UNITS_STD_FMT::format("{:%Q}", std::numeric_limits<double>::quiet_NaN() * isq::length[m]) == "nan");
} }
SECTION("inf") SECTION("inf")
{ {
CHECK(STD_FMT::format("{:%Q}", std::numeric_limits<double>::infinity() * isq::length[m]) == "inf"); CHECK(UNITS_STD_FMT::format("{:%Q}", std::numeric_limits<double>::infinity() * isq::length[m]) == "inf");
} }
SECTION("-inf") SECTION("-inf")
{ {
CHECK(STD_FMT::format("{:%Q}", -std::numeric_limits<double>::infinity() * isq::length[m]) == "-inf"); CHECK(UNITS_STD_FMT::format("{:%Q}", -std::numeric_limits<double>::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") 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 // TODO enable this when resistance is defined
// CHECK(STD_FMT::format("{:%Uq}", 123 * isq::resistance[kilo<ohm>]) == "kΩ"); // CHECK(UNITS_STD_FMT::format("{:%Uq}", 123 * isq::resistance[kilo<ohm>]) == "kΩ");
CHECK(STD_FMT::format("{:%Uq}", 123 * isq::time[us]) == "µs"); CHECK(UNITS_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::acceleration[m / s2]) == "m/s²");
} }
SECTION("Unicode text output is used by default") SECTION("Unicode text output is used by default")
{ {
CHECK(STD_FMT::format("{:%q}", 123 * isq::speed[km / h]) == "km/h"); CHECK(UNITS_STD_FMT::format("{:%q}", 123 * isq::speed[km / h]) == "km/h");
// CHECK(STD_FMT::format("{:%q}", 123 * isq::resistance[kilo<ohm>]) == "kΩ"); // CHECK(UNITS_STD_FMT::format("{:%q}", 123 * isq::resistance[kilo<ohm>]) == "kΩ");
CHECK(STD_FMT::format("{:%q}", 123 * isq::time[us]) == "µs"); CHECK(UNITS_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::acceleration[m / s2]) == "m/s²");
} }
SECTION("ASCII text output") SECTION("ASCII text output")
{ {
CHECK(STD_FMT::format("{:%Aq}", 123 * isq::speed[km / h]) == "km/h"); CHECK(UNITS_STD_FMT::format("{:%Aq}", 123 * isq::speed[km / h]) == "km/h");
// CHECK(STD_FMT::format("{:%Aq}", 123 * isq::resistance[kilo<ohm>]) == "kohm"); // CHECK(UNITS_STD_FMT::format("{:%Aq}", 123 * isq::resistance[kilo<ohm>]) == "kohm");
CHECK(STD_FMT::format("{:%Aq}", 123 * isq::time[us]) == "us"); CHECK(UNITS_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::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") SECTION("Solidus for only one element in denominator")
{ {
CHECK(STD_FMT::format("{:%oq}", 123 * isq::speed[km / h]) == "km/h"); CHECK(UNITS_STD_FMT::format("{:%oq}", 123 * isq::speed[km / h]) == "km/h");
CHECK(STD_FMT::format("{:%oq}", 123 * isq::acceleration[m / s2]) == "m/s²"); CHECK(UNITS_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::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
} }
SECTION("Solidus for only one element in denominator is used by default") 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(UNITS_STD_FMT::format("{:%q}", 123 * isq::speed[km / h]) == "km/h");
CHECK(STD_FMT::format("{:%q}", 123 * isq::acceleration[m / s2]) == "m/s²"); CHECK(UNITS_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::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
} }
SECTION("Always use solidus") SECTION("Always use solidus")
{ {
CHECK(STD_FMT::format("{:%aq}", 123 * isq::speed[km / h]) == "km/h"); CHECK(UNITS_STD_FMT::format("{:%aq}", 123 * isq::speed[km / h]) == "km/h");
CHECK(STD_FMT::format("{:%aq}", 123 * isq::acceleration[m / s2]) == "m/s²"); CHECK(UNITS_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::pressure[kg / m / s2]) == "kg/(m s²)");
} }
SECTION("Never use solidus") SECTION("Never use solidus")
{ {
CHECK(STD_FMT::format("{:%nq}", 123 * isq::speed[km / h]) == "km h⁻¹"); CHECK(UNITS_STD_FMT::format("{:%nq}", 123 * isq::speed[km / h]) == "km h⁻¹");
CHECK(STD_FMT::format("{:%nq}", 123 * isq::acceleration[m / s2]) == "m s⁻²"); CHECK(UNITS_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::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") SECTION("Space")
{ {
CHECK(STD_FMT::format("{:%sq}", 123 * isq::force[kg * m / s2]) == "kg m/s²"); CHECK(UNITS_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(UNITS_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("{:%asq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m s²)");
} }
SECTION("Space is used by default") SECTION("Space is used by default")
{ {
CHECK(STD_FMT::format("{:%q}", 123 * isq::force[kg * m / s2]) == "kg m/s²"); CHECK(UNITS_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(UNITS_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("{:%aq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m s²)");
} }
SECTION("Dot") SECTION("Dot")
{ {
CHECK(STD_FMT::format("{:%dq}", 123 * isq::force[kg * m / s2]) == "kg⋅m/s²"); CHECK(UNITS_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(UNITS_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("{:%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") SECTION("only the invalid modifier")
{ {
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%xq}", STD_FMT::make_format_args(1 * isq::length[m])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%xq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified")); UNITS_STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified"));
} }
SECTION("invalid modifier in the front") SECTION("invalid modifier in the front")
{ {
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%xUdaq}", STD_FMT::make_format_args(1 * isq::length[m])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%xUdaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified")); UNITS_STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified"));
} }
SECTION("invalid modifier in the end") SECTION("invalid modifier in the end")
{ {
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%Udaxq}", STD_FMT::make_format_args(1 * isq::length[m])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%Udaxq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified")); UNITS_STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified"));
} }
SECTION("invalid modifier in the middle") SECTION("invalid modifier in the middle")
{ {
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%Udxaq}", STD_FMT::make_format_args(1 * isq::length[m])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%Udxaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified")); 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") SECTION("text encoding")
{ {
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%UdaUq}", STD_FMT::make_format_args(1 * isq::length[m])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%UdaUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec")); 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])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dUaUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec")); 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])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dUUaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec")); Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec"));
} }
SECTION("solidus") SECTION("solidus")
{ {
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%aUdaq}", STD_FMT::make_format_args(1 * isq::length[m])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%aUdaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec")); 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])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%daUaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec")); 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])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%daaUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec")); Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec"));
} }
SECTION("separator") SECTION("separator")
{ {
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%dUadq}", STD_FMT::make_format_args(1 * isq::length[m])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dUadq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec")); 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])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dadUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec")); 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])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%addUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec")); 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") SECTION("text encoding")
{ {
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%UdaAq}", STD_FMT::make_format_args(1 * isq::length[m])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%UdaAq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec")); 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])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dAaUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec")); 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])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dAUaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec")); Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec"));
} }
SECTION("solidus") SECTION("solidus")
{ {
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%aUdnq}", STD_FMT::make_format_args(1 * isq::length[m])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%aUdnq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec")); 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])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dnUaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec")); 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])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%daoUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec")); Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec"));
} }
SECTION("separator") SECTION("separator")
{ {
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%dUasq}", STD_FMT::make_format_args(1 * isq::length[m])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dUasq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec")); 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])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%sadUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec")); 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])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%adsUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec")); 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]") 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])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dAaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, UNITS_STD_FMT::format_error,
Catch::Matchers::Message("dot unit separator allowed only for Unicode encoding")); 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]") 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]") 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") SECTION("default format {} on a quantity")
{ {
CHECK(STD_FMT::format("|{:0}|", 123 * isq::length[m]) == "|123 m|"); CHECK(UNITS_STD_FMT::format("|{:0}|", 123 * isq::length[m]) == "|123 m|");
CHECK(STD_FMT::format("|{:10}|", 123 * isq::length[m]) == "| 123 m|"); CHECK(UNITS_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("|{:<10}|", 123 * isq::length[m]) == "|123 m |");
CHECK(STD_FMT::format("|{:>10}|", 123 * isq::length[m]) == "| 123 m|"); CHECK(UNITS_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("|{:^10}|", 123 * isq::length[m]) == "| 123 m |");
CHECK(STD_FMT::format("|{:*<10}|", 123 * isq::length[m]) == "|123 m*****|"); CHECK(UNITS_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("|{:*>10}|", 123 * isq::length[m]) == "|*****123 m|");
CHECK(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") SECTION("full format {:%Q %q} on a quantity")
{ {
CHECK(STD_FMT::format("|{:0%Q%q}|", 123 * isq::length[m]) == "|123m|"); CHECK(UNITS_STD_FMT::format("|{:0%Q%q}|", 123 * isq::length[m]) == "|123m|");
CHECK(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(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(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(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(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(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(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") SECTION("value only format {:%Q} on a quantity")
{ {
CHECK(STD_FMT::format("|{:0%Q}|", 123 * isq::length[m]) == "|123|"); CHECK(UNITS_STD_FMT::format("|{:0%Q}|", 123 * isq::length[m]) == "|123|");
CHECK(STD_FMT::format("|{:10%Q}|", 123 * isq::length[m]) == "| 123|"); CHECK(UNITS_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("|{:<10%Q}|", 123 * isq::length[m]) == "|123 |");
CHECK(STD_FMT::format("|{:>10%Q}|", 123 * isq::length[m]) == "| 123|"); CHECK(UNITS_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("|{:^10%Q}|", 123 * isq::length[m]) == "| 123 |");
CHECK(STD_FMT::format("|{:*<10%Q}|", 123 * isq::length[m]) == "|123*******|"); CHECK(UNITS_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("|{:*>10%Q}|", 123 * isq::length[m]) == "|*******123|");
CHECK(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") SECTION("symbol only format {:%q} on a quantity")
{ {
CHECK(STD_FMT::format("|{:0%q}|", 123 * isq::length[m]) == "|m|"); CHECK(UNITS_STD_FMT::format("|{:0%q}|", 123 * isq::length[m]) == "|m|");
CHECK(STD_FMT::format("|{:10%q}|", 123 * isq::length[m]) == "| m|"); CHECK(UNITS_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("|{:<10%q}|", 123 * isq::length[m]) == "|m |");
CHECK(STD_FMT::format("|{:>10%q}|", 123 * isq::length[m]) == "| m|"); CHECK(UNITS_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("|{:^10%q}|", 123 * isq::length[m]) == "| m |");
CHECK(STD_FMT::format("|{:*<10%q}|", 123 * isq::length[m]) == "|m*********|"); CHECK(UNITS_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("|{:*>10%q}|", 123 * isq::length[m]) == "|*********m|");
CHECK(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") 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(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(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(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}", 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}", nan) == "nanm,+nanm,nanm, nanm");
} }
SECTION("value only format {:%Q} on a quantity") 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(UNITS_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(UNITS_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(UNITS_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}", nan) == "nan,+nan,nan, nan");
} }
} }
@ -586,24 +589,24 @@ TEST_CASE("precision specification", "[text][fmt]")
{ {
SECTION("full format {:%Q %q} on a quantity") SECTION("full format {:%Q %q} on a quantity")
{ {
CHECK(STD_FMT::format("{:%.0Q %q}", 1.2345 * isq::length[m]) == "1 m"); CHECK(UNITS_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(UNITS_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(UNITS_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(UNITS_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(UNITS_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(UNITS_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("{:%.10Q %q}", 1.2345 * isq::length[m]) == "1.2345000000 m");
} }
SECTION("value only format {:%Q} on a quantity") SECTION("value only format {:%Q} on a quantity")
{ {
CHECK(STD_FMT::format("{:%.0Q}", 1.2345 * isq::length[m]) == "1"); CHECK(UNITS_STD_FMT::format("{:%.0Q}", 1.2345 * isq::length[m]) == "1");
CHECK(STD_FMT::format("{:%.1Q}", 1.2345 * isq::length[m]) == "1.2"); CHECK(UNITS_STD_FMT::format("{:%.1Q}", 1.2345 * isq::length[m]) == "1.2");
CHECK(STD_FMT::format("{:%.2Q}", 1.2345 * isq::length[m]) == "1.23"); CHECK(UNITS_STD_FMT::format("{:%.2Q}", 1.2345 * isq::length[m]) == "1.23");
CHECK(STD_FMT::format("{:%.3Q}", 1.2345 * isq::length[m]) == "1.234"); CHECK(UNITS_STD_FMT::format("{:%.3Q}", 1.2345 * isq::length[m]) == "1.234");
CHECK(STD_FMT::format("{:%.4Q}", 1.2345 * isq::length[m]) == "1.2345"); CHECK(UNITS_STD_FMT::format("{:%.4Q}", 1.2345 * isq::length[m]) == "1.2345");
CHECK(STD_FMT::format("{:%.5Q}", 1.2345 * isq::length[m]) == "1.23450"); CHECK(UNITS_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("{:%.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") 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])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%.1Q %q}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, UNITS_STD_FMT::format_error,
Catch::Matchers::Message("precision not allowed for integral quantity representation")); Catch::Matchers::Message("precision not allowed for integral quantity representation"));
} }
SECTION("value only format {:%Q} on a quantity") SECTION("value only format {:%Q} on a quantity")
{ {
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%.1Q}", STD_FMT::make_format_args(1 * isq::length[m])), REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%.1Q}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, UNITS_STD_FMT::format_error,
Catch::Matchers::Message("precision not allowed for integral quantity representation")); 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") SECTION("full format {:%Q %q} on a quantity")
{ {
CHECK(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(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(STD_FMT::format("{:%dQ %q}", 42 * isq::length[m]) == "42 m"); CHECK(UNITS_STD_FMT::format("{:%dQ %q}", 42 * isq::length[m]) == "42 m");
CHECK(STD_FMT::format("{:%oQ %q}", 42 * isq::length[m]) == "52 m"); CHECK(UNITS_STD_FMT::format("{:%oQ %q}", 42 * isq::length[m]) == "52 m");
CHECK(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("{:%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(UNITS_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(UNITS_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(UNITS_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(UNITS_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(UNITS_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(UNITS_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(UNITS_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(UNITS_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(UNITS_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(UNITS_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(UNITS_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("{:%.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(UNITS_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(UNITS_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(UNITS_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("{:%.3GQ %q}", 1.2345678e8 * isq::length[m]) == "1.23E+08 m");
} }
SECTION("value only format {:%Q} on a quantity") SECTION("value only format {:%Q} on a quantity")
{ {
CHECK(STD_FMT::format("{:%bQ}", 42 * isq::length[m]) == "101010"); CHECK(UNITS_STD_FMT::format("{:%bQ}", 42 * isq::length[m]) == "101010");
CHECK(STD_FMT::format("{:%BQ}", 42 * isq::length[m]) == "101010"); CHECK(UNITS_STD_FMT::format("{:%BQ}", 42 * isq::length[m]) == "101010");
CHECK(STD_FMT::format("{:%dQ}", 42 * isq::length[m]) == "42"); CHECK(UNITS_STD_FMT::format("{:%dQ}", 42 * isq::length[m]) == "42");
CHECK(STD_FMT::format("{:%oQ}", 42 * isq::length[m]) == "52"); CHECK(UNITS_STD_FMT::format("{:%oQ}", 42 * isq::length[m]) == "52");
CHECK(STD_FMT::format("{:%xQ}", 42 * isq::length[m]) == "2a"); CHECK(UNITS_STD_FMT::format("{:%xQ}", 42 * isq::length[m]) == "2a");
CHECK(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(UNITS_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(UNITS_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(UNITS_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(UNITS_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(UNITS_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(UNITS_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(UNITS_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(UNITS_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(UNITS_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(UNITS_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(UNITS_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("{:%.3gQ}", 1.2345678e8 * isq::length[m]) == "1.23e+08");
CHECK(STD_FMT::format("{:%GQ}", 1.2345678 * isq::length[m]) == "1.23457"); CHECK(UNITS_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(UNITS_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(UNITS_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("{:%.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") SECTION("full format {:%Q %q} on a quantity")
{ {
CHECK(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(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(STD_FMT::format("{:%#oQ %q}", 42 * isq::length[m]) == "052 m"); CHECK(UNITS_STD_FMT::format("{:%#oQ %q}", 42 * isq::length[m]) == "052 m");
CHECK(STD_FMT::format("{:%#xQ %q}", 42 * isq::length[m]) == "0x2a m"); CHECK(UNITS_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("{:%#XQ %q}", 42 * isq::length[m]) == "0X2A m");
} }
SECTION("value only format {:%Q} on a quantity") SECTION("value only format {:%Q} on a quantity")
{ {
CHECK(STD_FMT::format("{:%#bQ}", 42 * isq::length[m]) == "0b101010"); CHECK(UNITS_STD_FMT::format("{:%#bQ}", 42 * isq::length[m]) == "0b101010");
CHECK(STD_FMT::format("{:%#BQ}", 42 * isq::length[m]) == "0B101010"); CHECK(UNITS_STD_FMT::format("{:%#BQ}", 42 * isq::length[m]) == "0B101010");
CHECK(STD_FMT::format("{:%#oQ}", 42 * isq::length[m]) == "052"); CHECK(UNITS_STD_FMT::format("{:%#oQ}", 42 * isq::length[m]) == "052");
CHECK(STD_FMT::format("{:%#xQ}", 42 * isq::length[m]) == "0x2a"); CHECK(UNITS_STD_FMT::format("{:%#xQ}", 42 * isq::length[m]) == "0x2a");
CHECK(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") 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(UNITS_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(grp3, "{:%LQ %q}", 299792458 * isq::speed[m / s]) == "299'792'458 m/s");
} }
} }

View File

@ -23,28 +23,26 @@
import os import os
from conan import ConanFile 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 from conan.tools.cmake import CMake, cmake_layout
class TestPackageConan(ConanFile): class TestPackageConan(ConanFile):
settings = "os", "compiler", "build_type", "arch" settings = "os", "arch", "compiler", "build_type"
generators = "CMakeDeps", "CMakeToolchain", "VirtualBuildEnv", "VirtualRunEnv" generators = "CMakeDeps", "CMakeToolchain", "VirtualRunEnv"
apply_env = False
test_type = "explicit" # TODO Remove for Conan 2.0
def requirements(self): def requirements(self):
self.requires(self.tested_reference_str) self.requires(self.tested_reference_str)
def layout(self):
cmake_layout(self)
def build(self): def build(self):
cmake = CMake(self) cmake = CMake(self)
cmake.configure() cmake.configure()
cmake.build() cmake.build()
def layout(self):
cmake_layout(self)
def test(self): def test(self):
if not cross_building(self): if can_run(self):
cmd = os.path.join(self.cpp.build.bindirs[0], "test_package") bin_path = os.path.join(self.cpp.build.bindirs[0], "test_package")
self.run(cmd, env="conanrun") self.run(bin_path, env="conanrun")