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:
check:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Check
run: |
pre-commit run --all-files
- uses: actions/checkout@v3
- name: Set up Python 3.8
uses: actions/setup-python@v4
with:
python-version: 3.8
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Check
run: |
pre-commit run --all-files

View File

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

View File

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

View File

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

View File

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

View File

@ -35,35 +35,44 @@ on:
- "docs/**"
- "src/**"
- "example/**"
env:
CC: gcc-10
CXX: g++-10
CMAKE_GENERATOR: Ninja
CONAN_CMAKE_GENERATOR: Ninja
jobs:
docs:
name: Generate documentation
runs-on: ubuntu-20.04
runs-on: ${{ matrix.os }}
env:
CC: gcc-10
CXX: g++-10
CMAKE_GENERATOR: Ninja
CONAN_CMAKE_GENERATOR: Ninja
OS: ubuntu-22.04
BUILD_TYPE: Debug
COMPILER_TYPE: GCC
COMPILER_VERSION: 10
STDLIB: libstdc++11
strategy:
fail-fast: false
matrix:
os: ["ubuntu-latest"]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Cache Conan data
uses: actions/cache@v2
uses: actions/cache@v3
env:
cache-name: cache-conan-data
with:
path: ~/.conan/data
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/metadata.json') }}
path: ~/.conan2/p
key: build-${{ matrix.os }}-$BUILD_TYPE-$COMPILER_TYPE-$COMPILER_VERSION-$STDLIB-docs
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
build-${{ matrix.os }}-$BUILD_TYPE-$COMPILER_TYPE-$COMPILER_VERSION-$STDLIB
build-${{ matrix.os }}-$BUILD_TYPE-$COMPILER_TYPE-$COMPILER_VERSION-
build-${{ matrix.os }}-$BUILD_TYPE-$COMPILER_TYPE-
build-${{ matrix.os }}-$BUILD_TYPE-
build-${{ matrix.os }}-
- name: Install Ninja
run: |
sudo apt install -y ninja-build
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: "3.8"
- name: Install Python dependencies
@ -74,17 +83,17 @@ jobs:
pip install -U conan
- name: Configure Conan
run: |
conan config init
conan remote add -i 0 upload https://mpusz.jfrog.io/artifactory/api/conan/conan-oss
conan profile detect --force
conan remote add artifactory https://mpusz.jfrog.io/artifactory/api/conan/conan-oss
- name: Install Conan dependencies
run: |
conan install . -s compiler.cppstd=20 -s compiler.libcxx=libstdc++11 -c user.build:all=True -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" -b outdated -u
conan install . -s compiler.cppstd=20 -s compiler.libcxx=$STDLIB -c user.build:all=True -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" -b missing
- name: Configure CMake
run: |
cmake --preset default
cmake --preset conan-default
- name: Generate documentation
run: |
cmake --build --preset release --target documentation
cmake --build --preset conan-release --target documentation
- name: Deploy documentation
if: github.ref == 'refs/heads/master'
uses: peaceiris/actions-gh-pages@v3

6
.gitpod.Dockerfile vendored
View File

@ -2,13 +2,13 @@ FROM trainiteu/gitpod-cpp
# Add clang-12 and clang-15 apt repositories
RUN lsb_rel=`lsb_release -cs` \
&& sudo add-apt-repository "deb http://apt.llvm.org/${lsb_rel}/ llvm-toolchain-${lsb_rel}-12 main" \
&& sudo add-apt-repository "deb http://apt.llvm.org/${lsb_rel}/ llvm-toolchain-${lsb_rel}-13 main" \
&& sudo add-apt-repository "deb http://apt.llvm.org/${lsb_rel}/ llvm-toolchain-${lsb_rel}-14 main" \
&& sudo add-apt-repository "deb http://apt.llvm.org/${lsb_rel}/ llvm-toolchain-${lsb_rel} main"
# Install older compilers supported by the project as well as clang-format-15 for code formatting
RUN sudo install-packages \
g++-10 \
clang-12 \
g++-11 \
clang-13 \
clang-format-15
clang-14

View File

@ -36,8 +36,7 @@ vscode:
- vivaxy.vscode-conventional-commits
- hbenl.vscode-test-explorer
- matepek.vscode-catch2-test-adapter
- trond-snekvik.simple-rst
- lextudio.restructuredtext
- redhat.vscode-yaml
- ritwickdey.liveserver
- ms-python.python
@ -49,24 +48,28 @@ tasks:
mkdir -p "$PWD/.vscode";
cat << 'EOF' > "$PWD/.vscode/settings.json"
{
"cmake.buildDirectory": "${workspaceFolder}/build/${buildKitVendor}-${buildKitVersionMajor}",
"cmake.configureArgs": [
"--toolchain conan_toolchain.cmake"
],
"cmake.generator": "Ninja Multi-Config",
"cmake.configureOnOpen": true,
"clang-format.executable": "/usr/bin/clang-format-15",
"editor.tabSize": 2,
"editor.formatOnPaste": true,
"editor.formatOnSave": true,
"editor.formatOnType": true,
"editor.bracketPairColorization.enabled": true,
"editor.guides.bracketPairs": "active",
"restructuredtext.preview.scrollEditorWithPreview": false,
"restructuredtext.preview.scrollPreviewWithEditor": false,
"liveServer.settings.root": "/build/docs/docs/sphinx/",
"esbonio.sphinx.confDir": "${workspaceFolder}/docs",
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools"
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"yaml.schemas": {
"https://squidfunk.github.io/mkdocs-material/schema.json": "mkdocs.yml"
},
"yaml.customTags": [
"!ENV scalar",
"!ENV sequence",
"tag:yaml.org,2002:python/name:materialx.emoji.to_svg",
"tag:yaml.org,2002:python/name:materialx.emoji.twemoji",
"tag:yaml.org,2002:python/name:pymdownx.superfences.fence_code_format"
]
}
EOF
@ -77,96 +80,103 @@ tasks:
conan config init
conan profile update settings.compiler.libcxx=libstdc++11 default
conan profile update settings.compiler.cppstd=20 default
conan profile update conf.tools.cmake.cmaketoolchain:generator=Ninja default
conan remote add -i 0 conan-mpusz https://mpusz.jfrog.io/artifactory/api/conan/conan-oss
pushd /workspace/.conan/profiles
cp default gcc10
cp default gcc11
cp default clang12
cp default gcc12
cp default clang13
cp default clang14
cp default clang15
popd
conan profile update settings.compiler.version=10 gcc10
conan profile update env.CXX=/usr/bin/g++-10 gcc10
conan profile update env.CC=/usr/bin/gcc-10 gcc10
conan profile update settings.compiler=clang clang12
conan profile update settings.compiler.version=12 clang12
conan profile update settings.compiler.libcxx=libstdc++11 clang12
conan profile update env.CXX=/usr/bin/clang++-12 clang12
conan profile update env.CC=/usr/bin/clang-12 clang12
conan profile update 'conf.tools.build.compiler_executables={"c": "gcc-10", "cpp": "g++-10"}' gcc10
conan profile update settings.compiler.version=11 gcc11
conan profile update 'conf.tools.build.compiler_executables={"c": "gcc-11", "cpp": "g++-11"}' gcc11
conan profile update settings.compiler.version=11 gcc12
conan profile update 'conf.tools.build.compiler_executables={"c": "gcc-12", "cpp": "g++-12"}' gcc12
conan profile update settings.compiler=clang clang13
conan profile update settings.compiler.version=13 clang13
conan profile update settings.compiler.libcxx=libstdc++11 clang13
conan profile update env.CXX=/usr/bin/clang++-13 clang13
conan profile update env.CC=/usr/bin/clang-13 clang13
conan profile update 'conf.tools.build.compiler_executables={"c": "clang-13", "cpp": "clang++-13"}' clang13
conan profile update settings.compiler=clang clang14
conan profile update settings.compiler.version=14 clang14
conan profile update settings.compiler.libcxx=libc++ clang14
conan profile update env.CXX=/usr/bin/clang++-14 clang14
conan profile update env.CC=/usr/bin/clang-14 clang14
conan profile update settings.compiler.libcxx=libstdc++11 clang14
conan profile update 'conf.tools.build.compiler_executables={"c": "clang-14", "cpp": "clang++-14"}' clang14
conan profile update settings.compiler=clang clang15
conan profile update settings.compiler.version=15 clang15
conan profile update settings.compiler.libcxx=libc++ clang15
conan profile update 'conf.tools.build.compiler_executables={"c": "clang-15", "cpp": "clang++-15"}' clang15
echo 'tools.cmake.cmaketoolchain:generator=Ninja Multi-Config' > /workspace/.conan/global.conf
echo 'tools.cmake.cmake_layout:build_folder_vars=["settings.compiler", "settings.compiler.version"]' >> /workspace/.conan/global.conf
gp sync-done conan-init
exit
- name: gcc-10
init: |
gp sync-await conan-init
mkdir -p build/GCC-10 && cd build/GCC-10
conan install ../.. -pr gcc10 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated
conan install ../.. -pr gcc10 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated -s build_type=Debug
cmake ../.. --no-warn-unused-cli --toolchain conan_toolchain.cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=TRUE -DCMAKE_C_COMPILER=/usr/bin/gcc-10 -DCMAKE_CXX_COMPILER=/usr/bin/g++-10
cmake --build . --config Release -j
cmake --build . --config Debug -j
conan install . -pr gcc10 -c user.build:all=True -c user.build:skip_docs=True -b outdated
conan install . -pr gcc10 -c user.build:all=True -c user.build:skip_docs=True -b outdated -s build_type=Debug
cmake --preset gcc-10
cmake --build --preset gcc-10-release -j
cmake --build --preset gcc-10-debug -j
ctest -C Release
ctest -C Debug
echo "🛠️ gcc-10 pre-build done! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️"
- name: gcc-11
init: |
gp sync-await conan-init
mkdir -p build/GCC-11 && cd build/GCC-11
conan install ../.. -pr gcc11 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated
gp sync-done conan-gcc-install
conan install ../.. -pr gcc11 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated -s build_type=Debug
cmake ../.. --no-warn-unused-cli --toolchain conan_toolchain.cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=TRUE -DCMAKE_C_COMPILER=/usr/bin/gcc -DCMAKE_CXX_COMPILER=/usr/bin/g++
cmake --build . --config Release -j
cmake --build . --config Debug -j
conan install . -pr gcc11 -c user.build:all=True -c user.build:skip_docs=True -b outdated
conan install . -pr gcc11 -c user.build:all=True -c user.build:skip_docs=True -b outdated -s build_type=Debug
cmake --preset gcc-11
cmake --build --preset gcc-11-release -j
cmake --build --preset gcc-11-debug -j
ctest -C Release
ctest -C Debug
echo "🛠️ gcc-11 pre-build done! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️"
- name: clang-12
- name: gcc-12
init: |
gp sync-await conan-init
mkdir -p build/Clang-12 && cd build/Clang-12
conan install ../.. -pr clang12 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated
conan install ../.. -pr clang12 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated -s build_type=Debug
cmake ../.. --no-warn-unused-cli --toolchain conan_toolchain.cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=TRUE -DCMAKE_C_COMPILER=/usr/bin/clang-12 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-12
cmake --build . --config Release -j
cmake --build . --config Debug -j
conan install . -pr gcc12 -c user.build:all=True -c user.build:skip_docs=True -b outdated
conan install . -pr gcc12 -c user.build:all=True -c user.build:skip_docs=True -b outdated -s build_type=Debug
cmake --preset gcc-12
cmake --build --preset gcc-12-release -j
cmake --build --preset gcc-12-debug -j
ctest -C Release
ctest -C Debug
echo "🛠️ clang-12 pre-build done! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️"
echo "🛠️ gcc-12 pre-build done! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️"
- name: clang-13
init: |
gp sync-await conan-init
mkdir -p build/Clang-13 && cd build/Clang-13
conan install ../.. -pr clang13 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated
conan install ../.. -pr clang13 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated -s build_type=Debug
cmake ../.. --no-warn-unused-cli --toolchain conan_toolchain.cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=TRUE -DCMAKE_C_COMPILER=/usr/bin/clang-13 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-13
cmake --build . --config Release -j
cmake --build . --config Debug -j
conan install . -pr clang13 -c user.build:all=True -c user.build:skip_docs=True -b outdated
conan install . -pr clang13 -c user.build:all=True -c user.build:skip_docs=True -b outdated -s build_type=Debug
cmake --preset clang-13
cmake --build --preset clang-13-release -j
cmake --build --preset clang-13-debug -j
ctest -C Release
ctest -C Debug
echo "🛠️ clang-13 pre-build done! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️"
- name: clang-14
init: |
gp sync-await conan-init
mkdir -p build/Clang-14 && cd build/Clang-14
conan install ../.. -pr clang4 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated
conan install ../.. -pr clang14 -e mp-units:CONAN_RUN_TESTS=True -o build_docs=False -b outdated -s build_type=Debug
cmake ../.. --no-warn-unused-cli --toolchain conan_toolchain.cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=TRUE -DCMAKE_C_COMPILER=/usr/bin/clang-14 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-14
cmake --build . --config Release -j
cmake --build . --config Debug -j
conan install . -pr clang14 -c user.build:all=True -c user.build:skip_docs=True -b outdated
conan install . -pr clang14 -c user.build:all=True -c user.build:skip_docs=True -b outdated -s build_type=Debug
cmake --preset clang-14
cmake --build --preset clang-14-release -j
cmake --build --preset clang-14-debug -j
ctest -C Release
ctest -C Debug
echo "🛠️ clang-14 pre-build done! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️"
- name: clang-15
init: |
gp sync-await conan-init
conan install . -pr clang15 -c user.build:all=True -c user.build:skip_docs=True -b outdated
conan install . -pr clang15 -c user.build:all=True -c user.build:skip_docs=True -b outdated -s build_type=Debug
cmake --preset clang-15
cmake --build --preset clang-15-release -j
cmake --build --preset clang-15-debug -j
ctest -C Release
ctest -C Debug
echo "🛠️ clang-15 pre-build done! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️"
- name: documentation
init: |
gp sync-await conan-init

View File

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

View File

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

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:
- all supported compilers for Linux development (`g++-10`, `g++-11`, `clang-12`, `clang-13`) and build tools like `cmake` and `conan`
- all supported compilers for Linux development (`g++-10`, `g++-11`, `g++12`, `clang-13`, `clang-14`, and `clang-15`)
and the latest version of build tools like `cmake` and `conan`
- all Conan dependencies preinstalled on the machine
- all documentation generation tools ready to use
- completed prebuilds for all targets (each compiler as well as a documentation)
- `clang-format-15` for source code formatting
- completed prebuilds for all targets (Debug and Release builds for each compiler as well as a documentation)
- VSCode preconfigured to benefit from all the above
## Download, Build, Install
@ -24,13 +24,19 @@ The above environment provides you with:
Alternatively, please refer to our official docs for [download, build, and install instructions](https://mpusz.github.io/units/usage.html)
if you want to setup a development environment on your local machine.
## Where to start?
## Code Formatting
If you are looking for a good issue to start with, please check the following:
There is a formatting standard enforced with the `pre-commit` script. Before committing your changes please do the following:
```bash
pip3 install -U pre-commit
pre-commit run --all-files
```
This will run `clang-format` for code formatting with the `.clang-format` file provided in the repo, `cmake-format` to format the CMake files, and some other check as well.
The script will run on all the files in the repo and will apply the changes in-place when needed.
After the script is done please make sure to stage all those changes to git commit.
- [good first issue](https://github.com/mpusz/units/labels/good%20first%20issue) - issues that should be pretty simple to implement.
- [help wanted](https://github.com/mpusz/units/labels/help%20wanted) - issues that typically are a bit more involved than beginner issues.
- [high priority](https://github.com/mpusz/units/labels/high%20priority) - things to fix ASAP but often of higher complexity.
## Naming Conventions
@ -40,18 +46,6 @@ Here are the main rules for naming things in this repo:
- template parameters in a `PascalCase`
- C++20 concepts names for now in a `PascalCase` but we plan to change it (see <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
@ -59,3 +53,12 @@ Before submission, please remember to check if the code compiles fine on the sup
The CI will check it anyway but it is good to check at least some of the configurations before pushing changes.
Especially older compilers can be tricky as those do not support all the C++20 features well enough. The official
list of supported compilers can be always found on our [Usage page](https://mpusz.github.io/units/usage.html).
## Where to start?
If you are looking for a good issue to start with, please check the following:
- [good first issue](https://github.com/mpusz/units/labels/good%20first%20issue) - issues that should be pretty simple to implement.
- [help wanted](https://github.com/mpusz/units/labels/help%20wanted) - issues that typically are a bit more involved than beginner issues.
- [high priority](https://github.com/mpusz/units/labels/high%20priority) - things to fix ASAP but often of higher complexity.

View File

@ -1,11 +1,15 @@
[![GitHub license](https://img.shields.io/github/license/mpusz/units?cacheSeconds=3600&color=informational&label=License)](./LICENSE.md)
[![Conan CI](https://img.shields.io/github/workflow/status/mpusz/units/Conan%20CI/master?label=Conan%20CI)](https://github.com/mpusz/units/actions?query=workflow%3A%22Conan%20CI%22+branch%3Amaster)
[![CMake CI](https://img.shields.io/github/workflow/status/mpusz/units/CMake%20Test%20Package%20CI/master?label=CMake%20CI)](https://github.com/mpusz/units/actions?query=workflow%3A%22CMake+Test+Package+CI%22+branch%3Amaster)
[![Check CI](https://img.shields.io/github/workflow/status/mpusz/units/Check%20CI/master?label=Check%20CI)](https://github.com/mpusz/units/actions?query=workflow%3A%22Check%20CI%22+branch%3Amaster)
[![GitHub Workflow Documentation](https://img.shields.io/github/workflow/status/mpusz/units/Documentation/master?label=Documentation%20CI)](https://github.com/mpusz/units/actions?query=workflow%3ADocumentation+branch%3Amaster)
[![Conan stable](https://img.shields.io/badge/ConanCenter-0.7.0%3Astable-blue)](https://conan.io/center/mp-units)
[![GitHub license](https://img.shields.io/badge/C%2B%2B-20-blue)](https://en.cppreference.com/w/cpp/compiler_support#cpp20)
[![Conan CI](https://img.shields.io/github/actions/workflow/status/mpusz/units/ci-conan.yml?branch=master&label=Conan%20CI)](https://github.com/mpusz/units/actions?query=workflow%3A%22Conan%20CI%22+branch%3Amaster)
[![CMake CI](https://img.shields.io/github/actions/workflow/status/mpusz/units/ci-test-package-cmake.yml?branch=master&label=CMake%20CI)](https://github.com/mpusz/units/actions?query=workflow%3A%22CMake+Test+Package+CI%22+branch%3Amaster)
[![Check CI](https://img.shields.io/github/actions/workflow/status/mpusz/units/ci-check.yml?branch=master&label=Check%20CI)](https://github.com/mpusz/units/actions?query=workflow%3A%22Check%20CI%22+branch%3Amaster)
[![GitHub Workflow Documentation](https://img.shields.io/github/actions/workflow/status/mpusz/units/documentation.yml?branch=master&label=Documentation)](https://github.com/mpusz/units/actions?query=workflow%3ADocumentation+branch%3Amaster)
[![Conan stable](https://img.shields.io/conan/v/mp-units?label=ConanCenter&color=blue)](https://conan.io/center/mp-units)
[![Conan testing](https://img.shields.io/badge/mpusz.jfrog.io-0.8.0%3Atesting-blue)](https://mpusz.jfrog.io/ui/packages/conan:%2F%2Fmp-units/0.8.0)
# `mp-units` - A Units Library for C++
**The mp-units library is the subject of ISO standardization for C++23/26. More on this can
@ -22,6 +26,15 @@ An extensive project documentation including installation instructions and user'
guide can be found on [mp-units GitHub Pages](https://mpusz.github.io/units).
## Terms and Definitions
This project uses the official metrology vocabulary defined by the ISO and BIPM.
Please familiarize yourself with those terms to better understand the documentation
and improve domain-related communication and discussions. You can find essential
project-related definitions in [our documentation's "Glossary" chapter](https://mpusz.github.io/units/glossary.html).
Even more terms are provided in the official vocabulary of the [ISO](https://www.iso.org/obp/ui#iso:std:iso-iec:guide:99:ed-1:v2:en)
and [BIPM](https://jcgm.bipm.org/vim/en).
## TL;DR
`mp-units` is a compile-time enabled Modern C++ library that provides compile-time dimensional
@ -59,7 +72,7 @@ static_assert(10 * km / (5 * km) == 2);
static_assert(1000 / (1 * s) == 1 * kHz);
```
_Try it on the [Compiler Explorer](https://godbolt.org/z/5dvY8Woh1)._
_Try it on the [Compiler Explorer](https://godbolt.org/z/qbbbnfK3s)._
This library requires some C++20 features (concepts, classes as NTTPs, ...). Thanks to
them the user gets a powerful but still easy to use interface and all unit conversions
@ -107,4 +120,4 @@ int main()
}
```
_Try it on the [Compiler Explorer](https://godbolt.org/z/bcb87Kvea)._
_Try it on the [Compiler Explorer](https://godbolt.org/z/b4a3Ya6dY)._

View File

@ -24,19 +24,19 @@ import os
import re
from conan import ConanFile
from conan.errors import ConanInvalidConfiguration
from conan.tools.build import check_min_cppstd
from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout
from conan.tools.files import copy, load, rmdir
from conan.tools.scm import Version
from conans.errors import ConanInvalidConfiguration
required_conan_version = ">=1.50.0"
required_conan_version = ">=2.0.0"
class MPUnitsConan(ConanFile):
name = "mp-units"
homepage = "https://github.com/mpusz/units"
description = "Physical Units library for C++"
description = "Physical Quantities and Units library for C++"
topics = (
"units",
"dimensions",
@ -45,15 +45,15 @@ class MPUnitsConan(ConanFile):
"physical-quantities",
"physical-units",
"system-of-units",
"cpp23",
"cpp20",
"system-of-quantities",
"isq",
"si",
"library",
"quantity-manipulation",
)
license = "MIT"
url = "https://github.com/mpusz/units"
settings = "os", "compiler", "build_type", "arch"
requires = "gsl-lite/0.40.0"
settings = "os", "arch", "compiler", "build_type"
exports = ["LICENSE.md"]
exports_sources = [
"docs/*",
@ -63,24 +63,34 @@ class MPUnitsConan(ConanFile):
"example/*",
"CMakeLists.txt",
]
package_type = "header-library"
no_copy_source = True
generators = "cmake_paths"
@property
def _min_cppstd(self):
return "20"
@property
def _minimum_compilers_version(self):
return {"gcc": "10.3", "clang": "12", "apple-clang": "13", "msvc": "192"}
@property
def _build_all(self):
return bool(self.conf["user.build:all"])
return bool(self.conf.get("user.build:all", default=False))
@property
def _skip_la(self):
return bool(self.conf.get("user.build:skip_la", default=False))
@property
def _skip_docs(self):
return bool(self.conf["user.build:skip_docs"])
return bool(self.conf.get("user.build:skip_docs", default=True))
@property
def _use_libfmt(self):
compiler = self.settings.compiler
version = Version(self.settings.compiler.version)
std_support = (
compiler == "Visual Studio" and version >= 17 and compiler.cppstd == 23
) or (compiler == "msvc" and version >= 193 and compiler.cppstd == 23)
std_support = compiler == "msvc" and version >= 193 and compiler.cppstd == 23
return not std_support
@property
@ -89,14 +99,6 @@ class MPUnitsConan(ConanFile):
version = Version(self.settings.compiler.version)
return "clang" in compiler and compiler.libcxx == "libc++" and version < 14
@property
def _msvc_version(self):
compiler = self.settings.compiler
if compiler.update:
return int(f"{compiler.version}{compiler.update}")
else:
return int(f"{compiler.version}0")
def set_version(self):
content = load(self, os.path.join(self.recipe_folder, "src/CMakeLists.txt"))
version = re.search(
@ -105,51 +107,42 @@ class MPUnitsConan(ConanFile):
self.version = version.strip()
def requirements(self):
self.requires("gsl-lite/0.40.0")
if self._use_libfmt:
self.requires("fmt/8.1.1")
if self._use_range_v3:
self.requires("range-v3/0.11.0")
def build_requirements(self):
if self._build_all:
self.test_requires("catch2/3.1.0")
self.test_requires("wg21-linear_algebra/0.7.2")
if not self._skip_la:
self.test_requires("wg21-linear_algebra/0.7.3")
if not self._skip_docs:
self.tool_requires("doxygen/1.9.4")
# TODO Replace with `valdate()` for Conan 2.0 (https://github.com/conan-io/conan/issues/10723)
def configure(self):
def validate(self):
check_min_cppstd(self, self._min_cppstd)
def loose_lt_semver(v1, v2):
lv1 = [int(v) for v in v1.split(".")]
lv2 = [int(v) for v in v2.split(".")]
min_length = min(len(lv1), len(lv2))
return lv1[:min_length] < lv2[:min_length]
compiler = self.settings.compiler
version = Version(self.settings.compiler.version)
if compiler == "gcc":
if version < 10:
raise ConanInvalidConfiguration("mp-units requires at least g++-10")
elif compiler == "clang":
if version < 12:
raise ConanInvalidConfiguration("mp-units requires at least clang++-12")
elif compiler == "apple-clang":
if version < 13:
raise ConanInvalidConfiguration(
"mp-units requires at least AppleClang 13"
)
elif compiler == "Visual Studio":
if version < 16:
raise ConanInvalidConfiguration(
"mp-units requires at least Visual Studio 16.9"
)
elif compiler == "msvc":
if self._msvc_version < 1928:
raise ConanInvalidConfiguration("mp-units requires at least MSVC 19.28")
else:
raise ConanInvalidConfiguration("Unsupported compiler")
check_min_cppstd(self, 20)
min_version = self._minimum_compilers_version.get(str(compiler))
if min_version and loose_lt_semver(str(compiler.version), min_version):
raise ConanInvalidConfiguration(
f"{self.ref} requires at least {compiler} {min_version} ({compiler.version} in use)"
)
def layout(self):
cmake_layout(self)
def generate(self):
tc = CMakeToolchain(self)
tc.variables["UNITS_BUILD_LA"] = self._build_all and not self._skip_la
tc.variables["UNITS_BUILD_DOCS"] = self._build_all and not self._skip_docs
tc.variables["UNITS_USE_LIBFMT"] = self._use_libfmt
tc.generate()
@ -182,7 +175,7 @@ class MPUnitsConan(ConanFile):
# core
self.cpp_info.components["core"].requires = ["gsl-lite::gsl-lite"]
if compiler == "Visual Studio":
if compiler == "msvc":
self.cpp_info.components["core"].cxxflags = ["/utf-8"]
if self._use_range_v3:
self.cpp_info.components["core"].requires.append("range-v3::range-v3")
@ -192,29 +185,32 @@ class MPUnitsConan(ConanFile):
self.cpp_info.components["core-fmt"].requires = ["core"]
if self._use_libfmt:
self.cpp_info.components["core-fmt"].requires.append("fmt::fmt")
self.cpp_info.components["utility"].requires = ["core", "isq", "si", "angular"]
self.cpp_info.components["isq"].requires = ["core"]
self.cpp_info.components["isq-natural"].requires = ["isq"]
self.cpp_info.components["angular"].requires = ["isq"]
self.cpp_info.components["isq_angular"].requires = ["isq", "angular"]
self.cpp_info.components["natural"].requires = ["isq"]
self.cpp_info.components["si"].requires = ["isq"]
self.cpp_info.components["si-cgs"].requires = ["si"]
self.cpp_info.components["si-fps"].requires = ["si-international"]
self.cpp_info.components["si-hep"].requires = ["si"]
self.cpp_info.components["si-iau"].requires = ["si"]
self.cpp_info.components["si-imperial"].requires = ["si"]
self.cpp_info.components["si-international"].requires = ["si"]
self.cpp_info.components["si-typographic"].requires = ["si"]
self.cpp_info.components["si-uscs"].requires = ["si"]
self.cpp_info.components["isq-iec80000"].requires = ["si"]
self.cpp_info.components["cgs"].requires = ["si"]
self.cpp_info.components["hep"].requires = ["si"]
self.cpp_info.components["iau"].requires = ["si"]
self.cpp_info.components["imperial"].requires = ["si"]
self.cpp_info.components["international"].requires = ["si"]
self.cpp_info.components["typographic"].requires = ["usc"]
self.cpp_info.components["usc"].requires = ["international"]
self.cpp_info.components["iec80000"].requires = ["isq", "si"]
self.cpp_info.components["systems"].requires = [
"isq",
"isq-natural",
"angular",
"isq_angular",
"natural",
"si",
"si-cgs",
"si-fps",
"si-hep",
"si-iau",
"si-imperial",
"si-international",
"si-typographic",
"si-uscs",
"isq-iec80000",
"cgs",
"hep",
"iau",
"imperial",
"international",
"typographic",
"usc",
"iec80000",
]

View File

@ -46,6 +46,10 @@ set(unitsSphinxDocs
"${CMAKE_CURRENT_SOURCE_DIR}/_static/img/units.svg"
"${CMAKE_CURRENT_SOURCE_DIR}/_static/img/quantity_like.svg"
"${CMAKE_CURRENT_SOURCE_DIR}/CHANGELOG.md"
"${CMAKE_CURRENT_SOURCE_DIR}/defining_systems.rst"
"${CMAKE_CURRENT_SOURCE_DIR}/defining_systems/angular_units.rst"
"${CMAKE_CURRENT_SOURCE_DIR}/defining_systems/isq.rst"
"${CMAKE_CURRENT_SOURCE_DIR}/defining_systems/si.rst"
"${CMAKE_CURRENT_SOURCE_DIR}/design.rst"
"${CMAKE_CURRENT_SOURCE_DIR}/design/directories.rst"
"${CMAKE_CURRENT_SOURCE_DIR}/design/downcasting.rst"
@ -82,7 +86,6 @@ set(unitsSphinxDocs
"${CMAKE_CURRENT_SOURCE_DIR}/examples/kalman_filter/kalman.rst"
"${CMAKE_CURRENT_SOURCE_DIR}/faq.rst"
"${CMAKE_CURRENT_SOURCE_DIR}/framework.rst"
"${CMAKE_CURRENT_SOURCE_DIR}/framework/angular_units.rst"
"${CMAKE_CURRENT_SOURCE_DIR}/framework/arithmetics.rst"
"${CMAKE_CURRENT_SOURCE_DIR}/framework/basic_concepts.rst"
"${CMAKE_CURRENT_SOURCE_DIR}/framework/constants.rst"

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.
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
---------------------------------

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/constants
framework/conversions_and_casting
framework/angular_units
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 of other dimensions.
The [SIBrochure]_ states:
All other quantities, with the exception of counts, are derived quantities, which may be
written in terms of base quantities according to the equations of physics. The dimensions of
the derived quantities are written as products of powers of the dimensions of the base
quantities using the equations that relate the derived quantities to the base quantities.
In general the dimension of any quantity :math:`Q` is written in the form of a dimensional product,
:math:`dim Q = T^\alpha L^\beta M^\gamma I^\delta \Theta^\varepsilon N^\zeta J^\eta`
where the exponents :math:`\alpha`, :math:`\beta`, :math:`\gamma`, :math:`\delta`, :math:`\varepsilon`,
:math:`\zeta` and :math:`\eta`, which are generally small integers, which can be positive,
negative, or zero, are called the dimensional exponents.
Looking at the previous code snippets the area, speed, or frequency are
the examples of such quantities. Each derived quantity can be represented
as a unique list of exponents of base quantities. For example:

View File

@ -313,7 +313,7 @@ Quantity References vs Unit-specific Aliases
- Unit-specific Aliases
As aliases are defined in terms of types rather variables no major shadowing issues were found
so far. In case of identifiers abiguity it was always possible to disambiguate with more
so far. In case of identifiers ambiguity it was always possible to disambiguate with more
namespaces prefixed in front of the alias.
2. Adjustable verbosity
@ -385,7 +385,7 @@ Quantity References vs Unit-specific Aliases
- Quantity References
The syntax for references uses ``*`` operator which has some predefined precedence. This operator
always takes a magnitude or a reference as ``lhs`` and a reference as ``rhs``. All other comibnations
always takes a magnitude or a reference as ``lhs`` and a reference as ``rhs``. All other combinations
are not allowed. It means that in order to satisfy the operators precedence sometimes quite a lot
of parenthesis have to be sprinkled in the code in order for the code to compile::
@ -399,7 +399,7 @@ Quantity References vs Unit-specific Aliases
- Quantity References
References have only to be defined for named units. Also for the user's conveniance references are
References have only to be defined for named units. Also for the user's convenience references are
predefined for units raised to a specific power (e.g. ``m2``, ``km3``, etc). All other derived units
can be constructed using the provided ones already even if they do not correspond to any predefined
dimension::

View File

@ -49,4 +49,4 @@ Unlike `quantity`, the library provides:
- no dimension-specific concepts, such as ``LengthPoint``
(there's the dimension-agnostic `QuantityPoint`),
- a more limited set of operations on quantity points
(see the :ref:`framework/dimensions:Quantity Points` chapter)
(see the :ref:`framework/arithmetics:Quantity Points` chapter)

View File

@ -203,27 +203,27 @@ complete list of all the :term:`SI` prefixes supported by the library::
namespace si {
struct yocto : prefix<yocto, "y", pow<-24>(mag<10>())> {};
struct zepto : prefix<zepto, "z", pow<-21>(mag<10>())> {};
struct atto : prefix<atto, "a", pow<-18>(mag<10>())> {};
struct femto : prefix<femto, "f", pow<-15>(mag<10>())> {};
struct pico : prefix<pico, "p", pow<-12>(mag<10>())> {};
struct nano : prefix<nano, "n", pow<-9>(mag<10>())> {};
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"},
pow<-6>(mag<10>())> {};
struct milli : prefix<milli, "m", pow<-3>(mag<10>())> {};
struct centi : prefix<centi, "c", pow<-2>(mag<10>())> {};
struct deci : prefix<deci, "d", pow<-1>(mag<10>())> {};
struct deca : prefix<deca, "da", pow<1>(mag<10>())> {};
struct hecto : prefix<hecto, "h", pow<2>(mag<10>())> {};
struct kilo : prefix<kilo, "k", pow<3>(mag<10>())> {};
struct mega : prefix<mega, "M", pow<6>(mag<10>())> {};
struct giga : prefix<giga, "G", pow<9>(mag<10>())> {};
struct tera : prefix<tera, "T", pow<12>(mag<10>())> {};
struct peta : prefix<peta, "P", pow<15>(mag<10>())> {};
struct exa : prefix<exa, "E", pow<18>(mag<10>())> {};
struct zetta : prefix<zetta, "Z", pow<21>(mag<10>())> {};
struct yotta : prefix<yotta, "Y", pow<24>(mag<10>())> {};
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>()> {};
}
@ -232,14 +232,14 @@ domain::
namespace iec80000 {
struct kibi : prefix<kibi, "Ki", pow<10>(mag<2>())> {};
struct mebi : prefix<mebi, "Mi", pow<20>(mag<2>())> {};
struct gibi : prefix<gibi, "Gi", pow<30>(mag<2>())> {};
struct tebi : prefix<tebi, "Ti", pow<40>(mag<2>())> {};
struct pebi : prefix<pebi, "Pi", pow<50>(mag<2>())> {};
struct exbi : prefix<exbi, "Ei", pow<60>(mag<2>())> {};
struct zebi : prefix<zebi, "Zi", pow<70>(mag<2>())> {};
struct yobi : prefix<yobi, "Yi", pow<80>(mag<2>())> {};
struct kibi : prefix<kibi, "Ki", mag_power<2, 10>()> {};
struct mebi : prefix<mebi, "Mi", mag_power<2, 20>()> {};
struct gibi : prefix<gibi, "Gi", mag_power<2, 30>()> {};
struct tebi : prefix<tebi, "Ti", mag_power<2, 40>()> {};
struct pebi : prefix<pebi, "Pi", mag_power<2, 50>()> {};
struct exbi : prefix<exbi, "Ei", mag_power<2, 60>()> {};
struct zebi : prefix<zebi, "Zi", mag_power<2, 70>()> {};
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).
.. note::
[SIBrochure]_ states:
However, when prefixes are used with SI units, the resulting units are no
longer coherent, because the prefix introduces a numerical factor other than one.
Derived Units
-------------
@ -268,10 +276,21 @@ Derived Units
names (i.e. ``N`` (newton)) or can be composed from the names of units of quantities
used to define thier derived quantity (i.e. ``km/h``).
The [SIBrochure]_ states:
Derived units are defined as products of powers of the base units. When the numerical
factor of this product is one, the derived units are called coherent derived units. The base
and coherent derived units of the SI form a coherent set, designated the set of coherent SI
units. The word “coherent” here means that equations between the numerical values of
quantities take exactly the same form as the equations between the quantities themselves.
Derived Named Units
^^^^^^^^^^^^^^^^^^^
The [SIBrochure]_ also says:
Some of the coherent derived units in the SI are given special names.
Derived named units have a unique symbol (i.e. ``N`` (newton) or ``Pa``
(pascal)) and they are defined in the same way as base units (which
always have to be a named unit)::

View File

@ -73,9 +73,9 @@ ISO 80000 [1]_ definitions
tensor character.
- In a given `system of quantities`:
- `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>` having the same quantity dimension are not necessarily of the same `kind`.
- `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>` having the same quantity dimension are not necessarily of the same `kind`.
quantity of dimension one
dimensionless quantity
@ -168,7 +168,15 @@ ISO 80000 [1]_ definitions
value of a quantity
value
- Number and reference together expressing magnitude of a `quantity`.
- The number can be complex.
- A quantity value can be presented in more than one way.
- In the case of vector or tensor quantities, each component has a quantity value.
- For example, force acting on a given particle, e.g. in Cartesian components
:math:`(F_x; F_y; F_z) = (31,5; 43,2; 17,0) N`, where
:math:`(31,5; 43,2; 17,0)` is a numerical-value vector and :math:`N` (newton)
is the unit, or :math:`(F_x; F_y; F_z) = (31,5 N; 43,2 N; 17,0 N)`
where each component is a quantity.
Other definitions

View File

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

View File

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

View File

@ -1,4 +1,4 @@
Sotrage Capacity
Storage Capacity
================
.. 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
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.
.. [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::
*~/.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
<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:
@ -146,8 +146,8 @@ Specifies how :ref:`design/downcasting:The Downcasting Facility` works:
Conan Configuration Properties
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
build_all
+++++++++
user.build:all
++++++++++++++
**Values**: ``True``/``False``
@ -158,14 +158,24 @@ To support this it requires some additional Conan build dependencies described i
`Repository Structure and Dependencies`_.
It also runs unit tests during Conan build (unless ``tools.build:skip_test`` configuration property is set to ``True``)
skip_docs
+++++++++
user.build:skip_la
++++++++++++++++++
**Values**: ``True``/``False``
**Defaulted to**: ``False``
If `build_all`_ is enabled, among others, Conan installs the documentation generation dependencies (i.e. doxygen) and
If `user.build:all`_ is enabled, among others, Conan installs the external `wg21-linear_algebra <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.
CMake Options
@ -181,6 +191,16 @@ UNITS_AS_SYSTEM_HEADERS
Exports library as system headers.
UNITS_BUILD_LA
++++++++++++++
**Values**: ``ON``/``OFF``
**Defaulted to**: ``ON``
Enables building code depending on the linear algebra library.
UNITS_BUILD_DOCS
++++++++++++++++
@ -387,8 +407,8 @@ differences:
.. code-block:: shell
conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -b=outdated -u
cmake --preset default
cmake --build --preset release
cmake --preset conan-default
cmake --build --preset conan-release
Install
@ -403,8 +423,8 @@ to find it, it is enough to perform the following steps:
conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -b=missing
mv CMakeUserPresets.json src
cd src
cmake --preset default -DCMAKE_INSTALL_PREFIX=<your_installation_path>
cmake --build --preset release --target install
cmake --preset conan-default -DCMAKE_INSTALL_PREFIX=<your_installation_path>
cmake --build --preset conan-release --target install
Contributing (or just building all the tests and examples)
@ -414,13 +434,13 @@ In case you would like to build all the source code (with unit tests and example
you should:
1. Use the *CMakeLists.txt* from the top-level directory.
2. Run Conan with `build_all`_ = ``True``
(use ``-o build_docs=False`` if you want to skip the documentation generation).
2. Run Conan with `user.build:all`_ = ``True``
(use ``-c user.build:skip_docs=True`` if you want to skip the documentation generation).
.. code-block:: shell
git clone https://github.com/mpusz/units.git && cd units
conan install . -pr <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 .
The above will download and install all of the dependencies needed for the development of the library,
@ -431,9 +451,9 @@ step with the explicit CMake build:
.. code-block:: shell
cmake --preset default
cmake --build --preset release
cmake --build --preset release --target test
cmake --preset conan-default
cmake --build --preset conan-release
cmake --build --preset conan-release --target test
Building documentation
@ -443,15 +463,15 @@ In case you would like to build the project's documentation, you should:
1. Use the *CMakeLists.txt* from the top-level directory.
2. Obtain Python dependencies.
3. Run Conan with `build_all`_ = ``True``.
3. Run Conan with `user.build:all`_ = ``True``.
.. code-block:: shell
git clone https://github.com/mpusz/units.git && cd units
pip3 install -r docs/requirements.txt
conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -c user.build:all=True -b missing
cmake --preset default
cmake --build --preset release --target documentation
cmake --preset conan-default
cmake --build --preset conan-release --target documentation
The above will download and install all of the dependencies needed and build the documentation.
@ -463,7 +483,7 @@ To test CMake installation and Conan packaging or create a Conan package run:
.. code-block:: shell
conan create . <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.

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>`_.
Also, to simplify the examples all of them assume::
using namespace std::math;
using namespace STD_LA;
Linear Algebra of Quantities
@ -27,16 +27,16 @@ The official :term:`quantity` definition states:
So the most common use case would be to create a vector or matrix of quantities::
fs_vector<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 };
fs_vector<si::length<si::kilometre>, 3> t = { 3 * km, 2 * km, 1 * km };
fixed_size_column_vector<si::length<si::metre>, 3> v = { 1 * m, 2 * m, 3 * m };
fixed_size_column_vector<si::length<si::metre>, 3> u = { 3 * m, 2 * m, 1 * m };
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
allowed by the Linear Algebra rules. For example::
std::cout << "v + u = " << v + u << "\n";
std::cout << "v + t = " << v + t << "\n";
std::cout << "t[m] = " << fs_vector<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 << "2 * m * v = " << 2 * m * v << "\n";
@ -66,9 +66,9 @@ types provided to a `quantity` class template.
With this the above vector definitions can be rewritten as follows::
si::length<si::metre, fs_vector<int, 3>> v(fs_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::kilometre, fs_vector<int, 3>> t(fs_vector<int, 3>{ 3, 2, 1 });
si::length<si::metre, fixed_size_column_vector<int, 3>> v(fixed_size_column_vector<int, 3>{ 1, 2, 3 });
si::length<si::metre, fixed_size_column_vector<int, 3>> u(fixed_size_column_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
output:

View File

@ -1,10 +1,10 @@
Math
====
.. doxygenfunction:: units::pow
.. doxygenfunction:: mp_units::pow
.. doxygenfunction:: units::sqrt
.. doxygenfunction:: mp_units::sqrt
.. doxygenfunction:: units::abs
.. doxygenfunction:: mp_units::abs
.. doxygenfunction:: units::epsilon
.. doxygenfunction:: mp_units::epsilon

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(unmanned_aerial_vehicle mp-units::core-fmt mp-units::core-io mp-units::si mp-units::international mp-units::utility example_utils)
find_package(wg21_linear_algebra CONFIG REQUIRED)
add_example(linear_algebra mp-units::core-fmt mp-units::core-io mp-units::si)
target_link_libraries(linear_algebra PRIVATE wg21_linear_algebra::wg21_linear_algebra)
add_subdirectory(glide_computer)
add_subdirectory(kalman_filter)

View File

@ -113,7 +113,7 @@ void calcs_comparison()
const auto L1A = 2.f * fm;
const auto L2A = 3.f * fm;
const auto LrA = L1A + L2A;
std::cout << STD_FMT::format("{:%.30Q %q}\n + {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1A, L2A, LrA);
std::cout << UNITS_STD_FMT::format("{:%.30Q %q}\n + {:%.30Q %q}\n = {:%.30Q %q}\n\n", L1A, L2A, LrA);
std::cout << "The single unit method must convert large\n"
"or small values in other units to the base unit.\n"
@ -122,17 +122,17 @@ void calcs_comparison()
const auto L1B = L1A[m];
const auto L2B = L2A[m];
const auto LrB = L1B + L2B;
std::cout << STD_FMT::format("{:%.30eQ %q}\n + {:%.30eQ %q}\n = {:%.30eQ %q}\n\n", L1B, L2B, LrB);
std::cout << UNITS_STD_FMT::format("{:%.30eQ %q}\n + {:%.30eQ %q}\n = {:%.30eQ %q}\n\n", L1B, L2B, LrB);
std::cout << "In multiplication and division:\n\n";
const quantity<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";
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

View File

@ -48,13 +48,13 @@ int main()
constexpr auto lengthA = 2.0 * m;
constexpr auto lengthB = lengthA[mm];
std::cout << STD_FMT::format("lengthA( {} ) and lengthB( {} )\n", lengthA, lengthB)
std::cout << UNITS_STD_FMT::format("lengthA( {} ) and lengthB( {} )\n", lengthA, lengthB)
<< "represent the same length in different units.\n\n";
std::cout << STD_FMT::format("therefore ratio lengthA / lengthB == {}\n\n", lengthA / lengthB);
std::cout << UNITS_STD_FMT::format("therefore ratio lengthA / lengthB == {}\n\n", lengthA / lengthB);
std::cout << STD_FMT::format("conversion factor from lengthA::unit of {:%q} to lengthB::unit of {:%q}:\n\n", lengthA,
lengthB)
<< STD_FMT::format("lengthB.number( {} ) == lengthA.number( {} ) * conversion_factor( {} )\n",
lengthB.number(), lengthA.number(), conversion_factor(lengthB, lengthA));
std::cout << UNITS_STD_FMT::format("conversion factor from lengthA::unit of {:%q} to lengthB::unit of {:%q}:\n\n",
lengthA, lengthB)
<< UNITS_STD_FMT::format("lengthB.number( {} ) == lengthA.number( {} ) * conversion_factor( {} )\n",
lengthB.number(), lengthA.number(), conversion_factor(lengthB, lengthA));
}

View File

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

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,
const glide_computer::flight_point& new_point)
{
std::cout << STD_FMT::format(
std::cout << UNITS_STD_FMT::format(
"| {:<12} | {:>9%.1Q %q} (Total: {:>9%.1Q %q}) | {:>8%.1Q %q} (Total: {:>8%.1Q %q}) | {:>7%.0Q %q} ({:>6%.0Q %q}) "
"|\n",
phase_name, value_cast<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,
const aircraft_tow& at)
{
std::cout << STD_FMT::format("| {:<12} | {:^28} | {:^26} | {:^21} |\n", "Flight phase", "Duration", "Distance",
"Height");
std::cout << STD_FMT::format("|{0:-^14}|{0:-^30}|{0:-^28}|{0:-^23}|\n", "");
std::cout << UNITS_STD_FMT::format("| {:<12} | {:^28} | {:^26} | {:^21} |\n", "Flight phase", "Duration", "Distance",
"Height");
std::cout << UNITS_STD_FMT::format("|{0:-^14}|{0:-^30}|{0:-^28}|{0:-^23}|\n", "");
// ready to takeoff
flight_point pos = takeoff(start_ts, t);

View File

@ -84,7 +84,7 @@ void print(const R& gliders)
std::cout << "- Polar:\n";
for (const auto& p : g.polar) {
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
// for strong angle in a different namespace)
value_cast<si::degree>(isq::asin(1 / ratio)));
@ -102,8 +102,8 @@ void print(const R& conditions)
for (const auto& c : conditions) {
std::cout << "- " << c.first << "\n";
const auto& w = c.second;
std::cout << " * Cloud base: " << STD_FMT::format("{:%.0Q %q}", w.cloud_base) << " AGL\n";
std::cout << " * Thermals strength: " << STD_FMT::format("{:%.1Q %q}", w.thermal_strength) << "\n";
std::cout << " * Cloud base: " << UNITS_STD_FMT::format("{:%.0Q %q}", w.cloud_base) << " AGL\n";
std::cout << " * Thermals strength: " << UNITS_STD_FMT::format("{:%.1Q %q}", w.thermal_strength) << "\n";
std::cout << "\n";
}
}
@ -115,7 +115,7 @@ void print(const R& waypoints)
std::cout << "Waypoints:\n";
std::cout << "==========\n";
for (const auto& w : waypoints)
std::cout << STD_FMT::format("- {}: {} {}, {:%.1Q %q}\n", w.name, w.pos.lat, w.pos.lon, w.alt);
std::cout << UNITS_STD_FMT::format("- {}: {} {}, {:%.1Q %q}\n", w.name, w.pos.lat, w.pos.lon, w.alt);
std::cout << "\n";
}
@ -126,12 +126,12 @@ void print(const task& t)
std::cout << "- Start: " << t.get_start().name << "\n";
std::cout << "- Finish: " << t.get_finish().name << "\n";
std::cout << "- Length: " << STD_FMT::format("{:%.1Q %q}", t.get_distance()) << "\n";
std::cout << "- Length: " << UNITS_STD_FMT::format("{:%.1Q %q}", t.get_distance()) << "\n";
std::cout << "- Legs: "
<< "\n";
for (const auto& l : t.get_legs())
std::cout << STD_FMT::format(" * {} -> {} ({:%.1Q %q})\n", l.begin().name, l.end().name, l.get_distance());
std::cout << UNITS_STD_FMT::format(" * {} -> {} ({:%.1Q %q})\n", l.begin().name, l.end().name, l.get_distance());
std::cout << "\n";
}
@ -139,7 +139,7 @@ void print(const safety& s)
{
std::cout << "Safety:\n";
std::cout << "=======\n";
std::cout << "- Min AGL separation: " << STD_FMT::format("{:%.0Q %q}", s.min_agl_height) << "\n";
std::cout << "- Min AGL separation: " << UNITS_STD_FMT::format("{:%.0Q %q}", s.min_agl_height) << "\n";
std::cout << "\n";
}
@ -148,8 +148,8 @@ void print(const aircraft_tow& tow)
std::cout << "Tow:\n";
std::cout << "====\n";
std::cout << "- Type: aircraft\n";
std::cout << "- Height: " << STD_FMT::format("{:%.0Q %q}", tow.height_agl) << "\n";
std::cout << "- Performance: " << STD_FMT::format("{:%.1Q %q}", tow.performance) << "\n";
std::cout << "- Height: " << UNITS_STD_FMT::format("{:%.0Q %q}", tow.height_agl) << "\n";
std::cout << "- Performance: " << UNITS_STD_FMT::format("{:%.1Q %q}", tow.performance) << "\n";
std::cout << "\n";
}
@ -178,7 +178,7 @@ void example()
for (const auto& c : weather_conditions) {
std::string txt = "Scenario: Glider = " + g.name + ", Weather = " + c.first;
std::cout << txt << "\n";
std::cout << STD_FMT::format("{0:=^{1}}\n\n", "", txt.size());
std::cout << UNITS_STD_FMT::format("{0:=^{1}}\n\n", "", txt.size());
estimate(start_time, g, c.second, t, sfty, tow);

View File

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

View File

@ -53,12 +53,12 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
} // namespace geographic
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>
auto format(const geographic::msl_altitude& a, FormatContext& 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>
struct STD_FMT::formatter<geographic::latitude<T>> : formatter<T> {
struct UNITS_STD_FMT::formatter<geographic::latitude<T>> : formatter<T> {
template<typename FormatContext>
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);
}
};
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>
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);
}
};

View File

@ -26,21 +26,29 @@
#include <mp-units/bits/external/hacks.h>
#include <mp-units/customization_points.h>
#include <algorithm>
#include <concepts>
#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; };
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>);
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>> {
public:
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{}) {}
[[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>

View File

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

View File

@ -79,13 +79,9 @@ public:
uncertainty_type uncertainty;
};
#if UNITS_COMP_CLANG <= 14
template<QuantityOrQuantityPoint QQP, mp_units::Quantity U>
estimation(state<QQP>, U) -> estimation<QQP>;
#endif
// kalman gain
template<mp_units::Quantity Q>
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
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)
{
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);
if (specs.precision != -1) {
if constexpr (sizeof...(Qs) == 1)
STD_FMT::format_to(to_value_buffer, "{1:%.{0}Q %q}", specs.precision, kalman::get<0>(s));
UNITS_STD_FMT::format_to(to_value_buffer, "{1:%.{0}Q %q}", specs.precision, kalman::get<0>(s));
else if constexpr (sizeof...(Qs) == 2)
STD_FMT::format_to(to_value_buffer, "{{ {1:%.{0}Q %q}, {2:%.{0}Q %q} }}", specs.precision, kalman::get<0>(s),
kalman::get<1>(s));
UNITS_STD_FMT::format_to(to_value_buffer, "{{ {1:%.{0}Q %q}, {2:%.{0}Q %q} }}", specs.precision,
kalman::get<0>(s), kalman::get<1>(s));
else
STD_FMT::format_to(to_value_buffer, "{{ {1:%.{0}Q %q}, {2:%.{0}Q %q}, {3:%.{0}Q %q} }}", specs.precision,
kalman::get<0>(s), kalman::get<1>(s), kalman::get<2>(s));
UNITS_STD_FMT::format_to(to_value_buffer, "{{ {1:%.{0}Q %q}, {2:%.{0}Q %q}, {3:%.{0}Q %q} }}", specs.precision,
kalman::get<0>(s), kalman::get<1>(s), kalman::get<2>(s));
} else {
if constexpr (sizeof...(Qs) == 1)
STD_FMT::format_to(to_value_buffer, "{}", kalman::get<0>(s));
UNITS_STD_FMT::format_to(to_value_buffer, "{}", kalman::get<0>(s));
else if constexpr (sizeof...(Qs) == 2)
STD_FMT::format_to(to_value_buffer, "{{ {}, {} }}", kalman::get<0>(s), kalman::get<1>(s));
UNITS_STD_FMT::format_to(to_value_buffer, "{{ {}, {} }}", kalman::get<0>(s), kalman::get<1>(s));
else
STD_FMT::format_to(to_value_buffer, "{{ {}, {}, {} }}", kalman::get<0>(s), kalman::get<1>(s),
kalman::get<2>(s));
UNITS_STD_FMT::format_to(to_value_buffer, "{{ {}, {}, {} }}", kalman::get<0>(s), kalman::get<1>(s),
kalman::get<2>(s));
}
std::string global_format_buffer;
mp_units::detail::quantity_global_format_specs<char> global_specs = {specs.fill, specs.align, specs.width};
mp_units::detail::format_global_buffer(std::back_inserter(global_format_buffer), global_specs);
return STD_FMT::vformat_to(ctx.out(), global_format_buffer, STD_FMT::make_format_args(value_buffer));
return UNITS_STD_FMT::vformat_to(ctx.out(), global_format_buffer, UNITS_STD_FMT::make_format_args(value_buffer));
}
private:
mp_units::detail::dynamic_format_specs<char> specs;
};
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)
{
mp_units::detail::dynamic_specs_handler handler(specs, ctx);
@ -221,16 +217,17 @@ struct STD_FMT::formatter<kalman::estimation<Q>> {
std::string value_buffer;
auto to_value_buffer = std::back_inserter(value_buffer);
if (specs.precision != -1) {
STD_FMT::format_to(to_value_buffer, "{0:%.{2}Q} ± {1:%.{2}Q} {0:%q}", q, sqrt(e.uncertainty), specs.precision);
UNITS_STD_FMT::format_to(to_value_buffer, "{0:%.{2}Q} ± {1:%.{2}Q} {0:%q}", q, sqrt(e.uncertainty),
specs.precision);
} else {
STD_FMT::format_to(to_value_buffer, "{0:%Q} ± {1:%Q} {0:%q}", q, sqrt(e.uncertainty));
UNITS_STD_FMT::format_to(to_value_buffer, "{0:%Q} ± {1:%Q} {0:%q}", q, sqrt(e.uncertainty));
}
std::string global_format_buffer;
mp_units::detail::quantity_global_format_specs<char> global_specs = {specs.fill, specs.align, specs.width};
mp_units::detail::format_global_buffer(std::back_inserter(global_format_buffer), global_specs);
return STD_FMT::vformat_to(ctx.out(), global_format_buffer, STD_FMT::make_format_args(value_buffer));
return UNITS_STD_FMT::vformat_to(ctx.out(), global_format_buffer, UNITS_STD_FMT::make_format_args(value_buffer));
}
private:
mp_units::detail::dynamic_format_specs<char> specs;

View File

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

View File

@ -37,13 +37,14 @@ using namespace mp_units;
void print_header(const kalman::State auto& initial)
{
std::cout << STD_FMT::format("Initial: {}\n", initial);
std::cout << STD_FMT::format("{:>2} | {:>8} | {:>23} | {:>23}\n", "N", "Measured", "Curr. Estimate", "Next Estimate");
std::cout << UNITS_STD_FMT::format("Initial: {}\n", initial);
std::cout << UNITS_STD_FMT::format("{:>2} | {:>8} | {:>23} | {:>23}\n", "N", "Measured", "Curr. Estimate",
"Next Estimate");
}
void print(auto iteration, Quantity auto measured, const kalman::State auto& current, const kalman::State auto& next)
{
std::cout << STD_FMT::format("{:2} | {:8} | {:.1} | {:.1}\n", iteration, measured, current, next);
std::cout << UNITS_STD_FMT::format("{:2} | {:8} | {:.1} | {:.1}\n", iteration, measured, current, next);
}
int main()

View File

@ -37,13 +37,14 @@ using namespace mp_units;
void print_header(const kalman::State auto& initial)
{
std::cout << STD_FMT::format("Initial: {}\n", initial);
std::cout << STD_FMT::format("{:>2} | {:>8} | {:>24} | {:>24}\n", "N", "Measured", "Curr. Estimate", "Next Estimate");
std::cout << UNITS_STD_FMT::format("Initial: {}\n", initial);
std::cout << UNITS_STD_FMT::format("{:>2} | {:>8} | {:>24} | {:>24}\n", "N", "Measured", "Curr. Estimate",
"Next Estimate");
}
void print(auto iteration, Quantity auto measured, const kalman::State auto& current, const kalman::State auto& next)
{
std::cout << STD_FMT::format("{:2} | {:8} | {:>24.1} | {:>24.1}\n", iteration, measured, current, next);
std::cout << UNITS_STD_FMT::format("{:2} | {:8} | {:>24.1} | {:>24.1}\n", iteration, measured, current, next);
}
int main()

View File

@ -37,13 +37,14 @@ using namespace mp_units;
void print_header(const kalman::State auto& initial)
{
std::cout << STD_FMT::format("Initial: {}\n", initial);
std::cout << STD_FMT::format("{:>2} | {:>8} | {:>35} | {:>35}\n", "N", "Measured", "Curr. Estimate", "Next Estimate");
std::cout << UNITS_STD_FMT::format("Initial: {}\n", initial);
std::cout << UNITS_STD_FMT::format("{:>2} | {:>8} | {:>35} | {:>35}\n", "N", "Measured", "Curr. Estimate",
"Next Estimate");
}
void print(auto iteration, Quantity auto measured, const kalman::State auto& current, const kalman::State auto& next)
{
std::cout << STD_FMT::format("{:2} | {:8} | {:>35.1} | {:>35.1}\n", iteration, measured, current, next);
std::cout << UNITS_STD_FMT::format("{:2} | {:8} | {:>35.1} | {:>35.1}\n", iteration, measured, current, next);
}
int main()

View File

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

View File

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

View File

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

View File

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

View File

@ -35,20 +35,20 @@ int main()
using namespace mp_units::si::unit_symbols;
std::cout << "The seven defining constants of the SI and the seven corresponding units they define:\n";
std::cout << STD_FMT::format("- hyperfine transition frequency of Cs: {} = {:%.0Q %q}\n",
std::cout << UNITS_STD_FMT::format("- hyperfine transition frequency of Cs: {} = {:%.0Q %q}\n",
1. * si2019::hyperfine_structure_transition_frequency_of_cs,
(1. * si2019::hyperfine_structure_transition_frequency_of_cs)[Hz]);
std::cout << STD_FMT::format("- speed of light in vacuum: {} = {:%.0Q %q}\n",
std::cout << UNITS_STD_FMT::format("- speed of light in vacuum: {} = {:%.0Q %q}\n",
1. * si2019::speed_of_light_in_vacuum, (1. * si2019::speed_of_light_in_vacuum)[m / s]);
std::cout << STD_FMT::format("- Planck constant: {} = {:%.8eQ %q}\n",
std::cout << UNITS_STD_FMT::format("- Planck constant: {} = {:%.8eQ %q}\n",
1. * si2019::planck_constant, (1. * si2019::planck_constant)[J * s]);
std::cout << STD_FMT::format("- elementary charge: {} = {:%.9eQ %q}\n",
std::cout << UNITS_STD_FMT::format("- elementary charge: {} = {:%.9eQ %q}\n",
1. * si2019::elementary_charge, (1. * si2019::elementary_charge)[C]);
std::cout << STD_FMT::format("- Boltzmann constant: {} = {:%.6eQ %q}\n",
std::cout << UNITS_STD_FMT::format("- Boltzmann constant: {} = {:%.6eQ %q}\n",
1. * si2019::boltzmann_constant, (1. * si2019::boltzmann_constant)[J / K]);
std::cout << STD_FMT::format("- Avogadro constant: {} = {:%.8eQ %q}\n",
std::cout << UNITS_STD_FMT::format("- Avogadro constant: {} = {:%.8eQ %q}\n",
1. * si2019::avogadro_constant, (1. * si2019::avogadro_constant)[1 / mol]);
// TODO uncomment the below when ISQ is done
// std::cout << STD_FMT::format("- luminous efficacy: {} = {}\n", si2019::luminous_efficacy(1.),
// std::cout << UNITS_STD_FMT::format("- luminous efficacy: {} = {}\n", si2019::luminous_efficacy(1.),
// si2019::luminous_efficacy(1.)[lm / W]);
}

View File

@ -119,10 +119,10 @@ int main()
const auto fill_ratio = fill_level / height;
std::cout << STD_FMT::format("fill height at {} = {} ({} full)\n", fill_time, fill_level, fill_ratio[percent]);
std::cout << STD_FMT::format("fill weight at {} = {} ({})\n", fill_time, filled_weight, filled_weight[N]);
std::cout << STD_FMT::format("spare capacity at {} = {}\n", fill_time, spare_capacity);
std::cout << STD_FMT::format("input flow rate = {}\n", input_flow_rate);
std::cout << STD_FMT::format("float rise rate = {}\n", float_rise_rate);
std::cout << STD_FMT::format("tank full E.T.A. at current flow rate = {}\n", fill_time_left[s]);
std::cout << UNITS_STD_FMT::format("fill height at {} = {} ({} full)\n", fill_time, fill_level, fill_ratio[percent]);
std::cout << UNITS_STD_FMT::format("fill weight at {} = {} ({})\n", fill_time, filled_weight, filled_weight[N]);
std::cout << UNITS_STD_FMT::format("spare capacity at {} = {}\n", fill_time, spare_capacity);
std::cout << UNITS_STD_FMT::format("input flow rate = {}\n", input_flow_rate);
std::cout << UNITS_STD_FMT::format("float rise rate = {}\n", float_rise_rate);
std::cout << UNITS_STD_FMT::format("tank full E.T.A. at current flow rate = {}\n", fill_time_left[s]);
}

View File

@ -75,12 +75,12 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
template<QuantityPoint QP>
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>
auto format(const QP& a, FormatContext& 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<>
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>
auto format(const hal_altitude& a, FormatContext& 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;
uav.take_off(msl_altitude{6'000 * ft});
uav.current(msl_altitude{10'000 * ft});
std::cout << STD_FMT::format("hal = {}\n", uav.hal());
std::cout << UNITS_STD_FMT::format("hal = {}\n", uav.hal());
msl_altitude ground_level{123 * m};
std::cout << STD_FMT::format("agl = {}\n", uav.current() - ground_level);
std::cout << UNITS_STD_FMT::format("agl = {}\n", uav.current() - ground_level);
struct waypoint {
std::string name;
@ -162,6 +162,6 @@ int main()
};
waypoint wpt = {"EPPR", {54.24772_N, 18.6745_E}, msl_altitude{16. * ft}};
std::cout << STD_FMT::format("{}: {} {}, {:%.2Q %q}, {:%.2Q %q}\n", wpt.name, wpt.pos.lat, wpt.pos.lon, wpt.msl_alt,
std::cout << UNITS_STD_FMT::format("{}: {} {}, {:%.2Q %q}, {:%.2Q %q}\n", wpt.name, wpt.pos.lat, wpt.pos.lon, wpt.msl_alt,
to_hae<earth_gravity_model::egm2008_1>(wpt.msl_alt, wpt.pos));
}

View File

@ -1,2 +1,3 @@
conan
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}>)
if(${projectPrefix}USE_LIBFMT)
find_package(fmt CONFIG REQUIRED)
if(NOT TARGET fmt::fmt)
find_package(fmt CONFIG REQUIRED)
endif()
target_link_libraries(mp-units-core-fmt INTERFACE fmt::fmt)
endif()

View File

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

View File

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

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) {
begin = parse_precision(begin, end, handler);
} else
throw STD_FMT::format_error("precision not allowed for integral quantity representation");
throw UNITS_STD_FMT::format_error("precision not allowed for integral quantity representation");
if (begin == end) return begin;
}
@ -126,12 +126,12 @@ constexpr It parse_units_format(It begin, S end, Handler&& handler)
}
if (begin != ptr) handler.on_text(begin, ptr);
begin = ++ptr; // consume '%'
if (ptr == end) throw STD_FMT::format_error("invalid format");
if (ptr == end) throw UNITS_STD_FMT::format_error("invalid format");
c = *ptr++;
constexpr auto units_types = std::string_view{"Qq"};
const auto new_end = find_first_of(begin, end, units_types.begin(), units_types.end());
if (new_end == end) throw STD_FMT::format_error("invalid format");
if (new_end == end) throw UNITS_STD_FMT::format_error("invalid format");
if (*new_end == 'Q') {
handler.on_quantity_value(begin, new_end); // Edit `on_quantity_value` to add rep modifiers
} else {
@ -152,43 +152,43 @@ template<typename CharT, typename Rep, typename OutputIt, typename Locale>
std::basic_string<CharT> buffer;
auto to_buffer = std::back_inserter(buffer);
STD_FMT::format_to(to_buffer, "{{:");
UNITS_STD_FMT::format_to(to_buffer, "{{:");
switch (rep_specs.sign) {
case fmt_sign::none:
break;
case fmt_sign::plus:
STD_FMT::format_to(to_buffer, "+");
UNITS_STD_FMT::format_to(to_buffer, "+");
break;
case fmt_sign::minus:
STD_FMT::format_to(to_buffer, "-");
UNITS_STD_FMT::format_to(to_buffer, "-");
break;
case fmt_sign::space:
STD_FMT::format_to(to_buffer, " ");
UNITS_STD_FMT::format_to(to_buffer, " ");
break;
}
if (rep_specs.alt) {
STD_FMT::format_to(to_buffer, "#");
UNITS_STD_FMT::format_to(to_buffer, "#");
}
auto type = rep_specs.type;
if (auto precision = rep_specs.precision; precision >= 0) {
STD_FMT::format_to(to_buffer, ".{}{}", precision, type == '\0' ? 'f' : type);
UNITS_STD_FMT::format_to(to_buffer, ".{}{}", precision, type == '\0' ? 'f' : type);
} else if constexpr (treat_as_floating_point<Rep>) {
STD_FMT::format_to(to_buffer, "{}", type == '\0' ? 'g' : type);
UNITS_STD_FMT::format_to(to_buffer, "{}", type == '\0' ? 'g' : type);
} else {
if (type != '\0') {
STD_FMT::format_to(to_buffer, "{}", type);
UNITS_STD_FMT::format_to(to_buffer, "{}", type);
}
}
if (rep_specs.localized) {
STD_FMT::format_to(to_buffer, "L");
UNITS_STD_FMT::format_to(to_buffer, "L");
}
STD_FMT::format_to(to_buffer, "}}");
UNITS_STD_FMT::format_to(to_buffer, "}}");
if (rep_specs.localized) {
return STD_FMT::vformat_to(out, FMT_LOCALE(loc), buffer, STD_FMT::make_format_args(val));
return UNITS_STD_FMT::vformat_to(out, UNITS_FMT_LOCALE(loc), buffer, UNITS_STD_FMT::make_format_args(val));
}
return STD_FMT::vformat_to(out, buffer, STD_FMT::make_format_args(val));
return UNITS_STD_FMT::vformat_to(out, buffer, UNITS_STD_FMT::make_format_args(val));
}
// Creates a global format string
@ -196,25 +196,25 @@ template<typename CharT, typename Rep, typename OutputIt, typename Locale>
template<typename CharT, typename OutputIt>
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] != ' ') {
STD_FMT::format_to(out, "{}", specs.fill.data());
UNITS_STD_FMT::format_to(out, "{}", specs.fill.data());
}
switch (specs.align) {
case fmt_align::left:
STD_FMT::format_to(out, "<");
UNITS_STD_FMT::format_to(out, "<");
break;
case fmt_align::right:
STD_FMT::format_to(out, ">");
UNITS_STD_FMT::format_to(out, ">");
break;
case fmt_align::center:
STD_FMT::format_to(out, "^");
UNITS_STD_FMT::format_to(out, "^");
break;
default:
break;
}
if (specs.width >= 1) STD_FMT::format_to(out, "{}", specs.width);
return STD_FMT::format_to(out, "}}");
if (specs.width >= 1) UNITS_STD_FMT::format_to(out, "{}", specs.width);
return UNITS_STD_FMT::format_to(out, "}}");
}
template<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());
if (it != end && find_first_of(it + 1, end, modifiers.begin(), modifiers.end()) != end)
throw STD_FMT::format_error("only one of '" + std::string(modifiers) +
"' unit modifiers may be used in the format spec");
throw UNITS_STD_FMT::format_error("only one of '" + std::string(modifiers) +
"' unit modifiers may be used in the format spec");
return it;
}
} // namespace mp_units::detail
template<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:
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_unit = false;
@ -274,7 +274,7 @@ private:
struct spec_handler {
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_align(mp_units::detail::fmt_align align) { f.specs.global.align = align; }
@ -290,7 +290,7 @@ private:
if (valid_rep_types.find(type) != std::string_view::npos) {
f.specs.rep.type = type;
} else {
throw STD_FMT::format_error("invalid quantity type specifier");
throw UNITS_STD_FMT::format_error("invalid quantity type specifier");
}
}
@ -326,7 +326,7 @@ private:
constexpr auto valid_modifiers = std::string_view{"UAoansd"};
for (auto it = begin; it != end; ++it) {
if (valid_modifiers.find(*it) == std::string_view::npos)
throw STD_FMT::format_error("invalid unit modifier specified");
throw UNITS_STD_FMT::format_error("invalid unit modifier specified");
}
if (auto it = mp_units::detail::at_most_one_of(begin, end, "UA"); it != end) {
@ -350,7 +350,7 @@ private:
f.specs.unit.separator = mp_units::unit_symbol_separator::space;
else {
if (f.specs.unit.encoding == mp_units::text_encoding::ascii)
throw STD_FMT::format_error("dot unit separator allowed only for Unicode encoding");
throw UNITS_STD_FMT::format_error("dot unit separator allowed only for Unicode encoding");
f.specs.unit.separator = mp_units::unit_symbol_separator::dot;
}
}
@ -359,7 +359,7 @@ private:
}
};
[[nodiscard]] constexpr std::pair<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 end = ctx.end();
@ -409,7 +409,7 @@ private:
}
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);
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);
// Format the `quantity buffer` using specs from `global_format_buffer`
// In the example, equivalent to STD_FMT::format("{:*^10}", "1.2_m")
return STD_FMT::vformat_to(ctx.out(), global_format_buffer, STD_FMT::make_format_args(quantity_buffer));
// In the example, equivalent to UNITS_STD_FMT::format("{:*^10}", "1.2_m")
return UNITS_STD_FMT::vformat_to(ctx.out(), global_format_buffer,
UNITS_STD_FMT::make_format_args(quantity_buffer));
}
}
};

View File

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

View File

@ -23,7 +23,9 @@
cmake_minimum_required(VERSION 3.19)
# find dependencies
find_package(gsl-lite CONFIG REQUIRED)
if(NOT TARGET gsl::gsl-lite)
find_package(gsl-lite CONFIG REQUIRED)
endif()
# check if libc++ is being used
include(CheckLibcxxInUse)
@ -81,7 +83,9 @@ target_include_directories(
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
if(${projectPrefix}LIBCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "14")
find_package(range-v3 CONFIG REQUIRED)
if(NOT TARGET range-v3::range-v3)
find_package(range-v3 CONFIG REQUIRED)
endif()
target_link_libraries(mp-units-core INTERFACE range-v3::range-v3)
endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")

View File

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

View File

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

View File

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

View File

@ -42,7 +42,7 @@ struct AlmostEqualsMatcher : Catch::Matchers::MatcherGenericBase {
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:
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("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
}
SECTION("floating-point representation")
@ -65,9 +65,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
SECTION("iostream") { CHECK(os.str() == "1023.5 Pa"); }
SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
}
}
@ -78,9 +78,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
SECTION("iostream") { CHECK(os.str() == "125 µs"); }
SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
}
SECTION("quantity with a derived unit")
@ -94,9 +94,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
SECTION("iostream") { CHECK(os.str() == "10 m/s²"); }
SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
}
SECTION("volume")
@ -106,9 +106,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
SECTION("iostream") { CHECK(os.str() == "2 m³"); }
SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
}
SECTION("surface tension")
@ -118,9 +118,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
SECTION("iostream") { CHECK(os.str() == "10 N/m"); }
SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
}
}
@ -133,9 +133,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
SECTION("iostream") { CHECK(os.str() == "10 km/h"); }
SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
}
SECTION("angular impulse")
@ -145,9 +145,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
SECTION("iostream") { CHECK(os.str() == "123 N m s"); }
SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
}
SECTION("compressibility")
@ -157,9 +157,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
SECTION("iostream") { CHECK(os.str() == "123 1/Pa"); }
SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
}
SECTION("angular acceleration")
@ -169,9 +169,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
SECTION("iostream") { CHECK(os.str() == "123 rad/s²"); }
SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
}
}
}
@ -186,9 +186,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
SECTION("iostream") { CHECK(os.str() == "2"); }
SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == "2 "); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == "2 "); }
}
SECTION("one with ratio.exp != 0")
@ -198,9 +198,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
SECTION("iostream") { CHECK(os.str() == "2 km/m"); }
SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == "2 km/m"); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == "2 km/m"); }
}
SECTION("percents")
@ -210,9 +210,9 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
SECTION("iostream") { CHECK(os.str() == "15 %"); }
SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with default format {} on a quantity") { CHECK(UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(STD_FMT::format("{:%Q %q}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(UNITS_STD_FMT::format("{:%Q %q}", q) == os.str()); }
}
}
}
@ -221,36 +221,39 @@ TEST_CASE("format string with only %Q should print quantity value only", "[text]
{
SECTION("integral representation")
{
SECTION("positive value") { CHECK(STD_FMT::format("{:%Q}", 123 * isq::speed[km / h]) == "123"); }
SECTION("positive value") { CHECK(UNITS_STD_FMT::format("{:%Q}", 123 * isq::speed[km / h]) == "123"); }
SECTION("negative value") { CHECK(STD_FMT::format("{:%Q}", 5 * isq::length[m] - 10 * isq::length[m]) == "-5"); }
SECTION("negative value")
{
CHECK(UNITS_STD_FMT::format("{:%Q}", 5 * isq::length[m] - 10 * isq::length[m]) == "-5");
}
}
SECTION("floating-point representation")
{
SECTION("positive value")
{
CHECK(STD_FMT::format("{:%Q}", 221. * isq::length[km] / (2 * isq::time[h])) == "110.5");
CHECK(UNITS_STD_FMT::format("{:%Q}", 221. * isq::length[km] / (2 * isq::time[h])) == "110.5");
}
SECTION("negative value")
{
CHECK(STD_FMT::format("{:%Q}", 3.14 * isq::length[m] - 10 * isq::length[m]) == "-6.86");
CHECK(UNITS_STD_FMT::format("{:%Q}", 3.14 * isq::length[m] - 10 * isq::length[m]) == "-6.86");
}
SECTION("nan")
{
CHECK(STD_FMT::format("{:%Q}", std::numeric_limits<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")
{
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")
{
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")
{
CHECK(STD_FMT::format("{:%Uq}", 123 * isq::speed[km / h]) == "km/h");
CHECK(UNITS_STD_FMT::format("{:%Uq}", 123 * isq::speed[km / h]) == "km/h");
// TODO enable this when resistance is defined
// CHECK(STD_FMT::format("{:%Uq}", 123 * isq::resistance[kilo<ohm>]) == "kΩ");
CHECK(STD_FMT::format("{:%Uq}", 123 * isq::time[us]) == "µs");
CHECK(STD_FMT::format("{:%Uq}", 123 * isq::acceleration[m / s2]) == "m/s²");
// CHECK(UNITS_STD_FMT::format("{:%Uq}", 123 * isq::resistance[kilo<ohm>]) == "kΩ");
CHECK(UNITS_STD_FMT::format("{:%Uq}", 123 * isq::time[us]) == "µs");
CHECK(UNITS_STD_FMT::format("{:%Uq}", 123 * isq::acceleration[m / s2]) == "m/s²");
}
SECTION("Unicode text output is used by default")
{
CHECK(STD_FMT::format("{:%q}", 123 * isq::speed[km / h]) == "km/h");
// CHECK(STD_FMT::format("{:%q}", 123 * isq::resistance[kilo<ohm>]) == "kΩ");
CHECK(STD_FMT::format("{:%q}", 123 * isq::time[us]) == "µs");
CHECK(STD_FMT::format("{:%q}", 123 * isq::acceleration[m / s2]) == "m/s²");
CHECK(UNITS_STD_FMT::format("{:%q}", 123 * isq::speed[km / h]) == "km/h");
// CHECK(UNITS_STD_FMT::format("{:%q}", 123 * isq::resistance[kilo<ohm>]) == "kΩ");
CHECK(UNITS_STD_FMT::format("{:%q}", 123 * isq::time[us]) == "µs");
CHECK(UNITS_STD_FMT::format("{:%q}", 123 * isq::acceleration[m / s2]) == "m/s²");
}
SECTION("ASCII text output")
{
CHECK(STD_FMT::format("{:%Aq}", 123 * isq::speed[km / h]) == "km/h");
// CHECK(STD_FMT::format("{:%Aq}", 123 * isq::resistance[kilo<ohm>]) == "kohm");
CHECK(STD_FMT::format("{:%Aq}", 123 * isq::time[us]) == "us");
CHECK(STD_FMT::format("{:%Aq}", 123 * isq::acceleration[m / s2]) == "m/s^2");
CHECK(UNITS_STD_FMT::format("{:%Aq}", 123 * isq::speed[km / h]) == "km/h");
// CHECK(UNITS_STD_FMT::format("{:%Aq}", 123 * isq::resistance[kilo<ohm>]) == "kohm");
CHECK(UNITS_STD_FMT::format("{:%Aq}", 123 * isq::time[us]) == "us");
CHECK(UNITS_STD_FMT::format("{:%Aq}", 123 * isq::acceleration[m / s2]) == "m/s^2");
}
}
@ -289,30 +292,30 @@ TEST_CASE("format string with only %q should print quantity unit symbol only", "
{
SECTION("Solidus for only one element in denominator")
{
CHECK(STD_FMT::format("{:%oq}", 123 * isq::speed[km / h]) == "km/h");
CHECK(STD_FMT::format("{:%oq}", 123 * isq::acceleration[m / s2]) == "m/s²");
CHECK(STD_FMT::format("{:%oq}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
CHECK(UNITS_STD_FMT::format("{:%oq}", 123 * isq::speed[km / h]) == "km/h");
CHECK(UNITS_STD_FMT::format("{:%oq}", 123 * isq::acceleration[m / s2]) == "m/s²");
CHECK(UNITS_STD_FMT::format("{:%oq}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
}
SECTION("Solidus for only one element in denominator is used by default")
{
CHECK(STD_FMT::format("{:%q}", 123 * isq::speed[km / h]) == "km/h");
CHECK(STD_FMT::format("{:%q}", 123 * isq::acceleration[m / s2]) == "m/s²");
CHECK(STD_FMT::format("{:%q}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
CHECK(UNITS_STD_FMT::format("{:%q}", 123 * isq::speed[km / h]) == "km/h");
CHECK(UNITS_STD_FMT::format("{:%q}", 123 * isq::acceleration[m / s2]) == "m/s²");
CHECK(UNITS_STD_FMT::format("{:%q}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
}
SECTION("Always use solidus")
{
CHECK(STD_FMT::format("{:%aq}", 123 * isq::speed[km / h]) == "km/h");
CHECK(STD_FMT::format("{:%aq}", 123 * isq::acceleration[m / s2]) == "m/s²");
CHECK(STD_FMT::format("{:%aq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m s²)");
CHECK(UNITS_STD_FMT::format("{:%aq}", 123 * isq::speed[km / h]) == "km/h");
CHECK(UNITS_STD_FMT::format("{:%aq}", 123 * isq::acceleration[m / s2]) == "m/s²");
CHECK(UNITS_STD_FMT::format("{:%aq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m s²)");
}
SECTION("Never use solidus")
{
CHECK(STD_FMT::format("{:%nq}", 123 * isq::speed[km / h]) == "km h⁻¹");
CHECK(STD_FMT::format("{:%nq}", 123 * isq::acceleration[m / s2]) == "m s⁻²");
CHECK(STD_FMT::format("{:%nq}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
CHECK(UNITS_STD_FMT::format("{:%nq}", 123 * isq::speed[km / h]) == "km h⁻¹");
CHECK(UNITS_STD_FMT::format("{:%nq}", 123 * isq::acceleration[m / s2]) == "m s⁻²");
CHECK(UNITS_STD_FMT::format("{:%nq}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
}
}
@ -320,23 +323,23 @@ TEST_CASE("format string with only %q should print quantity unit symbol only", "
{
SECTION("Space")
{
CHECK(STD_FMT::format("{:%sq}", 123 * isq::force[kg * m / s2]) == "kg m/s²");
CHECK(STD_FMT::format("{:%sq}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
CHECK(STD_FMT::format("{:%asq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m s²)");
CHECK(UNITS_STD_FMT::format("{:%sq}", 123 * isq::force[kg * m / s2]) == "kg m/s²");
CHECK(UNITS_STD_FMT::format("{:%sq}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
CHECK(UNITS_STD_FMT::format("{:%asq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m s²)");
}
SECTION("Space is used by default")
{
CHECK(STD_FMT::format("{:%q}", 123 * isq::force[kg * m / s2]) == "kg m/s²");
CHECK(STD_FMT::format("{:%q}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
CHECK(STD_FMT::format("{:%aq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m s²)");
CHECK(UNITS_STD_FMT::format("{:%q}", 123 * isq::force[kg * m / s2]) == "kg m/s²");
CHECK(UNITS_STD_FMT::format("{:%q}", 123 * isq::pressure[kg / m / s2]) == "kg m⁻¹ s⁻²");
CHECK(UNITS_STD_FMT::format("{:%aq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m s²)");
}
SECTION("Dot")
{
CHECK(STD_FMT::format("{:%dq}", 123 * isq::force[kg * m / s2]) == "kg⋅m/s²");
CHECK(STD_FMT::format("{:%dq}", 123 * isq::pressure[kg / m / s2]) == "kg⋅m⁻¹⋅s⁻²");
CHECK(STD_FMT::format("{:%adq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m⋅s²)");
CHECK(UNITS_STD_FMT::format("{:%dq}", 123 * isq::force[kg * m / s2]) == "kg⋅m/s²");
CHECK(UNITS_STD_FMT::format("{:%dq}", 123 * isq::pressure[kg / m / s2]) == "kg⋅m⁻¹⋅s⁻²");
CHECK(UNITS_STD_FMT::format("{:%adq}", 123 * isq::pressure[kg / m / s2]) == "kg/(m⋅s²)");
}
}
}
@ -345,26 +348,26 @@ TEST_CASE("unknown unit modifiers should throw", "[text][fmt][exception]")
{
SECTION("only the invalid modifier")
{
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%xq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified"));
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%xq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified"));
}
SECTION("invalid modifier in the front")
{
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%xUdaq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified"));
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%xUdaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified"));
}
SECTION("invalid modifier in the end")
{
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%Udaxq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified"));
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%Udaxq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified"));
}
SECTION("invalid modifier in the middle")
{
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%Udxaq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified"));
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%Udxaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error, Catch::Matchers::Message("invalid unit modifier specified"));
}
}
@ -372,40 +375,40 @@ TEST_CASE("repeated unit modifiers should throw", "[text][fmt][exception]")
{
SECTION("text encoding")
{
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%UdaUq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error,
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%UdaUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%dUaUq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error,
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dUaUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%dUUaq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error,
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dUUaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec"));
}
SECTION("solidus")
{
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%aUdaq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error,
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%aUdaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%daUaq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error,
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%daUaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%daaUq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error,
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%daaUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec"));
}
SECTION("separator")
{
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%dUadq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error,
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dUadq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%dadUq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error,
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dadUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%addUq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error,
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%addUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec"));
}
}
@ -414,58 +417,58 @@ TEST_CASE("more then one modifier of the same kind should throw", "[text][fmt][e
{
SECTION("text encoding")
{
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%UdaAq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error,
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%UdaAq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%dAaUq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error,
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dAaUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%dAUaq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error,
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dAUaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'UA' unit modifiers may be used in the format spec"));
}
SECTION("solidus")
{
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%aUdnq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error,
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%aUdnq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%dnUaq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error,
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dnUaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%daoUq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error,
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%daoUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'oan' unit modifiers may be used in the format spec"));
}
SECTION("separator")
{
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%dUasq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error,
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dUasq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%sadUq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error,
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%sadUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec"));
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%adsUq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error,
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%adsUq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error,
Catch::Matchers::Message("only one of 'sd' unit modifiers may be used in the format spec"));
}
}
TEST_CASE("dot separator requested for ASCII encoding should throw", "[text][fmt][exception]")
{
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%dAaq}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error,
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%dAaq}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error,
Catch::Matchers::Message("dot unit separator allowed only for Unicode encoding"));
}
TEST_CASE("%q and %Q can be put anywhere in a format string", "[text][fmt]")
{
SECTION("no space") { CHECK(STD_FMT::format("{:%Q%q}", 123 * isq::speed[km / h]) == "123km/h"); }
SECTION("no space") { CHECK(UNITS_STD_FMT::format("{:%Q%q}", 123 * isq::speed[km / h]) == "123km/h"); }
SECTION("separator") { CHECK(STD_FMT::format("{:%Q###%q}", 123 * isq::speed[km / h]) == "123###km/h"); }
SECTION("separator") { CHECK(UNITS_STD_FMT::format("{:%Q###%q}", 123 * isq::speed[km / h]) == "123###km/h"); }
SECTION("opposite order") { CHECK(STD_FMT::format("{:%q %Q}", 123 * isq::speed[km / h]) == "km/h 123"); }
SECTION("opposite order") { CHECK(UNITS_STD_FMT::format("{:%q %Q}", 123 * isq::speed[km / h]) == "km/h 123"); }
}
TEST_CASE("fill and align specification", "[text][fmt][ostream]")
@ -513,50 +516,50 @@ TEST_CASE("fill and align specification", "[text][fmt][ostream]")
SECTION("default format {} on a quantity")
{
CHECK(STD_FMT::format("|{:0}|", 123 * isq::length[m]) == "|123 m|");
CHECK(STD_FMT::format("|{:10}|", 123 * isq::length[m]) == "| 123 m|");
CHECK(STD_FMT::format("|{:<10}|", 123 * isq::length[m]) == "|123 m |");
CHECK(STD_FMT::format("|{:>10}|", 123 * isq::length[m]) == "| 123 m|");
CHECK(STD_FMT::format("|{:^10}|", 123 * isq::length[m]) == "| 123 m |");
CHECK(STD_FMT::format("|{:*<10}|", 123 * isq::length[m]) == "|123 m*****|");
CHECK(STD_FMT::format("|{:*>10}|", 123 * isq::length[m]) == "|*****123 m|");
CHECK(STD_FMT::format("|{:*^10}|", 123 * isq::length[m]) == "|**123 m***|");
CHECK(UNITS_STD_FMT::format("|{:0}|", 123 * isq::length[m]) == "|123 m|");
CHECK(UNITS_STD_FMT::format("|{:10}|", 123 * isq::length[m]) == "| 123 m|");
CHECK(UNITS_STD_FMT::format("|{:<10}|", 123 * isq::length[m]) == "|123 m |");
CHECK(UNITS_STD_FMT::format("|{:>10}|", 123 * isq::length[m]) == "| 123 m|");
CHECK(UNITS_STD_FMT::format("|{:^10}|", 123 * isq::length[m]) == "| 123 m |");
CHECK(UNITS_STD_FMT::format("|{:*<10}|", 123 * isq::length[m]) == "|123 m*****|");
CHECK(UNITS_STD_FMT::format("|{:*>10}|", 123 * isq::length[m]) == "|*****123 m|");
CHECK(UNITS_STD_FMT::format("|{:*^10}|", 123 * isq::length[m]) == "|**123 m***|");
}
SECTION("full format {:%Q %q} on a quantity")
{
CHECK(STD_FMT::format("|{:0%Q%q}|", 123 * isq::length[m]) == "|123m|");
CHECK(STD_FMT::format("|{:10%Q%q}|", 123 * isq::length[m]) == "| 123m|");
CHECK(STD_FMT::format("|{:<10%Q%q}|", 123 * isq::length[m]) == "|123m |");
CHECK(STD_FMT::format("|{:>10%Q%q}|", 123 * isq::length[m]) == "| 123m|");
CHECK(STD_FMT::format("|{:^10%Q%q}|", 123 * isq::length[m]) == "| 123m |");
CHECK(STD_FMT::format("|{:*<10%Q%q}|", 123 * isq::length[m]) == "|123m******|");
CHECK(STD_FMT::format("|{:*>10%Q%q}|", 123 * isq::length[m]) == "|******123m|");
CHECK(STD_FMT::format("|{:*^10%Q%q}|", 123 * isq::length[m]) == "|***123m***|");
CHECK(UNITS_STD_FMT::format("|{:0%Q%q}|", 123 * isq::length[m]) == "|123m|");
CHECK(UNITS_STD_FMT::format("|{:10%Q%q}|", 123 * isq::length[m]) == "| 123m|");
CHECK(UNITS_STD_FMT::format("|{:<10%Q%q}|", 123 * isq::length[m]) == "|123m |");
CHECK(UNITS_STD_FMT::format("|{:>10%Q%q}|", 123 * isq::length[m]) == "| 123m|");
CHECK(UNITS_STD_FMT::format("|{:^10%Q%q}|", 123 * isq::length[m]) == "| 123m |");
CHECK(UNITS_STD_FMT::format("|{:*<10%Q%q}|", 123 * isq::length[m]) == "|123m******|");
CHECK(UNITS_STD_FMT::format("|{:*>10%Q%q}|", 123 * isq::length[m]) == "|******123m|");
CHECK(UNITS_STD_FMT::format("|{:*^10%Q%q}|", 123 * isq::length[m]) == "|***123m***|");
}
SECTION("value only format {:%Q} on a quantity")
{
CHECK(STD_FMT::format("|{:0%Q}|", 123 * isq::length[m]) == "|123|");
CHECK(STD_FMT::format("|{:10%Q}|", 123 * isq::length[m]) == "| 123|");
CHECK(STD_FMT::format("|{:<10%Q}|", 123 * isq::length[m]) == "|123 |");
CHECK(STD_FMT::format("|{:>10%Q}|", 123 * isq::length[m]) == "| 123|");
CHECK(STD_FMT::format("|{:^10%Q}|", 123 * isq::length[m]) == "| 123 |");
CHECK(STD_FMT::format("|{:*<10%Q}|", 123 * isq::length[m]) == "|123*******|");
CHECK(STD_FMT::format("|{:*>10%Q}|", 123 * isq::length[m]) == "|*******123|");
CHECK(STD_FMT::format("|{:*^10%Q}|", 123 * isq::length[m]) == "|***123****|");
CHECK(UNITS_STD_FMT::format("|{:0%Q}|", 123 * isq::length[m]) == "|123|");
CHECK(UNITS_STD_FMT::format("|{:10%Q}|", 123 * isq::length[m]) == "| 123|");
CHECK(UNITS_STD_FMT::format("|{:<10%Q}|", 123 * isq::length[m]) == "|123 |");
CHECK(UNITS_STD_FMT::format("|{:>10%Q}|", 123 * isq::length[m]) == "| 123|");
CHECK(UNITS_STD_FMT::format("|{:^10%Q}|", 123 * isq::length[m]) == "| 123 |");
CHECK(UNITS_STD_FMT::format("|{:*<10%Q}|", 123 * isq::length[m]) == "|123*******|");
CHECK(UNITS_STD_FMT::format("|{:*>10%Q}|", 123 * isq::length[m]) == "|*******123|");
CHECK(UNITS_STD_FMT::format("|{:*^10%Q}|", 123 * isq::length[m]) == "|***123****|");
}
SECTION("symbol only format {:%q} on a quantity")
{
CHECK(STD_FMT::format("|{:0%q}|", 123 * isq::length[m]) == "|m|");
CHECK(STD_FMT::format("|{:10%q}|", 123 * isq::length[m]) == "| m|");
CHECK(STD_FMT::format("|{:<10%q}|", 123 * isq::length[m]) == "|m |");
CHECK(STD_FMT::format("|{:>10%q}|", 123 * isq::length[m]) == "| m|");
CHECK(STD_FMT::format("|{:^10%q}|", 123 * isq::length[m]) == "| m |");
CHECK(STD_FMT::format("|{:*<10%q}|", 123 * isq::length[m]) == "|m*********|");
CHECK(STD_FMT::format("|{:*>10%q}|", 123 * isq::length[m]) == "|*********m|");
CHECK(STD_FMT::format("|{:*^10%q}|", 123 * isq::length[m]) == "|****m*****|");
CHECK(UNITS_STD_FMT::format("|{:0%q}|", 123 * isq::length[m]) == "|m|");
CHECK(UNITS_STD_FMT::format("|{:10%q}|", 123 * isq::length[m]) == "| m|");
CHECK(UNITS_STD_FMT::format("|{:<10%q}|", 123 * isq::length[m]) == "|m |");
CHECK(UNITS_STD_FMT::format("|{:>10%q}|", 123 * isq::length[m]) == "| m|");
CHECK(UNITS_STD_FMT::format("|{:^10%q}|", 123 * isq::length[m]) == "| m |");
CHECK(UNITS_STD_FMT::format("|{:*<10%q}|", 123 * isq::length[m]) == "|m*********|");
CHECK(UNITS_STD_FMT::format("|{:*>10%q}|", 123 * isq::length[m]) == "|*********m|");
CHECK(UNITS_STD_FMT::format("|{:*^10%q}|", 123 * isq::length[m]) == "|****m*****|");
}
}
@ -567,18 +570,18 @@ TEST_CASE("sign specification", "[text][fmt]")
SECTION("full format {:%Q %q} on a quantity")
{
CHECK(STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", 1 * isq::length[m]) == "1m,+1m,1m, 1m");
CHECK(STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", -1 * isq::length[m]) == "-1m,-1m,-1m,-1m");
CHECK(STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", inf) == "infm,+infm,infm, infm");
CHECK(STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", nan) == "nanm,+nanm,nanm, nanm");
CHECK(UNITS_STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", 1 * isq::length[m]) == "1m,+1m,1m, 1m");
CHECK(UNITS_STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", -1 * isq::length[m]) == "-1m,-1m,-1m,-1m");
CHECK(UNITS_STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", inf) == "infm,+infm,infm, infm");
CHECK(UNITS_STD_FMT::format("{0:%Q%q},{0:%+Q%q},{0:%-Q%q},{0:% Q%q}", nan) == "nanm,+nanm,nanm, nanm");
}
SECTION("value only format {:%Q} on a quantity")
{
CHECK(STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", 1 * isq::length[m]) == "1,+1,1, 1");
CHECK(STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", -1 * isq::length[m]) == "-1,-1,-1,-1");
CHECK(STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", inf) == "inf,+inf,inf, inf");
CHECK(STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", nan) == "nan,+nan,nan, nan");
CHECK(UNITS_STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", 1 * isq::length[m]) == "1,+1,1, 1");
CHECK(UNITS_STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", -1 * isq::length[m]) == "-1,-1,-1,-1");
CHECK(UNITS_STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", inf) == "inf,+inf,inf, inf");
CHECK(UNITS_STD_FMT::format("{0:%Q},{0:%+Q},{0:%-Q},{0:% Q}", nan) == "nan,+nan,nan, nan");
}
}
@ -586,24 +589,24 @@ TEST_CASE("precision specification", "[text][fmt]")
{
SECTION("full format {:%Q %q} on a quantity")
{
CHECK(STD_FMT::format("{:%.0Q %q}", 1.2345 * isq::length[m]) == "1 m");
CHECK(STD_FMT::format("{:%.1Q %q}", 1.2345 * isq::length[m]) == "1.2 m");
CHECK(STD_FMT::format("{:%.2Q %q}", 1.2345 * isq::length[m]) == "1.23 m");
CHECK(STD_FMT::format("{:%.3Q %q}", 1.2345 * isq::length[m]) == "1.234 m");
CHECK(STD_FMT::format("{:%.4Q %q}", 1.2345 * isq::length[m]) == "1.2345 m");
CHECK(STD_FMT::format("{:%.5Q %q}", 1.2345 * isq::length[m]) == "1.23450 m");
CHECK(STD_FMT::format("{:%.10Q %q}", 1.2345 * isq::length[m]) == "1.2345000000 m");
CHECK(UNITS_STD_FMT::format("{:%.0Q %q}", 1.2345 * isq::length[m]) == "1 m");
CHECK(UNITS_STD_FMT::format("{:%.1Q %q}", 1.2345 * isq::length[m]) == "1.2 m");
CHECK(UNITS_STD_FMT::format("{:%.2Q %q}", 1.2345 * isq::length[m]) == "1.23 m");
CHECK(UNITS_STD_FMT::format("{:%.3Q %q}", 1.2345 * isq::length[m]) == "1.234 m");
CHECK(UNITS_STD_FMT::format("{:%.4Q %q}", 1.2345 * isq::length[m]) == "1.2345 m");
CHECK(UNITS_STD_FMT::format("{:%.5Q %q}", 1.2345 * isq::length[m]) == "1.23450 m");
CHECK(UNITS_STD_FMT::format("{:%.10Q %q}", 1.2345 * isq::length[m]) == "1.2345000000 m");
}
SECTION("value only format {:%Q} on a quantity")
{
CHECK(STD_FMT::format("{:%.0Q}", 1.2345 * isq::length[m]) == "1");
CHECK(STD_FMT::format("{:%.1Q}", 1.2345 * isq::length[m]) == "1.2");
CHECK(STD_FMT::format("{:%.2Q}", 1.2345 * isq::length[m]) == "1.23");
CHECK(STD_FMT::format("{:%.3Q}", 1.2345 * isq::length[m]) == "1.234");
CHECK(STD_FMT::format("{:%.4Q}", 1.2345 * isq::length[m]) == "1.2345");
CHECK(STD_FMT::format("{:%.5Q}", 1.2345 * isq::length[m]) == "1.23450");
CHECK(STD_FMT::format("{:%.10Q}", 1.2345 * isq::length[m]) == "1.2345000000");
CHECK(UNITS_STD_FMT::format("{:%.0Q}", 1.2345 * isq::length[m]) == "1");
CHECK(UNITS_STD_FMT::format("{:%.1Q}", 1.2345 * isq::length[m]) == "1.2");
CHECK(UNITS_STD_FMT::format("{:%.2Q}", 1.2345 * isq::length[m]) == "1.23");
CHECK(UNITS_STD_FMT::format("{:%.3Q}", 1.2345 * isq::length[m]) == "1.234");
CHECK(UNITS_STD_FMT::format("{:%.4Q}", 1.2345 * isq::length[m]) == "1.2345");
CHECK(UNITS_STD_FMT::format("{:%.5Q}", 1.2345 * isq::length[m]) == "1.23450");
CHECK(UNITS_STD_FMT::format("{:%.10Q}", 1.2345 * isq::length[m]) == "1.2345000000");
}
}
@ -611,15 +614,15 @@ TEST_CASE("precision specification for integral representation should throw", "[
{
SECTION("full format {:%Q %q} on a quantity")
{
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%.1Q %q}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error,
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%.1Q %q}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error,
Catch::Matchers::Message("precision not allowed for integral quantity representation"));
}
SECTION("value only format {:%Q} on a quantity")
{
REQUIRE_THROWS_MATCHES(STD_FMT::vformat("{:%.1Q}", STD_FMT::make_format_args(1 * isq::length[m])),
STD_FMT::format_error,
REQUIRE_THROWS_MATCHES(UNITS_STD_FMT::vformat("{:%.1Q}", UNITS_STD_FMT::make_format_args(1 * isq::length[m])),
UNITS_STD_FMT::format_error,
Catch::Matchers::Message("precision not allowed for integral quantity representation"));
}
}
@ -628,56 +631,56 @@ TEST_CASE("type specification", "[text][fmt]")
{
SECTION("full format {:%Q %q} on a quantity")
{
CHECK(STD_FMT::format("{:%bQ %q}", 42 * isq::length[m]) == "101010 m");
CHECK(STD_FMT::format("{:%BQ %q}", 42 * isq::length[m]) == "101010 m");
CHECK(STD_FMT::format("{:%dQ %q}", 42 * isq::length[m]) == "42 m");
CHECK(STD_FMT::format("{:%oQ %q}", 42 * isq::length[m]) == "52 m");
CHECK(STD_FMT::format("{:%xQ %q}", 42 * isq::length[m]) == "2a m");
CHECK(STD_FMT::format("{:%XQ %q}", 42 * isq::length[m]) == "2A m");
CHECK(UNITS_STD_FMT::format("{:%bQ %q}", 42 * isq::length[m]) == "101010 m");
CHECK(UNITS_STD_FMT::format("{:%BQ %q}", 42 * isq::length[m]) == "101010 m");
CHECK(UNITS_STD_FMT::format("{:%dQ %q}", 42 * isq::length[m]) == "42 m");
CHECK(UNITS_STD_FMT::format("{:%oQ %q}", 42 * isq::length[m]) == "52 m");
CHECK(UNITS_STD_FMT::format("{:%xQ %q}", 42 * isq::length[m]) == "2a m");
CHECK(UNITS_STD_FMT::format("{:%XQ %q}", 42 * isq::length[m]) == "2A m");
CHECK(STD_FMT::format("{:%aQ %q}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0 m");
CHECK(STD_FMT::format("{:%.3aQ %q}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0 m");
CHECK(STD_FMT::format("{:%AQ %q}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0 m");
CHECK(STD_FMT::format("{:%.3AQ %q}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0 m");
CHECK(STD_FMT::format("{:%eQ %q}", 1.2345678 * isq::length[m]) == "1.234568e+00 m");
CHECK(STD_FMT::format("{:%.3eQ %q}", 1.2345678 * isq::length[m]) == "1.235e+00 m");
CHECK(STD_FMT::format("{:%EQ %q}", 1.2345678 * isq::length[m]) == "1.234568E+00 m");
CHECK(STD_FMT::format("{:%.3EQ %q}", 1.2345678 * isq::length[m]) == "1.235E+00 m");
CHECK(STD_FMT::format("{:%gQ %q}", 1.2345678 * isq::length[m]) == "1.23457 m");
CHECK(STD_FMT::format("{:%gQ %q}", 1.2345678e8 * isq::length[m]) == "1.23457e+08 m");
CHECK(STD_FMT::format("{:%.3gQ %q}", 1.2345678 * isq::length[m]) == "1.23 m");
CHECK(STD_FMT::format("{:%.3gQ %q}", 1.2345678e8 * isq::length[m]) == "1.23e+08 m");
CHECK(STD_FMT::format("{:%GQ %q}", 1.2345678 * isq::length[m]) == "1.23457 m");
CHECK(STD_FMT::format("{:%GQ %q}", 1.2345678e8 * isq::length[m]) == "1.23457E+08 m");
CHECK(STD_FMT::format("{:%.3GQ %q}", 1.2345678 * isq::length[m]) == "1.23 m");
CHECK(STD_FMT::format("{:%.3GQ %q}", 1.2345678e8 * isq::length[m]) == "1.23E+08 m");
CHECK(UNITS_STD_FMT::format("{:%aQ %q}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0 m");
CHECK(UNITS_STD_FMT::format("{:%.3aQ %q}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0 m");
CHECK(UNITS_STD_FMT::format("{:%AQ %q}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0 m");
CHECK(UNITS_STD_FMT::format("{:%.3AQ %q}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0 m");
CHECK(UNITS_STD_FMT::format("{:%eQ %q}", 1.2345678 * isq::length[m]) == "1.234568e+00 m");
CHECK(UNITS_STD_FMT::format("{:%.3eQ %q}", 1.2345678 * isq::length[m]) == "1.235e+00 m");
CHECK(UNITS_STD_FMT::format("{:%EQ %q}", 1.2345678 * isq::length[m]) == "1.234568E+00 m");
CHECK(UNITS_STD_FMT::format("{:%.3EQ %q}", 1.2345678 * isq::length[m]) == "1.235E+00 m");
CHECK(UNITS_STD_FMT::format("{:%gQ %q}", 1.2345678 * isq::length[m]) == "1.23457 m");
CHECK(UNITS_STD_FMT::format("{:%gQ %q}", 1.2345678e8 * isq::length[m]) == "1.23457e+08 m");
CHECK(UNITS_STD_FMT::format("{:%.3gQ %q}", 1.2345678 * isq::length[m]) == "1.23 m");
CHECK(UNITS_STD_FMT::format("{:%.3gQ %q}", 1.2345678e8 * isq::length[m]) == "1.23e+08 m");
CHECK(UNITS_STD_FMT::format("{:%GQ %q}", 1.2345678 * isq::length[m]) == "1.23457 m");
CHECK(UNITS_STD_FMT::format("{:%GQ %q}", 1.2345678e8 * isq::length[m]) == "1.23457E+08 m");
CHECK(UNITS_STD_FMT::format("{:%.3GQ %q}", 1.2345678 * isq::length[m]) == "1.23 m");
CHECK(UNITS_STD_FMT::format("{:%.3GQ %q}", 1.2345678e8 * isq::length[m]) == "1.23E+08 m");
}
SECTION("value only format {:%Q} on a quantity")
{
CHECK(STD_FMT::format("{:%bQ}", 42 * isq::length[m]) == "101010");
CHECK(STD_FMT::format("{:%BQ}", 42 * isq::length[m]) == "101010");
CHECK(STD_FMT::format("{:%dQ}", 42 * isq::length[m]) == "42");
CHECK(STD_FMT::format("{:%oQ}", 42 * isq::length[m]) == "52");
CHECK(STD_FMT::format("{:%xQ}", 42 * isq::length[m]) == "2a");
CHECK(STD_FMT::format("{:%XQ}", 42 * isq::length[m]) == "2A");
CHECK(UNITS_STD_FMT::format("{:%bQ}", 42 * isq::length[m]) == "101010");
CHECK(UNITS_STD_FMT::format("{:%BQ}", 42 * isq::length[m]) == "101010");
CHECK(UNITS_STD_FMT::format("{:%dQ}", 42 * isq::length[m]) == "42");
CHECK(UNITS_STD_FMT::format("{:%oQ}", 42 * isq::length[m]) == "52");
CHECK(UNITS_STD_FMT::format("{:%xQ}", 42 * isq::length[m]) == "2a");
CHECK(UNITS_STD_FMT::format("{:%XQ}", 42 * isq::length[m]) == "2A");
CHECK(STD_FMT::format("{:%aQ}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0");
CHECK(STD_FMT::format("{:%.3aQ}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0");
CHECK(STD_FMT::format("{:%AQ}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0");
CHECK(STD_FMT::format("{:%.3AQ}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0");
CHECK(STD_FMT::format("{:%eQ}", 1.2345678 * isq::length[m]) == "1.234568e+00");
CHECK(STD_FMT::format("{:%.3eQ}", 1.2345678 * isq::length[m]) == "1.235e+00");
CHECK(STD_FMT::format("{:%EQ}", 1.2345678 * isq::length[m]) == "1.234568E+00");
CHECK(STD_FMT::format("{:%.3EQ}", 1.2345678 * isq::length[m]) == "1.235E+00");
CHECK(STD_FMT::format("{:%gQ}", 1.2345678 * isq::length[m]) == "1.23457");
CHECK(STD_FMT::format("{:%gQ}", 1.2345678e8 * isq::length[m]) == "1.23457e+08");
CHECK(STD_FMT::format("{:%.3gQ}", 1.2345678 * isq::length[m]) == "1.23");
CHECK(STD_FMT::format("{:%.3gQ}", 1.2345678e8 * isq::length[m]) == "1.23e+08");
CHECK(STD_FMT::format("{:%GQ}", 1.2345678 * isq::length[m]) == "1.23457");
CHECK(STD_FMT::format("{:%GQ}", 1.2345678e8 * isq::length[m]) == "1.23457E+08");
CHECK(STD_FMT::format("{:%.3GQ}", 1.2345678 * isq::length[m]) == "1.23");
CHECK(STD_FMT::format("{:%.3GQ}", 1.2345678e8 * isq::length[m]) == "1.23E+08");
CHECK(UNITS_STD_FMT::format("{:%aQ}", 1.2345678 * isq::length[m]) == "0x1.3c0ca2a5b1d5dp+0");
CHECK(UNITS_STD_FMT::format("{:%.3aQ}", 1.2345678 * isq::length[m]) == "0x1.3c1p+0");
CHECK(UNITS_STD_FMT::format("{:%AQ}", 1.2345678 * isq::length[m]) == "0X1.3C0CA2A5B1D5DP+0");
CHECK(UNITS_STD_FMT::format("{:%.3AQ}", 1.2345678 * isq::length[m]) == "0X1.3C1P+0");
CHECK(UNITS_STD_FMT::format("{:%eQ}", 1.2345678 * isq::length[m]) == "1.234568e+00");
CHECK(UNITS_STD_FMT::format("{:%.3eQ}", 1.2345678 * isq::length[m]) == "1.235e+00");
CHECK(UNITS_STD_FMT::format("{:%EQ}", 1.2345678 * isq::length[m]) == "1.234568E+00");
CHECK(UNITS_STD_FMT::format("{:%.3EQ}", 1.2345678 * isq::length[m]) == "1.235E+00");
CHECK(UNITS_STD_FMT::format("{:%gQ}", 1.2345678 * isq::length[m]) == "1.23457");
CHECK(UNITS_STD_FMT::format("{:%gQ}", 1.2345678e8 * isq::length[m]) == "1.23457e+08");
CHECK(UNITS_STD_FMT::format("{:%.3gQ}", 1.2345678 * isq::length[m]) == "1.23");
CHECK(UNITS_STD_FMT::format("{:%.3gQ}", 1.2345678e8 * isq::length[m]) == "1.23e+08");
CHECK(UNITS_STD_FMT::format("{:%GQ}", 1.2345678 * isq::length[m]) == "1.23457");
CHECK(UNITS_STD_FMT::format("{:%GQ}", 1.2345678e8 * isq::length[m]) == "1.23457E+08");
CHECK(UNITS_STD_FMT::format("{:%.3GQ}", 1.2345678 * isq::length[m]) == "1.23");
CHECK(UNITS_STD_FMT::format("{:%.3GQ}", 1.2345678e8 * isq::length[m]) == "1.23E+08");
}
}
@ -685,20 +688,20 @@ TEST_CASE("different base types with the # specifier", "[text][fmt]")
{
SECTION("full format {:%Q %q} on a quantity")
{
CHECK(STD_FMT::format("{:%#bQ %q}", 42 * isq::length[m]) == "0b101010 m");
CHECK(STD_FMT::format("{:%#BQ %q}", 42 * isq::length[m]) == "0B101010 m");
CHECK(STD_FMT::format("{:%#oQ %q}", 42 * isq::length[m]) == "052 m");
CHECK(STD_FMT::format("{:%#xQ %q}", 42 * isq::length[m]) == "0x2a m");
CHECK(STD_FMT::format("{:%#XQ %q}", 42 * isq::length[m]) == "0X2A m");
CHECK(UNITS_STD_FMT::format("{:%#bQ %q}", 42 * isq::length[m]) == "0b101010 m");
CHECK(UNITS_STD_FMT::format("{:%#BQ %q}", 42 * isq::length[m]) == "0B101010 m");
CHECK(UNITS_STD_FMT::format("{:%#oQ %q}", 42 * isq::length[m]) == "052 m");
CHECK(UNITS_STD_FMT::format("{:%#xQ %q}", 42 * isq::length[m]) == "0x2a m");
CHECK(UNITS_STD_FMT::format("{:%#XQ %q}", 42 * isq::length[m]) == "0X2A m");
}
SECTION("value only format {:%Q} on a quantity")
{
CHECK(STD_FMT::format("{:%#bQ}", 42 * isq::length[m]) == "0b101010");
CHECK(STD_FMT::format("{:%#BQ}", 42 * isq::length[m]) == "0B101010");
CHECK(STD_FMT::format("{:%#oQ}", 42 * isq::length[m]) == "052");
CHECK(STD_FMT::format("{:%#xQ}", 42 * isq::length[m]) == "0x2a");
CHECK(STD_FMT::format("{:%#XQ}", 42 * isq::length[m]) == "0X2A");
CHECK(UNITS_STD_FMT::format("{:%#bQ}", 42 * isq::length[m]) == "0b101010");
CHECK(UNITS_STD_FMT::format("{:%#BQ}", 42 * isq::length[m]) == "0B101010");
CHECK(UNITS_STD_FMT::format("{:%#oQ}", 42 * isq::length[m]) == "052");
CHECK(UNITS_STD_FMT::format("{:%#xQ}", 42 * isq::length[m]) == "0x2a");
CHECK(UNITS_STD_FMT::format("{:%#XQ}", 42 * isq::length[m]) == "0X2A");
}
}
@ -719,8 +722,8 @@ TEST_CASE("localization with the 'L' specifier", "[text][fmt][localization]")
SECTION("full format {:%LQ %q} on a quantity")
{
CHECK(STD_FMT::format(grp2, "{:%LQ %q}", 299792458 * isq::speed[m / s]) == "2_99_79_24_58 m/s");
CHECK(STD_FMT::format(grp3, "{:%LQ %q}", 299792458 * isq::speed[m / s]) == "299'792'458 m/s");
CHECK(UNITS_STD_FMT::format(grp2, "{:%LQ %q}", 299792458 * isq::speed[m / s]) == "2_99_79_24_58 m/s");
CHECK(UNITS_STD_FMT::format(grp3, "{:%LQ %q}", 299792458 * isq::speed[m / s]) == "299'792'458 m/s");
}
}

View File

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