Merge branch 'master' into feature/more-value-casts

This commit is contained in:
Yves Delley
2024-06-02 08:03:30 +02:00
97 changed files with 2063 additions and 941 deletions

138
.github/workflows/ci-clang-tidy.yml vendored Normal file
View File

@ -0,0 +1,138 @@
# The MIT License (MIT)
#
# Copyright (c) 2018 Mateusz Pusz
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
name: clang-tidy CI
on:
push:
paths-ignore:
- "docs/**"
pull_request:
paths-ignore:
- "docs/**"
jobs:
build:
name: "${{ matrix.formatting }} ${{ matrix.contracts }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}"
runs-on: ${{ matrix.config.os }}
strategy:
fail-fast: false
matrix:
formatting: ["std::format", "fmtlib"]
contracts: ["none", "gsl-lite", "ms-gsl"]
std: [20, 23]
config:
- {
name: "Clang-18",
os: ubuntu-24.04,
compiler:
{
type: CLANG,
version: 18,
cc: "clang-18",
cxx: "clang++-18",
},
lib: "libc++",
cxx_modules: "False",
std_format_support: "True",
conan-config: "",
}
build_type: ["Release", "Debug"]
env:
CC: ${{ matrix.config.compiler.cc }}
CXX: ${{ matrix.config.compiler.cxx }}
steps:
- uses: actions/checkout@v4
- run: echo "cache_id=$(/bin/date -u "+%Y%m%d")" >> $GITHUB_ENV
- name: Cache Conan data
uses: actions/cache@v4
if: always()
env:
cache-name: cache-conan-data
with:
path: ~/.conan2/p
key: clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }}
restore-keys: |
clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-
clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-
clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-
clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-
clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-
clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-
clang-tidy-${{ matrix.config.os }}-${{ matrix.formatting }}-
clang-tidy-${{ matrix.config.os }}-
- uses: hendrikmuhs/ccache-action@v1.2
if: runner.os == 'Linux'
with:
key: ${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}
max-size: 50M
- name: Install Clang
if: matrix.config.compiler.type == 'CLANG'
shell: bash
working-directory: ${{ env.HOME }}
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh ${{ matrix.config.compiler.version }}
sudo apt install -y clang-tools-${{ matrix.config.compiler.version }}
- name: Install Libc++
if: matrix.config.compiler.type == 'CLANG' && matrix.config.lib == 'libc++'
shell: bash
run: |
sudo apt install -y libc++-${{ matrix.config.compiler.version }}-dev libc++abi-${{ matrix.config.compiler.version }}-dev libunwind-${{ matrix.config.compiler.version }}-dev
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.x
- name: Install Ninja
shell: bash
run: |
pip install -U ninja
- name: Install Conan
shell: bash
run: |
pip install -U conan
- name: Configure Conan
shell: bash
run: |
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
sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.cppstd=.*/compiler.cppstd=${{ matrix.std }}/' ~/.conan2/profiles/default
sed -i.backup '/^\[settings\]$/,/^\[/ s/^build_type=.*/build_type=${{ matrix.build_type }}/' ~/.conan2/profiles/default
conan profile show -pr default
- run: echo "std_format=$([ "${{ matrix.formatting }}" == "std::format" ] && echo "True" || echo "False")" >> $GITHUB_ENV
- name: Run clang-tidy
shell: bash
run: |
conan build . -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" \
-c user.mp-units.build:all=True -c user.mp-units.analyze:clang-tidy=True \
-o cxx_modules=${{ matrix.config.cxx_modules }} -o std_format=${{ env.std_format }} -o contracts=${{ matrix.contracts }} ${{ matrix.config.conan-config }}
- name: Clean Conan cache before backup
shell: bash
run: |
conan remove *#~latest --confirm
conan remove *:*#~latest --confirm
conan cache clean "*" -s -b -d

View File

@ -35,12 +35,13 @@ env:
jobs:
build:
name: "${{ matrix.formatting }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}"
name: "${{ matrix.formatting }} ${{ matrix.contracts }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}"
runs-on: ${{ matrix.config.os }}
strategy:
fail-fast: false
matrix:
formatting: ["std::format", "fmtlib"]
contracts: ["none", "gsl-lite", "ms-gsl"]
std: [20, 23]
config:
# - {
@ -57,7 +58,7 @@ jobs:
# }
- {
name: "GCC-12",
os: ubuntu-22.04,
os: ubuntu-24.04,
compiler:
{
type: GCC,
@ -71,7 +72,7 @@ jobs:
}
- {
name: "GCC-13",
os: ubuntu-22.04,
os: ubuntu-24.04,
compiler:
{
type: GCC,
@ -83,6 +84,20 @@ jobs:
std_format_support: "True",
conan-config: "",
}
- {
name: "GCC-14",
os: ubuntu-24.04,
compiler:
{
type: GCC,
version: 14,
cc: "gcc-14",
cxx: "g++-14",
},
cxx_modules: "False",
std_format_support: "True",
conan-config: "",
}
- {
name: "Clang-16",
os: ubuntu-22.04,
@ -100,7 +115,7 @@ jobs:
}
- {
name: "Clang-17",
os: ubuntu-22.04,
os: ubuntu-24.04,
compiler:
{
type: CLANG,
@ -115,7 +130,7 @@ jobs:
}
- {
name: "Clang-18",
os: ubuntu-22.04,
os: ubuntu-24.04,
compiler:
{
type: CLANG,
@ -161,19 +176,20 @@ jobs:
cache-name: cache-conan-data
with:
path: ~/.conan2/p
key: conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }}
key: conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }}
restore-keys: |
conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-
conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-
conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-
conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-
conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-
conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-
conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-
conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-
conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-
conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-
conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-
conan-${{ matrix.config.os }}-${{ matrix.formatting }}-
conan-${{ matrix.config.os }}-
- uses: hendrikmuhs/ccache-action@v1.2
if: runner.os == 'Linux'
with:
key: ${{ matrix.config.os }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}
key: ${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}
max-size: 50M
- name: Install gcc-13
if: matrix.config.compiler.type == 'GCC' && matrix.config.compiler.version == '13'
@ -202,7 +218,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.8"
python-version: 3.x
- name: Install Ninja
shell: bash
run: |
@ -227,7 +243,7 @@ jobs:
run: |
conan create . --user mpusz --channel ${CHANNEL} --lockfile-out=package.lock \
-b mp-units/* -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" \
-c user.mp-units.build:all=True -o cxx_modules=${{ matrix.config.cxx_modules }} -o std_format=${{ env.std_format }} ${{ matrix.config.conan-config }}
-c user.mp-units.build:all=True -o cxx_modules=${{ matrix.config.cxx_modules }} -o std_format=${{ env.std_format }} -o contracts=${{ matrix.contracts }} ${{ matrix.config.conan-config }}
- name: Obtain package reference
id: get-package-ref
shell: bash
@ -244,8 +260,8 @@ jobs:
shell: bash
run: |
conan remove mp-units --confirm
conan remove *#!latest --confirm
conan remove *:*#!latest --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 }}
@ -254,13 +270,13 @@ jobs:
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
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.8"
python-version: 3.x
- name: Install Conan
shell: bash
run: |

View File

@ -26,13 +26,13 @@ on: [push, pull_request]
jobs:
check:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.8
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: 3.8
python-version: 3.x
- name: Install dependencies
run: |
python -m pip install --upgrade pip

156
.github/workflows/ci-freestanding.yml vendored Normal file
View File

@ -0,0 +1,156 @@
# The MIT License (MIT)
#
# Copyright (c) 2018 Mateusz Pusz
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
name: Freestanding CI
on:
push:
paths-ignore:
- "docs/**"
pull_request:
paths-ignore:
- "docs/**"
jobs:
build:
name: "${{ matrix.formatting }} ${{ matrix.contracts }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}"
runs-on: ${{ matrix.config.os }}
strategy:
fail-fast: false
matrix:
formatting: ["std::format", "fmtlib"]
contracts: ["none"]
std: [23]
config:
- {
name: "GCC-14",
os: ubuntu-24.04,
compiler:
{
type: GCC,
version: 14,
cc: "gcc-14",
cxx: "g++-14",
},
cxx_modules: "False",
std_format_support: "True",
conan-config: "",
}
- {
name: "Clang-18",
os: ubuntu-24.04,
compiler:
{
type: CLANG,
version: 18,
cc: "clang-18",
cxx: "clang++-18",
},
lib: "libc++",
cxx_modules: "True",
std_format_support: "True",
conan-config: "",
}
build_type: ["Release", "Debug"]
# TODO For some reason Clang-18 Debug with -ffreestanding does not pass CMakeTestCXXCompiler
exclude:
- build_type: "Debug"
config: { name: "Clang-18" }
env:
CC: ${{ matrix.config.compiler.cc }}
CXX: ${{ matrix.config.compiler.cxx }}
steps:
- uses: actions/checkout@v4
- run: echo "cache_id=$(/bin/date -u "+%Y%m%d")" >> $GITHUB_ENV
- name: Cache Conan data
uses: actions/cache@v4
if: always()
env:
cache-name: cache-conan-data
with:
path: ~/.conan2/p
key: freestanding-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }}
restore-keys: |
freestanding-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-
freestanding-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-
freestanding-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-
freestanding-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-
freestanding-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-
freestanding-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-
freestanding-${{ matrix.config.os }}-${{ matrix.formatting }}-
freestanding-${{ matrix.config.os }}-
- uses: hendrikmuhs/ccache-action@v1.2
if: runner.os == 'Linux'
with:
key: ${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}
max-size: 50M
- name: Install Clang
if: matrix.config.compiler.type == 'CLANG'
shell: bash
working-directory: ${{ env.HOME }}
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh ${{ matrix.config.compiler.version }}
sudo apt install -y clang-tools-${{ matrix.config.compiler.version }}
- name: Install Libc++
if: matrix.config.compiler.type == 'CLANG' && matrix.config.lib == 'libc++'
shell: bash
run: |
sudo apt install -y libc++-${{ matrix.config.compiler.version }}-dev libc++abi-${{ matrix.config.compiler.version }}-dev libunwind-${{ matrix.config.compiler.version }}-dev
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.x
- name: Install Ninja
shell: bash
run: |
pip install -U ninja
- name: Install Conan
shell: bash
run: |
pip install -U conan
- name: Configure Conan
shell: bash
run: |
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
sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.cppstd=.*/compiler.cppstd=${{ matrix.std }}/' ~/.conan2/profiles/default
sed -i.backup '/^\[settings\]$/,/^\[/ s/^build_type=.*/build_type=${{ matrix.build_type }}/' ~/.conan2/profiles/default
conan profile show -pr default
- run: echo "std_format=$([ "${{ matrix.formatting }}" == "std::format" ] && echo "True" || echo "False")" >> $GITHUB_ENV
- name: Build freestanding
shell: bash
run: |
conan build . -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" \
-c user.mp-units.build:all=True -c tools.build:cxxflags="['-ffreestanding']" \
-o cxx_modules=${{ matrix.config.cxx_modules }} -o std_format=${{ env.std_format }} -o contracts=${{ matrix.contracts }} -o freestanding=True ${{ matrix.config.conan-config }}
- name: Clean Conan cache before backup
shell: bash
run: |
conan remove *#~latest --confirm
conan remove *:*#~latest --confirm
conan cache clean "*" -s -b -d

View File

@ -36,12 +36,13 @@ on:
jobs:
test_package:
name: "${{ matrix.formatting }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}"
name: "${{ matrix.formatting }} ${{ matrix.contracts }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}"
runs-on: ${{ matrix.config.os }}
strategy:
fail-fast: false
matrix:
formatting: ["std::format", "fmtlib"]
contracts: ["none", "gsl-lite", "ms-gsl"]
std: [20, 23]
config:
# - {
@ -56,7 +57,7 @@ jobs:
# }
- {
name: "GCC-12",
os: ubuntu-22.04,
os: ubuntu-24.04,
compiler:
{
type: GCC,
@ -69,7 +70,7 @@ jobs:
}
- {
name: "GCC-13",
os: ubuntu-22.04,
os: ubuntu-24.04,
compiler:
{
type: GCC,
@ -80,6 +81,19 @@ jobs:
cxx_modules: "False",
std_format_support: "True",
}
- {
name: "GCC-14",
os: ubuntu-24.04,
compiler:
{
type: GCC,
version: 14,
cc: "gcc-14",
cxx: "g++-14",
},
cxx_modules: "False",
std_format_support: "True"
}
- {
name: "Clang-16",
os: ubuntu-22.04,
@ -96,7 +110,7 @@ jobs:
}
- {
name: "Clang-17",
os: ubuntu-22.04,
os: ubuntu-24.04,
compiler:
{
type: CLANG,
@ -110,7 +124,7 @@ jobs:
}
- {
name: "Clang-18",
os: ubuntu-22.04,
os: ubuntu-24.04,
compiler:
{
type: CLANG,
@ -124,7 +138,7 @@ jobs:
}
- {
name: "Apple Clang 15",
os: macos-13,
os: macos-14,
compiler:
{
type: APPLE_CLANG,
@ -147,7 +161,7 @@ jobs:
steps:
- name: Downcase 'build_type'
id: build_type
uses: ASzc/change-string-case-action@v5
uses: ASzc/change-string-case-action@v6
with:
string: ${{ matrix.build_type }}
- uses: actions/checkout@v4
@ -159,13 +173,14 @@ jobs:
cache-name: cache-conan-data
with:
path: ~/.conan2/p
key: cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }}
key: cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }}
restore-keys: |
cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-
cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-
cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-
cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-
cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-
cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-
cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-
cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-
cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-
cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-
cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-
cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-
cmake-${{ matrix.config.os }}-
- name: Install gcc-13
@ -195,7 +210,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.8"
python-version: 3.x
- name: Install Ninja
shell: bash
run: |
@ -220,7 +235,7 @@ jobs:
shell: bash
run: |
conan install . -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" -c user.mp-units.build:all=False \
-o cxx_modules=${{ matrix.config.cxx_modules }} -o std_format=${{ env.std_format }}
-o cxx_modules=${{ matrix.config.cxx_modules }} -o std_format=${{ env.std_format }} -o contracts=${{ matrix.contracts }}
- name: Provide dependencies for the build
shell: bash
working-directory: src
@ -291,3 +306,9 @@ jobs:
working-directory: test_package/build/install/${{ matrix.build_type }}
run: |
./test_package
- name: Clean Conan cache before backup
shell: bash
run: |
conan remove *#~latest --confirm
conan remove *:*#~latest --confirm
conan cache clean "*" -s -b -d

View File

@ -59,7 +59,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@ -74,7 +74,7 @@ jobs:
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
if: matrix.language != 'cpp'
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@v3
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@ -97,7 +97,7 @@ jobs:
if: matrix.language == 'cpp'
uses: actions/setup-python@v5
with:
python-version: "3.8"
python-version: 3.x
- name: Conan build
if: matrix.language == 'cpp'
run: |
@ -108,6 +108,6 @@ jobs:
conan build .. -s compiler.cppstd=20 -c user.mp-units.build:all=True -o std_format=False -b missing
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"

View File

@ -8,15 +8,18 @@
- (!) feat: formatting grammar improved and units formatting support added
- (!) feat: `has_unit_symbol` support removed
- (!) feat: ABI concerns resolved with introduction of u8 strings for symbols
- (!) feat: API-related Conan, CMake, and preprocessor options redesigned
- (!) feat: :boom: `core.h` removed
- feat: implicit point origins support added
- feat: unit default point origin support added
- feat: `fma`, `isfinite`, `isinf`, and `isnan` math function added by [@NAThompson](https://github.com/NAThompson)
- feat: `fma` for quantity points added
- feat: `quantity_point` support added for `quantity_cast` and `value_cast`
- feat: `value_cast<Unit, Representation>` added
- feat: `value_cast<Quantity>(q)`, `value_cast<Quantity>(qp)` and `value_cast<QuantityPoint>(qp)` added by [@burnpanck](https://github.com/burnpanck)
- feat: `interconvertible(QuantitySpec, QuantitySpec)` added
- feat: `qp.quantity_from_zero()` added
- feat: `underlying_type` type trait added
- feat: `value_type` type trait added
- feat: do not print space between a number and `percent` or `per_mille`
- feat: `ppm` parts per million added by [@nebkat](https://github.com/nebkat)
- feat: `atan2` 2-argument arctangent added by [@nebkat](https://github.com/nebkat)
@ -26,6 +29,10 @@
- feat: unit text output support added
- feat: formatting error messages improved
- feat: improve types readability by eliminating extraneous `()` in references, prefixes, and `kind_of`
- feat: dimension text output added
- feat: some light and radiation ISQ quantities added
- feat: New formatting specification implemented
- feat: allow configuring GSL library use
- (!) refactor: `zero_Fahrenheit` renamed to `zeroth_degree_Fahrenheit`
- (!) refactor: SI-related trigonometric functions moved to the `si` subnamespace
- (!) refactor: `math.h` header file broke up to smaller pieces
@ -33,15 +40,28 @@
- (!) refactor: `ReferenceOf` does not take a dimension anymore
- (!) refactor: 'o' replaced with '1' as a modifier for `unit_symbol_solidus::one_denominator`
- (!) refactor: `get_kind()` now returns `kind_of`
- (!) refactor: FMT macros moved to `compat_macros.h`
- (!) refactor: `fixed_string` refactored to reflect the latest changes to [P3094R2](https://wg21.link/P3094R2)
- (!) refactor: `basic_symbol_text` renamed to `symbol_text`
- (!) refactor: `ratio` hidden as an implementation detail behind `mag_ratio`
- (!) refactor: `framework.h` introduced
- (!) refactor: type list tools made an implementation detail of the library
- (!) refactor: header files with the entire system definitions moved up in the directory tree
- refactor: math functions constraints refactored
- refactor: `si_quantities.h` added to improve compile-times
- refactor: `validate_ascii_string` refactored to `is_basic_literal_character_set`
- refactor: `underlying_type` split to `wrapped_type` and `value_type` and used in code
- refactor: code refactored to comply with clang-tidy
- refactor: remove dependency on `<ranges>` header and switch to use an iterator-based `copy` algorithm
- refactor: `terminate` replaced with `abort` and a header file added
- fix: `QuantityLike` conversions required `Q::rep` instead of using one provided by `quantity_like_traits`
- fix: `QuantitySpec[Unit]` replaced with `make_reference` in `value_cast`
- fix: `ice_point` is now defined with the integral offset from `absolute_zero`
- fix: performance regression in `sudo_cast` fixed
- fix: explicit object parameter support fixed
- fix: missing `version` header file added to `hacks.h`
- fix: `quantity_cast` to accept lvalue references (thanks [@burnpanck](https://github.com/burnpanck))
- fix: `value_cast` with lvalue references to `quantity_point` (thanks [@burnpanck](https://github.com/burnpanck))
- docs: project blog and first posts added
- docs: project documentation layout refactored
- docs: "Interoperability with Other Libraries" chapter added
@ -56,16 +76,32 @@
- docs: mkdocs social plugin enabled
- docs: project logo and custom color scheme added
- docs: minimum compiler requirements updated
- docs: unit symbols admonition extended in the "Quick Start" chapter
- docs: Cairo dependency described in the MkDocs section
- docs: "hello units" example updated with dimensions output
- docs: "Text Output" chapter updated with the recent formatting changes
- docs: formatting grammar language changed to EBNF
- docs: "Project structure" chapter expanded
- (!) build: Conan and CMake options refactored
- (!) build: `MP_UNITS_AS_SYSTEM_HEADERS` support removed
- (!) build: `MP_UNITS_AS_SYSTEM_HEADERS` renamed to `MP_UNITS_BUILD_AS_SYSTEM_HEADERS`
- (!) build: `MP_UNITS_BUILD_LA` and `MP_UNITS_IWYU` CMake options now have `_DEV_` in the name
- build: gsl-lite updated to 0.41.0
- build: catch2 updated to 3.5.1
- build: fmt updated to 10.2.1
- build: gitpod environment updated
- build: `check_cxx_feature_supported` added
- build: IWYU path handling fixed
- build: IWYU enabled on GCC
- build: `CMAKE_EXPORT_COMPILE_COMMANDS` flag enabled for the developer's build
- build(conan): `generate()` now set `cache_variables`
- build(conan): `can_run` check added before running tests
- ci: Conan and CMake CI now use different cache names
- ci: gcc-14 added
### 2.1.1 <small>May 16, 2024</small> { id="2.1.1" }
- fix: unit tests compilation on gcc-14 fixed
- fix: explicit `this` parameter support fixed
### 2.1.0 <small>December 9, 2023</small> { id="2.1.0" }

View File

@ -36,13 +36,13 @@ contact:
given-names: Mateusz
family-names: Pusz
repository-code: "https://github.com/mpusz/units"
url: "https://mpusz.github.io/units"
repository-code: "https://github.com/mpusz/mp-units"
url: "https://mpusz.github.io/mp-units"
repository-artifact: "https://conan.io/center/mp-units"
version: 0.7.0
date-released: "2021-05-11"
version: 2.1.1
date-released: "2024-05-16"
identifiers:
- description: "The GitHub release URL of tag 0.7.0"
- description: "The GitHub release URL of tag 2.1.1"
type: url
value: "https://github.com/mpusz/units/releases/tag/v0.7.0"
value: "https://github.com/mpusz/mp-units/releases/tag/v2.1.1"

View File

@ -67,8 +67,10 @@ endif()
# add project code
add_subdirectory(src)
# add usage example
add_subdirectory(example)
if(NOT ${projectPrefix}API_FREESTANDING)
# add usage example
add_subdirectory(example)
endif()
# add unit tests
enable_testing()

View File

@ -1,11 +1,13 @@
<img align="right" height=135px src="docs/assets/images/mp-units-color.svg">
[![GitHub license](https://img.shields.io/github/license/mpusz/mp-units?cacheSeconds=3600&color=informational&label=License)](./LICENSE.md)
[![GitHub license](https://img.shields.io/badge/C%2B%2B-20-blue)](https://en.cppreference.com/w/cpp/compiler_support#cpp20)
[![GitHub license](https://img.shields.io/badge/C%2B%2B-20%2F23-blue)](https://en.cppreference.com/w/cpp/compiler_support#cpp20)
[![Conan CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-conan.yml?branch=master&label=Conan%20CI)](https://github.com/mpusz/mp-units/actions?query=workflow%3A%22Conan%20CI%22+branch%3Amaster)
[![CMake CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-test-package-cmake.yml?branch=master&label=CMake%20CI)](https://github.com/mpusz/mp-units/actions?query=workflow%3A%22CMake+Test+Package+CI%22+branch%3Amaster)
[![Formatting CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-formatting.yml?branch=master&label=Formatting%20CI)](https://github.com/mpusz/mp-units/actions?query=workflow%3A%22Formatting%20CI%22+branch%3Amaster)
[![Conan CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-conan.yml?branch=master&label=Conan%20CI)](https://github.com/mpusz/mp-units/actions/workflows/ci-conan.yml)
[![CMake CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-test-package-cmake.yml?branch=master&label=CMake%20CI)](https://github.com/mpusz/mp-units/actions/workflows/ci-test-package-cmake.yml)
[![clang-tidy CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-clang-tidy.yml?branch=master&label=clang-tidy%20CI)](https://github.com/mpusz/mp-units/actions/workflows/ci-clang-tidy.yml)
[![Freestanding CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-freestanding.yml?branch=master&label=Freestanding%20CI)](https://github.com/mpusz/mp-units/actions/workflows/ci-freestanding.yml)
[![Formatting CI](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/ci-formatting.yml?branch=master&label=Formatting%20CI)](https://github.com/mpusz/mp-units/actions/workflows/ci-formatting.yml)
[![GitHub Workflow Documentation](https://img.shields.io/github/actions/workflow/status/mpusz/mp-units/documentation.yml?branch=master&label=Documentation)](https://github.com/mpusz/mp-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)
@ -130,4 +132,4 @@ int main()
}
```
_Try it on the [Compiler Explorer](https://godbolt.org/z/fsdovTcYh)._
_Try it on the [Compiler Explorer](https://godbolt.org/z/nhqhT8Mzb)._

View File

@ -65,12 +65,16 @@ class MPUnitsConan(ConanFile):
"std_format": ["auto", True, False],
"string_view_ret": ["auto", True, False],
"no_crtp": ["auto", True, False],
"contracts": ["none", "gsl-lite", "ms-gsl"],
"freestanding": [True, False],
}
default_options = {
"cxx_modules": "auto",
"std_format": "auto",
"string_view_ret": "auto",
"no_crtp": "auto",
"contracts": "gsl-lite",
"freestanding": "False",
}
tool_requires = "cmake/[>=3.29]"
implements = "auto_header_only"
@ -191,6 +195,10 @@ class MPUnitsConan(ConanFile):
def _skip_la(self):
return bool(self.conf.get("user.mp-units.build:skip_la", default=False))
@property
def _run_clang_tidy(self):
return bool(self.conf.get("user.mp-units.analyze:clang-tidy", default=False))
def set_version(self):
content = load(self, os.path.join(self.recipe_folder, "src/CMakeLists.txt"))
version = re.search(
@ -199,13 +207,17 @@ class MPUnitsConan(ConanFile):
self.version = version.strip()
def requirements(self):
self.requires("gsl-lite/0.41.0")
if self._use_fmtlib:
if self.options.contracts == "gsl-lite":
self.requires("gsl-lite/0.41.0")
elif self.options.contracts == "ms-gsl":
self.requires("ms-gsl/4.0.0")
if self._use_fmtlib and not self.options.freestanding:
self.requires("fmt/10.2.1")
def build_requirements(self):
if self._build_all:
self.test_requires("catch2/3.5.1")
if not self.options.freestanding:
self.test_requires("catch2/3.5.1")
if not self._skip_la:
self.test_requires("wg21-linear_algebra/0.7.3")
@ -214,16 +226,23 @@ class MPUnitsConan(ConanFile):
for key, value in self._option_feature_map.items():
if self.options.get_safe(key) == True:
self._check_feature_supported(key, value)
if self.options.freestanding and self.options.contracts != "none":
raise ConanInvalidConfiguration(
"'contracts' should be set to 'none' for a freestanding build"
)
def layout(self):
cmake_layout(self)
def generate(self):
tc = CMakeToolchain(self)
tc.absolute_paths = True # only needed for CMake CI
if self._build_all:
tc.cache_variables["CMAKE_EXPORT_COMPILE_COMMANDS"] = True
tc.cache_variables["CMAKE_VERIFY_INTERFACE_HEADER_SETS"] = True
tc.cache_variables["MP_UNITS_DEV_BUILD_LA"] = not self._skip_la
if self._run_clang_tidy:
tc.cache_variables["MP_UNITS_DEV_CLANG_TIDY"] = True
if self._build_cxx_modules:
tc.cache_variables["CMAKE_CXX_SCAN_FOR_MODULES"] = True
tc.cache_variables["MP_UNITS_BUILD_CXX_MODULES"] = str(
@ -236,6 +255,10 @@ class MPUnitsConan(ConanFile):
self.options.string_view_ret
).upper()
tc.cache_variables["MP_UNITS_API_NO_CRTP"] = str(self.options.no_crtp).upper()
tc.cache_variables["MP_UNITS_API_CONTRACTS"] = str(
self.options.contracts
).upper()
tc.cache_variables["MP_UNITS_API_FREESTANDING"] = self.options.freestanding
tc.generate()
deps = CMakeDeps(self)
deps.generate()
@ -262,8 +285,12 @@ class MPUnitsConan(ConanFile):
def package_info(self):
compiler = self.settings.compiler
self.cpp_info.components["core"].requires = ["gsl-lite::gsl-lite"]
if self._use_fmtlib:
self.cpp_info.components["core"]
if self.options.contracts == "gsl-lite":
self.cpp_info.components["core"].requires = ["gsl-lite::gsl-lite"]
elif self.options.contracts == "ms-gsl":
self.cpp_info.components["core"].requires = ["ms-gsl::ms-gsl"]
if self._use_fmtlib and not self.options.freestanding:
self.cpp_info.components["core"].requires.append("fmt::fmt")
if compiler == "msvc":
self.cpp_info.components["core"].cxxflags = ["/utf-8"]

View File

@ -31,12 +31,12 @@ the C++ modules' support is still really limited.
To benefit from C++ modules, we need at least:
- CMake 3.28.1
- CMake 3.29.3
- Ninja 1.11
- clang-17
In the upcoming months, hopefully, the situation will improve with the gcc-14 release and
bug fixes in MSVC.
In the upcoming months, hopefully, the situation will improve with the bug fixes in
CMake, gcc-14, and MSVC.
!!! note
@ -64,6 +64,11 @@ flowchart TD
The easiest way to use them is just to `import mp_units;` at the beginning of your translation unit
(see the [Quick Start](../../getting_started/quick_start.md) chapter for some usage examples).
!!! note
C++20 modules support still have some issues when imported from the installed CMake target.
See the following [GitLab issue for more details](https://gitlab.kitware.com/cmake/cmake/-/issues/25909#note_1525377).
In this release, we also highly limited the number of CMake targets (:boom: **breaking change** :boom:).
Now, they correspond exactly to the C++ modules they provide. This means that many smaller partial
targets were removed. We also merged text output targets with the core library's definition.
@ -93,9 +98,9 @@ In version 2.2, the following headers have a new location or contents:
Benefiting from this opportunity, we also cleaned up core and systems definitions
(:boom: **breaking change** :boom:).
Regarding the library's core, we exposed only one header `framework.h` that exposes all of
the library's framework so the user does not have to enumerate files like `unit.h`, `quantity.h`,
and `quantity_point.h` anymore. Those headers are not gone, they were put to
Regarding the library's core, we removed `core.h` and exposed only one header `framework.h` that
provides all of the library's framework so the user does not have to enumerate files like `unit.h`,
`quantity.h`, and `quantity_point.h` anymore. Those headers are not gone, they were put to
the `mp-units/framework` subheader. So they are still there if you really need them.
Regarding the changes in systems definitions, we moved the wrapper header files with the entire
@ -161,6 +166,21 @@ Additionally, some CMake options were renamed to better express the impact on ou
- `MP_UNITS_DEV_*` - options primarily useful for the project developers or people who want to
compile our unit tests and examples.
## Configurable contracts checking
Before this release, the library always depended on [gsl-lite](https://github.com/gsl-lite/gsl-lite)
to perform runtime contract and asserts checking. In this release we introduced new options
to control if contract checking should be based on [gsl-lite](https://github.com/gsl-lite/gsl-lite),
[ms-gsl](https://github.com/microsoft/GSL), or maybe completely disabled.
## Freestanding support
From this release it is possible to configure the library in the
[freestanding](https://en.cppreference.com/w/cpp/freestanding) mode. This limits the functionality
and interface of the library to be compatible with the freestanding implementations.
!!! info
To learn more, please refer to the [Build options](../../getting_started/installation_and_usage.md#build-options)
@ -191,7 +211,7 @@ origins. For example:
```
As we can see above, the new design allows
[direct-initialization](https://en.cppreference.com/w/cpp/language/direct_initialization) of a
[direct-initializing](https://en.cppreference.com/w/cpp/language/direct_initialization)
`quantity_point` class template from a `quantity`, but only if the former one is defined in terms
of the implicit point origin. Otherwise, an explicit origin still always has to be provided during
initialization.
@ -199,7 +219,7 @@ initialization.
Also, we introduced the possibility of specifying a default point origin in the unit definition.
With that, we could provide proper temperature scales without forcing the user to always use
the origins explicitly. Also, a new member function, `.quantity_from_zero(),` was introduced
that always returns the quantity from the unit's specific point origin or from the absolute
that always returns the quantity from the unit's specific point origin or from the implicit
point origin otherwise.
=== "Now"
@ -244,20 +264,30 @@ named with its corresponding unit and with the `si::zeroth_degree_Celsius`
about potential ABI issues when different translation units are compiled with different ordinary
literal encodings. Those issues were resolved with a change to units definitions
(:boom: **breaking change** :boom:). It affects only units that specify both Unicode and ASCII
symbols. The new design requires the Unicode symbol to be provided as a UTF-8 literal:
symbols. The new design requires the Unicode symbol to be provided as a UTF-8 literal.
This also means that the `basic_symbol_text` has fixed character types for both symbols. This
is why it was renamed to `symbol_text` (:boom: **breaking change** :boom:).
=== "Now"
```cpp
inline constexpr struct ohm : named_unit<{u8"Ω", "ohm"}, volt / ampere> {} ohm;
inline constexpr struct ohm : named_unit<symbol_text{u8"Ω", "ohm"}, volt / ampere> {} ohm;
```
=== "Before"
```cpp
inline constexpr struct ohm : named_unit<{"Ω", "ohm"}, volt / ampere> {} ohm;
inline constexpr struct ohm : named_unit<basic_symbol_text{"Ω", "ohm"}, volt / ampere> {} ohm;
```
!!! note
On C++20-compliant compilers it should be enough to type the following in the unit's definition:
```cpp
inline constexpr struct ohm : named_unit<{u8"Ω", "ohm"}, volt / ampere> {} ohm;
```
## Improved text output
@ -333,7 +363,7 @@ Example 1 (clang):
61 | concept QuantityOf = Quantity<Q> && QuantitySpecOf<std::remove_const_t<decltype(Q::quantity_spec)>, QS>;
| ^
note: because 'implicitly_convertible(kind_of_<derived_quantity_spec<isq::time, per<isq::length> > >{}, struct speed{{{}}})' evaluated to false
147 | QuantitySpec<T> && QuantitySpec<std::remove_const_t<decltype(QS)>> && implicitly_convertible(T{}, QS) &&
147 | QuantitySpec<T> && QuantitySpec<decltype(QS)> && implicitly_convertible(T{}, QS) &&
| ^
1 error generated.
Compiler returned: 1
@ -356,7 +386,7 @@ Example 1 (clang):
61 | concept QuantityOf = Quantity<Q> && QuantitySpecOf<std::remove_const_t<decltype(Q::quantity_spec)>, QS>;
| ^
note: because 'implicitly_convertible(kind_of_<derived_quantity_spec<isq::time, per<isq::length> >{{}, {{}}}>{}, struct speed{{{}}})' evaluated to false
147 | QuantitySpec<T> && QuantitySpec<std::remove_const_t<decltype(QS)>> && implicitly_convertible(T{}, QS) &&
147 | QuantitySpec<T> && QuantitySpec<decltype(QS)> && implicitly_convertible(T{}, QS) &&
| ^
1 error generated.
Compiler returned: 1

View File

@ -13,13 +13,13 @@
The table below provides the minimum compiler version required to compile the code using a specific
C++ feature:
| C++ Feature | C++ version | gcc | clang | apple-clang | MSVC |
|-----------------------------------------------------------|:-----------:|:---:|:-----:|:-----------:|:----:|
| **Minimum support** | 20 | 12 | 16 | 15 | None |
| **`std::format`** | 20 | 13 | 17 | None | None |
| **C++ modules** | 20 | 14 | 17 | None | None |
| **Static `constexpr` variables in `constexpr` functions** | 23 | 13 | 17 | None | None |
| **Explicit `this` parameter** | 23 | 14 | 18 | None | None |
| C++ Feature | C++ version | gcc | clang | apple-clang | MSVC |
|-----------------------------------------------------------|:-----------:|:----:|:-----:|:-----------:|:----:|
| **Minimum support** | 20 | 12 | 16 | 15 | None |
| **`std::format`** | 20 | 13 | 17 | None | None |
| **C++ modules** | 20 | None | 17 | None | None |
| **Static `constexpr` variables in `constexpr` functions** | 23 | 13 | 17 | None | None |
| **Explicit `this` parameter** | 23 | 14 | 18 | None | None |
!!! important
@ -29,7 +29,7 @@ C++ feature:
## `std::format`
- Provides [powerful text formatting capabilities](../users_guide/framework_basics/text_output.md#stdformat)
- Provides [powerful text formatting capabilities](../users_guide/framework_basics/text_output.md#text-formatting)
for C++.
- An alternative [fmtlib](https://github.com/fmtlib/fmt) library can be used instead if
- the C++ language feature is not supported,

View File

@ -176,7 +176,7 @@ we have to obey the rules and be consistent with ISO specifications.
!!! note
We do understand engineering reality and the constraints of some environments. This is why the library
has the option of [ASCII-only Quantity Symbols](../users_guide/framework_basics/text_output.md#unit-symbol-formatting).
has the option of [ASCII-only Quantity Symbols](../users_guide/framework_basics/text_output.md#unit_symbol_formatting).
## Why don't we have CMake options to disable the building of tests and examples?

View File

@ -18,8 +18,8 @@ projects:
- in case this library becomes part of the C++ standard, it will have no external dependencies
but until then, it depends on the following:
- [gsl-lite](https://github.com/gsl-lite/gsl-lite) to verify runtime contracts with
the `gsl_Expects` macro,
- [gsl-lite](https://github.com/gsl-lite/gsl-lite) or [ms-gsl](https://github.com/microsoft/GSL)
to verify runtime contracts (if contract checking is enabled),
- [{fmt}](https://github.com/fmtlib/fmt) to provide text formatting of quantities
(if `std::format` is not supported yet on a specific compiler).
@ -56,7 +56,7 @@ projects:
handle the dependencies.
To learn more about the rationale, please check our
[FAQ](faq.md#why-dont-we-have-cmake-options-to-disable-building-of-tests-and-examples).
[FAQ](faq.md#why-dont-we-have-cmake-options-to-disable-the-building-of-tests-and-examples).
### Modules
@ -229,7 +229,7 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"}
### Conan options
[cxx_modules](#cxx_modules){ #cxx_modules }
[`cxx_modules`](#cxx_modules){ #cxx_modules }
: [:octicons-tag-24: 2.2.0][conan C++ modules support] · :octicons-milestone-24: `auto`/`True`/`False` (Default: `auto`)
@ -237,7 +237,7 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"}
[conan C++ modules support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[std_format](#std_format){ #std_format }
[`std_format`](#std_format){ #std_format }
: [:octicons-tag-24: 2.2.0][conan std::format support] · :octicons-milestone-24: `auto`/`True`/`False` (Default: `auto`)
@ -247,7 +247,7 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"}
[conan std::format support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[string_view_ret](#string_view_ret){ #string_view_ret }
[`string_view_ret`](#string_view_ret){ #string_view_ret }
: [:octicons-tag-24: 2.2.0][conan returning string_view] · :octicons-milestone-24: `auto`/`True`/`False` (Default: `auto`)
@ -259,7 +259,7 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"}
[conan returning string_view]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[no_crtp](#no_crtp){ #no_crtp }
[`no_crtp`](#no_crtp){ #no_crtp }
: [:octicons-tag-24: 2.2.0][conan no crtp support] · :octicons-milestone-24: `auto`/`True`/`False` (Default: `auto`)
@ -268,6 +268,24 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"}
[conan no crtp support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[`contracts`](#contracts){ #contracts }
: [:octicons-tag-24: 2.2.0][conan contracts] · :octicons-milestone-24: `none`/`gsl-lite`/`ms-gsl` (Default: `gsl-lite`)
Enables checking of preconditions and additional asserts in the code.
[conan contracts]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[`freestanding`](#freestanding){ #freestanding }
: [:octicons-tag-24: 2.2.0][conan freestanding] · :octicons-milestone-24: `True`/`False` (Default: `False`)
Configures the library in the [freestanding](https://en.cppreference.com/w/cpp/freestanding)
mode. When enabled, the library's source code should build with the compiler's
[`-ffreestanding`](https://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html) compilation option
without any issues.
[conan freestanding]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
### Conan configuration properties
@ -276,7 +294,7 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"}
: [:octicons-tag-24: 2.2.0][conan build all support] · :octicons-milestone-24: `True`/`False` (Default: `False`)
Enables compilation of all the source code, including tests and examples. To support this, it requires some additional Conan build dependencies described in
[Repository Structure and Dependencies](#repository-structure-and-dependencies).
[Repository directory tree and dependencies](#repository-directory-tree-and-dependencies).
It also runs unit tests during Conan build (unless
[`tools.build:skip_test`](https://docs.conan.io/2/reference/commands/config.html?highlight=tools.build:skip_test#conan-config-list)
configuration property is set to `True`).
@ -296,6 +314,14 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"}
[conan skip la support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[`user.mp-units.analyze:clang-tidy`](#user.mp-units.analyze-clang-tidy){ #user.mp-units.analyze-clang-tidy }
: [:octicons-tag-24: 2.2.0][conan clang-tidy support] · :octicons-milestone-24: `True`/`False` (Default: `False`)
Enables clang-tidy analysis.
[conan clang-tidy support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
### CMake options
[`MP_UNITS_BUILD_AS_SYSTEM_HEADERS`](#MP_UNITS_BUILD_AS_SYSTEM_HEADERS){ #MP_UNITS_BUILD_AS_SYSTEM_HEADERS }
@ -345,24 +371,50 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"}
[cmake no crtp support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[`MP_UNITS_API_CONTRACTS`](#MP_UNITS_API_CONTRACTS){ #MP_UNITS_API_CONTRACTS }
: [:octicons-tag-24: 2.2.0][cmake contracts] · :octicons-milestone-24: `NONE`/`GSL-LITE`/`MS-GSL` (Default: `GSL-LITE`)
Enables checking of preconditions and additional asserts in the code.
[cmake contracts]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[`MP_UNITS_API_FREESTANDING`](#MP_UNITS_API_FREESTANDING){ #MP_UNITS_API_FREESTANDING }
: [:octicons-tag-24: 2.2.0][cmake freestanding] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`)
Configures the library in the [freestanding](https://en.cppreference.com/w/cpp/freestanding)
mode. When enabled, the library's source code should build with the compiler's
[`-ffreestanding`](https://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html) compilation option
without any issues.
[cmake freestanding]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
#### Options for mp-units project developers
[`MP_UNITS_DEV_BUILD_LA`](#MP_UNITS_DEV_BUILD_LA){ #MP_UNITS_DEV_BUILD_LA }
: [:octicons-tag-24: 2.0.0][build la support] · :octicons-milestone-24: `ON`/`OFF` (Default: `ON`)
: [:octicons-tag-24: 2.2.0][cmake build la support] · :octicons-milestone-24: `ON`/`OFF` (Default: `ON`)
Enables building code depending on the linear algebra library.
[build la support]: https://github.com/mpusz/mp-units/releases/tag/v2.0.0
[cmake build la support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[`MP_UNITS_DEV_IWYU`](#MP_UNITS_DEV_IWYU){ #MP_UNITS_DEV_IWYU }
: [:octicons-tag-24: 2.0.0][iwyu support] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`)
: [:octicons-tag-24: 2.2.0][cmake iwyu support] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`)
Enables `include-what-you-use` when compiling with a clang compiler.
Enables include-what-you-use analysis.
[iwyu support]: https://github.com/mpusz/mp-units/releases/tag/v2.0.0
[cmake iwyu support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[`MP_UNITS_DEV_CLANG_TIDY`](#MP_UNITS_DEV_CLANG_TIDY){ #MP_UNITS_DEV_CLANG_TIDY }
: [:octicons-tag-24: 2.2.0][cmake clang-tidy support] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`)
Enables clang-tidy analysis.
[cmake clang-tidy support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
## CMake with presets support

View File

@ -80,6 +80,6 @@ To achieve this goal, several techniques are applied:
[Highly adjustable text-output formatting]: ../users_guide/framework_basics/text_output.md
[Each entity can be defined with a single line of code]: ../users_guide/framework_basics/interface_introduction.md#new-style-of-definitions
[User can easily extend the systems with custom dimensions, quantities, and units]: ../users_guide/use_cases/extending_the_library.md#new-style-of-definitions
[User can easily extend the systems with custom dimensions, quantities, and units]: ../users_guide/use_cases/extending_the_library.md
[freestanding]: https://en.cppreference.com/w/cpp/freestanding

View File

@ -150,7 +150,7 @@ performed without sacrificing accuracy. Please see the below example for a quick
}
```
!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/fsdovTcYh)"
!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/nhqhT8Mzb)"
!!! note

View File

@ -95,7 +95,7 @@ and when `T` is implicitly convertible to `V`.
and is satisfied by:
- All units derived from a `named_unit` class template instantiated with a unique symbol identifier
and a [`QuantitySpec`](#quantityspec) of a [quantity kind](../../appendix/glossary.md#kind).
and a [`QuantitySpec`](#QuantitySpec) of a [quantity kind](../../appendix/glossary.md#kind).
- All units being a result of [unit equations](../../appendix/glossary.md#unit-equation) on other
associated units.

View File

@ -231,8 +231,8 @@ For example:
A unit can be defined by the user in one of the following ways:
```cpp
template<PrefixableUnit auto U> struct kilo_ : prefixed_unit<"k", mag_power<10, 3>, U> {};
template<PrefixableUnit auto U> inline constexpr kilo_<U> kilo;
template<PrefixableUnit U> struct kilo_ : prefixed_unit<"k", mag_power<10, 3>, U{}> {};
template<PrefixableUnit auto U> inline constexpr kilo_<decltype(U)> kilo;
inline constexpr struct second : named_unit<"s", kind_of<isq::time>> {} second;
inline constexpr struct minute : named_unit<"min", mag<60> * second> {} minute;

View File

@ -137,8 +137,8 @@ unit magnitude in a more flexible way.
Each prefix is implemented similarly to the following:
```cpp
template<PrefixableUnit auto U> struct quecto_ : prefixed_unit<"q", mag_power<10, -30>, U> {};
template<PrefixableUnit auto U> inline constexpr quecto_<U> quecto;
template<PrefixableUnit U> struct quecto_ : prefixed_unit<"q", mag_power<10, -30>, U{}> {};
template<PrefixableUnit auto U> inline constexpr quecto_<decltype(U)> quecto;
```
and then a [PrefixableUnit](concepts.md#PrefixableUnit) can be prefixed in the following
@ -153,8 +153,8 @@ efficiently represent any rational magnitude. For example, IEC 80000 prefixes us
IT industry can be implemented as:
```cpp
template<PrefixableUnit auto U> struct yobi_ : prefixed_unit<"Yi", mag_power<2, 80>, U> {};
template<PrefixableUnit auto U> inline constexpr yobi_<U> yobi;
template<PrefixableUnit U> struct yobi_ : prefixed_unit<"Yi", mag_power<2, 80>, U{}> {};
template<PrefixableUnit auto U> inline constexpr yobi_<decltype(U)> yobi;
```
## Scaled units

View File

@ -62,14 +62,14 @@ and units of derived quantities.
=== "Prefixes"
```cpp
template<PrefixableUnit auto U> struct micro_ : prefixed_unit<{u8"µ", "u"}, mag_power<10, -6>, U> {};
template<PrefixableUnit auto U> struct milli_ : prefixed_unit<"m", mag_power<10, -3>, U> {};
template<PrefixableUnit auto U> struct centi_ : prefixed_unit<"c", mag_power<10, -2>, U> {};
template<PrefixableUnit auto U> struct deci_ : prefixed_unit<"d", mag_power<10, -1>, U> {};
template<PrefixableUnit auto U> struct deca_ : prefixed_unit<"da", mag_power<10, 1>, U> {};
template<PrefixableUnit auto U> struct hecto_ : prefixed_unit<"h", mag_power<10, 2>, U> {};
template<PrefixableUnit auto U> struct kilo_ : prefixed_unit<"k", mag_power<10, 3>, U> {};
template<PrefixableUnit auto U> struct mega_ : prefixed_unit<"M", mag_power<10, 6>, U> {};
template<PrefixableUnit U> struct micro_ : prefixed_unit<{u8"µ", "u"}, mag_power<10, -6>, U{}> {};
template<PrefixableUnit U> struct milli_ : prefixed_unit<"m", mag_power<10, -3>, U{}> {};
template<PrefixableUnit U> struct centi_ : prefixed_unit<"c", mag_power<10, -2>, U{}> {};
template<PrefixableUnit U> struct deci_ : prefixed_unit<"d", mag_power<10, -1>, U{}> {};
template<PrefixableUnit U> struct deca_ : prefixed_unit<"da", mag_power<10, 1>, U{}> {};
template<PrefixableUnit U> struct hecto_ : prefixed_unit<"h", mag_power<10, 2>, U{}> {};
template<PrefixableUnit U> struct kilo_ : prefixed_unit<"k", mag_power<10, 3>, U{}> {};
template<PrefixableUnit U> struct mega_ : prefixed_unit<"M", mag_power<10, 6>, U{}> {};
```
=== "Constants"
@ -241,7 +241,7 @@ static_assert(unit_symbol<{.solidus = unit_symbol_solidus::never,
`std::string_view` is returned only when C++23 is available. Otherwise, an instance of a
`basic_fixed_string` is being returned. See more in the
[C++ compiler support](../../getting_started/installation_and_usage.md#static-constexpr-variables-in-constexpr-functions)
[C++ compiler support](../../getting_started/cpp_compiler_support.md#static-constexpr-variables-in-constexpr-functions)
chapter.
#### `unit_symbol_to()`

View File

@ -219,7 +219,7 @@ the origin and the _displacement vector_ measured from it to the point we create
[Why can't I create a quantity by passing a number to a constructor?](../../getting_started/faq.md#why-cant-i-create-a-quantity-by-passing-a-number-to-a-constructor)
chapter.
Similarly to [creation of a quantity](../../getting_started/quick_start.md#creating-a-quantity),
Similarly to [creation of a quantity](../../getting_started/quick_start.md#quantities),
if someone does not like the operator-based syntax to create a `quantity_point`, the same results
can be achieved with a two-parameter constructor:
@ -403,7 +403,7 @@ inline constexpr struct zeroth_degree_Celsius : decltype(ice_point) {} zeroth_de
namespace usc {
inline constexpr struct zeroth_degree_Fahrenheit :
relative_point_origin<si::zeroth_degree_Celsius - 32 * (mag_ratio<5, 9> * si::degree_Celsius)> {} zeroth_degree_Fahrenheit;
relative_point_origin<quantity_point{-32 * (mag_ratio<5, 9> * si::degree_Celsius)}> {} zeroth_degree_Fahrenheit;
}
```

View File

@ -23,21 +23,37 @@
cmake_minimum_required(VERSION 3.5)
# find dependencies
if(NOT TARGET gsl::gsl-lite)
find_package(gsl-lite REQUIRED)
endif()
if(${projectPrefix}BUILD_CXX_MODULES)
add_library(example_utils INTERFACE)
target_compile_features(example_utils INTERFACE cxx_std_20)
target_compile_definitions(example_utils INTERFACE ${projectPrefix}MODULES)
target_include_directories(example_utils INTERFACE include)
target_link_libraries(example_utils INTERFACE gsl::gsl-lite)
endif()
add_library(example_utils-headers INTERFACE)
target_include_directories(example_utils-headers INTERFACE include)
target_link_libraries(example_utils-headers INTERFACE gsl::gsl-lite)
if(${projectPrefix}API_CONTRACTS STREQUAL "NONE")
target_compile_definitions(mp-units-core INTERFACE ${projectPrefix}API_CONTRACTS=0)
elseif(${projectPrefix}API_CONTRACTS STREQUAL "GSL-LITE")
if(NOT TARGET gsl::gsl-lite)
find_package(gsl-lite REQUIRED)
endif()
if(${projectPrefix}BUILD_CXX_MODULES)
target_link_libraries(example_utils INTERFACE gsl::gsl-lite)
endif()
target_link_libraries(example_utils-headers INTERFACE gsl::gsl-lite)
target_compile_definitions(mp-units-core INTERFACE ${projectPrefix}API_CONTRACTS=2)
elseif(${projectPrefix}API_CONTRACTS STREQUAL "MS-GSL")
if(NOT TARGET Microsoft.GSL::GSL)
find_package(Microsoft.GSL REQUIRED)
endif()
if(${projectPrefix}BUILD_CXX_MODULES)
target_link_libraries(example_utils INTERFACE Microsoft.GSL::GSL)
endif()
target_link_libraries(example_utils-headers INTERFACE Microsoft.GSL::GSL)
target_compile_definitions(mp-units-core INTERFACE ${projectPrefix}API_CONTRACTS=3)
endif()
#
# add_example(target <depependencies>...)
@ -72,4 +88,4 @@ add_example(total_energy)
add_example(unmanned_aerial_vehicle example_utils)
add_subdirectory(glide_computer_lib)
# add_subdirectory(kalman_filter)
add_subdirectory(kalman_filter)

View File

@ -90,8 +90,6 @@ void print(const R& gliders)
for (const auto& p : g.polar) {
const auto ratio = glide_ratio(g.polar[0]).force_in(one);
std::cout << MP_UNITS_STD_FMT::format(" * {::N[.4]} @ {::N[.1]} -> {::N[.1]} ({::N[.1]})\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)
si::asin(1 / ratio).force_in(si::degree));
}
std::cout << "\n";
@ -168,8 +166,6 @@ void example()
const auto weather_conditions = get_weather_conditions();
const task t = {waypoints[0], waypoints[1], waypoints[0]};
const aircraft_tow tow = {400 * m, 1.6 * m / s};
// TODO use C++20 date library when available
// set `start_time` to 11:00 am today
const timestamp start_time(std::chrono::system_clock::now());
print(sfty);

View File

@ -22,7 +22,6 @@
#pragma once
#include <gsl/gsl-lite.hpp>
#include <mp-units/bits/hacks.h>
#include <mp-units/compat_macros.h>
#include <compare> // IWYU pragma: export
@ -50,13 +49,13 @@ public:
requires std::copyable<T>
: value_(value)
{
gsl_Expects(validate(value_));
MP_UNITS_EXPECTS(validate(value_));
}
constexpr explicit validated_type(T&& value) noexcept(std::is_nothrow_move_constructible_v<T>) :
value_(std::move(value))
{
gsl_Expects(validate(value_));
MP_UNITS_EXPECTS(validate(value_));
}
constexpr validated_type(const T& value, validated_tag) noexcept(std::is_nothrow_copy_constructible_v<T>)

View File

@ -23,21 +23,23 @@
#pragma once
#include <mp-units/compat_macros.h>
#include <algorithm>
#include <locale>
#include <tuple>
#ifdef MP_UNITS_MODULES
import mp_units;
#else
#include <mp-units/ext/algorithm.h>
#include <mp-units/format.h>
#include <mp-units/framework/quantity.h>
#include <mp-units/framework/quantity_point.h>
#include <mp-units/math.h>
#include <mp-units/quantity.h>
#include <mp-units/quantity_point.h>
#include <mp-units/systems/isq/space_and_time.h>
#include <mp-units/systems/isq/base_quantities.h>
#endif
namespace kalman {
template<typename T>
concept QuantityOrQuantityPoint = mp_units::Quantity<T> || mp_units::QuantityPoint<T>;
namespace detail {
template<mp_units::Dimension auto... Ds>
inline constexpr bool are_time_derivatives = false;
@ -49,192 +51,262 @@ template<mp_units::Dimension auto D1, mp_units::Dimension auto D2, mp_units::Dim
inline constexpr bool are_time_derivatives<D1, D2, Ds...> =
(D1 / D2 == mp_units::isq::dim_time) && are_time_derivatives<D2, Ds...>;
// state
template<QuantityOrQuantityPoint... QQPs>
requires(sizeof...(QQPs) > 0) && (sizeof...(QQPs) <= 3) && are_time_derivatives<QQPs::dimension...>
struct state {
std::tuple<QQPs...> variables_;
constexpr state(QQPs... qqps) : variables_(std::move(qqps)...) {}
} // namespace detail
// system state
template<mp_units::QuantityPoint... QPs>
requires(sizeof...(QPs) > 0) && (sizeof...(QPs) <= 3) && detail::are_time_derivatives<QPs::dimension...>
class system_state {
std::tuple<QPs...> variables_;
public:
constexpr explicit system_state(QPs... qps) : variables_(std::move(qps)...) {}
template<std::size_t Idx>
[[nodiscard]] friend constexpr auto& get(system_state<QPs...>& s)
{
return get<Idx>(s.variables_);
}
template<std::size_t Idx>
[[nodiscard]] friend constexpr const auto& get(const system_state<QPs...>& s)
{
return get<Idx>(s.variables_);
}
};
template<typename T>
concept State = mp_units::is_specialization_of<T, state>;
concept SystemState = mp_units::is_specialization_of<T, system_state>;
template<std::size_t Idx, typename... Qs>
constexpr auto& get(state<Qs...>& s)
{
return get<Idx>(s.variables_);
}
template<std::size_t Idx, typename... Qs>
constexpr const auto& get(const state<Qs...>& s)
{
return get<Idx>(s.variables_);
}
// estimation
template<QuantityOrQuantityPoint QQP, QuantityOrQuantityPoint... QQPs>
struct estimation {
private:
static constexpr auto uncertainty_ref = QQP::reference * QQP::reference;
using uncertainty_type = mp_units::quantity<uncertainty_ref, typename QQP::rep>;
// system state estimation
template<mp_units::QuantityPoint QP, mp_units::QuantityPoint... Rest>
requires requires { typename system_state<QP, Rest...>; }
class system_state_estimate {
public:
kalman::state<QQP, QQPs...> state; // TODO extend kalman functions to work with this variadic parameter list
uncertainty_type uncertainty;
using state_type = system_state<QP, Rest...>;
using standard_deviation_type = QP::quantity_type;
using variance_type =
mp_units::quantity<pow<2>(standard_deviation_type::reference), typename standard_deviation_type::rep>;
private:
state_type state_;
variance_type variance_;
public:
constexpr system_state_estimate(state_type state, standard_deviation_type standard_deviation) :
state_(state), variance_(pow<2>(standard_deviation))
{
}
constexpr system_state_estimate(state_type state, variance_type variance) : state_(state), variance_(variance) {}
[[nodiscard]] constexpr const state_type& state() const { return state_; }
[[nodiscard]] constexpr const variance_type& variance() const { return variance_; }
[[nodiscard]] constexpr standard_deviation_type standard_deviation() const { return sqrt(variance_); }
};
template<QuantityOrQuantityPoint QQP, mp_units::Quantity U>
estimation(state<QQP>, U) -> estimation<QQP>;
// kalman gain
template<mp_units::Quantity Q>
constexpr mp_units::quantity<mp_units::dimensionless[mp_units::one]> kalman_gain(Q estimate_uncertainty,
Q measurement_uncertainty)
template<mp_units::Quantity Q1, mp_units::Quantity Q2>
requires requires { mp_units::common_reference(Q1::reference, Q2::reference); }
[[nodiscard]] constexpr mp_units::quantity<mp_units::dimensionless[mp_units::one]> kalman_gain(
Q1 variance_in_estimate, Q2 variance_in_measurement)
{
return estimate_uncertainty / (estimate_uncertainty + measurement_uncertainty);
return variance_in_estimate / (variance_in_estimate + variance_in_measurement);
}
// state update
template<typename Q, QuantityOrQuantityPoint QM, mp_units::QuantityOf<mp_units::dimensionless> K>
requires(implicitly_convertible(QM::quantity_spec, Q::quantity_spec))
constexpr state<Q> state_update(const state<Q>& predicted, QM measured, K gain)
template<typename QP, mp_units::QuantityPoint QM, mp_units::QuantityOf<mp_units::dimensionless> K>
requires(implicitly_convertible(QM::quantity_spec, QP::quantity_spec))
[[nodiscard]] constexpr system_state<QP> state_update(const system_state<QP>& predicted, QM measured, K gain)
{
return {get<0>(predicted) + gain * (measured - get<0>(predicted))};
return system_state<QP>{get<0>(predicted) + gain * (measured - get<0>(predicted))};
}
template<typename Q1, typename Q2, QuantityOrQuantityPoint QM, mp_units::QuantityOf<mp_units::dimensionless> K,
template<typename QP1, typename QP2, mp_units::QuantityPoint QM, mp_units::QuantityOf<mp_units::dimensionless> K,
mp_units::QuantityOf<mp_units::isq::time> T>
requires(implicitly_convertible(QM::quantity_spec, Q1::quantity_spec))
constexpr state<Q1, Q2> state_update(const state<Q1, Q2>& predicted, QM measured, std::array<K, 2> gain, T interval)
requires(implicitly_convertible(QM::quantity_spec, QP1::quantity_spec))
[[nodiscard]] constexpr system_state<QP1, QP2> state_update(const system_state<QP1, QP2>& predicted, QM measured,
std::array<K, 2> gain, T interval)
{
const auto q1 = get<0>(predicted) + get<0>(gain) * (measured - get<0>(predicted));
const auto q2 = get<1>(predicted) + get<1>(gain) * (measured - get<0>(predicted)) / interval;
return {q1, q2};
const auto qp1 = fma(get<0>(gain), measured - get<0>(predicted), get<0>(predicted));
const auto qp2 = fma(get<1>(gain), (measured - get<0>(predicted)) / interval, get<1>(predicted));
return system_state<QP1, QP2>{qp1, qp2};
}
template<typename Q1, typename Q2, typename Q3, QuantityOrQuantityPoint QM,
template<typename QP1, typename QP2, typename QP3, mp_units::QuantityPoint QM,
mp_units::QuantityOf<mp_units::dimensionless> K, mp_units::QuantityOf<mp_units::isq::time> T>
requires(implicitly_convertible(QM::quantity_spec, Q1::quantity_spec))
constexpr state<Q1, Q2, Q3> state_update(const state<Q1, Q2, Q3>& predicted, QM measured, std::array<K, 3> gain,
T interval)
requires(implicitly_convertible(QM::quantity_spec, QP1::quantity_spec))
[[nodiscard]] constexpr system_state<QP1, QP2, QP3> state_update(const system_state<QP1, QP2, QP3>& predicted,
QM measured, std::array<K, 3> gain, T interval)
{
const auto q1 = get<0>(predicted) + get<0>(gain) * (measured - get<0>(predicted));
const auto q2 = get<1>(predicted) + get<1>(gain) * (measured - get<0>(predicted)) / interval;
const auto q3 = get<2>(predicted) + get<2>(gain) * (measured - get<0>(predicted)) / (interval * interval / 2);
return {q1, q2, q3};
const auto qp1 = fma(get<0>(gain), measured - get<0>(predicted), get<0>(predicted));
const auto qp2 = fma(get<1>(gain), (measured - get<0>(predicted)) / interval, get<1>(predicted));
const auto qp3 = fma(get<2>(gain), (measured - get<0>(predicted)) / (interval * interval / 2), get<2>(predicted));
return system_state<QP1, QP2, QP3>{qp1, qp2, qp3};
}
// covariance update
template<mp_units::Quantity Q, mp_units::QuantityOf<mp_units::dimensionless> K>
constexpr Q covariance_update(Q uncertainty, K gain)
[[nodiscard]] constexpr Q covariance_update(Q uncertainty, K gain)
{
return (1 * mp_units::one - gain) * uncertainty;
}
// state extrapolation
template<typename Q1, typename Q2, mp_units::QuantityOf<mp_units::isq::time> T>
constexpr state<Q1, Q2> state_extrapolation(const state<Q1, Q2>& estimated, T interval)
template<mp_units::QuantityPoint... QPs, mp_units::QuantityPoint QP, mp_units::QuantityOf<mp_units::dimensionless> K>
[[nodiscard]] constexpr system_state_estimate<QPs...> state_estimate_update(
const system_state_estimate<QPs...>& previous, QP measurement, K gain)
{
const auto q1 = get<0>(estimated) + get<1>(estimated) * interval;
const auto q2 = get<1>(estimated);
return {q1, q2};
return {state_update(previous.state(), measurement, gain), covariance_update(previous.variance(), gain)};
};
// state extrapolation
template<typename QP1, typename QP2, mp_units::QuantityOf<mp_units::isq::time> T>
[[nodiscard]] constexpr system_state<QP1, QP2> state_extrapolation(const system_state<QP1, QP2>& estimated, T interval)
{
auto to_quantity = [](const auto& qp) { return qp.quantity_ref_from(qp.point_origin); };
const auto qp1 = fma(to_quantity(get<1>(estimated)), interval, get<0>(estimated));
const auto qp2 = get<1>(estimated);
return system_state<QP1, QP2>{qp1, qp2};
}
template<typename Q1, typename Q2, typename Q3, mp_units::QuantityOf<mp_units::isq::time> T>
constexpr state<Q1, Q2, Q3> state_extrapolation(const state<Q1, Q2, Q3>& estimated, T interval)
template<typename QP1, typename QP2, typename QP3, mp_units::QuantityOf<mp_units::isq::time> T>
[[nodiscard]] constexpr system_state<QP1, QP2, QP3> state_extrapolation(const system_state<QP1, QP2, QP3>& estimated,
T interval)
{
const auto q1 = get<0>(estimated) + get<1>(estimated) * interval + get<2>(estimated) * pow<2>(interval) / 2;
const auto q2 = get<1>(estimated) + get<2>(estimated) * interval;
const auto q3 = get<2>(estimated);
return {q1, q2, q3};
auto to_quantity = [](const auto& qp) { return qp.quantity_ref_from(qp.point_origin); };
const auto qp1 = to_quantity(get<2>(estimated)) * pow<2>(interval) / 2 +
fma(to_quantity(get<1>(estimated)), interval, get<0>(estimated));
const auto qp2 = fma(to_quantity(get<2>(estimated)), interval, get<1>(estimated));
const auto qp3 = get<2>(estimated);
return system_state<QP1, QP2, QP3>{qp1, qp2, qp3};
}
// covariance extrapolation
template<mp_units::Quantity Q>
constexpr Q covariance_extrapolation(Q uncertainty, Q process_noise_variance)
template<mp_units::Quantity Q1, mp_units::Quantity Q2>
requires requires { mp_units::common_reference(Q1::reference, Q2::reference); }
[[nodiscard]] constexpr mp_units::Quantity auto covariance_extrapolation(Q1 uncertainty, Q2 process_noise_variance)
{
return uncertainty + process_noise_variance;
}
} // namespace kalman
template<typename... Qs>
struct MP_UNITS_STD_FMT::formatter<kalman::state<Qs...>> {
constexpr auto parse(format_parse_context& ctx)
template<auto R, auto PO, typename Rep, typename Char>
struct MP_UNITS_STD_FMT::formatter<mp_units::quantity_point<R, PO, Rep>, Char> :
MP_UNITS_STD_FMT::formatter<typename mp_units::quantity_point<R, PO, Rep>::quantity_type> {
template<typename FormatContext>
auto format(const mp_units::quantity_point<R, PO, Rep>& qp, FormatContext& ctx) const -> decltype(ctx.out())
{
mp_units::detail::dynamic_specs_handler handler(specs, ctx);
return mp_units::detail::parse_format_specs(ctx.begin(), ctx.end(), handler);
return MP_UNITS_STD_FMT::formatter<typename mp_units::quantity_point<R, PO, Rep>::quantity_type>::format(
qp.quantity_ref_from(qp.point_origin), ctx);
}
};
template<typename... QPs, typename Char>
class MP_UNITS_STD_FMT::formatter<kalman::system_state<QPs...>, Char> {
using format_specs = mp_units::detail::fill_align_width_format_specs<Char>;
format_specs specs_{};
std::array<std::basic_string<Char>, sizeof...(QPs)> format_str_;
std::tuple<MP_UNITS_STD_FMT::formatter<typename QPs::quantity_type, Char>...> formatters_{};
template<typename Formatter>
constexpr const Char* parse_default_spec(const Char* begin, const Char* end, Formatter& f, std::string& format_str)
{
if (begin == end || *begin++ != '[')
throw MP_UNITS_STD_FMT::format_error("`default-spec` should contain a `[` character");
auto it = begin;
for (int nested_brackets = 0; it != end && !(*it == ']' && nested_brackets == 0); it++) {
if (*it == '[') ++nested_brackets;
if (*it == ']') {
if (nested_brackets == 0) throw MP_UNITS_STD_FMT::format_error("unmatched ']' in format string");
--nested_brackets;
}
}
format_str = "{:" + std::string(begin, it) + '}';
if (it == end) throw MP_UNITS_STD_FMT::format_error("unmatched '[' in format string");
MP_UNITS_STD_FMT::basic_format_parse_context<Char> ctx(std::string_view(begin, it));
auto ptr = f.parse(ctx);
if (ptr != it) throw MP_UNITS_STD_FMT::format_error("invalid subentity format '" + std::string(begin, it) + "'");
return ++it; // skip `]`
}
template<std::size_t... Is>
[[nodiscard]] constexpr const Char* parse_default_spec(const Char* begin, const Char* end, size_t idx,
std::index_sequence<Is...>)
{
auto parse = [&](bool flag, auto& f, std::basic_string<Char>& str) {
return flag ? parse_default_spec(begin, end, f, str) : begin;
};
return std::max({parse(idx == Is, std::get<Is>(formatters_), format_str_[Is])...});
}
[[nodiscard]] constexpr const Char* parse_defaults_specs(const Char* begin, const Char* end)
{
if (begin == end || *begin == '}') return begin;
if (*begin++ != ':') throw MP_UNITS_STD_FMT::format_error("`defaults-specs` should start with a `:`");
do {
auto c = *begin++;
if (c < '0' || c >= static_cast<char>('0' + sizeof...(QPs)))
throw MP_UNITS_STD_FMT::format_error(std::string("unknown `subentity-id` token '") + c + "'");
const size_t idx = static_cast<size_t>(c - '0');
begin = parse_default_spec(begin, end, idx, std::index_sequence_for<QPs...>{});
} while (begin != end && *begin != '}');
return begin;
}
template<typename OutputIt, typename FormatContext, std::size_t Idx>
OutputIt format_system_state(OutputIt out, const kalman::system_state<QPs...>& s, FormatContext& ctx,
std::index_sequence<Idx>) const
{
const std::locale locale = MP_UNITS_FMT_LOCALE(ctx.locale());
auto f = [&](const std::basic_string<Char>& str, const mp_units::QuantityPoint auto& qp) {
return MP_UNITS_STD_FMT::vformat_to(out, locale, str, MP_UNITS_STD_FMT::make_format_args(qp));
};
return f(get<Idx>(format_str_), get<Idx>(s));
}
template<typename OutputIt, typename FormatContext, std::size_t Idx, std::size_t... Rest>
requires(sizeof...(Rest) > 0)
OutputIt format_system_state(OutputIt out, const kalman::system_state<QPs...>& s, FormatContext& ctx,
std::index_sequence<Idx, Rest...>) const
{
const std::locale locale = MP_UNITS_FMT_LOCALE(ctx.locale());
auto f = [&](const std::basic_string<Char>& str, const mp_units::QuantityPoint auto& qp) {
return MP_UNITS_STD_FMT::vformat_to(out, locale, str, MP_UNITS_STD_FMT::make_format_args(qp));
};
return f(get<Idx>(format_str_), get<Idx>(s)), ((*out++ = ' ', f(get<Rest>(format_str_), get<Rest>(s))), ...);
}
public:
constexpr formatter()
{
for (auto& str : format_str_) str = "{}";
}
constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin())
{
auto begin = ctx.begin(), end = ctx.end();
begin = parse_fill_align_width(ctx, begin, end, specs_, mp_units::detail::fmt_align::right);
if (begin == end) return begin;
return parse_defaults_specs(begin, end);
}
template<typename FormatContext>
auto format(const kalman::state<Qs...>& s, FormatContext& ctx)
auto format(const kalman::system_state<QPs...>& s, FormatContext& ctx) const -> decltype(ctx.out())
{
std::string value_buffer;
auto to_value_buffer = std::back_inserter(value_buffer);
if (specs.precision != -1) {
if constexpr (sizeof...(Qs) == 1)
MP_UNITS_STD_FMT::format_to(to_value_buffer, "{1:%.{0}Q %q}", specs.precision, kalman::get<0>(s));
else if constexpr (sizeof...(Qs) == 2)
MP_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
MP_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)
MP_UNITS_STD_FMT::format_to(to_value_buffer, "{}", kalman::get<0>(s));
else if constexpr (sizeof...(Qs) == 2)
MP_UNITS_STD_FMT::format_to(to_value_buffer, "{{ {}, {} }}", kalman::get<0>(s), kalman::get<1>(s));
else
MP_UNITS_STD_FMT::format_to(to_value_buffer, "{{ {}, {}, {} }}", kalman::get<0>(s), kalman::get<1>(s),
kalman::get<2>(s));
auto specs = specs_;
mp_units::detail::handle_dynamic_spec<mp_units::detail::width_checker>(specs.width, specs.width_ref, ctx);
if (specs.width == 0) {
// Avoid extra copying if width is not specified
format_system_state(ctx.out(), s, ctx, std::index_sequence_for<QPs...>{});
return ctx.out();
}
std::basic_string<Char> quantity_buffer;
format_system_state(std::back_inserter(quantity_buffer), s, ctx, std::index_sequence_for<QPs...>{});
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 MP_UNITS_STD_FMT::vformat_to(ctx.out(), global_format_buffer,
MP_UNITS_STD_FMT::make_format_args(value_buffer));
std::basic_string<Char> fill_align_width_format_str;
mp_units::detail::format_global_buffer(std::back_inserter(fill_align_width_format_str), specs);
return MP_UNITS_STD_FMT::vformat_to(ctx.out(), fill_align_width_format_str,
MP_UNITS_STD_FMT::make_format_args(quantity_buffer));
}
private:
mp_units::detail::dynamic_format_specs<char> specs;
};
template<typename Q>
struct MP_UNITS_STD_FMT::formatter<kalman::estimation<Q>> {
constexpr auto parse(format_parse_context& ctx)
{
mp_units::detail::dynamic_specs_handler handler(specs, ctx);
return mp_units::detail::parse_format_specs(ctx.begin(), ctx.end(), handler);
}
template<typename FormatContext>
auto format(kalman::estimation<Q> e, FormatContext& ctx)
{
mp_units::Quantity auto q = [](const Q& t) {
if constexpr (mp_units::Quantity<Q>)
return t;
else
return t.quantity_ref_from(t.point_origin);
}(kalman::get<0>(e.state));
std::string value_buffer;
auto to_value_buffer = std::back_inserter(value_buffer);
if (specs.precision != -1) {
MP_UNITS_STD_FMT::format_to(to_value_buffer, "{0:%.{2}Q} ± {1:%.{2}Q} {0:%q}", q, sqrt(e.uncertainty),
specs.precision);
} else {
MP_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 MP_UNITS_STD_FMT::vformat_to(ctx.out(), global_format_buffer,
MP_UNITS_STD_FMT::make_format_args(value_buffer));
}
private:
mp_units::detail::dynamic_format_specs<char> specs;
};

View File

@ -35,36 +35,37 @@ import mp_units;
using namespace mp_units;
void print_header(const kalman::State auto& initial)
void print_header(const kalman::SystemState auto& initial)
{
std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial);
std::cout << MP_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)
void print(auto iteration, QuantityOf<dimensionless> auto gain, QuantityPoint auto measured,
const kalman::SystemState auto& current, const kalman::SystemState auto& next)
{
std::cout << MP_UNITS_STD_FMT::format("{:2} | {:9} | {:8} | {:14} | {:14}\n", iteration, gain, measured, current,
next);
std::cout << MP_UNITS_STD_FMT::format("{:2} | {:9:N[.2f]} | {:8} | {:14:0[:N[.2f]]} | {:14:0[:N[.2f]]}\n", iteration,
gain, measured, current, next);
}
int main()
{
using namespace mp_units::si::unit_symbols;
using state = kalman::state<quantity<isq::mass[g]>>;
using state = kalman::system_state<quantity_point<isq::mass[g]>>;
using qp = quantity_point<isq::mass[g]>;
const state initial = {1 * kg};
const std::array measurements = {1'030 * g, 989 * g, 1'017 * g, 1'009 * g, 1'013 * g,
979 * g, 1'008 * g, 1'042 * g, 1'012 * g, 1'011 * g};
const state initial_guess{qp{1 * kg}};
const std::array measurements = {qp{996 * g}, qp{994 * g}, qp{1021 * g}, qp{1000 * g}, qp{1002 * g},
qp{1010 * g}, qp{983 * g}, qp{971 * g}, qp{993 * g}, qp{1023 * g}};
print_header(initial);
state next = initial;
for (int index = 1; const auto& v : measurements) {
const auto& previous = next;
const auto gain = 1. / index * one;
const auto current = state_update(previous, v, gain);
print_header(initial_guess);
state next = initial_guess;
for (int index = 1; const auto& measurement : measurements) {
const state& previous = next;
const quantity gain = 1. / index * one;
const state current = state_update(previous, measurement, gain);
next = current;
print(index++, gain, v, current, next);
print(index++, gain, measurement, current, next);
}
}

View File

@ -39,36 +39,38 @@ inline constexpr bool mp_units::is_vector<T> = true;
using namespace mp_units;
void print_header(const kalman::State auto& initial)
void print_header(const kalman::SystemState auto& initial)
{
std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial);
std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>8} | {:>23} | {:>23}\n", "N", "Measured", "Curr. Estimate",
"Next Estimate");
}
void print(auto iteration, Quantity auto measured, const kalman::State auto& current, const kalman::State auto& next)
void print(auto iteration, QuantityPoint auto measured, const kalman::SystemState auto& current,
const kalman::SystemState auto& next)
{
std::cout << MP_UNITS_STD_FMT::format("{:2} | {:8} | {:.1} | {:.1}\n", iteration, measured, current, next);
std::cout << MP_UNITS_STD_FMT::vformat("{:2} | {:8} | {:23:0[:N[.2f]]1[:N[.2f]]} | {:23:0[:N[.2f]]1[:N[.2f]]}\n",
MP_UNITS_STD_FMT::make_format_args(iteration, measured, current, next));
}
int main()
{
using namespace mp_units::si::unit_symbols;
using state = kalman::state<quantity<isq::position_vector[m]>, quantity<isq::velocity[m / s]>>;
using qp = quantity_point<isq::position_vector[m]>;
using state = kalman::system_state<qp, quantity_point<isq::velocity[m / s]>>;
const auto interval = isq::duration(5 * s);
const state initial = {30 * km, 40 * m / s};
const quantity<isq::position_vector[m], int> measurements[] = {30'110 * m, 30'265 * m, 30'740 * m, 30'750 * m,
31'135 * m, 31'015 * m, 31'180 * m, 31'610 * m,
31'960 * m, 31'865 * m};
const quantity interval = isq::duration(5 * s);
const state initial{qp{30 * km}, quantity_point{40 * m / s}};
const std::array measurements = {qp{30'171 * m}, qp{30'353 * m}, qp{30'756 * m}, qp{30'799 * m}, qp{31'018 * m},
qp{31'278 * m}, qp{31'276 * m}, qp{31'379 * m}, qp{31'748 * m}, qp{32'175 * m}};
std::array gain = {0.2 * one, 0.1 * one};
print_header(initial);
state next = state_extrapolation(initial, interval);
for (int index = 1; const auto& measured : measurements) {
const auto& previous = next;
const auto current = state_update(previous, measured, gain, interval);
for (int index = 1; const auto& measurement : measurements) {
const state& previous = next;
const state current = state_update(previous, measurement, gain, interval);
next = state_extrapolation(current, interval);
print(index++, measured, current, next);
print(index++, measurement, current, next);
}
}

View File

@ -39,36 +39,38 @@ inline constexpr bool mp_units::is_vector<T> = true;
using namespace mp_units;
void print_header(const kalman::State auto& initial)
void print_header(const kalman::SystemState auto& initial)
{
std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial);
std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>8} | {:>24} | {:>24}\n", "N", "Measured", "Curr. Estimate",
std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>8} | {:>23} | {:>23}\n", "N", "Measured", "Curr. Estimate",
"Next Estimate");
}
void print(auto iteration, Quantity auto measured, const kalman::State auto& current, const kalman::State auto& next)
void print(auto iteration, QuantityPoint auto measured, const kalman::SystemState auto& current,
const kalman::SystemState auto& next)
{
std::cout << MP_UNITS_STD_FMT::format("{:2} | {:8} | {:>24.1} | {:>24.1}\n", iteration, measured, current, next);
std::cout << MP_UNITS_STD_FMT::format("{:2} | {:8} | {:23:0[:N[.2f]]1[:N[.2f]]} | {:23:0[:N[.2f]]1[:N[.2f]]}\n",
iteration, measured, current, next);
}
int main()
{
using namespace mp_units::si::unit_symbols;
using state = kalman::state<quantity<isq::position_vector[m]>, quantity<isq::velocity[m / s]>>;
using qp = quantity_point<isq::position_vector[m]>;
using state = kalman::system_state<qp, quantity_point<isq::velocity[m / s]>>;
const auto interval = isq::duration(5 * s);
const state initial = {30 * km, 50 * m / s};
const quantity<isq::position_vector[m], int> measurements[] = {30'160 * m, 30'365 * m, 30'890 * m, 31'050 * m,
31'785 * m, 32'215 * m, 33'130 * m, 34'510 * m,
36'010 * m, 37'265 * m};
const quantity interval = isq::duration(5 * s);
const state initial{qp{30 * km}, quantity_point{50 * m / s}};
const std::array measurements = {qp{30'221 * m}, qp{30'453 * m}, qp{30'906 * m}, qp{30'999 * m}, qp{31'368 * m},
qp{31'978 * m}, qp{32'526 * m}, qp{33'379 * m}, qp{34'698 * m}, qp{36'275 * m}};
std::array gain = {0.2 * one, 0.1 * one};
print_header(initial);
state next = state_extrapolation(initial, interval);
for (int index = 1; const auto& measured : measurements) {
const auto& previous = next;
const auto current = state_update(previous, measured, gain, interval);
for (int index = 1; const auto& measurement : measurements) {
const state& previous = next;
const state current = state_update(previous, measurement, gain, interval);
next = state_extrapolation(current, interval);
print(index++, measured, current, next);
print(index++, measurement, current, next);
}
}

View File

@ -39,37 +39,40 @@ inline constexpr bool mp_units::is_vector<T> = true;
using namespace mp_units;
void print_header(const kalman::State auto& initial)
void print_header(const kalman::SystemState auto& initial)
{
std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial);
std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>8} | {:>35} | {:>35}\n", "N", "Measured", "Curr. Estimate",
"Next Estimate");
}
void print(auto iteration, Quantity auto measured, const kalman::State auto& current, const kalman::State auto& next)
void print(auto iteration, QuantityPoint auto measured, const kalman::SystemState auto& current,
const kalman::SystemState auto& next)
{
std::cout << MP_UNITS_STD_FMT::format("{:2} | {:8} | {:>35.1} | {:>35.1}\n", iteration, measured, current, next);
std::cout << MP_UNITS_STD_FMT::format(
"{:2} | {:8} | {:35:0[:N[.2f]]1[:N[.2f]]2[:N[.2f]]} | {:35:0[:N[.2f]]1[:N[.2f]]2[:N[.2f]]}\n", iteration, measured,
current, next);
}
int main()
{
using namespace mp_units::si::unit_symbols;
using state = kalman::state<quantity<isq::position_vector[m]>, quantity<isq::velocity[m / s]>,
quantity<isq::acceleration[m / s2]>>;
const auto interval = isq::duration(5. * s);
const state initial = {30 * km, 50 * m / s, 0 * m / s2};
using qp = quantity_point<isq::position_vector[m]>;
using state =
kalman::system_state<qp, quantity_point<isq::velocity[m / s]>, quantity_point<isq::acceleration[m / s2]>>;
const quantity<isq::position_vector[m], int> measurements[] = {30'160 * m, 30'365 * m, 30'890 * m, 31'050 * m,
31'785 * m, 32'215 * m, 33'130 * m, 34'510 * m,
36'010 * m, 37'265 * m};
const quantity interval = isq::duration(5. * s);
const state initial{qp{30 * km}, quantity_point{50 * m / s}, quantity_point{0 * m / s2}};
const std::array measurements = {qp{30'221 * m}, qp{30'453 * m}, qp{30'906 * m}, qp{30'999 * m}, qp{31'368 * m},
qp{31'978 * m}, qp{32'526 * m}, qp{33'379 * m}, qp{34'698 * m}, qp{36'275 * m}};
std::array gain = {0.5 * one, 0.4 * one, 0.1 * one};
print_header(initial);
state next = state_extrapolation(initial, interval);
for (int index = 1; const auto& measured : measurements) {
const auto& previous = next;
const auto current = state_update(previous, measured, gain, interval);
for (int index = 1; const auto& measurement : measurements) {
const state& previous = next;
const state current = state_update(previous, measurement, gain, interval);
next = state_extrapolation(current, interval);
print(index++, measured, current, next);
print(index++, measurement, current, next);
}
}

View File

@ -36,45 +36,45 @@ import mp_units;
using namespace mp_units;
template<Quantity Q>
void print_header(kalman::estimation<Q> initial)
template<QuantityPoint QP>
void print_header(kalman::system_state_estimate<QP> initial)
{
std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial);
std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>5} | {:>8} | {:>16} | {:>16}\n", "N", "Gain", "Measured",
std::cout << MP_UNITS_STD_FMT::format("Initial: {} {}\n", initial.state(), initial.variance());
std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>8} | {:>5} | {:>16} | {:>16}\n", "N", "Measured", "Gain",
"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)
template<QuantityPoint QP, QuantityOf<dimensionless> K>
void print(auto iteration, QP measured, K gain, kalman::system_state_estimate<QP> current,
kalman::system_state_estimate<QP> next)
{
std::cout << MP_UNITS_STD_FMT::format("{:2} | {:5%.2Q} | {:8} | {:>16.2} | {:>16.2}\n", iteration, gain, measured,
current, next);
std::cout << MP_UNITS_STD_FMT::format(
"{:2} | {:8} | {:5:N[.2f]} | {:6:0[:N[.2f]]} {:8:N[.2f]} | {:6:0[:N[.2f]]} {:8:N[.2f]}\n", iteration, measured,
gain, current.state(), current.variance(), next.state(), next.variance());
}
int main()
{
using namespace kalman;
using namespace mp_units::si::unit_symbols;
using qp = quantity_point<isq::height[m]>;
using estimate = kalman::system_state_estimate<qp>;
using state = estimate::state_type;
const estimation initial = {state{isq::height(60. * m)}, pow<2>(isq::height(15. * m))};
const quantity<isq::height[m]> measurements[] = {48.54 * m, 47.11 * m, 55.01 * m, 55.15 * m, 49.89 * m,
40.85 * m, 46.72 * m, 50.05 * m, 51.27 * m, 49.95 * m};
const auto measurement_uncertainty = pow<2>(isq::height(5. * m));
const estimate initial{state{qp{60. * m}}, 15. * m};
const std::array measurements = {qp{49.03 * m}, qp{48.44 * m}, qp{55.21 * m}, qp{49.98 * m}, qp{50.6 * m},
qp{52.61 * m}, qp{45.87 * m}, qp{42.64 * m}, qp{48.26 * m}, qp{55.84 * m}};
const quantity measurement_error = isq::height(5. * m);
const quantity measurement_variance = pow<2>(measurement_error);
auto update = [=]<Quantity Q>(const estimation<Q>& previous, const Q& measurement,
QuantityOf<dimensionless> auto gain) {
return estimation{state_update(previous.state, measurement, gain), covariance_update(previous.uncertainty, gain)};
};
auto predict = []<Quantity Q>(const estimation<Q>& current) { return current; };
auto predict = [](const estimate& current) { return current; };
print_header(initial);
estimation next = predict(initial);
for (int index = 1; const auto& measured : measurements) {
const auto& previous = next;
const auto gain = kalman_gain(previous.uncertainty, measurement_uncertainty);
const estimation current = update(previous, measured, gain);
estimate next = predict(initial);
for (int index = 1; const auto& measurement : measurements) {
const estimate& previous = next;
const quantity gain = kalman::kalman_gain(previous.variance(), measurement_variance);
const estimate current = state_estimate_update(previous, measurement, gain);
next = predict(current);
print(index++, gain, measured, current, next);
print(index++, measurement, gain, current, next);
}
}

View File

@ -27,62 +27,59 @@
import mp_units;
#else
#include <mp-units/format.h>
#include <mp-units/framework/quantity_point.h>
#include <mp-units/math.h>
#include <mp-units/quantity_point.h>
#include <mp-units/systems/isq/thermodynamics.h>
#include <mp-units/systems/si/si.h>
#include <mp-units/systems/si.h>
#endif
// Based on: https://www.kalmanfilter.net/kalman1d.html#ex6
// Based on: https://www.kalmanfilter.net/kalman1d_pn.html#ex6
using namespace mp_units;
template<QuantityPoint QP>
void print_header(kalman::estimation<QP> initial)
void print_header(kalman::system_state_estimate<QP> initial)
{
std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial);
std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>7} | {:>10} | {:>18} | {:>18}\n", "N", "Gain", "Measured",
std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial.state(), initial.variance());
std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>10} | {:>7} | {:>22} | {:>22}\n", "N", "Measured", "Gain",
"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)
void print(auto iteration, QP measured, K gain, kalman::system_state_estimate<QP> current,
kalman::system_state_estimate<QP> next)
{
std::cout << MP_UNITS_STD_FMT::format("{:2} | {:7%.4Q} | {:10%.3Q %q} | {:>18.3} | {:>18.3}\n", iteration, gain,
measured.quantity_ref_from(QP::point_origin), current, next);
std::cout << MP_UNITS_STD_FMT::format(
"{:2} | {:10} | {:7:N[.4f]} | {:10:0[:N[.3f]]} {:11:N[.4f]} | {:10:0[:N[.3f]]} {:11:N[.4f]}\n", iteration, measured,
gain, current.state(), current.variance(), next.state(), next.variance());
}
int main()
{
constexpr auto deg_C = isq::Celsius_temperature[si::degree_Celsius];
using namespace mp_units::si::unit_symbols;
using qp = quantity_point<isq::Celsius_temperature[deg_C]>;
using estimate = kalman::system_state_estimate<qp>;
using state = estimate::state_type;
using namespace kalman;
const quantity process_noise_variance = 0.0001 * pow<2>(deg_C);
const estimate initial{state{qp{60. * deg_C}}, 100. * deg_C};
const std::array measurements = {qp{49.986 * deg_C}, qp{49.963 * deg_C}, qp{50.09 * deg_C}, qp{50.001 * deg_C},
qp{50.018 * deg_C}, qp{50.05 * deg_C}, qp{49.938 * deg_C}, qp{49.858 * deg_C},
qp{49.965 * deg_C}, qp{50.114 * deg_C}};
const quantity measurement_error = 0.1 * deg_C;
const quantity measurement_variance = pow<2>(measurement_error);
const auto process_noise_variance = 0.0001 * (deg_C * deg_C);
const estimation initial = {state{si::ice_point + 10. * deg_C}, pow<2>(100. * deg_C)};
const std::array measurements = {si::ice_point + 49.95 * deg_C, si::ice_point + 49.967 * deg_C,
si::ice_point + 50.1 * deg_C, si::ice_point + 50.106 * deg_C,
si::ice_point + 49.992 * deg_C, si::ice_point + 49.819 * deg_C,
si::ice_point + 49.933 * deg_C, si::ice_point + 50.007 * deg_C,
si::ice_point + 50.023 * deg_C, si::ice_point + 49.99 * deg_C};
const auto measurement_uncertainty = pow<2>(0.1 * deg_C);
auto update = [=]<QuantityPoint QP>(const estimation<QP>& previous, const QP& meassurement,
QuantityOf<dimensionless> auto gain) {
return estimation{state_update(previous.state, meassurement, gain), covariance_update(previous.uncertainty, gain)};
};
auto predict = [=]<QuantityPoint QP>(const estimation<QP>& current) {
return estimation{current.state, covariance_extrapolation(current.uncertainty, process_noise_variance)};
auto predict = [=](const estimate& current) {
return estimate{current.state(), kalman::covariance_extrapolation(current.variance(), process_noise_variance)};
};
print_header(initial);
estimation next = predict(initial);
for (int index = 1; const auto& m : measurements) {
const auto& previous = next;
const auto gain = kalman_gain(previous.uncertainty, measurement_uncertainty);
const estimation current = update(previous, m, gain);
estimate next = predict(initial);
for (int index = 1; const auto& measurement : measurements) {
const estimate& previous = next;
const quantity gain = kalman::kalman_gain(previous.variance(), measurement_variance);
const estimate current = state_estimate_update(previous, measurement, gain);
next = predict(current);
print(index++, gain, m, current, next);
print(index++, measurement, gain, current, next);
}
}

View File

@ -27,62 +27,59 @@
import mp_units;
#else
#include <mp-units/format.h>
#include <mp-units/framework/quantity_point.h>
#include <mp-units/math.h>
#include <mp-units/quantity_point.h>
#include <mp-units/systems/isq/thermodynamics.h>
#include <mp-units/systems/si/si.h>
#include <mp-units/systems/si.h>
#endif
// Based on: https://www.kalmanfilter.net/kalman1d.html#ex7
// Based on: https://www.kalmanfilter.net/kalman1d_pn.html#ex7
using namespace mp_units;
template<QuantityPoint QP>
void print_header(kalman::estimation<QP> initial)
void print_header(kalman::system_state_estimate<QP> initial)
{
std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial);
std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>7} | {:>10} | {:>18} | {:>18}\n", "N", "Gain", "Measured",
std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial.state(), initial.variance());
std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>10} | {:>7} | {:>22} | {:>22}\n", "N", "Measured", "Gain",
"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)
void print(auto iteration, QP measured, K gain, kalman::system_state_estimate<QP> current,
kalman::system_state_estimate<QP> next)
{
std::cout << MP_UNITS_STD_FMT::format("{:2} | {:7%.4Q} | {:10%.3Q %q} | {:>18.3} | {:>18.3}\n", iteration, gain,
measured.quantity_ref_from(QP::point_origin), current, next);
std::cout << MP_UNITS_STD_FMT::format(
"{:2} | {:10} | {:7:N[.4f]} | {:10:0[:N[.3f]]} {:11:N[.4f]} | {:10:0[:N[.3f]]} {:11:N[.4f]}\n", iteration, measured,
gain, current.state(), current.variance(), next.state(), next.variance());
}
int main()
{
constexpr auto deg_C = isq::Celsius_temperature[si::degree_Celsius];
using namespace mp_units::si::unit_symbols;
using qp = quantity_point<isq::Celsius_temperature[deg_C]>;
using estimate = kalman::system_state_estimate<qp>;
using state = estimate::state_type;
using namespace kalman;
const quantity process_noise_variance = 0.0001 * pow<2>(deg_C);
const estimate initial{state{qp{10. * deg_C}}, 100. * deg_C};
const std::array measurements = {qp{50.486 * deg_C}, qp{50.963 * deg_C}, qp{51.597 * deg_C}, qp{52.001 * deg_C},
qp{52.518 * deg_C}, qp{53.05 * deg_C}, qp{53.438 * deg_C}, qp{53.858 * deg_C},
qp{54.465 * deg_C}, qp{55.114 * deg_C}};
const quantity measurement_error = 0.1 * deg_C;
const quantity measurement_variance = pow<2>(measurement_error);
const auto process_noise_variance = 0.0001 * (deg_C * deg_C);
const estimation initial = {state{si::ice_point + 10. * deg_C}, pow<2>(100. * deg_C)};
const std::array measurements = {si::ice_point + 50.45 * deg_C, si::ice_point + 50.967 * deg_C,
si::ice_point + 51.6 * deg_C, si::ice_point + 52.106 * deg_C,
si::ice_point + 52.492 * deg_C, si::ice_point + 52.819 * deg_C,
si::ice_point + 53.433 * deg_C, si::ice_point + 54.007 * deg_C,
si::ice_point + 54.523 * deg_C, si::ice_point + 54.99 * deg_C};
const auto measurement_uncertainty = pow<2>(0.1 * deg_C);
auto update = [=]<QuantityPoint QP>(const estimation<QP>& previous, const QP& meassurement,
QuantityOf<dimensionless> auto gain) {
return estimation{state_update(previous.state, meassurement, gain), covariance_update(previous.uncertainty, gain)};
};
auto predict = [=]<QuantityPoint QP>(const estimation<QP>& current) {
return estimation{current.state, covariance_extrapolation(current.uncertainty, process_noise_variance)};
auto predict = [=](const estimate& current) {
return estimate{current.state(), kalman::covariance_extrapolation(current.variance(), process_noise_variance)};
};
print_header(initial);
estimation next = predict(initial);
for (int index = 1; const auto& m : measurements) {
const auto& previous = next;
const auto gain = kalman_gain(previous.uncertainty, measurement_uncertainty);
const estimation current = update(previous, m, gain);
estimate next = predict(initial);
for (int index = 1; const auto& measurement : measurements) {
const estimate& previous = next;
const quantity gain = kalman::kalman_gain(previous.variance(), measurement_variance);
const estimate current = state_estimate_update(previous, measurement, gain);
next = predict(current);
print(index++, gain, m, current, next);
print(index++, measurement, gain, current, next);
}
}

View File

@ -27,62 +27,59 @@
import mp_units;
#else
#include <mp-units/format.h>
#include <mp-units/framework/quantity_point.h>
#include <mp-units/math.h>
#include <mp-units/quantity_point.h>
#include <mp-units/systems/isq/thermodynamics.h>
#include <mp-units/systems/si/si.h>
#include <mp-units/systems/si.h>
#endif
// Based on: https://www.kalmanfilter.net/kalman1d.html#ex8
// Based on: https://www.kalmanfilter.net/kalman1d_pn.html#ex8
using namespace mp_units;
template<QuantityPoint QP>
void print_header(kalman::estimation<QP> initial)
void print_header(kalman::system_state_estimate<QP> initial)
{
std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial);
std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>7} | {:>10} | {:>16} | {:>16}\n", "N", "Gain", "Measured",
std::cout << MP_UNITS_STD_FMT::format("Initial: {}\n", initial.state(), initial.variance());
std::cout << MP_UNITS_STD_FMT::format("{:>2} | {:>10} | {:>7} | {:>22} | {:>22}\n", "N", "Measured", "Gain",
"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)
void print(auto iteration, QP measured, K gain, kalman::system_state_estimate<QP> current,
kalman::system_state_estimate<QP> next)
{
std::cout << MP_UNITS_STD_FMT::format("{:2} | {:7%.3Q} | {:10%.3Q %q} | {:>16.2} | {:>16.2}\n", iteration, gain,
measured.quantity_ref_from(QP::point_origin), current, next);
std::cout << MP_UNITS_STD_FMT::format(
"{:2} | {:10} | {:7:N[.4f]} | {:10:0[:N[.3f]]} {:11:N[.4f]} | {:10:0[:N[.3f]]} {:11:N[.4f]}\n", iteration, measured,
gain, current.state(), current.variance(), next.state(), next.variance());
}
int main()
{
constexpr auto deg_C = isq::Celsius_temperature[si::degree_Celsius];
using namespace mp_units::si::unit_symbols;
using qp = quantity_point<isq::Celsius_temperature[deg_C]>;
using estimate = kalman::system_state_estimate<qp>;
using state = estimate::state_type;
using namespace kalman;
const quantity process_noise_variance = 0.15 * pow<2>(deg_C);
const estimate initial{state{qp{10. * deg_C}}, 100. * deg_C};
const std::array measurements = {qp{50.486 * deg_C}, qp{50.963 * deg_C}, qp{51.597 * deg_C}, qp{52.001 * deg_C},
qp{52.518 * deg_C}, qp{53.05 * deg_C}, qp{53.438 * deg_C}, qp{53.858 * deg_C},
qp{54.465 * deg_C}, qp{55.114 * deg_C}};
const quantity measurement_error = 0.1 * deg_C;
const quantity measurement_variance = pow<2>(measurement_error);
const auto process_noise_variance = 0.15 * (deg_C * deg_C);
const estimation initial = {state{si::ice_point + 10. * deg_C}, pow<2>(100. * deg_C)};
const std::array measurements = {si::ice_point + 50.45 * deg_C, si::ice_point + 50.967 * deg_C,
si::ice_point + 51.6 * deg_C, si::ice_point + 52.106 * deg_C,
si::ice_point + 52.492 * deg_C, si::ice_point + 52.819 * deg_C,
si::ice_point + 53.433 * deg_C, si::ice_point + 54.007 * deg_C,
si::ice_point + 54.523 * deg_C, si::ice_point + 54.99 * deg_C};
const auto measurement_uncertainty = pow<2>(0.1 * deg_C);
auto update = [=]<QuantityPoint QP>(const estimation<QP>& previous, const QP& meassurement,
QuantityOf<dimensionless> auto gain) {
return estimation{state_update(previous.state, meassurement, gain), covariance_update(previous.uncertainty, gain)};
};
auto predict = [=]<QuantityPoint QP>(const estimation<QP>& current) {
return estimation{current.state, covariance_extrapolation(current.uncertainty, process_noise_variance)};
auto predict = [=](const estimate& current) {
return estimate{current.state(), kalman::covariance_extrapolation(current.variance(), process_noise_variance)};
};
print_header(initial);
estimation next = predict(initial);
for (int index = 1; const auto& m : measurements) {
const auto& previous = next;
const auto gain = kalman_gain(previous.uncertainty, measurement_uncertainty);
const estimation current = update(previous, m, gain);
estimate next = predict(initial);
for (int index = 1; const auto& measurement : measurements) {
const estimate& previous = next;
const quantity gain = kalman::kalman_gain(previous.variance(), measurement_variance);
const estimate current = state_estimate_update(previous, measurement, gain);
next = predict(current);
print(index++, gain, m, current, next);
print(index++, measurement, gain, current, next);
}
}

View File

@ -42,6 +42,9 @@ message(STATUS "${projectPrefix}BUILD_AS_SYSTEM_HEADERS: ${${projectPrefix}BUILD
option(${projectPrefix}BUILD_CXX_MODULES "Add C++ modules to the list of default targets" OFF)
message(STATUS "${projectPrefix}BUILD_CXX_MODULES: ${${projectPrefix}BUILD_CXX_MODULES}")
option(${projectPrefix}API_FREESTANDING "Builds only freestanding part of the library" OFF)
message(STATUS "${projectPrefix}API_FREESTANDING: ${${projectPrefix}API_FREESTANDING}")
if(${projectPrefix}BUILD_AS_SYSTEM_HEADERS)
set(${projectPrefix}_AS_SYSTEM SYSTEM)
endif()
@ -68,13 +71,21 @@ cache_var_values(API_STRING_VIEW_RET AUTO TRUE FALSE)
set(${projectPrefix}API_NO_CRTP AUTO CACHE STRING "Enable class definitions without CRTP idiom")
cache_var_values(API_NO_CRTP AUTO TRUE FALSE)
set(${projectPrefix}API_CONTRACTS GSL-LITE CACHE STRING "Enable contract checking")
cache_var_values(API_CONTRACTS NONE GSL-LITE MS-GSL)
if(${projectPrefix}API_FREESTANDING AND NOT ${projectPrefix}API_CONTRACTS STREQUAL "NONE")
message(FATAL_ERROR "'${projectPrefix}API_CONTRACTS' should be set to 'NONE' for a freestanding build")
endif()
# C++ features
check_cxx_feature_supported(__cpp_lib_format ${projectPrefix}LIB_FORMAT_SUPPORTED)
check_cxx_feature_supported("__cpp_constexpr >= 202211L" ${projectPrefix}STATIC_CONSTEXPR_VARS_IN_CONSTEXPR_FUNCTIONS)
check_cxx_feature_supported(__cpp_explicit_this_parameter ${projectPrefix}EXPLICIT_THIS_PARAMETER_SUPPORTED)
# validate settings
if(${projectPrefix}API_STD_FORMAT STREQUAL "TRUE"
if(NOT ${projectPrefix}API_FREESTANDING
AND ${projectPrefix}API_STD_FORMAT STREQUAL "TRUE"
AND NOT
(${projectPrefix}LIB_FORMAT_SUPPORTED
# libc++ has a basic supports for std::format but does not set __cpp_lib_format

View File

@ -54,7 +54,7 @@ function(add_mp_units_module name target_name)
# validate and process arguments
validate_unparsed(${name} ARG)
validate_arguments_exists(${name} ARG DEPENDENCIES MODULE_INTERFACE_UNIT)
validate_arguments_exists(${name} ARG MODULE_INTERFACE_UNIT)
if(${projectPrefix}TARGET_SCOPE STREQUAL INTERFACE)
set(SCOPE "INTERFACE")

View File

@ -31,17 +31,10 @@ function(set_feature_flag name)
endif()
endfunction()
# find dependencies
if(NOT TARGET gsl::gsl-lite)
find_package(gsl-lite REQUIRED)
endif()
# core library definition
add_mp_units_module(
core mp-units-core
DEPENDENCIES gsl::gsl-lite
HEADERS include/mp-units/bits/core_gmf.h
include/mp-units/bits/fmt.h
include/mp-units/bits/get_associated_quantity.h
include/mp-units/bits/get_common_base.h
include/mp-units/bits/hacks.h
@ -79,20 +72,35 @@ add_mp_units_module(
include/mp-units/framework/value_cast.h
include/mp-units/compat_macros.h
include/mp-units/concepts.h
include/mp-units/format.h
include/mp-units/framework.h
include/mp-units/math.h
include/mp-units/ostream.h
include/mp-units/random.h
MODULE_INTERFACE_UNIT mp-units-core.cpp
)
if(NOT ${projectPrefix}API_FREESTANDING)
target_sources(
mp-units-core
PUBLIC FILE_SET
HEADERS
BASE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}/include
FILES
include/mp-units/bits/fmt.h
include/mp-units/bits/requires_hosted.h
include/mp-units/math.h
include/mp-units/ostream.h
include/mp-units/format.h
include/mp-units/random.h
)
endif()
set_feature_flag(API_STD_FORMAT)
set_feature_flag(API_STRING_VIEW_RET)
set_feature_flag(API_NO_CRTP)
if(${projectPrefix}API_STD_FORMAT STREQUAL "FALSE" OR (${projectPrefix}API_STD_FORMAT STREQUAL "AUTO"
AND NOT ${projectPrefix}LIB_FORMAT_SUPPORTED)
# Text formatting
if(NOT ${projectPrefix}API_FREESTANDING
AND (${projectPrefix}API_STD_FORMAT STREQUAL "FALSE" OR (${projectPrefix}API_STD_FORMAT STREQUAL "AUTO"
AND NOT ${projectPrefix}LIB_FORMAT_SUPPORTED))
)
if(NOT TARGET fmt::fmt)
find_package(fmt REQUIRED)
@ -100,6 +108,24 @@ if(${projectPrefix}API_STD_FORMAT STREQUAL "FALSE" OR (${projectPrefix}API_STD_F
target_link_libraries(mp-units-core ${${projectPrefix}TARGET_SCOPE} fmt::fmt)
endif()
# Contracts checking
if(${projectPrefix}API_CONTRACTS STREQUAL "NONE")
target_compile_definitions(mp-units-core ${${projectPrefix}TARGET_SCOPE} ${projectPrefix}API_CONTRACTS=0)
elseif(${projectPrefix}API_CONTRACTS STREQUAL "GSL-LITE")
if(NOT TARGET gsl::gsl-lite)
find_package(gsl-lite REQUIRED)
endif()
target_link_libraries(mp-units-core ${${projectPrefix}TARGET_SCOPE} gsl::gsl-lite)
target_compile_definitions(mp-units-core ${${projectPrefix}TARGET_SCOPE} ${projectPrefix}API_CONTRACTS=2)
elseif(${projectPrefix}API_CONTRACTS STREQUAL "MS-GSL")
if(NOT TARGET Microsoft.GSL::GSL)
find_package(Microsoft.GSL REQUIRED)
endif()
target_link_libraries(mp-units-core ${${projectPrefix}TARGET_SCOPE} Microsoft.GSL::GSL)
target_compile_definitions(mp-units-core ${${projectPrefix}TARGET_SCOPE} ${projectPrefix}API_CONTRACTS=3)
endif()
# C++20 modules
if(${projectPrefix}BUILD_CXX_MODULES)
if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 18)
@ -112,6 +138,7 @@ if(${projectPrefix}BUILD_CXX_MODULES)
endif()
endif()
# UTF-8 source and execution character set
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_compile_options(
mp-units-core ${${projectPrefix}TARGET_SCOPE}
@ -119,4 +146,8 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
)
endif()
# target_compile_options(mp-units-core ${${projectPrefix}TARGET_SCOPE} "-ftime-trace")
# Freestanding
target_compile_definitions(
mp-units-core ${${projectPrefix}TARGET_SCOPE}
${projectPrefix}HOSTED=$<NOT:$<BOOL:${${projectPrefix}API_FREESTANDING}>>
)

View File

@ -22,15 +22,14 @@
#pragma once
#include <gsl/gsl-lite.hpp>
#include <mp-units/bits/hacks.h>
#include <mp-units/compat_macros.h>
#include <array>
#include <cmath>
#include <compare>
#include <concepts>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <functional>
#include <initializer_list>
#include <iterator>
@ -38,14 +37,20 @@
#include <numbers>
#include <numeric>
#include <optional>
#include <random>
#include <sstream>
#include <string>
#include <ranges>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#if MP_UNITS_HOSTED
#include <cmath>
#include <locale>
#include <ostream>
#include <random>
#include <sstream>
#include <string>
#if MP_UNITS_USE_FMTLIB
MP_UNITS_DIAGNOSTIC_PUSH
MP_UNITS_DIAGNOSTIC_IGNORE_UNREACHABLE
@ -56,6 +61,8 @@ MP_UNITS_DIAGNOSTIC_POP
#include <format>
#endif
#endif
#if __cpp_lib_text_encoding
#include <text_encoding>
#endif

View File

@ -30,11 +30,12 @@
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-type-union-access)
#pragma once
#include <mp-units/bits/module_macros.h>
#include <mp-units/compat_macros.h>
#include <mp-units/ext/algorithm.h>
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#include <gsl/gsl-lite.hpp>
#include <array>
#include <concepts>
#include <cstdint>
#include <limits>
@ -45,6 +46,9 @@
namespace mp_units::detail {
// TODO the below should be exposed by the C++ Standard Library (used in our examples)
MP_UNITS_EXPORT_BEGIN
enum class fmt_align : std::int8_t { none, left, right, center, numeric };
enum class fmt_arg_id_kind : std::int8_t {
none,
@ -109,6 +113,8 @@ public:
[[nodiscard]] constexpr const Char& operator[](size_t index) const { return data_[index]; }
};
MP_UNITS_EXPORT_END
template<typename T>
inline constexpr bool is_integer =
std::is_integral_v<T> && !std::is_same_v<T, bool> && !std::is_same_v<T, char> && !std::is_same_v<T, wchar_t>;
@ -131,24 +137,10 @@ template<typename Char>
template<typename Int>
[[nodiscard]] constexpr std::make_unsigned_t<Int> to_unsigned(Int value)
{
gsl_Expects(std::is_unsigned_v<Int> || value >= 0);
MP_UNITS_EXPECTS(std::is_unsigned_v<Int> || value >= 0);
return static_cast<std::make_unsigned_t<Int>>(value);
}
struct width_checker {
template<typename T>
[[nodiscard]] constexpr unsigned long long operator()(T value) const
{
if constexpr (is_integer<T>) {
if constexpr (std::numeric_limits<T>::is_signed)
if (value < 0) MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("negative width"));
return static_cast<unsigned long long>(value);
}
MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("width is not integer"));
return 0;
}
};
template<class Handler, typename FormatArg>
[[nodiscard]] constexpr int get_dynamic_spec(FormatArg arg)
{
@ -167,6 +159,9 @@ template<typename Context, typename ID>
return arg;
}
// TODO the below should be exposed by the C++ Standard Library (used in our examples)
MP_UNITS_EXPORT_BEGIN
template<class Handler, typename Context>
constexpr void handle_dynamic_spec(int& value, fmt_arg_ref<typename Context::char_type> ref, Context& ctx)
{
@ -184,12 +179,28 @@ constexpr void handle_dynamic_spec(int& value, fmt_arg_ref<typename Context::cha
}
}
struct width_checker {
template<typename T>
[[nodiscard]] constexpr unsigned long long operator()(T value) const
{
if constexpr (is_integer<T>) {
if constexpr (std::numeric_limits<T>::is_signed)
if (value < 0) MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("negative width"));
return static_cast<unsigned long long>(value);
}
MP_UNITS_THROW(MP_UNITS_STD_FMT::format_error("width is not integer"));
return 0;
}
};
MP_UNITS_EXPORT_END
// Parses the range [begin, end) as an unsigned integer. This function assumes
// that the range is non-empty and the first character is a digit.
template<typename Char>
[[nodiscard]] constexpr int parse_nonnegative_int(const Char*& begin, const Char* end, int error_value)
{
gsl_Expects(begin != end && '0' <= *begin && *begin <= '9');
MP_UNITS_EXPECTS(begin != end && '0' <= *begin && *begin <= '9');
unsigned value = 0, prev = 0;
auto p = begin;
do {
@ -250,7 +261,7 @@ template<typename Char, typename Handler>
template<typename Char, typename Handler>
[[nodiscard]] constexpr const Char* parse_arg_id(const Char* begin, const Char* end, Handler& handler)
{
gsl_Expects(begin != end);
MP_UNITS_EXPECTS(begin != end);
Char c = *begin;
if (c != '}' && c != ':') return ::mp_units::detail::do_parse_arg_id(begin, end, handler);
handler.on_auto();
@ -292,7 +303,7 @@ template<typename Char>
fmt_arg_ref<Char>& ref,
MP_UNITS_STD_FMT::basic_format_parse_context<Char>& ctx)
{
gsl_Expects(begin != end);
MP_UNITS_EXPECTS(begin != end);
if ('0' <= *begin && *begin <= '9') {
const int val = ::mp_units::detail::parse_nonnegative_int(begin, end, -1);
if (val != -1)
@ -329,7 +340,7 @@ template<typename Char, typename Specs>
[[nodiscard]] constexpr const Char* parse_align(const Char* begin, const Char* end, Specs& specs,
fmt_align default_align = fmt_align::none)
{
gsl_Expects(begin != end);
MP_UNITS_EXPECTS(begin != end);
auto align = fmt_align::none;
auto p = begin + code_point_length(begin);
if (end - p <= 0) p = begin;

View File

@ -59,7 +59,7 @@ template<AssociatedUnit U>
[[nodiscard]] consteval auto get_associated_quantity_impl(U u);
template<AssociatedUnit U>
using to_quantity_spec = std::remove_const_t<decltype(get_associated_quantity_impl(U{}))>;
using to_quantity_spec = decltype(get_associated_quantity_impl(U{}));
template<AssociatedUnit U>
[[nodiscard]] consteval auto get_associated_quantity_impl(U u)

View File

@ -73,6 +73,10 @@
#define MP_UNITS_DIAGNOSTIC_IGNORE_DEPRECATED
#endif
#if !defined MP_UNITS_HOSTED && defined __STDC_HOSTED__
#define MP_UNITS_HOSTED __STDC_HOSTED__
#endif
#if MP_UNITS_COMP_MSVC
#define MP_UNITS_TYPENAME typename
@ -83,10 +87,26 @@
#endif
// TODO revise the below when clang-18 is released
#if MP_UNITS_COMP_CLANG >= 18 && __cplusplus >= 202300L && !defined __cpp_explicit_this_parameter
#if MP_UNITS_COMP_GCC
#define __cpp_explicit_this_parameter 202110L
#define MP_UNITS_REMOVE_CONST(expr) std::remove_const_t<expr>
#else
#define MP_UNITS_REMOVE_CONST(expr) expr
#endif
#if !defined __cpp_lib_ranges_to_container
namespace std {
struct from_range_t {
explicit from_range_t() = default;
};
inline constexpr from_range_t from_range{};
} // namespace std
#endif

View File

@ -24,9 +24,9 @@
#include <mp-units/bits/hacks.h>
#include <mp-units/bits/module_macros.h>
#include <mp-units/compat_macros.h>
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#include <gsl/gsl-lite.hpp>
#include <compare> // IWYU pragma: export
#include <cstdint>
#include <numeric>
@ -44,16 +44,16 @@ template<typename T>
{
constexpr std::intmax_t c = std::uintmax_t{1} << (sizeof(std::intmax_t) * 4);
const std::intmax_t a0 = abs(lhs) % c;
const std::intmax_t a1 = abs(lhs) / c;
const std::intmax_t b0 = abs(rhs) % c;
const std::intmax_t b1 = abs(rhs) / c;
[[maybe_unused]] const std::intmax_t a0 = abs(lhs) % c;
[[maybe_unused]] const std::intmax_t a1 = abs(lhs) / c;
[[maybe_unused]] const std::intmax_t b0 = abs(rhs) % c;
[[maybe_unused]] const std::intmax_t b1 = abs(rhs) / c;
// overflow in multiplication
gsl_Assert(a1 == 0 || b1 == 0);
gsl_Assert(a0 * b1 + b0 * a1 < (c >> 1)); // NOLINT(hicpp-signed-bitwise)
gsl_Assert(b0 * a0 <= INTMAX_MAX);
gsl_Assert((a0 * b1 + b0 * a1) * c <= INTMAX_MAX - b0 * a0);
MP_UNITS_ASSERT(a1 == 0 || b1 == 0);
MP_UNITS_ASSERT(a0 * b1 + b0 * a1 < (c >> 1)); // NOLINT(hicpp-signed-bitwise)
MP_UNITS_ASSERT(b0 * a0 <= INTMAX_MAX);
MP_UNITS_ASSERT((a0 * b1 + b0 * a1) * c <= INTMAX_MAX - b0 * a0);
return lhs * rhs;
}
@ -72,7 +72,7 @@ MP_UNITS_EXPORT struct ratio {
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters, google-explicit-constructor, hicpp-explicit-conversions)
MP_UNITS_CONSTEVAL explicit(false) ratio(std::intmax_t n, std::intmax_t d = 1) : num{n}, den{d}
{
gsl_Expects(den != 0);
MP_UNITS_EXPECTS(den != 0);
if (num == 0)
den = 1;
else {
@ -112,9 +112,9 @@ MP_UNITS_EXPORT struct ratio {
if (r1.num == r2.num && r1.den == r2.den) return ratio{r1.num, r1.den};
// gcd(a/b,c/d) = gcd(a⋅d, c⋅b) / b⋅d
gsl_Assert(std::numeric_limits<std::intmax_t>::max() / r1.num > r2.den);
gsl_Assert(std::numeric_limits<std::intmax_t>::max() / r2.num > r1.den);
gsl_Assert(std::numeric_limits<std::intmax_t>::max() / r1.den > r2.den);
MP_UNITS_ASSERT(std::numeric_limits<std::intmax_t>::max() / r1.num > r2.den);
MP_UNITS_ASSERT(std::numeric_limits<std::intmax_t>::max() / r2.num > r1.den);
MP_UNITS_ASSERT(std::numeric_limits<std::intmax_t>::max() / r1.den > r2.den);
const std::intmax_t num = std::gcd(r1.num * r2.den, r2.num * r1.den);
const std::intmax_t den = r1.den * r2.den;

View File

@ -0,0 +1,29 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <mp-units/bits/hacks.h>
#if !MP_UNITS_HOSTED
#error "This header is not available in freestanding mode."
#endif

View File

@ -23,6 +23,7 @@
#pragma once
#include <mp-units/bits/ratio.h>
#include <mp-units/compat_macros.h>
#include <mp-units/ext/fixed_string.h>
#include <mp-units/ext/type_traits.h>
#include <mp-units/framework/symbol_text.h>
@ -100,12 +101,12 @@ constexpr Out copy(const symbol_text<N, M>& txt, text_encoding encoding, Out out
for (const char8_t ch : txt.unicode()) *out++ = static_cast<char>(ch);
return out;
} else
throw std::invalid_argument("Unicode text can't be copied to CharT output");
MP_UNITS_THROW(std::invalid_argument("Unicode text can't be copied to CharT output"));
} else {
if constexpr (is_same_v<CharT, char>)
return ::mp_units::detail::copy(txt.ascii().begin(), txt.ascii().end(), out);
else
throw std::invalid_argument("ASCII text can't be copied to CharT output");
MP_UNITS_THROW(std::invalid_argument("ASCII text can't be copied to CharT output"));
}
}

View File

@ -40,6 +40,15 @@
#endif
#if MP_UNITS_HOSTED
#define MP_UNITS_THROW(expr) throw expr
#else
#include <cstdlib>
#define MP_UNITS_THROW(expr) std::abort()
#endif
#if MP_UNITS_HOSTED
#if defined MP_UNITS_API_STD_FORMAT && !MP_UNITS_API_STD_FORMAT
#define MP_UNITS_USE_FMTLIB 1
@ -59,21 +68,7 @@
#define MP_UNITS_FMT_TO_ARG_ID(arg) (arg)
#define MP_UNITS_FMT_FROM_ARG_ID(arg) (arg)
// This re-uses code from fmt;
#if FMT_EXCEPTIONS
#if FMT_MSC_VERSION || defined(__NVCC__)
#define MP_UNITS_THROW(x) ::fmt::detail::do_throw(x)
#else
#define MP_UNITS_THROW(x) throw x
#endif
#else
#define MP_UNITS_THROW(x) \
do { \
FMT_ASSERT(false, (x).what()); \
} while (false)
#endif
#else
#else // MP_UNITS_USE_FMTLIB
#if !defined __cpp_lib_format && !defined MP_UNITS_COMP_CLANG
#error "std::formatting facility not supported"
@ -83,10 +78,8 @@
#define MP_UNITS_FMT_LOCALE(loc) loc
#define MP_UNITS_FMT_TO_ARG_ID(arg) static_cast<std::size_t>(arg)
#define MP_UNITS_FMT_FROM_ARG_ID(arg) static_cast<int>(arg)
#define MP_UNITS_THROW(arg) throw arg
#endif
#endif // MP_UNITS_USE_FMTLIB
#ifndef MP_UNITS_IN_MODULE_INTERFACE
@ -102,5 +95,39 @@ MP_UNITS_DIAGNOSTIC_POP
#endif
// IWYU pragma: end_exports
#endif
#endif // MP_UNITS_HOSTED
#if MP_UNITS_API_CONTRACTS == 2 || __has_include(<gsl/gsl-lite.hpp>)
#include <gsl/gsl-lite.hpp>
#define MP_UNITS_EXPECTS(expr) gsl_Expects(expr)
#define MP_UNITS_EXPECTS_DEBUG(expr) gsl_ExpectsDebug(expr)
#define MP_UNITS_ASSERT(expr) gsl_Assert(expr)
#define MP_UNITS_ASSERT_DEBUG(expr) gsl_AssertDebug(expr)
#elif MP_UNITS_API_CONTRACTS == 3 || __has_include(<gsl/gsl>)
#include <gsl/gsl>
#include <cassert>
#define MP_UNITS_EXPECTS(expr) Expects(expr)
#if defined NDEBUG
#define MP_UNITS_EXPECTS_DEBUG(expr) static_cast<void>(0)
#else
#define MP_UNITS_EXPECTS_DEBUG(expr) Expects(expr)
#endif
#define MP_UNITS_ASSERT(expr) Expects(expr)
#define MP_UNITS_ASSERT_DEBUG(expr) assert(expr)
#else
#define MP_UNITS_EXPECTS(expr) static_cast<void>(0)
#define MP_UNITS_EXPECTS_DEBUG(expr) static_cast<void>(0)
#define MP_UNITS_ASSERT(expr) static_cast<void>(0)
#define MP_UNITS_ASSERT_DEBUG(expr) static_cast<void>(0)
#endif
// NOLINTEND(cppcoreguidelines-macro-usage)

View File

@ -20,8 +20,9 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// Copy-pasted C++ standard libraries to be replaced with `import std;` when available
// `#include <algorithm.h>` is too heavy to do in every translation unit
// Copy-pasted C++ standard libraries
// TODO To be replaced with `import std;` when available
// `#include <algorithm.h>` is too heavy to do in every translation unit
#pragma once
@ -72,45 +73,6 @@ constexpr bool all_of(InputIt first, InputIt last, UnaryPred p)
return find_if_not(first, last, p) == last;
}
template<class InputIt1, class InputIt2>
constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2)
{
for (; first1 != last1; ++first1, ++first2) {
if (!(*first1 == *first2)) {
return false;
}
}
return true;
}
template<class I1, class I2, class Cmp>
constexpr auto lexicographical_compare_three_way(I1 first1, I1 last1, I2 first2, I2 last2, Cmp comp)
-> decltype(comp(*first1, *first2))
{
using ret_t = decltype(comp(*first1, *first2));
static_assert(std::disjunction_v<std::is_same<ret_t, std::strong_ordering>, std::is_same<ret_t, std::weak_ordering>,
std::is_same<ret_t, std::partial_ordering>>,
"The return type must be a comparison category type.");
bool exhaust1 = (first1 == last1);
bool exhaust2 = (first2 == last2);
MP_UNITS_DIAGNOSTIC_PUSH
MP_UNITS_DIAGNOSTIC_IGNORE_ZERO_AS_NULLPOINTER_CONSTANT
for (; !exhaust1 && !exhaust2; exhaust1 = (++first1 == last1), exhaust2 = (++first2 == last2))
if (auto c = comp(*first1, *first2); c != 0) return c;
MP_UNITS_DIAGNOSTIC_POP
if (!exhaust1) return std::strong_ordering::greater;
if (!exhaust2) return std::strong_ordering::less;
return std::strong_ordering::equal;
}
template<class I1, class I2>
constexpr auto lexicographical_compare_three_way(I1 first1, I1 last1, I2 first2, I2 last2)
{
return ::mp_units::detail::lexicographical_compare_three_way(first1, last1, first2, last2, std::compare_three_way());
}
template<class ForwardIt>
constexpr ForwardIt max_element(ForwardIt first, ForwardIt last)
{
@ -170,4 +132,18 @@ constexpr OutputIt copy(InputIt first, InputIt last, OutputIt d_first)
return d_first;
}
template<class ForwardIt1, class ForwardIt2>
constexpr void iter_swap(ForwardIt1 a, ForwardIt2 b)
{
using std::swap;
swap(*a, *b);
}
template<class ForwardIt1, class ForwardIt2>
constexpr ForwardIt2 swap_ranges(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2)
{
for (; first1 != last1; ++first1, ++first2) iter_swap(first1, first2);
return first2;
}
} // namespace mp_units::detail

View File

@ -26,18 +26,20 @@
// NOLINTBEGIN(*-avoid-c-arrays)
#pragma once
// TODO use <algorithm> when moved to C++20 modules (parsing takes too long for each translation unit)
#include <mp-units/bits/hacks.h> // IWYU pragma: keep
#include <mp-units/bits/module_macros.h>
#include <mp-units/compat_macros.h> // IWYU pragma: keep
#include <mp-units/ext/algorithm.h>
#include <mp-units/ext/type_traits.h>
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#include <gsl/gsl-lite.hpp>
#include <compare> // IWYU pragma: export
#include <cstddef>
#include <cstdlib>
#include <ostream>
#include <ranges>
#include <string_view>
#if MP_UNITS_HOSTED
#include <ostream>
#endif
#endif
MP_UNITS_EXPORT
@ -49,121 +51,247 @@ namespace mp_units {
* @tparam CharT Character type to be used by the string
* @tparam N The size of the string
*/
template<typename CharT, std::size_t N>
struct basic_fixed_string {
CharT data_[N + 1] = {};
template<typename CharT, std::size_t N, typename Traits = std::char_traits<CharT>>
class basic_fixed_string {
public:
CharT data_[N + 1] = {}; // exposition only
// types
using traits_type = Traits;
using value_type = CharT;
using pointer = CharT*;
using const_pointer = const CharT*;
using reference = CharT&;
using const_reference = const CharT&;
using const_iterator = const CharT*;
using pointer = value_type*;
using const_pointer = const value_type*;
using reference = value_type&;
using const_reference = const value_type&;
using const_iterator = const value_type*;
using iterator = const_iterator;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using reverse_iterator = const_reverse_iterator;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
constexpr explicit(false) basic_fixed_string(const CharT (&txt)[N + 1]) noexcept
// construction and assignment
template<std::convertible_to<CharT>... Chars>
requires(sizeof...(Chars) == N) && (... && !std::is_pointer_v<Chars>)
constexpr explicit basic_fixed_string(Chars... chars) noexcept : data_{chars..., CharT{}}
{
gsl_Expects(txt[N] == CharT{});
}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
consteval explicit(false) basic_fixed_string(const CharT (&txt)[N + 1]) noexcept
{
MP_UNITS_EXPECTS(txt[N] == CharT{});
for (std::size_t i = 0; i < N; ++i) data_[i] = txt[i];
}
template<std::input_iterator It, std::sentinel_for<It> S>
requires std::convertible_to<std::iter_value_t<It>, CharT>
constexpr explicit basic_fixed_string(It first, S last) noexcept
constexpr basic_fixed_string(It begin, S end)
{
gsl_Expects(std::distance(first, last) == N);
for (auto it = data_; first != last; ++first, ++it) *it = *first;
MP_UNITS_EXPECTS(std::distance(begin, end) == N);
for (auto it = data_; begin != end; ++begin, ++it) *it = *begin;
}
template<std::convertible_to<CharT>... Rest>
requires(1 + sizeof...(Rest) == N)
constexpr explicit basic_fixed_string(CharT first, Rest... rest) noexcept : data_{first, rest..., CharT{}}
template<std::ranges::input_range R>
requires std::convertible_to<std::ranges::range_reference_t<R>, CharT>
constexpr basic_fixed_string(std::from_range_t, R&& r)
{
MP_UNITS_EXPECTS(std::ranges::size(r) == N);
for (auto it = data_; auto&& v : std::forward<R>(r)) *it++ = std::forward<decltype(v)>(v);
}
[[nodiscard]] constexpr bool empty() const noexcept { return N == 0; }
[[nodiscard]] constexpr size_type size() const noexcept { return N; }
[[nodiscard]] constexpr const_pointer data() const noexcept { return static_cast<const_pointer>(data_); }
[[nodiscard]] constexpr const CharT* c_str() const noexcept { return data(); }
[[nodiscard]] constexpr value_type operator[](size_type index) const noexcept
{
gsl_Expects(index < N);
return data()[index];
}
constexpr basic_fixed_string(const basic_fixed_string&) noexcept = default;
constexpr basic_fixed_string& operator=(const basic_fixed_string&) noexcept = default;
// iterator support
[[nodiscard]] constexpr const_iterator begin() const noexcept { return data(); }
[[nodiscard]] constexpr const_iterator cbegin() const noexcept { return data(); }
[[nodiscard]] constexpr const_iterator end() const noexcept { return data() + size(); }
[[nodiscard]] constexpr const_iterator cend() const noexcept { return data() + size(); }
[[nodiscard]] constexpr const_iterator cbegin() const noexcept { return begin(); }
[[nodiscard]] constexpr const_iterator cend() const noexcept { return end(); }
[[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); }
[[nodiscard]] constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); }
[[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); }
[[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return rend(); }
// capacity
[[nodiscard]] static constexpr std::integral_constant<size_type, N> size() noexcept { return {}; }
[[nodiscard]] static constexpr std::integral_constant<size_type, N> length() noexcept { return {}; }
[[nodiscard]] static constexpr std::integral_constant<size_type, N> max_size() noexcept { return {}; }
[[nodiscard]] static constexpr std::bool_constant<N == 0> empty() noexcept { return {}; }
// element access
[[nodiscard]] constexpr const_reference operator[](size_type pos) const
{
MP_UNITS_EXPECTS(pos < N);
return data()[pos];
}
#if MP_UNITS_HOSTED
[[nodiscard]] constexpr const_reference at(size_type pos) const
{
if (pos >= size()) throw std::out_of_range("basic_fixed_string::at");
return (*this)[pos];
}
#endif
[[nodiscard]] constexpr const_reference front() const
{
MP_UNITS_EXPECTS(!empty());
return (*this)[0];
}
[[nodiscard]] constexpr const_reference back() const
{
MP_UNITS_EXPECTS(!empty());
return (*this)[N - 1];
}
// modifiers
constexpr void swap(basic_fixed_string& s) noexcept { swap_ranges(begin(), end(), s.begin()); }
// string operations
[[nodiscard]] constexpr const_pointer c_str() const noexcept { return data(); }
[[nodiscard]] constexpr const_pointer data() const noexcept { return static_cast<const_pointer>(data_); }
[[nodiscard]] constexpr std::basic_string_view<CharT, Traits> view() const noexcept
{
return std::basic_string_view<CharT, Traits>(cbegin(), cend());
}
// NOLINTNEXTLINE(*-explicit-conversions, google-explicit-constructor)
[[nodiscard]] constexpr explicit(false) operator std::basic_string_view<CharT>() const noexcept
[[nodiscard]] constexpr explicit(false) operator std::basic_string_view<CharT, Traits>() const noexcept
{
return std::basic_string_view<CharT>(cbegin(), cend());
return view();
}
template<std::size_t N2>
[[nodiscard]] constexpr friend basic_fixed_string<CharT, N + N2> operator+(
const basic_fixed_string& lhs, const basic_fixed_string<CharT, N2>& rhs) noexcept
[[nodiscard]] constexpr friend basic_fixed_string<CharT, N + N2, Traits> operator+(
const basic_fixed_string& lhs, const basic_fixed_string<CharT, N2, Traits>& rhs) noexcept
{
CharT txt[N + N2 + 1] = {};
for (size_t i = 0; i != N; ++i) txt[i] = lhs[i];
for (size_t i = 0; i != N2; ++i) txt[N + i] = rhs[i];
return basic_fixed_string<CharT, N + N2>(txt);
CharT txt[N + N2];
CharT* it = txt;
for (CharT c : lhs) *it++ = c;
for (CharT c : rhs) *it++ = c;
return basic_fixed_string<CharT, N + N2, Traits>(txt, it);
}
[[nodiscard]] constexpr bool operator==(const basic_fixed_string&) const = default;
template<std::size_t N2>
[[nodiscard]] friend constexpr bool operator==(const basic_fixed_string&, const basic_fixed_string<CharT, N2>&)
[[nodiscard]] constexpr friend basic_fixed_string<CharT, N + 1, Traits> operator+(const basic_fixed_string& lhs,
CharT rhs) noexcept
{
return false;
CharT txt[N + 1];
CharT* it = txt;
for (CharT c : lhs) *it++ = c;
*it++ = rhs;
return basic_fixed_string<CharT, N + 1, Traits>(txt, it);
}
[[nodiscard]] constexpr friend basic_fixed_string<CharT, 1 + N, Traits> operator+(
const CharT lhs, const basic_fixed_string& rhs) noexcept
{
CharT txt[1 + N];
CharT* it = txt;
*it++ = lhs;
for (CharT c : rhs) *it++ = c;
return basic_fixed_string<CharT, 1 + N, Traits>(txt, it);
}
template<std::size_t N2>
[[nodiscard]] consteval friend basic_fixed_string<CharT, N + N2 - 1, Traits> operator+(
const basic_fixed_string& lhs, const CharT (&rhs)[N2]) noexcept
{
MP_UNITS_EXPECTS(rhs[N2 - 1] == CharT{});
CharT txt[N + N2];
CharT* it = txt;
for (CharT c : lhs) *it++ = c;
for (CharT c : rhs) *it++ = c;
return txt;
}
template<std::size_t N1>
[[nodiscard]] consteval friend basic_fixed_string<CharT, N1 + N - 1, Traits> operator+(
const CharT (&lhs)[N1], const basic_fixed_string& rhs) noexcept
{
MP_UNITS_EXPECTS(lhs[N1 - 1] == CharT{});
CharT txt[N1 + N];
CharT* it = txt;
for (size_t i = 0; i != N1 - 1; ++i) *it++ = lhs[i];
for (CharT c : rhs) *it++ = c;
*it++ = CharT();
return txt;
}
// non-member comparison functions
template<size_t N2>
[[nodiscard]] friend constexpr bool operator==(const basic_fixed_string& lhs,
const basic_fixed_string<CharT, N2, Traits>& rhs)
{
return lhs.view() == rhs.view();
}
template<size_t N2>
[[nodiscard]] friend consteval bool operator==(const basic_fixed_string& lhs, const CharT (&rhs)[N2])
{
MP_UNITS_EXPECTS(rhs[N2 - 1] == CharT{});
return lhs.view() == std::basic_string_view<CharT, Traits>(std::cbegin(rhs), std::cend(rhs) - 1);
}
template<size_t N2>
[[nodiscard]] friend constexpr auto operator<=>(const basic_fixed_string& lhs,
const basic_fixed_string<CharT, N2>& rhs)
const basic_fixed_string<CharT, N2, Traits>& rhs)
{
// TODO std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
return detail::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
return lhs.view() <=> rhs.view();
}
template<size_t N2>
[[nodiscard]] friend consteval auto operator<=>(const basic_fixed_string& lhs, const CharT (&rhs)[N2])
{
MP_UNITS_EXPECTS(rhs[N2 - 1] == CharT{});
return lhs.view() <=> std::basic_string_view<CharT, Traits>(std::cbegin(rhs), std::cend(rhs) - 1);
}
template<typename Traits>
// inserters and extractors
#if MP_UNITS_HOSTED
friend std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
const basic_fixed_string<CharT, N>& str)
const basic_fixed_string& str)
{
return os << str.c_str();
}
#endif
};
// deduction guides
template<one_of<char, char8_t, char16_t, char32_t, wchar_t> CharT, std::convertible_to<CharT>... Rest>
basic_fixed_string(CharT, Rest...) -> basic_fixed_string<CharT, 1 + sizeof...(Rest)>;
template<typename CharT, std::size_t N>
basic_fixed_string(const CharT (&str)[N]) -> basic_fixed_string<CharT, N - 1>;
template<typename CharT, std::convertible_to<CharT>... Rest>
basic_fixed_string(CharT, Rest...) -> basic_fixed_string<CharT, 1 + sizeof...(Rest)>;
template<one_of<char, char8_t, char16_t, char32_t, wchar_t> CharT, std::size_t N>
basic_fixed_string(std::from_range_t, std::array<CharT, N>) -> basic_fixed_string<CharT, N>;
// typedef-names
template<std::size_t N>
using fixed_string = basic_fixed_string<char, N>;
template<std::size_t N>
using fixed_u8string = basic_fixed_string<char8_t, N>;
template<std::size_t N>
using fixed_u16string = basic_fixed_string<char16_t, N>;
template<std::size_t N>
using fixed_u32string = basic_fixed_string<char32_t, N>;
template<std::size_t N>
using fixed_wstring = basic_fixed_string<wchar_t, N>;
template<std::size_t N>
using fixed_u8string = basic_fixed_string<char8_t, N>;
template<std::size_t N>
using fixed_u16string = basic_fixed_string<char16_t, N>;
template<std::size_t N>
using fixed_u32string = basic_fixed_string<char32_t, N>;
} // namespace mp_units
// hash support
template<std::size_t N>
struct std::hash<mp_units::fixed_string<N>> : std::hash<std::string_view> {};
template<std::size_t N>
struct std::hash<mp_units::fixed_u8string<N>> : std::hash<std::u8string_view> {};
template<std::size_t N>
struct std::hash<mp_units::fixed_u16string<N>> : std::hash<std::u16string_view> {};
template<std::size_t N>
struct std::hash<mp_units::fixed_u32string<N>> : std::hash<std::u32string_view> {};
template<std::size_t N>
struct std::hash<mp_units::fixed_wstring<N>> : std::hash<std::wstring_view> {};
#if MP_UNITS_HOSTED
// formatting support
template<typename CharT, std::size_t N>
struct MP_UNITS_STD_FMT::formatter<mp_units::basic_fixed_string<CharT, N>> : formatter<std::basic_string_view<CharT>> {
template<typename FormatContext>
@ -172,4 +300,6 @@ struct MP_UNITS_STD_FMT::formatter<mp_units::basic_fixed_string<CharT, N>> : for
return formatter<std::basic_string_view<CharT>>::format(std::basic_string_view<CharT>(str), ctx);
}
};
#endif
// NOLINTEND(*-avoid-c-arrays)

View File

@ -88,9 +88,8 @@ void to_base_specialization_of(const volatile Type<Params...>*);
} // namespace detail
template<typename T, template<typename...> typename Type>
// inline constexpr bool // TODO: Replace with concept when it works with MSVC
concept is_derived_from_specialization_of = requires(T* t) { detail::to_base_specialization_of<Type>(t); };
inline constexpr bool is_derived_from_specialization_of =
requires(T* t) { detail::to_base_specialization_of<Type>(t); };
namespace detail {
@ -137,7 +136,7 @@ concept one_of = (false || ... || std::same_as<T, Ts>);
template<typename T, auto... Vs>
[[nodiscard]] consteval bool contains()
{
return (false || ... || is_same_v<std::remove_const_t<decltype(Vs)>, T>);
return (false || ... || is_same_v<MP_UNITS_REMOVE_CONST(decltype(Vs)), T>);
}
template<template<typename...> typename T, typename... Ts>
@ -161,7 +160,7 @@ template<typename T, std::same_as<T> auto V>
template<typename T, auto V1, auto V2, auto... Vs>
[[nodiscard]] consteval auto get()
{
if constexpr (is_same_v<T, std::remove_const_t<decltype(V1)>>)
if constexpr (is_same_v<T, MP_UNITS_REMOVE_CONST(decltype(V1))>)
return V1;
else
return get<T, V2, Vs...>();

View File

@ -24,22 +24,21 @@
#pragma once
#include <mp-units/bits/requires_hosted.h>
//
#include <mp-units/bits/fmt.h>
#include <mp-units/bits/module_macros.h>
#include <mp-units/compat_macros.h>
#include <mp-units/ext/algorithm.h>
#include <mp-units/framework/customization_points.h>
#include <mp-units/framework/quantity.h>
#include <mp-units/framework/unit.h>
namespace mp_units::detail {
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#include <locale>
#endif
template<typename Char>
struct fill_align_width_format_specs {
fill_t<Char> fill;
fmt_align align : 4 = fmt_align::none;
int width = 0;
fmt_arg_ref<Char> width_ref;
};
namespace mp_units::detail {
template<typename Char>
[[nodiscard]] constexpr const Char* at_most_one_of(const Char* begin, const Char* end, std::string_view modifiers)
@ -51,6 +50,17 @@ template<typename Char>
return it;
}
// TODO the below should be exposed by the C++ Standard Library (used in our examples)
MP_UNITS_EXPORT_BEGIN
template<typename Char>
struct fill_align_width_format_specs {
fill_t<Char> fill;
fmt_align align : 4 = fmt_align::none;
int width = 0;
fmt_arg_ref<Char> width_ref;
};
template<typename Char, typename Specs>
[[nodiscard]] constexpr const Char* parse_fill_align_width(MP_UNITS_STD_FMT::basic_format_parse_context<Char>& ctx,
const Char* begin, const Char* end, Specs& specs,
@ -89,6 +99,8 @@ OutputIt format_global_buffer(OutputIt out, const fill_align_width_format_specs<
return MP_UNITS_STD_FMT::format_to(out, "}}");
}
MP_UNITS_EXPORT_END
} // namespace mp_units::detail
//

View File

@ -26,6 +26,7 @@
#include <mp-units/bits/hacks.h>
#include <mp-units/bits/module_macros.h>
#include <mp-units/bits/text_tools.h>
#include <mp-units/compat_macros.h>
#include <mp-units/ext/fixed_string.h>
#include <mp-units/ext/type_traits.h>
#include <mp-units/framework/dimension_concepts.h>
@ -33,12 +34,13 @@
#include <mp-units/framework/symbol_text.h>
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#include <gsl/gsl-lite.hpp>
#include <array>
#include <cstdint>
#include <iterator>
#include <string>
#include <string_view>
#if MP_UNITS_HOSTED
#include <string>
#endif
#endif
namespace mp_units {
@ -280,7 +282,8 @@ template<typename CharT, std::output_iterator<CharT> Out, typename... Expr>
constexpr Out dimension_symbol_impl(Out out, const derived_dimension<Expr...>&, const dimension_symbol_formatting& fmt,
bool negative_power)
{
gsl_Expects(negative_power == false);
(void)negative_power;
MP_UNITS_EXPECTS(negative_power == false);
return dimension_symbol_impl<CharT>(out, typename derived_dimension<Expr...>::_num_{},
typename derived_dimension<Expr...>::_den_{}, fmt);
}
@ -317,9 +320,15 @@ MP_UNITS_EXPORT template<dimension_symbol_formatting fmt = dimension_symbol_form
#endif
{
auto get_size = []() consteval {
#if MP_UNITS_HOSTED
std::basic_string<CharT> buffer;
dimension_symbol_to<CharT>(std::back_inserter(buffer), D{}, fmt);
return buffer.size();
#else
std::array<CharT, 128> buffer; // TODO unsafe
auto end = dimension_symbol_to<CharT>(buffer.begin(), D{}, fmt);
return end - buffer.begin();
#endif
};
#if MP_UNITS_API_STRING_VIEW_RET // Permitting static constexpr variables in constexpr functions
@ -328,8 +337,7 @@ MP_UNITS_EXPORT template<dimension_symbol_formatting fmt = dimension_symbol_form
return std::string_view(buffer.data(), size);
#else
constexpr std::size_t size = get_size();
constexpr auto buffer = detail::get_symbol_buffer<CharT, size, fmt>(D{});
return basic_fixed_string<CharT, size>(buffer.begin(), buffer.end());
return basic_fixed_string(std::from_range, detail::get_symbol_buffer<CharT, size, fmt>(D{}));
#endif
}

View File

@ -109,6 +109,6 @@ concept Dimension = detail::BaseDimension<T> || detail::DerivedDimension<T>;
* Satisfied when both argument satisfy a `Dimension` concept and when they compare equal.
*/
MP_UNITS_EXPORT template<typename T, auto D>
concept DimensionOf = Dimension<T> && Dimension<std::remove_const_t<decltype(D)>> && (T{} == D);
concept DimensionOf = Dimension<T> && Dimension<MP_UNITS_REMOVE_CONST(decltype(D))> && (T{} == D);
} // namespace mp_units

View File

@ -534,8 +534,8 @@ template<template<typename> typename Proj, template<typename...> typename To, ty
expr_type_projectable<Proj>... Dens>
[[nodiscard]] consteval auto expr_map_impl(type_list<Nums...>, type_list<Dens...>)
{
return (OneType{} * ... * map_power(typename expr_type_map<std::remove_const_t<Nums>, Proj>::type{})) /
(OneType{} * ... * map_power(typename expr_type_map<std::remove_const_t<Dens>, Proj>::type{}));
return (OneType{} * ... * map_power(typename expr_type_map<Nums, Proj>::type{})) /
(OneType{} * ... * map_power(typename expr_type_map<Dens, Proj>::type{}));
}
/**

View File

@ -38,6 +38,7 @@
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#include <concepts>
#include <cstdint>
#include <cstdlib>
#include <numbers>
#include <optional>
#endif
@ -278,7 +279,7 @@ template<typename T>
// 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) {
std::terminate(); // int_power only supports positive integer powers
std::abort(); // int_power only supports positive integer powers
}
constexpr auto checked_multiply = [](auto a, auto b) {
@ -286,7 +287,7 @@ template<typename T>
MP_UNITS_DIAGNOSTIC_PUSH
MP_UNITS_DIAGNOSTIC_IGNORE_FLOAT_EQUAL
if (result / a != b) {
std::terminate(); // Wraparound detected
std::abort(); // Wraparound detected
}
MP_UNITS_DIAGNOSTIC_POP
return result;
@ -294,17 +295,8 @@ template<typename T>
constexpr auto checked_square = [checked_multiply](auto a) { return checked_multiply(a, a); };
// TODO(chogg): Unify this implementation with the one in pow.h. That one takes its exponent as a
// template parameter, rather than a function parameter.
if (exp == 0) {
return T{1};
}
if (exp % 2 == 1) {
return checked_multiply(base, int_power(base, exp - 1));
}
if (exp == 0) return T{1};
if (exp % 2 == 1) return checked_multiply(base, int_power(base, exp - 1));
return checked_square(int_power(base, exp / 2));
}
@ -319,12 +311,12 @@ template<typename T>
// terminations is to act as "static_assert substitutes", not to actually terminate at runtime.
const auto exp = get_exponent(el);
if (exp.den != 1) {
std::terminate(); // Rational powers not yet supported
std::abort(); // Rational powers not yet supported
}
if (exp.num < 0) {
if constexpr (std::is_integral_v<T>) {
std::terminate(); // Cannot represent reciprocal as integer
std::abort(); // Cannot represent reciprocal as integer
} else {
return T{1} / compute_base_power<T>(inverse(el));
}
@ -339,7 +331,6 @@ template<typename T>
// The input is the desired result, but in a (wider) intermediate type. The point of this function
// is to cast to the desired type, but avoid overflow in doing so.
template<typename To, typename From>
// TODO(chogg): Migrate this to use `treat_as_floating_point`.
requires(!std::is_integral_v<To> || std::is_integral_v<From>)
[[nodiscard]] consteval To checked_static_cast(From x)
{
@ -347,7 +338,7 @@ template<typename To, typename From>
// 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)) {
std::terminate(); // Cannot represent magnitude in this type
std::abort(); // Cannot represent magnitude in this type
}
}

View File

@ -26,6 +26,7 @@
// IWYU pragma: private, include <mp-units/framework.h>
#include <mp-units/bits/module_macros.h>
#include <mp-units/bits/sudo_cast.h>
#include <mp-units/compat_macros.h>
#include <mp-units/framework/customization_points.h>
#include <mp-units/framework/dimension_concepts.h>
#include <mp-units/framework/quantity_concepts.h>
@ -36,7 +37,6 @@
#include <mp-units/framework/unit_concepts.h>
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#include <gsl/gsl-lite.hpp>
#include <compare> // IWYU pragma: export
#include <utility>
#endif
@ -140,8 +140,7 @@ public:
template<typename Value>
requires std::same_as<std::remove_cvref_t<Value>, Rep>
constexpr quantity(Value&& v, std::remove_const_t<decltype(R)>) :
numerical_value_is_an_implementation_detail_(std::forward<Value>(v))
constexpr quantity(Value&& v, decltype(R)) : numerical_value_is_an_implementation_detail_(std::forward<Value>(v))
{
}
@ -353,7 +352,7 @@ public:
friend constexpr decltype(auto) operator%=(Q&& lhs, const quantity& rhs)
{
gsl_ExpectsDebug(rhs != zero());
MP_UNITS_EXPECTS_DEBUG(rhs != zero());
lhs.numerical_value_is_an_implementation_detail_ %= rhs.numerical_value_is_an_implementation_detail_;
return std::forward<Q>(lhs);
}
@ -393,7 +392,7 @@ public:
}
friend constexpr decltype(auto) operator/=(Q&& lhs, const Value& v)
{
gsl_ExpectsDebug(v != quantity_values<Value>::zero());
MP_UNITS_EXPECTS_DEBUG(v != quantity_values<Value>::zero());
lhs.numerical_value_is_an_implementation_detail_ /= v;
return std::forward<Q>(lhs);
}
@ -407,7 +406,7 @@ public:
}
friend constexpr decltype(auto) operator/=(Q1&& lhs, const Q2& rhs)
{
gsl_ExpectsDebug(rhs != rhs.zero());
MP_UNITS_EXPECTS_DEBUG(rhs != rhs.zero());
lhs.numerical_value_is_an_implementation_detail_ /= rhs.numerical_value_is_an_implementation_detail_;
return std::forward<Q1>(lhs);
}
@ -451,7 +450,7 @@ template<auto R1, typename Rep1, auto R2, typename Rep2>
detail::CommonlyInvocableQuantities<std::modulus<>, quantity<R1, Rep1>, quantity<R2, Rep2>>
[[nodiscard]] constexpr Quantity auto operator%(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{
gsl_ExpectsDebug(rhs != rhs.zero());
MP_UNITS_EXPECTS_DEBUG(rhs != rhs.zero());
using ret = detail::common_quantity_for<std::modulus<>, quantity<R1, Rep1>, quantity<R2, Rep2>>;
const ret ret_lhs(lhs);
const ret ret_rhs(rhs);
@ -486,7 +485,7 @@ template<auto R1, typename Rep1, auto R2, typename Rep2>
requires detail::InvocableQuantities<std::divides<>, quantity<R1, Rep1>, quantity<R2, Rep2>>
[[nodiscard]] constexpr Quantity auto operator/(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{
gsl_ExpectsDebug(rhs != rhs.zero());
MP_UNITS_EXPECTS_DEBUG(rhs != rhs.zero());
return quantity{lhs.numerical_value_ref_in(get_unit(R1)) / rhs.numerical_value_ref_in(get_unit(R2)), R1 / R2};
}
@ -495,7 +494,7 @@ template<auto R, typename Rep, typename Value>
detail::InvokeResultOf<get_quantity_spec(R).character, std::divides<>, Rep, const Value&>
[[nodiscard]] constexpr QuantityOf<get_quantity_spec(R)> auto operator/(const quantity<R, Rep>& q, const Value& v)
{
gsl_ExpectsDebug(v != quantity_values<Value>::zero());
MP_UNITS_EXPECTS_DEBUG(v != quantity_values<Value>::zero());
return quantity{q.numerical_value_ref_in(get_unit(R)) / v, R};
}

View File

@ -41,7 +41,7 @@ void to_base_specialization_of_quantity(const volatile quantity<R, Rep>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_quantity =
requires(std::remove_reference_t<T>* t) { to_base_specialization_of_quantity(t); };
requires(T* t) { to_base_specialization_of_quantity(t); };
} // namespace detail

View File

@ -169,8 +169,7 @@ public:
template<typename Q>
requires QuantityOf<std::remove_cvref_t<Q>, get_quantity_spec(R)> && std::constructible_from<quantity_type, Q>
constexpr quantity_point(Q&& q, std::remove_const_t<decltype(PO)>) :
quantity_from_origin_is_an_implementation_detail_(std::forward<Q>(q))
constexpr quantity_point(Q&& q, decltype(PO)) : quantity_from_origin_is_an_implementation_detail_(std::forward<Q>(q))
{
}
@ -187,7 +186,6 @@ public:
template<QuantityPointOf<absolute_point_origin> QP>
requires std::constructible_from<quantity_type, typename QP::quantity_type>
// TODO add perfect forwarding
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
constexpr explicit(!std::convertible_to<typename QP::quantity_type, quantity_type>) quantity_point(const QP& qp) :
quantity_from_origin_is_an_implementation_detail_([&] {
@ -222,7 +220,7 @@ public:
[[nodiscard]] constexpr MP_UNITS_CONSTRAINED_AUTO_WORKAROUND(QuantityPointOf<NewPO{}>) auto point_for(
NewPO new_origin) const
{
if constexpr (is_same_v<NewPO, std::remove_const_t<decltype(point_origin)>>)
if constexpr (is_same_v<NewPO, decltype(PO)>)
return *this;
else
return ::mp_units::quantity_point{*this - new_origin, new_origin};
@ -388,7 +386,7 @@ explicit(
template<auto R1, auto PO1, typename Rep1, auto R2, typename Rep2>
// TODO simplify when gcc catches up
requires ReferenceOf<std::remove_const_t<decltype(R2)>, PO1.quantity_spec>
requires ReferenceOf<MP_UNITS_REMOVE_CONST(decltype(R2)), PO1.quantity_spec>
[[nodiscard]] constexpr QuantityPoint auto operator+(const quantity_point<R1, PO1, Rep1>& qp,
const quantity<R2, Rep2>& q)
requires requires { qp.quantity_ref_from(PO1) + q; }
@ -401,7 +399,7 @@ template<auto R1, auto PO1, typename Rep1, auto R2, typename Rep2>
template<auto R1, typename Rep1, auto R2, auto PO2, typename Rep2>
// TODO simplify when gcc catches up
requires ReferenceOf<std::remove_const_t<decltype(R1)>, PO2.quantity_spec>
requires ReferenceOf<MP_UNITS_REMOVE_CONST(decltype(R1)), PO2.quantity_spec>
[[nodiscard]] constexpr QuantityPoint auto operator+(const quantity<R1, Rep1>& q,
const quantity_point<R2, PO2, Rep2>& qp)
requires requires { q + qp.quantity_ref_from(PO2); }
@ -425,7 +423,7 @@ template<Quantity Q, PointOrigin PO>
template<auto R1, auto PO1, typename Rep1, auto R2, typename Rep2>
// TODO simplify when gcc catches up
requires ReferenceOf<std::remove_const_t<decltype(R2)>, PO1.quantity_spec>
requires ReferenceOf<MP_UNITS_REMOVE_CONST(decltype(R2)), PO1.quantity_spec>
[[nodiscard]] constexpr QuantityPoint auto operator-(const quantity_point<R1, PO1, Rep1>& qp,
const quantity<R2, Rep2>& q)
requires requires { qp.quantity_ref_from(PO1) - q; }

View File

@ -105,7 +105,7 @@ concept PointOrigin = detail::AbsolutePointOrigin<T> || detail::RelativePointOri
* Satisfied by all quantity point origins that are defined using a provided quantity specification.
*/
MP_UNITS_EXPORT template<typename T, auto QS>
concept PointOriginFor = PointOrigin<T> && QuantitySpecOf<std::remove_const_t<decltype(QS)>, T::quantity_spec>;
concept PointOriginFor = PointOrigin<T> && QuantitySpecOf<MP_UNITS_REMOVE_CONST(decltype(QS)), T::quantity_spec>;
MP_UNITS_EXPORT template<Reference auto R, PointOriginFor<get_quantity_spec(R)> auto PO,
RepresentationOf<get_quantity_spec(R).character> Rep>
@ -141,7 +141,7 @@ template<PointOrigin PO1, PointOrigin PO2>
template<typename T, auto V>
concept SameAbsolutePointOriginAs =
PointOrigin<T> && PointOrigin<std::remove_const_t<decltype(V)>> && same_absolute_point_origins(T{}, V);
PointOrigin<T> && PointOrigin<MP_UNITS_REMOVE_CONST(decltype(V))> && same_absolute_point_origins(T{}, V);
} // namespace detail

View File

@ -38,8 +38,10 @@
#include <mp-units/framework/representation_concepts.h>
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#include <concepts>
#include <cstdint>
#include <tuple>
#include <type_traits>
#endif
namespace mp_units {
@ -216,11 +218,11 @@ MP_UNITS_EXPORT_END
*/
#ifdef MP_UNITS_API_NO_CRTP
template<detail::BaseDimension auto Dim, one_of<quantity_character> auto... Args>
requires(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>)
requires(... && !QuantitySpec<decltype(Args)>)
struct quantity_spec<Dim, Args...> : detail::quantity_spec_interface {
#else
template<typename Self, detail::BaseDimension auto Dim, one_of<quantity_character> auto... Args>
requires(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>)
requires(... && !QuantitySpec<decltype(Args)>)
struct quantity_spec<Self, Dim, Args...> : detail::quantity_spec_interface<Self> {
#endif
static constexpr detail::BaseDimension auto dimension = Dim;
@ -259,11 +261,11 @@ struct quantity_spec<Self, Dim, Args...> : detail::quantity_spec_interface<Self>
*/
#ifdef MP_UNITS_API_NO_CRTP
template<detail::IntermediateDerivedQuantitySpec auto Eq, one_of<quantity_character> auto... Args>
requires(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>)
requires(... && !QuantitySpec<decltype(Args)>)
struct quantity_spec<Eq, Args...> : detail::quantity_spec_interface {
#else
template<typename Self, detail::IntermediateDerivedQuantitySpec auto Eq, one_of<quantity_character> auto... Args>
requires(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>)
requires(... && !QuantitySpec<decltype(Args)>)
struct quantity_spec<Self, Eq, Args...> : detail::quantity_spec_interface<Self> {
#endif
static constexpr auto _equation_ = Eq;
@ -300,12 +302,12 @@ struct quantity_spec<Self, Eq, Args...> : detail::quantity_spec_interface<Self>
*/
#ifdef MP_UNITS_API_NO_CRTP
template<detail::NamedQuantitySpec auto QS, one_of<quantity_character, struct is_kind> auto... Args>
requires(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>)
struct quantity_spec<QS, Args...> : std::remove_const_t<decltype(QS)> {
requires(... && !QuantitySpec<decltype(Args)>)
struct quantity_spec<QS, Args...> : decltype(QS) {
#else
template<typename Self, detail::NamedQuantitySpec auto QS, one_of<quantity_character, struct is_kind> auto... Args>
requires(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>)
struct quantity_spec<Self, QS, Args...> : std::remove_const_t<decltype(QS)> {
requires(... && !QuantitySpec<decltype(Args)>)
struct quantity_spec<Self, QS, Args...> : decltype(QS) {
#endif
static constexpr auto _parent_ = QS;
static constexpr quantity_character character = detail::quantity_character_init<Args...>(QS.character);
@ -359,16 +361,16 @@ struct quantity_spec<Self, QS, Args...> : std::remove_const_t<decltype(QS)> {
#ifdef MP_UNITS_API_NO_CRTP
template<detail::NamedQuantitySpec auto QS, detail::IntermediateDerivedQuantitySpec auto Eq,
one_of<quantity_character, struct is_kind> auto... Args>
requires(!requires { QS._equation_; } ||
(requires { QS._equation_; } && (explicitly_convertible(Eq, QS._equation_)))) &&
(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>)
requires(!requires { QS._equation_; } || (requires {
QS._equation_;
} && (explicitly_convertible(Eq, QS._equation_)))) && (... && !QuantitySpec<decltype(Args)>)
struct quantity_spec<QS, Eq, Args...> : quantity_spec<QS, Args...> {
#else
template<typename Self, detail::NamedQuantitySpec auto QS, detail::IntermediateDerivedQuantitySpec auto Eq,
one_of<quantity_character, struct is_kind> auto... Args>
requires(!requires { QS._equation_; } ||
(requires { QS._equation_; } && (explicitly_convertible(Eq, QS._equation_)))) &&
(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>)
requires(!requires { QS._equation_; } || (requires {
QS._equation_;
} && (explicitly_convertible(Eq, QS._equation_)))) && (... && !QuantitySpec<decltype(Args)>)
struct quantity_spec<Self, QS, Eq, Args...> : quantity_spec<Self, QS, Args...> {
#endif
static constexpr auto _equation_ = Eq;
@ -481,7 +483,7 @@ struct kind_of_<Q> : quantity_spec<kind_of_<Q>, Q{}> {
MP_UNITS_EXPORT template<detail::QuantitySpecWithNoSpecifiers auto Q>
requires(detail::get_kind_tree_root(Q) == Q)
inline constexpr kind_of_<std::remove_const_t<decltype(Q)>> kind_of;
inline constexpr kind_of_<MP_UNITS_REMOVE_CONST(decltype(Q))> kind_of;
namespace detail {
@ -491,7 +493,7 @@ struct is_dimensionless<struct dimensionless> : std::true_type {};
template<QuantitySpec auto... From, QuantitySpec Q>
[[nodiscard]] consteval QuantitySpec auto clone_kind_of(Q q)
{
if constexpr ((... && QuantityKindSpec<std::remove_const_t<decltype(From)>>))
if constexpr ((... && QuantityKindSpec<MP_UNITS_REMOVE_CONST(decltype(From))>))
return kind_of<Q{}>;
else
return q;
@ -550,11 +552,6 @@ template<std::intmax_t Num, std::intmax_t Den = 1, QuantitySpec Q>
requires detail::non_zero<Den>
[[nodiscard]] consteval QuantitySpec auto pow(Q q)
{
// TODO Does the below make sense?
// `2 * 2` should compare to `4`
// `2 * one * (2 * one)` should compare to `4 * one`
// `2 * rad * (2 * rad)` should compare to `4 * rad^2`
// all are dimensionless quantities :-(
if constexpr (Num == 0 || Q{} == dimensionless)
return dimensionless;
else if constexpr (detail::ratio{Num, Den} == 1)
@ -564,11 +561,9 @@ template<std::intmax_t Num, std::intmax_t Den = 1, QuantitySpec Q>
detail::expr_pow<Num, Den, derived_quantity_spec, struct dimensionless, detail::type_list_of_quantity_spec_less>(
detail::remove_kind(q)));
else if constexpr (Den == 1)
return detail::clone_kind_of<Q{}>(
derived_quantity_spec<power<std::remove_const_t<decltype(detail::remove_kind(Q{}))>, Num>>{});
return detail::clone_kind_of<Q{}>(derived_quantity_spec<power<decltype(detail::remove_kind(Q{})), Num>>{});
else
return detail::clone_kind_of<Q{}>(
derived_quantity_spec<power<std::remove_const_t<decltype(detail::remove_kind(Q{}))>, Num, Den>>{});
return detail::clone_kind_of<Q{}>(derived_quantity_spec<power<decltype(detail::remove_kind(Q{})), Num, Den>>{});
}
@ -915,12 +910,11 @@ template<typename From, typename To>
return extract_results{false};
else if constexpr (from_exp > to_exp)
return extract_results{true, pow<to_exp.num, to_exp.den>(from_factor), pow<to_exp.num, to_exp.den>(to_factor),
prepend_rest::first,
power_or_T<std::remove_cvref_t<decltype(from_factor)>, from_exp - to_exp>{}};
prepend_rest::first, power_or_T<decltype(from_factor), from_exp - to_exp>{}};
else
return extract_results{true, pow<from_exp.num, from_exp.den>(from_factor),
pow<from_exp.num, from_exp.den>(to_factor), prepend_rest::second,
power_or_T<std::remove_cvref_t<decltype(to_factor)>, to_exp - from_exp>{}};
power_or_T<decltype(to_factor), to_exp - from_exp>{}};
}
}
@ -934,7 +928,7 @@ template<process_entities Entities, auto Ext, TypeList NumFrom, TypeList DenFrom
if constexpr (Ext.prepend == prepend_rest::no)
return min(res, are_ingredients_convertible(num_from, den_from, num_to, den_to));
else {
using elem = std::remove_cvref_t<decltype(Ext.elem)>;
using elem = decltype(Ext.elem);
if constexpr (Entities == process_entities::numerators) {
if constexpr (Ext.prepend == prepend_rest::first)
return min(res, are_ingredients_convertible(type_list_push_front<NumFrom, elem>{}, den_from, num_to, den_to));
@ -959,7 +953,7 @@ template<process_entities Entities, auto Ext, TypeList NumFrom, TypeList DenFrom
if constexpr (Ext.prepend == prepend_rest::no)
return are_ingredients_convertible(num_from, den_from, num_to, den_to);
else {
using elem = std::remove_cvref_t<decltype(Ext.elem)>;
using elem = decltype(Ext.elem);
if constexpr (Entities == process_entities::from) {
if constexpr (Ext.prepend == prepend_rest::first)
return are_ingredients_convertible(type_list_push_front<NumFrom, elem>{}, den_from, num_to, den_to);
@ -1342,8 +1336,7 @@ template<QuantitySpec From, QuantitySpec To>
using enum specs_convertible_result;
return res == no ? no : yes;
};
if constexpr ((NamedQuantitySpec<std::remove_cvref_t<decltype(from_kind)>> &&
NamedQuantitySpec<std::remove_cvref_t<decltype(to_kind)>>) ||
if constexpr ((NamedQuantitySpec<decltype(from_kind)> && NamedQuantitySpec<decltype(to_kind)>) ||
get_complexity(from_kind) == get_complexity(to_kind))
return convertible_impl(from_kind, to_kind);
else if constexpr (get_complexity(from_kind) > get_complexity(to_kind))
@ -1396,7 +1389,7 @@ template<QuantitySpec From, QuantitySpec To>
return are_ingredients_convertible(from, to);
} else if constexpr (IntermediateDerivedQuantitySpec<From>) {
auto res = explode<get_complexity(to)>(from);
if constexpr (NamedQuantitySpec<std::remove_const_t<decltype(res.quantity)>>)
if constexpr (NamedQuantitySpec<decltype(res.quantity)>)
return convertible_impl(res.quantity, to);
else if constexpr (requires { to._equation_; }) {
auto eq = explode_to_equation(to);
@ -1405,7 +1398,7 @@ template<QuantitySpec From, QuantitySpec To>
return are_ingredients_convertible(from, to);
} else if constexpr (IntermediateDerivedQuantitySpec<To>) {
auto res = explode<get_complexity(from)>(to);
if constexpr (NamedQuantitySpec<std::remove_const_t<decltype(res.quantity)>>)
if constexpr (NamedQuantitySpec<decltype(res.quantity)>)
return min(res.result, convertible_impl(from, res.quantity));
else if constexpr (requires { from._equation_; })
return min(res.result, convertible_impl(from._equation_, res.quantity));
@ -1450,7 +1443,7 @@ namespace detail {
template<QuantitySpec Q>
requires requires(Q q) { get_kind_tree_root(q); }
using to_kind = std::remove_const_t<decltype(get_kind_tree_root(Q{}))>;
using to_kind = decltype(get_kind_tree_root(Q{}));
#ifdef MP_UNITS_API_NO_CRTP
template<NamedQuantitySpec auto QS, auto... Args>
@ -1507,8 +1500,8 @@ template<QuantitySpec Q1, QuantitySpec Q2>
requires(implicitly_convertible(get_kind_tree_root(q1), get_kind_tree_root(q2)) ||
implicitly_convertible(get_kind_tree_root(q2), get_kind_tree_root(q1)))
{
using QQ1 = std::remove_const_t<decltype(detail::remove_kind(q1))>;
using QQ2 = std::remove_const_t<decltype(detail::remove_kind(q2))>;
using QQ1 = decltype(detail::remove_kind(q1));
using QQ2 = decltype(detail::remove_kind(q2));
// NOLINTBEGIN(bugprone-branch-clone)
if constexpr (is_same_v<Q1, Q2>)

View File

@ -140,15 +140,14 @@ namespace detail {
template<auto To, auto From>
concept NestedQuantityKindSpecOf =
QuantitySpec<std::remove_const_t<decltype(From)>> && QuantitySpec<std::remove_const_t<decltype(To)>> &&
get_kind(From) != get_kind(To) &&
std::derived_from<std::remove_cvref_t<decltype(To)>, std::remove_cvref_t<decltype(get_kind(From)._quantity_spec_)>>;
QuantitySpec<decltype(From)> && QuantitySpec<decltype(To)> && get_kind(From) != get_kind(To) &&
std::derived_from<decltype(To), std::remove_const_t<decltype(get_kind(From)._quantity_spec_)>>;
}
MP_UNITS_EXPORT template<typename T, auto QS>
concept QuantitySpecOf =
QuantitySpec<T> && QuantitySpec<std::remove_const_t<decltype(QS)>> && implicitly_convertible(T{}, QS) &&
QuantitySpec<T> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS))> && implicitly_convertible(T{}, QS) &&
// the below is to make the following work
// static_assert(ReferenceOf<si::radian, isq::angular_measure>);
// static_assert(!ReferenceOf<si::radian, dimensionless>);

View File

@ -38,7 +38,7 @@ namespace mp_units {
namespace detail {
template<QuantitySpec auto Q, Unit auto U>
using reference_t = reference<std::remove_const_t<decltype(Q)>, std::remove_const_t<decltype(U)>>;
using reference_t = reference<MP_UNITS_REMOVE_CONST(decltype(Q)), MP_UNITS_REMOVE_CONST(decltype(U))>;
}
@ -258,13 +258,13 @@ MP_UNITS_EXPORT_END
namespace detail {
template<AssociatedUnit auto To, AssociatedUnit From>
[[nodiscard]] consteval std::remove_const_t<decltype(To)> clone_reference_with(From)
[[nodiscard]] consteval MP_UNITS_REMOVE_CONST(decltype(To)) clone_reference_with(From)
{
return {};
}
template<Unit auto To, QuantitySpec QS, Unit U>
[[nodiscard]] consteval reference<QS, std::remove_const_t<decltype(To)>> clone_reference_with(reference<QS, U>)
[[nodiscard]] consteval reference<QS, MP_UNITS_REMOVE_CONST(decltype(To))> clone_reference_with(reference<QS, U>)
{
return {};
}

View File

@ -76,7 +76,7 @@ template<typename Q, typename U>
* the provided quantity_spec type.
*/
template<typename T, auto QS>
concept ReferenceOf = Reference<T> && QuantitySpecOf<std::remove_const_t<decltype(get_quantity_spec(T{}))>, QS>;
concept ReferenceOf = Reference<T> && QuantitySpecOf<decltype(get_quantity_spec(T{})), QS>;
MP_UNITS_EXPORT_END

View File

@ -26,10 +26,11 @@
// IWYU pragma: private, include <mp-units/framework.h>
#include <mp-units/bits/hacks.h>
#include <mp-units/bits/module_macros.h>
#include <mp-units/compat_macros.h>
#include <mp-units/ext/algorithm.h>
#include <mp-units/ext/fixed_string.h>
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#include <gsl/gsl-lite.hpp>
#include <compare> // IWYU pragma: export
#include <cstddef>
#include <cstdint>
@ -85,41 +86,42 @@ constexpr fixed_u8string<N> to_u8string(fixed_string<N> txt)
* @tparam M The size of the ASCII-only symbol
*/
MP_UNITS_EXPORT template<std::size_t N, std::size_t M>
struct symbol_text {
class symbol_text {
public:
fixed_u8string<N> unicode_;
fixed_string<M> ascii_;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
constexpr explicit(false) symbol_text(char ch) : unicode_(static_cast<char8_t>(ch)), ascii_(ch)
{
gsl_Expects(detail::is_basic_literal_character_set_char(ch));
MP_UNITS_EXPECTS(detail::is_basic_literal_character_set_char(ch));
}
// NOLINTNEXTLINE(*-avoid-c-arrays, google-explicit-constructor, hicpp-explicit-conversions)
constexpr explicit(false) symbol_text(const char (&txt)[N + 1]) :
consteval explicit(false) symbol_text(const char (&txt)[N + 1]) :
unicode_(detail::to_u8string(basic_fixed_string{txt})), ascii_(txt)
{
gsl_Expects(txt[N] == char{});
gsl_Expects(detail::is_basic_literal_character_set(txt));
MP_UNITS_EXPECTS(txt[N] == char{});
MP_UNITS_EXPECTS(detail::is_basic_literal_character_set(txt));
}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
constexpr explicit(false) symbol_text(const fixed_string<N>& txt) : unicode_(detail::to_u8string(txt)), ascii_(txt)
{
gsl_Expects(detail::is_basic_literal_character_set(txt.data_));
MP_UNITS_EXPECTS(detail::is_basic_literal_character_set(txt.data_));
}
// NOLINTNEXTLINE(*-avoid-c-arrays)
constexpr symbol_text(const char8_t (&u)[N + 1], const char (&a)[M + 1]) : unicode_(u), ascii_(a)
consteval symbol_text(const char8_t (&u)[N + 1], const char (&a)[M + 1]) : unicode_(u), ascii_(a)
{
gsl_Expects(u[N] == char8_t{});
gsl_Expects(a[M] == char{});
gsl_Expects(detail::is_basic_literal_character_set(a));
MP_UNITS_EXPECTS(u[N] == char8_t{});
MP_UNITS_EXPECTS(a[M] == char{});
MP_UNITS_EXPECTS(detail::is_basic_literal_character_set(a));
}
constexpr symbol_text(const fixed_u8string<N>& u, const fixed_string<M>& a) : unicode_(u), ascii_(a)
{
gsl_Expects(detail::is_basic_literal_character_set(a.data_));
MP_UNITS_EXPECTS(detail::is_basic_literal_character_set(a.data_));
}
[[nodiscard]] constexpr const auto& unicode() const { return unicode_; }
@ -127,7 +129,7 @@ struct symbol_text {
[[nodiscard]] constexpr bool empty() const
{
gsl_AssertDebug(unicode().empty() == ascii().empty());
MP_UNITS_ASSERT_DEBUG(unicode().empty() == ascii().empty());
return unicode().empty();
}

View File

@ -60,7 +60,7 @@ namespace mp_units {
* @tparam CoU coherent unit for a quantity in this system
*/
template<QuantitySpec auto Q, Unit auto CoU>
requires(!AssociatedUnit<std::remove_const_t<decltype(CoU)>>) || (CoU == one)
requires(!AssociatedUnit<decltype(CoU)>) || (CoU == one)
struct system_reference {
static constexpr auto quantity_spec = Q;
static constexpr auto coherent_unit = CoU;
@ -68,9 +68,9 @@ struct system_reference {
template<Unit U>
requires(convertible(coherent_unit, U{}))
#if MP_UNITS_COMP_MSVC
[[nodiscard]] constexpr decltype(reference<std::remove_const_t<decltype(quantity_spec)>, U>{}) operator[](U) const
[[nodiscard]] constexpr decltype(reference<MP_UNITS_REMOVE_CONST(decltype(Q)), U>{}) operator[](U) const
#else
[[nodiscard]] constexpr reference<std::remove_const_t<decltype(quantity_spec)>, U> operator[](U) const
[[nodiscard]] constexpr reference<MP_UNITS_REMOVE_CONST(decltype(Q)), U> operator[](U) const
#endif
{
return {};

View File

@ -28,6 +28,7 @@
#include <mp-units/bits/module_macros.h>
#include <mp-units/bits/ratio.h>
#include <mp-units/bits/text_tools.h>
#include <mp-units/compat_macros.h>
#include <mp-units/ext/algorithm.h>
#include <mp-units/ext/fixed_string.h>
#include <mp-units/ext/type_name.h>
@ -40,12 +41,13 @@
#include <mp-units/framework/unit_concepts.h>
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#include <gsl/gsl-lite.hpp>
#include <array>
#include <cstdint>
#include <iterator>
#include <string>
#include <string_view>
#if MP_UNITS_HOSTED
#include <string>
#endif
#endif
namespace mp_units {
@ -173,13 +175,13 @@ struct named_unit<Symbol> {
*/
template<symbol_text Symbol, Unit auto U>
requires(!Symbol.empty())
struct named_unit<Symbol, U> : std::remove_const_t<decltype(U)> {
struct named_unit<Symbol, U> : decltype(U) {
static constexpr auto symbol = Symbol; ///< Unique unit identifier
};
template<symbol_text Symbol, Unit auto U, PointOrigin auto PO>
requires(!Symbol.empty())
struct named_unit<Symbol, U, PO> : std::remove_const_t<decltype(U)> {
struct named_unit<Symbol, U, PO> : decltype(U) {
static constexpr auto symbol = Symbol; ///< Unique unit identifier
static constexpr auto point_origin = PO;
};
@ -195,14 +197,14 @@ struct named_unit<Symbol, U, PO> : std::remove_const_t<decltype(U)> {
*/
template<symbol_text Symbol, AssociatedUnit auto U, detail::QuantityKindSpec auto QS>
requires(!Symbol.empty()) && (QS.dimension == detail::get_associated_quantity(U).dimension)
struct named_unit<Symbol, U, QS> : std::remove_const_t<decltype(U)> {
struct named_unit<Symbol, U, QS> : decltype(U) {
static constexpr auto symbol = Symbol; ///< Unique unit identifier
static constexpr auto quantity_spec = QS;
};
template<symbol_text Symbol, AssociatedUnit auto U, detail::QuantityKindSpec auto QS, PointOrigin auto PO>
requires(!Symbol.empty()) && (QS.dimension == detail::get_associated_quantity(U).dimension)
struct named_unit<Symbol, U, QS, PO> : std::remove_const_t<decltype(U)> {
struct named_unit<Symbol, U, QS, PO> : decltype(U) {
static constexpr auto symbol = Symbol; ///< Unique unit identifier
static constexpr auto quantity_spec = QS;
static constexpr auto point_origin = PO;
@ -232,7 +234,7 @@ struct named_unit<Symbol, U, QS, PO> : std::remove_const_t<decltype(U)> {
*/
MP_UNITS_EXPORT template<symbol_text Symbol, Magnitude auto M, PrefixableUnit auto U>
requires(!Symbol.empty())
struct prefixed_unit : std::remove_const_t<decltype(M * U)> {
struct prefixed_unit : decltype(M * U) {
static constexpr auto symbol = Symbol + U.symbol;
};
@ -390,7 +392,7 @@ template<typename T, typename F, int Num, int... Den>
return canonical_unit{pow<Num, Den...>(base.mag) * num.mag / den.mag, num.reference_unit / den.reference_unit};
} else {
return canonical_unit{pow<Num, Den...>(base.mag),
derived_unit<power<std::remove_const_t<decltype(base.reference_unit)>, Num, Den...>>{}};
derived_unit<power<decltype(base.reference_unit), Num, Den...>>{}};
}
}
@ -430,9 +432,9 @@ MP_UNITS_EXPORT_BEGIN
* Multiplication by `1` returns the same unit, otherwise `scaled_unit` is being returned.
*/
template<Magnitude M, Unit U>
[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator*(M, const U u)
[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator*(M, U u)
{
if constexpr (std::is_same_v<M, std::remove_cvref_t<decltype(mp_units::mag<1>)>>)
if constexpr (std::is_same_v<M, std::remove_const_t<decltype(mp_units::mag<1>)>>)
return u;
else
return scaled_unit<M{}, U>{};
@ -444,7 +446,7 @@ template<Magnitude M, Unit U>
* Returns the result of multiplication with an inverse unit.
*/
template<Magnitude M, Unit U>
[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator/(M mag, const U u)
[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator/(M mag, U u)
{
return mag * inverse(u);
}
@ -571,7 +573,7 @@ template<std::intmax_t Num, std::intmax_t Den = 1, Unit U>
else if constexpr (detail::ratio{Num, Den} == 1)
return u;
else if constexpr (detail::is_specialization_of_scaled_unit<U>)
return scaled_unit<pow<Num, Den>(U::mag), std::remove_const_t<decltype(pow<Num, Den>(U::reference_unit))>>{};
return scaled_unit<pow<Num, Den>(U::mag), decltype(pow<Num, Den>(U::reference_unit))>{};
else if constexpr (detail::is_specialization_of_derived_unit<U>)
return detail::expr_pow<Num, Den, derived_unit, struct one, detail::type_list_of_unit_less>(u);
else if constexpr (Den == 1)
@ -657,7 +659,7 @@ template<Unit U1, Unit U2>
return u1;
else {
constexpr auto cm = detail::common_magnitude(canonical_lhs.mag, canonical_rhs.mag);
return scaled_unit<cm, std::remove_const_t<decltype(canonical_lhs.reference_unit)>>{};
return scaled_unit<cm, decltype(canonical_lhs.reference_unit)>{};
}
}
}
@ -718,8 +720,8 @@ constexpr Out print_separator(Out out, const unit_symbol_formatting& fmt)
{
if (fmt.separator == unit_symbol_separator::half_high_dot) {
if (fmt.encoding != text_encoding::unicode)
throw std::invalid_argument(
"'unit_symbol_separator::half_high_dot' can be only used with 'text_encoding::unicode'");
MP_UNITS_THROW(
std::invalid_argument("'unit_symbol_separator::half_high_dot' can be only used with 'text_encoding::unicode'"));
const std::string_view dot = "";
out = detail::copy(dot.begin(), dot.end(), out);
} else {
@ -808,7 +810,8 @@ template<typename CharT, std::output_iterator<CharT> Out, typename... Expr>
constexpr Out unit_symbol_impl(Out out, const derived_unit<Expr...>&, const unit_symbol_formatting& fmt,
bool negative_power)
{
gsl_Expects(negative_power == false);
(void)negative_power;
MP_UNITS_EXPECTS(negative_power == false);
return unit_symbol_impl<CharT>(out, typename derived_unit<Expr...>::_num_{}, typename derived_unit<Expr...>::_den_{},
fmt);
}
@ -843,9 +846,15 @@ MP_UNITS_EXPORT template<unit_symbol_formatting fmt = unit_symbol_formatting{},
#endif
{
auto get_size = []() consteval {
#if MP_UNITS_HOSTED
std::basic_string<CharT> buffer;
unit_symbol_to<CharT>(std::back_inserter(buffer), U{}, fmt);
return buffer.size();
#else
std::array<CharT, 128> buffer; // TODO unsafe
auto end = unit_symbol_to<CharT>(buffer.begin(), U{}, fmt);
return end - buffer.begin();
#endif
};
#if MP_UNITS_API_STRING_VIEW_RET // Permitting static constexpr variables in constexpr functions
@ -854,8 +863,7 @@ MP_UNITS_EXPORT template<unit_symbol_formatting fmt = unit_symbol_formatting{},
return std::string_view(buffer.data(), size);
#else
constexpr std::size_t size = get_size();
constexpr auto buffer = detail::get_symbol_buffer<CharT, size, fmt>(U{});
return basic_fixed_string<CharT, size>(buffer.begin(), buffer.end());
return basic_fixed_string(std::from_range, detail::get_symbol_buffer<CharT, size, fmt>(U{}));
#endif
}

View File

@ -190,7 +190,7 @@ concept AssociatedUnit = Unit<U> && detail::has_associated_quantity(U{});
*/
MP_UNITS_EXPORT template<typename U, auto QS>
concept UnitOf =
AssociatedUnit<U> && QuantitySpec<std::remove_const_t<decltype(QS)>> &&
AssociatedUnit<U> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS))> &&
implicitly_convertible(get_quantity_spec(U{}), QS) &&
// the below is to make `dimensionless[radian]` invalid
(get_kind(QS) == get_kind(get_quantity_spec(U{})) || !detail::NestedQuantityKindSpecOf<get_quantity_spec(U{}), QS>);
@ -209,7 +209,7 @@ namespace detail {
*/
MP_UNITS_EXPORT template<typename U, auto U2, auto QS>
concept UnitCompatibleWith =
Unit<U> && Unit<std::remove_const_t<decltype(U2)>> && QuantitySpec<std::remove_const_t<decltype(QS)>> &&
Unit<U> && Unit<MP_UNITS_REMOVE_CONST(decltype(U2))> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS))> &&
(!AssociatedUnit<U> || UnitOf<U, QS>)&&detail::have_same_canonical_reference_unit(U{}, U2);

View File

@ -22,6 +22,8 @@
#pragma once
#include <mp-units/bits/requires_hosted.h>
//
#include <mp-units/bits/module_macros.h>
#include <mp-units/framework/customization_points.h>
#include <mp-units/framework/quantity.h>
@ -237,6 +239,29 @@ template<auto R, auto S, auto T, typename Rep1, typename Rep2, typename Rep3>
common_reference(R * S, T)};
}
/**
* @brief Computes the fma of 2 quantities and a quantity point
*
* @param a: Multiplicand
* @param x: Multiplicand
* @param b: Addend
* @return QuantityPoint: The nearest floating point representable to ax+b
*/
template<auto R, auto S, auto T, auto Origin, typename Rep1, typename Rep2, typename Rep3>
requires requires { common_quantity_spec(get_quantity_spec(R) * get_quantity_spec(S), get_quantity_spec(T)); } &&
(get_unit(R) * get_unit(S) == get_unit(T)) && requires(Rep1 v1, Rep2 v2, Rep3 v3) {
requires requires { fma(v1, v2, v3); } || requires { std::fma(v1, v2, v3); };
}
[[nodiscard]] constexpr QuantityPointOf<
common_quantity_spec(get_quantity_spec(R) * get_quantity_spec(S),
get_quantity_spec(T))> auto fma(const quantity<R, Rep1>& a, const quantity<S, Rep2>& x,
const quantity_point<T, Origin, Rep3>& b) noexcept
{
using std::fma;
return Origin + quantity{fma(a.numerical_value_ref_in(a.unit), x.numerical_value_ref_in(x.unit),
b.quantity_ref_from(b.point_origin).numerical_value_ref_in(b.unit)),
common_reference(R * S, T)};
}
/**
* @brief Computes the floating-point remainder of the division operation x / y.

View File

@ -23,6 +23,8 @@
#pragma once
#include <mp-units/bits/requires_hosted.h>
//
#include <mp-units/bits/module_macros.h>
#include <mp-units/framework/quantity.h>
#include <mp-units/framework/unit.h>

View File

@ -8,8 +8,11 @@ export module mp_units.core;
#include <mp-units/compat_macros.h>
#include <mp-units/concepts.h>
#include <mp-units/format.h>
#include <mp-units/framework.h>
#if MP_UNITS_HOSTED
#include <mp-units/format.h>
#include <mp-units/math.h>
#include <mp-units/ostream.h>
#include <mp-units/random.h>
#endif

View File

@ -22,10 +22,14 @@
include(CMakeFindDependencyMacro)
if(NOT MP_UNITS_API_STD_FORMAT)
if(NOT MP_UNITS_API_FREESTANDING AND NOT MP_UNITS_API_STD_FORMAT)
find_dependency(fmt)
endif()
find_dependency(gsl-lite)
if(MP_UNITS_API_CONTRACTS STREQUAL "GSL-LITE")
find_dependency(gsl-lite)
elseif(MP_UNITS_API_CONTRACTS STREQUAL "MS-GSL")
find_dependency(Microsoft.GSL)
endif()
include("${CMAKE_CURRENT_LIST_DIR}/mp-unitsTargets.cmake")

View File

@ -25,8 +25,7 @@ cmake_minimum_required(VERSION 3.23)
add_mp_units_module(
systems mp-units-systems
DEPENDENCIES mp-units::core
HEADERS include/mp-units/systems/angular/math.h
include/mp-units/systems/angular/units.h
HEADERS include/mp-units/systems/angular/units.h
include/mp-units/systems/iec80000/binary_prefixes.h
include/mp-units/systems/iec80000/quantities.h
include/mp-units/systems/iec80000/unit_symbols.h
@ -39,9 +38,7 @@ add_mp_units_module(
include/mp-units/systems/isq/si_quantities.h
include/mp-units/systems/isq/space_and_time.h
include/mp-units/systems/isq/thermodynamics.h
include/mp-units/systems/si/chrono.h
include/mp-units/systems/si/constants.h
include/mp-units/systems/si/math.h
include/mp-units/systems/si/prefixes.h
include/mp-units/systems/si/unit_symbols.h
include/mp-units/systems/si/units.h
@ -60,3 +57,17 @@ add_mp_units_module(
include/mp-units/systems/usc.h
MODULE_INTERFACE_UNIT mp-units-systems.cpp
)
if(NOT ${projectPrefix}API_FREESTANDING)
target_sources(
mp-units-systems
PUBLIC FILE_SET
HEADERS
BASE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}/include
FILES
include/mp-units/systems/angular/math.h
include/mp-units/systems/si/math.h
include/mp-units/systems/si/chrono.h
)
endif()

View File

@ -23,7 +23,9 @@
#pragma once
// IWYU pragma: begin_exports
#if MP_UNITS_HOSTED
#include <mp-units/systems/angular/math.h>
#endif
#include <mp-units/systems/angular/units.h>
#ifndef MP_UNITS_IN_MODULE_INTERFACE

View File

@ -22,6 +22,8 @@
#pragma once
#include <mp-units/bits/requires_hosted.h>
//
#include <mp-units/bits/module_macros.h>
#include <mp-units/systems/angular/units.h>

View File

@ -42,14 +42,14 @@ template<PrefixableUnit U> struct yobi_ : prefixed_unit<"Yi", mag_power<2, 80>,
MP_UNITS_EXPORT_BEGIN
template<PrefixableUnit auto U> inline constexpr kibi_<std::remove_const_t<decltype(U)>> kibi;
template<PrefixableUnit auto U> inline constexpr mebi_<std::remove_const_t<decltype(U)>> mebi;
template<PrefixableUnit auto U> inline constexpr gibi_<std::remove_const_t<decltype(U)>> gibi;
template<PrefixableUnit auto U> inline constexpr tebi_<std::remove_const_t<decltype(U)>> tebi;
template<PrefixableUnit auto U> inline constexpr pebi_<std::remove_const_t<decltype(U)>> pebi;
template<PrefixableUnit auto U> inline constexpr exbi_<std::remove_const_t<decltype(U)>> exbi;
template<PrefixableUnit auto U> inline constexpr zebi_<std::remove_const_t<decltype(U)>> zebi;
template<PrefixableUnit auto U> inline constexpr yobi_<std::remove_const_t<decltype(U)>> yobi;
template<PrefixableUnit auto U> inline constexpr kibi_<MP_UNITS_REMOVE_CONST(decltype(U))> kibi;
template<PrefixableUnit auto U> inline constexpr mebi_<MP_UNITS_REMOVE_CONST(decltype(U))> mebi;
template<PrefixableUnit auto U> inline constexpr gibi_<MP_UNITS_REMOVE_CONST(decltype(U))> gibi;
template<PrefixableUnit auto U> inline constexpr tebi_<MP_UNITS_REMOVE_CONST(decltype(U))> tebi;
template<PrefixableUnit auto U> inline constexpr pebi_<MP_UNITS_REMOVE_CONST(decltype(U))> pebi;
template<PrefixableUnit auto U> inline constexpr exbi_<MP_UNITS_REMOVE_CONST(decltype(U))> exbi;
template<PrefixableUnit auto U> inline constexpr zebi_<MP_UNITS_REMOVE_CONST(decltype(U))> zebi;
template<PrefixableUnit auto U> inline constexpr yobi_<MP_UNITS_REMOVE_CONST(decltype(U))> yobi;
// clang-format on
MP_UNITS_EXPORT_END

View File

@ -23,9 +23,11 @@
#pragma once
// IWYU pragma: begin_exports
#if MP_UNITS_HOSTED
#include <mp-units/systems/si/chrono.h>
#include <mp-units/systems/si/constants.h>
#include <mp-units/systems/si/math.h>
#endif
#include <mp-units/systems/si/constants.h>
#include <mp-units/systems/si/prefixes.h>
#include <mp-units/systems/si/unit_symbols.h>
#include <mp-units/systems/si/units.h>

View File

@ -22,6 +22,8 @@
#pragma once
#include <mp-units/bits/requires_hosted.h>
//
#include <mp-units/bits/module_macros.h>
#include <mp-units/systems/isq/si_quantities.h>
#include <mp-units/systems/si/prefixes.h>

View File

@ -22,6 +22,8 @@
#pragma once
#include <mp-units/bits/requires_hosted.h>
//
#include <mp-units/bits/module_macros.h>
#include <mp-units/systems/isq/space_and_time.h>
#include <mp-units/systems/si/units.h>

View File

@ -58,30 +58,30 @@ template<PrefixableUnit U> struct quetta_ : prefixed_unit<"Q", mag_power<10, 30>
MP_UNITS_EXPORT_BEGIN
template<PrefixableUnit auto U> inline constexpr quecto_<std::remove_const_t<decltype(U)>> quecto;
template<PrefixableUnit auto U> inline constexpr ronto_<std::remove_const_t<decltype(U)>> ronto;
template<PrefixableUnit auto U> inline constexpr yocto_<std::remove_const_t<decltype(U)>> yocto;
template<PrefixableUnit auto U> inline constexpr zepto_<std::remove_const_t<decltype(U)>> zepto;
template<PrefixableUnit auto U> inline constexpr atto_<std::remove_const_t<decltype(U)>> atto;
template<PrefixableUnit auto U> inline constexpr femto_<std::remove_const_t<decltype(U)>> femto;
template<PrefixableUnit auto U> inline constexpr pico_<std::remove_const_t<decltype(U)>> pico;
template<PrefixableUnit auto U> inline constexpr nano_<std::remove_const_t<decltype(U)>> nano;
template<PrefixableUnit auto U> inline constexpr micro_<std::remove_const_t<decltype(U)>> micro;
template<PrefixableUnit auto U> inline constexpr milli_<std::remove_const_t<decltype(U)>> milli;
template<PrefixableUnit auto U> inline constexpr centi_<std::remove_const_t<decltype(U)>> centi;
template<PrefixableUnit auto U> inline constexpr deci_<std::remove_const_t<decltype(U)>> deci;
template<PrefixableUnit auto U> inline constexpr deca_<std::remove_const_t<decltype(U)>> deca;
template<PrefixableUnit auto U> inline constexpr hecto_<std::remove_const_t<decltype(U)>> hecto;
template<PrefixableUnit auto U> inline constexpr kilo_<std::remove_const_t<decltype(U)>> kilo;
template<PrefixableUnit auto U> inline constexpr mega_<std::remove_const_t<decltype(U)>> mega;
template<PrefixableUnit auto U> inline constexpr giga_<std::remove_const_t<decltype(U)>> giga;
template<PrefixableUnit auto U> inline constexpr tera_<std::remove_const_t<decltype(U)>> tera;
template<PrefixableUnit auto U> inline constexpr peta_<std::remove_const_t<decltype(U)>> peta;
template<PrefixableUnit auto U> inline constexpr exa_<std::remove_const_t<decltype(U)>> exa;
template<PrefixableUnit auto U> inline constexpr zetta_<std::remove_const_t<decltype(U)>> zetta;
template<PrefixableUnit auto U> inline constexpr yotta_<std::remove_const_t<decltype(U)>> yotta;
template<PrefixableUnit auto U> inline constexpr ronna_<std::remove_const_t<decltype(U)>> ronna;
template<PrefixableUnit auto U> inline constexpr quetta_<std::remove_const_t<decltype(U)>> quetta;
template<PrefixableUnit auto U> inline constexpr quecto_<MP_UNITS_REMOVE_CONST(decltype(U))> quecto;
template<PrefixableUnit auto U> inline constexpr ronto_<MP_UNITS_REMOVE_CONST(decltype(U))> ronto;
template<PrefixableUnit auto U> inline constexpr yocto_<MP_UNITS_REMOVE_CONST(decltype(U))> yocto;
template<PrefixableUnit auto U> inline constexpr zepto_<MP_UNITS_REMOVE_CONST(decltype(U))> zepto;
template<PrefixableUnit auto U> inline constexpr atto_<MP_UNITS_REMOVE_CONST(decltype(U))> atto;
template<PrefixableUnit auto U> inline constexpr femto_<MP_UNITS_REMOVE_CONST(decltype(U))> femto;
template<PrefixableUnit auto U> inline constexpr pico_<MP_UNITS_REMOVE_CONST(decltype(U))> pico;
template<PrefixableUnit auto U> inline constexpr nano_<MP_UNITS_REMOVE_CONST(decltype(U))> nano;
template<PrefixableUnit auto U> inline constexpr micro_<MP_UNITS_REMOVE_CONST(decltype(U))> micro;
template<PrefixableUnit auto U> inline constexpr milli_<MP_UNITS_REMOVE_CONST(decltype(U))> milli;
template<PrefixableUnit auto U> inline constexpr centi_<MP_UNITS_REMOVE_CONST(decltype(U))> centi;
template<PrefixableUnit auto U> inline constexpr deci_<MP_UNITS_REMOVE_CONST(decltype(U))> deci;
template<PrefixableUnit auto U> inline constexpr deca_<MP_UNITS_REMOVE_CONST(decltype(U))> deca;
template<PrefixableUnit auto U> inline constexpr hecto_<MP_UNITS_REMOVE_CONST(decltype(U))> hecto;
template<PrefixableUnit auto U> inline constexpr kilo_<MP_UNITS_REMOVE_CONST(decltype(U))> kilo;
template<PrefixableUnit auto U> inline constexpr mega_<MP_UNITS_REMOVE_CONST(decltype(U))> mega;
template<PrefixableUnit auto U> inline constexpr giga_<MP_UNITS_REMOVE_CONST(decltype(U))> giga;
template<PrefixableUnit auto U> inline constexpr tera_<MP_UNITS_REMOVE_CONST(decltype(U))> tera;
template<PrefixableUnit auto U> inline constexpr peta_<MP_UNITS_REMOVE_CONST(decltype(U))> peta;
template<PrefixableUnit auto U> inline constexpr exa_<MP_UNITS_REMOVE_CONST(decltype(U))> exa;
template<PrefixableUnit auto U> inline constexpr zetta_<MP_UNITS_REMOVE_CONST(decltype(U))> zetta;
template<PrefixableUnit auto U> inline constexpr yotta_<MP_UNITS_REMOVE_CONST(decltype(U))> yotta;
template<PrefixableUnit auto U> inline constexpr ronna_<MP_UNITS_REMOVE_CONST(decltype(U))> ronna;
template<PrefixableUnit auto U> inline constexpr quetta_<MP_UNITS_REMOVE_CONST(decltype(U))> quetta;
// clang-format on
MP_UNITS_EXPORT_END

View File

@ -474,6 +474,32 @@ inline constexpr auto YF = yotta<farad>;
inline constexpr auto RF = ronna<farad>;
inline constexpr auto QF = quetta<farad>;
inline constexpr auto qohm = quecto<si::ohm>;
inline constexpr auto rohm = ronto<si::ohm>;
inline constexpr auto yohm = yocto<si::ohm>;
inline constexpr auto zohm = zepto<si::ohm>;
inline constexpr auto aohm = atto<si::ohm>;
inline constexpr auto fohm = femto<si::ohm>;
inline constexpr auto pohm = pico<si::ohm>;
inline constexpr auto nohm = nano<si::ohm>;
inline constexpr auto uohm = micro<si::ohm>;
inline constexpr auto mohm = milli<si::ohm>;
inline constexpr auto cohm = centi<si::ohm>;
inline constexpr auto dohm = deci<si::ohm>;
using si::ohm;
inline constexpr auto daohm = deca<si::ohm>;
inline constexpr auto hohm = hecto<si::ohm>;
inline constexpr auto kohm = kilo<si::ohm>;
inline constexpr auto Mohm = mega<si::ohm>;
inline constexpr auto Gohm = giga<si::ohm>;
inline constexpr auto Tohm = tera<si::ohm>;
inline constexpr auto Pohm = peta<si::ohm>;
inline constexpr auto Eohm = exa<si::ohm>;
inline constexpr auto Zohm = zetta<si::ohm>;
inline constexpr auto Yohm = yotta<si::ohm>;
inline constexpr auto Rohm = ronna<si::ohm>;
inline constexpr auto Qohm = quetta<si::ohm>;
inline constexpr auto qS = quecto<siemens>;
inline constexpr auto rS = ronto<siemens>;
inline constexpr auto yS = yocto<siemens>;

View File

@ -118,7 +118,7 @@ inline constexpr struct troy_pound : named_unit<"lb t", mag<12> * troy_once> {}
inline constexpr struct inch_of_mercury : named_unit<"inHg", mag_ratio<3'386'389, 1'000> * si::pascal> {} inch_of_mercury;
// https://en.wikipedia.org/wiki/United_States_customary_units#Temperature
inline constexpr struct zeroth_degree_Fahrenheit : relative_point_origin<si::zeroth_degree_Celsius - 32 * (mag_ratio<5, 9> * si::degree_Celsius)> {} zeroth_degree_Fahrenheit;
inline constexpr struct zeroth_degree_Fahrenheit : relative_point_origin<quantity_point{-32 * (mag_ratio<5, 9> * si::degree_Celsius)}> {} zeroth_degree_Fahrenheit;
inline constexpr struct degree_Fahrenheit : named_unit<symbol_text{u8"°F", "`F"}, mag_ratio<5, 9> * si::degree_Celsius, zeroth_degree_Fahrenheit> {} degree_Fahrenheit;
// clang-format on

View File

@ -22,5 +22,7 @@
cmake_minimum_required(VERSION 3.5)
add_subdirectory(runtime)
if(NOT ${projectPrefix}API_FREESTANDING)
add_subdirectory(runtime)
endif()
add_subdirectory(static)

View File

@ -24,7 +24,9 @@ cmake_minimum_required(VERSION 3.5)
find_package(Catch2 3 REQUIRED)
add_executable(unit_tests_runtime distribution_test.cpp fmt_test.cpp math_test.cpp atomic_test.cpp)
add_executable(
unit_tests_runtime distribution_test.cpp fixed_string_test.cpp fmt_test.cpp math_test.cpp atomic_test.cpp
)
if(${projectPrefix}BUILD_CXX_MODULES)
target_compile_definitions(unit_tests_runtime PUBLIC ${projectPrefix}MODULES)
endif()

View File

@ -0,0 +1,68 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_exception.hpp>
#include <mp-units/compat_macros.h>
#include <sstream>
#include <string_view>
#ifdef MP_UNITS_MODULES
import mp_units;
#else
#include <mp-units/ext/fixed_string.h>
#endif
using namespace mp_units;
TEST_CASE("fixed_string::at", "[fixed_string]")
{
basic_fixed_string txt = "abc";
SECTION("in range")
{
CHECK(txt.at(0) == 'a');
CHECK(txt.at(1) == 'b');
CHECK(txt.at(2) == 'c');
}
SECTION("out of range")
{
REQUIRE_THROWS_MATCHES(txt.at(3), std::out_of_range, Catch::Matchers::Message("basic_fixed_string::at"));
REQUIRE_THROWS_MATCHES(txt.at(1024), std::out_of_range, Catch::Matchers::Message("basic_fixed_string::at"));
}
}
TEST_CASE("fixed_string text output", "[fixed_string][ostream][fmt]")
{
basic_fixed_string txt = "units";
SECTION("iostream")
{
std::ostringstream os;
os << txt;
CHECK(os.str() == "units");
}
SECTION("fmt") { CHECK(MP_UNITS_STD_FMT::format("{}", txt) == "units"); }
}
TEST_CASE("fixed_string hash", "[fixed_string][hash]")
{
basic_fixed_string txt = "units";
CHECK(std::hash<fixed_string<5>>{}(txt) == std::hash<std::string_view>{}("units"));
}

View File

@ -34,7 +34,6 @@
#ifdef MP_UNITS_MODULES
import mp_units;
#else
#include <mp-units/ext/fixed_string.h>
#include <mp-units/format.h>
#include <mp-units/ostream.h> // IWYU pragma: keep
#include <mp-units/systems/cgs.h>
@ -51,18 +50,6 @@ inline constexpr bool mp_units::is_vector<T> = true;
using namespace mp_units;
using namespace mp_units::si::unit_symbols;
TEST_CASE("fixed_string", "[text][ostream][fmt]")
{
basic_fixed_string txt = "units";
SECTION("iostream")
{
std::ostringstream os;
os << txt;
CHECK(os.str() == "units");
}
SECTION("fmt") { CHECK(MP_UNITS_STD_FMT::format("{}", txt) == "units"); }
}
TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
{
std::ostringstream os;

View File

@ -32,7 +32,6 @@ add_library(
unit_tests_static
angular_test.cpp
cgs_test.cpp
chrono_test.cpp
compare_test.cpp
concepts_test.cpp
# custom_rep_test_min_expl.cpp
@ -40,7 +39,6 @@ add_library(
dimension_test.cpp
dimension_symbol_test.cpp
fixed_string_test.cpp
fractional_exponent_quantity.cpp
hep_test.cpp
iau_test.cpp
iec80000_test.cpp
@ -49,7 +47,6 @@ add_library(
isq_test.cpp
isq_angle_test.cpp
# magnitude_test.cpp
math_test.cpp
natural_test.cpp
prime_test.cpp
quantity_point_test.cpp
@ -65,5 +62,10 @@ add_library(
usc_test.cpp
)
if(NOT ${projectPrefix}API_FREESTANDING)
target_sources(unit_tests_static PRIVATE chrono_test.cpp fractional_exponent_quantity.cpp math_test.cpp)
endif()
target_compile_options(unit_tests_static PRIVATE $<$<CXX_COMPILER_ID:GNU>:-Wno-subobject-linkage>)
target_link_libraries(unit_tests_static PRIVATE mp-units::mp-units)
target_link_libraries(unit_tests_static PRIVATE unit_tests_static_truncating)

View File

@ -23,14 +23,18 @@
#include <mp-units/systems/isq/space_and_time.h>
#include <mp-units/systems/natural.h>
#include <mp-units/systems/si.h>
#include <optional>
#include <type_traits>
#if MP_UNITS_HOSTED
#include <chrono>
#include <complex>
#include <optional>
#include <string>
#include <type_traits>
#endif
#if MP_UNITS_HOSTED
template<typename T>
inline constexpr bool mp_units::is_scalar<std::complex<T>> = true;
#endif
namespace {
@ -45,9 +49,9 @@ struct dim_speed : decltype(isq::dim_length / isq::dim_time) {};
// BaseDimension
static_assert(detail::BaseDimension<struct isq::dim_length>);
static_assert(!detail::BaseDimension<std::remove_const_t<decltype(isq::dim_length / isq::dim_time)>>);
static_assert(!detail::BaseDimension<std::remove_const_t<decltype(inverse(isq::dim_time))>>);
static_assert(!detail::BaseDimension<std::remove_const_t<decltype(pow<2>(isq::dim_length))>>);
static_assert(!detail::BaseDimension<decltype(isq::dim_length / isq::dim_time)>);
static_assert(!detail::BaseDimension<decltype(inverse(isq::dim_time))>);
static_assert(!detail::BaseDimension<decltype(pow<2>(isq::dim_length))>);
static_assert(!detail::BaseDimension<derived_dimension<struct isq::dim_length, per<struct isq::dim_time>>>);
static_assert(!detail::BaseDimension<dim_speed>);
static_assert(!detail::BaseDimension<base_dimension<"L">>);
@ -55,9 +59,9 @@ static_assert(!detail::BaseDimension<struct si::metre>);
static_assert(!detail::BaseDimension<int>);
// DerivedDimension
static_assert(detail::DerivedDimension<std::remove_const_t<decltype(isq::dim_length / isq::dim_time)>>);
static_assert(detail::DerivedDimension<std::remove_const_t<decltype(inverse(isq::dim_time))>>);
static_assert(detail::DerivedDimension<std::remove_const_t<decltype(pow<2>(isq::dim_length))>>);
static_assert(detail::DerivedDimension<decltype(isq::dim_length / isq::dim_time)>);
static_assert(detail::DerivedDimension<decltype(inverse(isq::dim_time))>);
static_assert(detail::DerivedDimension<decltype(pow<2>(isq::dim_length))>);
static_assert(detail::DerivedDimension<derived_dimension<struct isq::dim_length, per<struct isq::dim_time>>>);
static_assert(detail::DerivedDimension<struct dimension_one>);
static_assert(!detail::DerivedDimension<dim_speed>);
@ -67,9 +71,9 @@ static_assert(!detail::DerivedDimension<int>);
// Dimension
static_assert(Dimension<struct isq::dim_length>);
static_assert(Dimension<std::remove_const_t<decltype(isq::dim_length / isq::dim_time)>>);
static_assert(Dimension<std::remove_const_t<decltype(inverse(isq::dim_time))>>);
static_assert(Dimension<std::remove_const_t<decltype(pow<2>(isq::dim_length))>>);
static_assert(Dimension<decltype(isq::dim_length / isq::dim_time)>);
static_assert(Dimension<decltype(inverse(isq::dim_time))>);
static_assert(Dimension<decltype(pow<2>(isq::dim_length))>);
static_assert(Dimension<derived_dimension<struct isq::dim_length, per<struct isq::dim_time>>>);
static_assert(Dimension<struct dimension_one>);
static_assert(!Dimension<dim_speed>);
@ -86,9 +90,9 @@ struct speed : decltype(isq::length / isq::time) {}; // this is not recommended
static_assert(QuantitySpec<struct isq::length>);
static_assert(QuantitySpec<struct isq::radius>);
static_assert(QuantitySpec<struct isq::speed>);
static_assert(QuantitySpec<std::remove_const_t<decltype(kind_of<isq::length>)>>);
static_assert(QuantitySpec<std::remove_const_t<decltype(isq::length / isq::time)>>);
static_assert(QuantitySpec<std::remove_const_t<decltype(pow<2>(isq::length))>>);
static_assert(QuantitySpec<decltype(kind_of<isq::length>)>);
static_assert(QuantitySpec<decltype(isq::length / isq::time)>);
static_assert(QuantitySpec<decltype(pow<2>(isq::length))>);
static_assert(QuantitySpec<struct dimensionless>);
static_assert(!QuantitySpec<speed>);
static_assert(!QuantitySpec<struct isq::dim_length>);
@ -99,8 +103,8 @@ static_assert(detail::NamedQuantitySpec<struct isq::length>);
static_assert(detail::NamedQuantitySpec<struct isq::radius>);
static_assert(detail::NamedQuantitySpec<struct isq::speed>);
static_assert(!detail::NamedQuantitySpec<std::remove_const_t<decltype(kind_of<isq::length>)>>);
static_assert(!detail::NamedQuantitySpec<std::remove_const_t<decltype(isq::length / isq::time)>>);
static_assert(!detail::NamedQuantitySpec<std::remove_const_t<decltype(pow<2>(isq::length))>>);
static_assert(!detail::NamedQuantitySpec<decltype(isq::length / isq::time)>);
static_assert(!detail::NamedQuantitySpec<decltype(pow<2>(isq::length))>);
static_assert(detail::NamedQuantitySpec<struct dimensionless>);
static_assert(!detail::NamedQuantitySpec<speed>);
static_assert(!detail::NamedQuantitySpec<struct isq::dim_length>);
@ -109,10 +113,10 @@ static_assert(!detail::NamedQuantitySpec<int>);
// IntermediateDerivedQuantitySpec
static_assert(!detail::IntermediateDerivedQuantitySpec<struct isq::length>);
static_assert(!detail::IntermediateDerivedQuantitySpec<struct isq::radius>);
static_assert(!detail::IntermediateDerivedQuantitySpec<std::remove_const_t<decltype(kind_of<isq::length>)>>);
static_assert(!detail::IntermediateDerivedQuantitySpec<decltype(kind_of<isq::length>)>);
static_assert(!detail::IntermediateDerivedQuantitySpec<struct isq::speed>);
static_assert(detail::IntermediateDerivedQuantitySpec<std::remove_const_t<decltype(isq::length / isq::time)>>);
static_assert(detail::IntermediateDerivedQuantitySpec<std::remove_const_t<decltype(pow<2>(isq::length))>>);
static_assert(detail::IntermediateDerivedQuantitySpec<decltype(isq::length / isq::time)>);
static_assert(detail::IntermediateDerivedQuantitySpec<decltype(pow<2>(isq::length))>);
static_assert(!detail::IntermediateDerivedQuantitySpec<struct dimensionless>);
static_assert(!detail::IntermediateDerivedQuantitySpec<speed>);
static_assert(!detail::IntermediateDerivedQuantitySpec<struct isq::dim_length>);
@ -123,8 +127,8 @@ static_assert(!detail::QuantityKindSpec<struct isq::length>);
static_assert(!detail::QuantityKindSpec<struct isq::radius>);
static_assert(detail::QuantityKindSpec<std::remove_const_t<decltype(kind_of<isq::length>)>>);
static_assert(!detail::QuantityKindSpec<struct isq::speed>);
static_assert(!detail::QuantityKindSpec<std::remove_const_t<decltype(isq::length / isq::time)>>);
static_assert(!detail::QuantityKindSpec<std::remove_const_t<decltype(pow<2>(isq::length))>>);
static_assert(!detail::QuantityKindSpec<decltype(isq::length / isq::time)>);
static_assert(!detail::QuantityKindSpec<decltype(pow<2>(isq::length))>);
static_assert(!detail::QuantityKindSpec<struct dimensionless>);
static_assert(!detail::QuantityKindSpec<speed>);
static_assert(!detail::QuantityKindSpec<struct isq::dim_length>);
@ -138,13 +142,13 @@ struct metre_per_second : decltype(si::metre / si::second) {};
static_assert(Unit<struct si::metre>);
static_assert(Unit<struct si::kilogram>);
static_assert(Unit<std::remove_const_t<decltype(si::kilo<si::gram>)>>);
static_assert(Unit<decltype(si::kilo<si::gram>)>);
static_assert(Unit<struct natural::electronvolt>);
static_assert(Unit<std::remove_const_t<decltype(si::metre / si::second)>>);
static_assert(Unit<std::remove_const_t<decltype(inverse(si::second))>>);
static_assert(Unit<std::remove_const_t<decltype(mag<10> * si::second)>>);
static_assert(Unit<std::remove_const_t<decltype(square(si::metre))>>);
static_assert(Unit<std::remove_const_t<decltype(pow<2>(si::metre))>>);
static_assert(Unit<decltype(si::metre / si::second)>);
static_assert(Unit<decltype(inverse(si::second))>);
static_assert(Unit<decltype(mag<10> * si::second)>);
static_assert(Unit<decltype(square(si::metre))>);
static_assert(Unit<decltype(pow<2>(si::metre))>);
static_assert(Unit<struct si::standard_gravity>);
static_assert(Unit<scaled_unit<mag<10>, struct si::second>>);
static_assert(Unit<metre_per_second>);
@ -157,18 +161,20 @@ static_assert(!Unit<named_unit<"?", si::metre, isq::length>>);
static_assert(!Unit<prefixed_unit<"?", mag<10>, si::second>>);
static_assert(!Unit<struct isq::dim_length>);
static_assert(!Unit<int>);
#if MP_UNITS_HOSTED
static_assert(!Unit<std::chrono::seconds>);
#endif
// NamedUnit
static_assert(detail::NamedUnit<struct si::metre>);
static_assert(detail::NamedUnit<struct natural::electronvolt>);
static_assert(!detail::NamedUnit<struct si::kilogram>);
static_assert(!detail::NamedUnit<std::remove_const_t<decltype(si::kilo<si::gram>)>>);
static_assert(!detail::NamedUnit<std::remove_const_t<decltype(si::metre / si::second)>>);
static_assert(!detail::NamedUnit<std::remove_const_t<decltype(inverse(si::second))>>);
static_assert(!detail::NamedUnit<std::remove_const_t<decltype(mag<10> * si::second)>>);
static_assert(!detail::NamedUnit<std::remove_const_t<decltype(square(si::metre))>>);
static_assert(!detail::NamedUnit<std::remove_const_t<decltype(pow<2>(si::metre))>>);
static_assert(!detail::NamedUnit<decltype(si::kilo<si::gram>)>);
static_assert(!detail::NamedUnit<decltype(si::metre / si::second)>);
static_assert(!detail::NamedUnit<decltype(inverse(si::second))>);
static_assert(!detail::NamedUnit<decltype(mag<10> * si::second)>);
static_assert(!detail::NamedUnit<decltype(square(si::metre))>);
static_assert(!detail::NamedUnit<decltype(pow<2>(si::metre))>);
static_assert(detail::NamedUnit<struct si::standard_gravity>);
static_assert(!detail::NamedUnit<scaled_unit<mag<10>, struct si::second>>);
static_assert(!detail::NamedUnit<metre_per_second>);
@ -181,18 +187,20 @@ static_assert(!detail::NamedUnit<named_unit<"?", si::metre, isq::length>>);
static_assert(!detail::NamedUnit<prefixed_unit<"?", mag<10>, si::second>>);
static_assert(!detail::NamedUnit<struct isq::dim_length>);
static_assert(!detail::NamedUnit<int>);
#if MP_UNITS_HOSTED
static_assert(!detail::NamedUnit<std::chrono::seconds>);
#endif
// PrefixableUnit
static_assert(PrefixableUnit<struct si::metre>);
static_assert(PrefixableUnit<struct natural::electronvolt>);
static_assert(!PrefixableUnit<struct si::kilogram>);
static_assert(!PrefixableUnit<std::remove_const_t<decltype(si::kilo<si::gram>)>>);
static_assert(!PrefixableUnit<std::remove_const_t<decltype(si::metre / si::second)>>);
static_assert(!PrefixableUnit<std::remove_const_t<decltype(inverse(si::second))>>);
static_assert(!PrefixableUnit<std::remove_const_t<decltype(mag<10> * si::second)>>);
static_assert(!PrefixableUnit<std::remove_const_t<decltype(square(si::metre))>>);
static_assert(!PrefixableUnit<std::remove_const_t<decltype(pow<2>(si::metre))>>);
static_assert(!PrefixableUnit<decltype(si::kilo<si::gram>)>);
static_assert(!PrefixableUnit<decltype(si::metre / si::second)>);
static_assert(!PrefixableUnit<decltype(inverse(si::second))>);
static_assert(!PrefixableUnit<decltype(mag<10> * si::second)>);
static_assert(!PrefixableUnit<decltype(square(si::metre))>);
static_assert(!PrefixableUnit<decltype(pow<2>(si::metre))>);
static_assert(PrefixableUnit<struct si::standard_gravity>);
static_assert(!PrefixableUnit<scaled_unit<mag<10>, struct si::second>>);
static_assert(!PrefixableUnit<metre_per_second>);
@ -205,18 +213,20 @@ static_assert(!PrefixableUnit<named_unit<"?", si::metre, isq::length>>);
static_assert(!PrefixableUnit<prefixed_unit<"?", mag<10>, si::second>>);
static_assert(!PrefixableUnit<struct isq::dim_length>);
static_assert(!PrefixableUnit<int>);
#if MP_UNITS_HOSTED
static_assert(!PrefixableUnit<std::chrono::seconds>);
#endif
// AssociatedUnit
static_assert(AssociatedUnit<struct si::metre>);
static_assert(!AssociatedUnit<struct natural::electronvolt>);
static_assert(AssociatedUnit<struct si::kilogram>);
static_assert(AssociatedUnit<std::remove_const_t<decltype(si::kilo<si::gram>)>>);
static_assert(AssociatedUnit<std::remove_const_t<decltype(si::metre / si::second)>>);
static_assert(AssociatedUnit<std::remove_const_t<decltype(inverse(si::second))>>);
static_assert(AssociatedUnit<std::remove_const_t<decltype(mag<10> * si::second)>>);
static_assert(AssociatedUnit<std::remove_const_t<decltype(square(si::metre))>>);
static_assert(AssociatedUnit<std::remove_const_t<decltype(pow<2>(si::metre))>>);
static_assert(AssociatedUnit<decltype(si::kilo<si::gram>)>);
static_assert(AssociatedUnit<decltype(si::metre / si::second)>);
static_assert(AssociatedUnit<decltype(inverse(si::second))>);
static_assert(AssociatedUnit<decltype(mag<10> * si::second)>);
static_assert(AssociatedUnit<decltype(square(si::metre))>);
static_assert(AssociatedUnit<decltype(pow<2>(si::metre))>);
static_assert(AssociatedUnit<struct si::standard_gravity>);
static_assert(AssociatedUnit<scaled_unit<mag<10>, struct si::second>>);
static_assert(AssociatedUnit<metre_per_second>);
@ -229,7 +239,9 @@ static_assert(!AssociatedUnit<named_unit<"?", si::metre, isq::length>>);
static_assert(!AssociatedUnit<prefixed_unit<"?", mag<10>, si::second>>);
static_assert(!AssociatedUnit<struct isq::dim_length>);
static_assert(!AssociatedUnit<int>);
#if MP_UNITS_HOSTED
static_assert(!AssociatedUnit<std::chrono::seconds>);
#endif
// UnitOf
static_assert(UnitOf<struct si::metre, isq::length>);
@ -248,13 +260,13 @@ static_assert(!UnitOf<struct natural::electronvolt, isq::energy>);
// Reference
static_assert(Reference<struct si::metre>);
static_assert(Reference<std::remove_const_t<decltype(si::metre / si::second)>>);
static_assert(Reference<std::remove_const_t<decltype(isq::length[si::metre])>>);
static_assert(Reference<std::remove_const_t<decltype(isq::radius[si::metre])>>);
static_assert(Reference<std::remove_const_t<decltype(isq::radius[si::metre] / isq::time[si::second])>>);
static_assert(Reference<decltype(si::metre / si::second)>);
static_assert(Reference<decltype(isq::length[si::metre])>);
static_assert(Reference<decltype(isq::radius[si::metre])>);
static_assert(Reference<decltype(isq::radius[si::metre] / isq::time[si::second])>);
static_assert(!Reference<struct natural::electronvolt>);
static_assert(!Reference<struct isq::length>);
static_assert(!Reference<std::remove_const_t<decltype(kind_of<isq::length>)>>);
static_assert(!Reference<decltype(kind_of<isq::length>)>);
static_assert(!Reference<struct isq::dim_length>);
static_assert(!Reference<int>);
@ -262,51 +274,57 @@ static_assert(!Reference<int>);
static_assert(ReferenceOf<struct si::metre, isq::length>);
static_assert(ReferenceOf<struct si::metre, isq::radius>);
static_assert(!ReferenceOf<struct si::second, isq::length>);
static_assert(ReferenceOf<std::remove_const_t<decltype(isq::length[si::metre])>, isq::length>);
static_assert(!ReferenceOf<std::remove_const_t<decltype(isq::length[si::metre])>, isq::radius>);
static_assert(ReferenceOf<std::remove_const_t<decltype(isq::radius[si::metre])>, isq::length>);
static_assert(ReferenceOf<std::remove_const_t<decltype(isq::radius[si::metre])>, isq::radius>);
static_assert(ReferenceOf<decltype(isq::length[si::metre]), isq::length>);
static_assert(!ReferenceOf<decltype(isq::length[si::metre]), isq::radius>);
static_assert(ReferenceOf<decltype(isq::radius[si::metre]), isq::length>);
static_assert(ReferenceOf<decltype(isq::radius[si::metre]), isq::radius>);
static_assert(!ReferenceOf<struct si::second, isq::dim_length>);
static_assert(ReferenceOf<struct one, dimensionless>);
static_assert(ReferenceOf<std::remove_const_t<decltype(dimensionless[one])>, dimensionless>);
static_assert(ReferenceOf<std::remove_const_t<decltype(isq::rotation[one])>, isq::rotation>);
static_assert(ReferenceOf<std::remove_const_t<decltype(isq::rotation[one])>, dimensionless>);
static_assert(ReferenceOf<decltype(dimensionless[one]), dimensionless>);
static_assert(ReferenceOf<decltype(isq::rotation[one]), isq::rotation>);
static_assert(ReferenceOf<decltype(isq::rotation[one]), dimensionless>);
static_assert(ReferenceOf<struct si::radian, isq::angular_measure>);
static_assert(!ReferenceOf<struct si::radian, dimensionless>);
static_assert(ReferenceOf<std::remove_const_t<decltype(isq::angular_measure[si::radian])>, isq::angular_measure>);
static_assert(!ReferenceOf<std::remove_const_t<decltype(isq::angular_measure[si::radian])>, dimensionless>);
static_assert(ReferenceOf<decltype(isq::angular_measure[si::radian]), isq::angular_measure>);
static_assert(!ReferenceOf<decltype(isq::angular_measure[si::radian]), dimensionless>);
static_assert(ReferenceOf<struct one, isq::rotation>);
static_assert(ReferenceOf<struct one, isq::angular_measure>);
static_assert(!ReferenceOf<std::remove_const_t<decltype(dimensionless[one])>, isq::rotation>);
static_assert(!ReferenceOf<std::remove_const_t<decltype(dimensionless[one])>, isq::angular_measure>);
static_assert(!ReferenceOf<decltype(dimensionless[one]), isq::rotation>);
static_assert(!ReferenceOf<decltype(dimensionless[one]), isq::angular_measure>);
// Representation
static_assert(Representation<int>);
static_assert(Representation<double>);
static_assert(Representation<std::complex<double>>);
static_assert(!Representation<bool>);
static_assert(!Representation<std::optional<int>>);
static_assert(!Representation<std::chrono::seconds>);
#if MP_UNITS_HOSTED
static_assert(Representation<std::complex<double>>);
static_assert(!Representation<std::string>);
static_assert(!Representation<std::chrono::seconds>);
#endif
// RepresentationOf
static_assert(RepresentationOf<int, quantity_character::scalar>);
static_assert(RepresentationOf<double, quantity_character::scalar>);
static_assert(RepresentationOf<std::complex<double>, quantity_character::scalar>);
static_assert(!RepresentationOf<bool, quantity_character::scalar>);
static_assert(!RepresentationOf<std::optional<int>, quantity_character::scalar>);
#if MP_UNITS_HOSTED
static_assert(RepresentationOf<std::complex<double>, quantity_character::scalar>);
static_assert(!RepresentationOf<std::chrono::seconds, quantity_character::scalar>);
static_assert(!RepresentationOf<std::string, quantity_character::scalar>);
#endif
// Quantity
static_assert(Quantity<quantity<si::metre>>);
static_assert(Quantity<quantity<isq::length[si::metre]>>);
static_assert(Quantity<quantity<si::metre, int>>);
static_assert(Quantity<quantity<isq::length[si::metre], int>>);
#if MP_UNITS_HOSTED
static_assert(!Quantity<std::chrono::seconds>);
#endif
static_assert(!Quantity<quantity_point<si::metre, my_origin>>);
static_assert(!Quantity<std::remove_const_t<decltype(isq::length[si::metre])>>);
static_assert(!Quantity<decltype(isq::length[si::metre])>);
// QuantityOf
static_assert(QuantityOf<quantity<si::metre>, isq::length>);
@ -332,8 +350,10 @@ static_assert(!QuantityOf<quantity<dimensionless[one]>, isq::rotation>);
static_assert(!QuantityOf<quantity<dimensionless[one]>, isq::angular_measure>);
// QuantityLike
#if MP_UNITS_HOSTED
static_assert(QuantityLike<std::chrono::seconds>);
static_assert(QuantityLike<std::chrono::hours>);
#endif
static_assert(!QuantityLike<quantity<isq::time[si::second]>>);
static_assert(!QuantityLike<quantity_point<isq::length[si::metre], my_origin>>);
static_assert(!QuantityLike<int>);
@ -345,12 +365,14 @@ static_assert(QuantityPoint<quantity_point<isq::length[si::metre], my_origin>>);
static_assert(QuantityPoint<quantity_point<isq::length[si::metre], my_relative_origin, int>>);
static_assert(QuantityPoint<quantity_point<isq::radius[si::metre], my_origin>>);
static_assert(QuantityPoint<quantity_point<isq::radius[si::metre], my_relative_origin>>);
static_assert(!QuantityPoint<std::remove_const_t<decltype(isq::length[si::metre])>>);
static_assert(!QuantityPoint<decltype(isq::length[si::metre])>);
static_assert(!QuantityPoint<absolute_point_origin<struct my_origin, isq::length>>);
static_assert(!QuantityPoint<struct my_origin>);
static_assert(!QuantityPoint<struct my_relative_origin>);
#if MP_UNITS_HOSTED
static_assert(!QuantityPoint<std::chrono::seconds>);
static_assert(!QuantityPoint<std::chrono::time_point<std::chrono::system_clock>>);
#endif
static_assert(!QuantityPoint<int>);
// QuantityPointOf
@ -383,9 +405,11 @@ static_assert(!PointOrigin<relative_point_origin<my_origin + 42 * si::metre>>);
static_assert(!PointOrigin<quantity_point<si::metre, my_origin>>);
static_assert(!PointOrigin<quantity_point<isq::length[si::metre], my_origin>>);
static_assert(!PointOrigin<quantity_point<isq::radius[si::metre], my_origin>>);
static_assert(!PointOrigin<std::remove_const_t<decltype(isq::length[si::metre])>>);
static_assert(!PointOrigin<decltype(isq::length[si::metre])>);
#if MP_UNITS_HOSTED
static_assert(!PointOrigin<std::chrono::seconds>);
static_assert(!PointOrigin<std::chrono::time_point<std::chrono::system_clock>>);
#endif
static_assert(!PointOrigin<int>);
// PointOriginFor
@ -407,14 +431,18 @@ static_assert(!PointOriginFor<quantity_point<isq::radius[si::metre], my_origin>,
static_assert(!PointOriginFor<quantity_point<isq::radius[si::metre], my_relative_origin>, isq::length>);
static_assert(!PointOriginFor<quantity_point<isq::radius[si::metre], my_relative_origin>, isq::radius>);
static_assert(!PointOriginFor<quantity_point<isq::radius[si::metre], my_relative_origin>, isq::time>);
static_assert(!PointOriginFor<std::remove_const_t<decltype(isq::length[si::metre])>, isq::length>);
static_assert(!PointOriginFor<decltype(isq::length[si::metre]), isq::length>);
#if MP_UNITS_HOSTED
static_assert(!PointOriginFor<std::chrono::seconds, isq::length>);
static_assert(!PointOriginFor<std::chrono::time_point<std::chrono::system_clock>, isq::length>);
#endif
static_assert(!PointOriginFor<int, isq::length>);
// QuantityPointLike
#if MP_UNITS_HOSTED
static_assert(QuantityPointLike<std::chrono::time_point<std::chrono::system_clock>>);
static_assert(!QuantityPointLike<std::chrono::seconds>);
#endif
static_assert(!QuantityPointLike<quantity<isq::time[si::second]>>);
static_assert(!QuantityPointLike<quantity_point<si::metre, my_origin>>);
static_assert(!QuantityPointLike<int>);

View File

@ -82,7 +82,7 @@ static_assert(!std::convertible_to<min_impl<double>, quantity<si::metre, min_imp
// multiply syntax should work
template<typename T, auto U>
concept creates_quantity = Unit<std::remove_cvref_t<decltype(U)>> && requires { T{} * U; };
concept creates_quantity = Unit<decltype(U)> && requires { T{} * U; };
static_assert(creates_quantity<min_impl<int>, si::metre>);
static_assert(creates_quantity<min_impl<double>, si::metre>);

View File

@ -21,42 +21,161 @@
// SOFTWARE.
#include <mp-units/ext/fixed_string.h>
#include <array>
#include <string_view>
using namespace mp_units;
namespace {
constexpr std::array array = {'a', 'b', 'c'};
auto from_string = [] {
std::string_view txt = "abc";
return fixed_string<3>(std::from_range, txt);
};
auto from_string_iter = [] {
std::string_view txt = "abc";
return fixed_string<3>(txt.begin(), txt.end());
};
constexpr fixed_string<0> txt0;
constexpr basic_fixed_string txt1('a');
constexpr basic_fixed_string txt2('a', 'b', 'c');
constexpr basic_fixed_string txt3 = "abc";
constexpr fixed_string<3> txt4(array.begin(), array.end());
constexpr basic_fixed_string txt5(std::from_range, array);
constexpr basic_fixed_string txt6(from_string());
constexpr basic_fixed_string txt7(from_string_iter());
constexpr fixed_string<3> txt8(txt2.begin(), txt2.end());
constexpr fixed_string<3> txt9(txt2.rbegin(), txt2.rend());
static_assert(txt0.size() == 0);
static_assert(txt1.size() == 1);
static_assert(txt2.size() == 3);
static_assert(txt3.size() == 3);
static_assert(txt4.size() == 3);
static_assert(txt5.size() == 3);
static_assert(txt6.size() == 3);
static_assert(txt7.size() == 3);
static_assert(txt8.size() == 3);
static_assert(txt9.size() == 3);
static_assert(txt0.length() == 0);
static_assert(txt1.length() == 1);
static_assert(txt2.length() == 3);
static_assert(txt0.max_size() == 0);
static_assert(txt1.max_size() == 1);
static_assert(txt2.max_size() == 3);
static_assert(txt0.empty() == true);
static_assert(txt1.empty() == false);
static_assert(txt2.empty() == false);
static_assert(txt3.empty() == false);
static_assert(txt4.empty() == false);
static_assert(txt5.empty() == false);
static_assert(txt6.empty() == false);
static_assert(txt7.empty() == false);
static_assert(txt8.empty() == false);
static_assert(txt9.empty() == false);
static_assert(txt1[0] == 'a');
static_assert(txt2[0] == 'a');
static_assert(txt2[1] == 'b');
static_assert(txt2[2] == 'c');
static_assert(txt9[0] == 'c');
static_assert(txt9[1] == 'b');
static_assert(txt9[2] == 'a');
#if MP_UNITS_HOSTED
static_assert(txt1.at(0) == 'a');
static_assert(txt2.at(0) == 'a');
static_assert(txt2.at(1) == 'b');
static_assert(txt2.at(2) == 'c');
static_assert(txt9.at(0) == 'c');
static_assert(txt9.at(1) == 'b');
static_assert(txt9.at(2) == 'a');
#endif
static_assert(txt1.front() == 'a');
static_assert(txt1.back() == 'a');
static_assert(txt2.front() == 'a');
static_assert(txt2.back() == 'c');
static_assert(txt5.front() == 'a');
static_assert(txt5.back() == 'c');
static_assert(txt6.front() == 'a');
static_assert(txt6.back() == 'c');
static_assert(txt7.front() == 'a');
static_assert(txt7.back() == 'c');
static_assert(txt8.front() == 'a');
static_assert(txt8.back() == 'c');
static_assert(txt9.front() == 'c');
static_assert(txt9.back() == 'a');
static_assert(std::string_view(txt0.data()) == "");
static_assert(std::string_view(txt0.c_str()) == "");
static_assert(std::string_view(txt1.data()) == "a");
static_assert(std::string_view(txt1.c_str()) == "a");
static_assert(std::string_view(txt2.data()) == "abc");
static_assert(std::string_view(txt2.c_str()) == "abc");
static_assert(txt0 == "");
static_assert("a" == txt1);
static_assert(txt2 == "abc");
static_assert(txt3 == "abc");
static_assert(txt4 == "abc");
static_assert(txt5 == "abc");
static_assert(txt6 == "abc");
static_assert(txt7 == "abc");
static_assert(txt8 == "abc");
static_assert(txt9 == "cba");
static_assert(txt1 == basic_fixed_string("a"));
static_assert(txt1 != basic_fixed_string("b"));
static_assert(txt1 != basic_fixed_string("aa"));
static_assert(txt1 < basic_fixed_string("b"));
static_assert(txt1 < basic_fixed_string("aa"));
static_assert(txt1 + basic_fixed_string('b') == basic_fixed_string("ab"));
static_assert(basic_fixed_string('b') + txt1 == basic_fixed_string("ba"));
static_assert(txt1 + basic_fixed_string("bc") == basic_fixed_string("abc"));
static_assert(basic_fixed_string("bc") + txt1 == basic_fixed_string("bca"));
static_assert(txt1 == "a");
static_assert(txt1 != "b");
static_assert(txt1 != "aa");
static_assert(txt1 < "b");
static_assert(txt1 < "aa");
static_assert(txt1 + basic_fixed_string('b') == "ab");
static_assert(basic_fixed_string('b') + txt1 == "ba");
static_assert(txt1 + basic_fixed_string("bc") == "abc");
static_assert(basic_fixed_string("bc") + txt1 == "bca");
static_assert(txt1 + 'b' == "ab");
static_assert('b' + txt1 == "ba");
static_assert(txt1 + "bc" == "abc");
static_assert("bc" + txt1 == "bca");
constexpr basic_fixed_string txt2("abc");
static_assert(txt2.size() == 3);
static_assert(txt2[0] == 'a');
static_assert(txt2[1] == 'b');
static_assert(txt2[2] == 'c');
static_assert(txt2 == basic_fixed_string("abc"));
static_assert(txt2 != basic_fixed_string("cba"));
static_assert(txt2 != basic_fixed_string("abcd"));
static_assert(txt2 < basic_fixed_string("b"));
static_assert(txt2 > basic_fixed_string("aa"));
static_assert(txt2 + basic_fixed_string('d') == basic_fixed_string("abcd"));
static_assert(basic_fixed_string('d') + txt2 == basic_fixed_string("dabc"));
static_assert(txt2 + basic_fixed_string("def") == basic_fixed_string("abcdef"));
static_assert(basic_fixed_string("def") + txt2 == basic_fixed_string("defabc"));
static_assert(txt2 == "abc");
static_assert(txt2 != "cba");
static_assert(txt2 != "abcd");
static_assert(txt2 < "b");
static_assert(txt2 > "aa");
#ifndef MP_UNITS_COMP_GCC
static_assert(std::string_view(basic_fixed_string("abcd")).find('c') == 2);
#endif
static_assert(txt2 + basic_fixed_string('d') == "abcd");
static_assert(basic_fixed_string('d') + txt2 == "dabc");
static_assert(txt2 + basic_fixed_string("def") == "abcdef");
static_assert(basic_fixed_string("def") + txt2 == "defabc");
static_assert(txt2 + 'd' == "abcd");
static_assert('d' + txt2 == "dabc");
static_assert(txt2 + "def" == "abcdef");
static_assert("def" + txt2 == "defabc");
static_assert(std::string_view(txt2) == "abc");
static_assert(txt2.view() == "abc");
static_assert(std::string_view(txt2).find('b') == 1);
static_assert(txt2.view().find('b') == 1);
} // namespace

View File

@ -20,7 +20,9 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#if MP_UNITS_HOSTED
#include <mp-units/math.h> // IWYU pragma: keep
#endif
#include <mp-units/systems/iau.h>
#include <mp-units/systems/isq/space_and_time.h>
#include <mp-units/systems/si.h>
@ -42,7 +44,7 @@ static_assert(isq::length(1 * LD) == 384'399 * si::kilo<si::metre>);
static_assert(isq::length(1 * ly) == 9'460'730'472'580'800 * si::metre);
static_assert(isq::length(10'000'000'000 * A) == 1 * si::metre);
#if __cpp_lib_constexpr_cmath || MP_UNITS_COMP_GCC
#if MP_UNITS_HOSTED && (__cpp_lib_constexpr_cmath || MP_UNITS_COMP_GCC)
// TODO Should the below work for `1 * pc`? If yes, how to extent the type and how to convert it to a floating-point
// representation for comparison purposes?
static_assert(round<si::metre>(isq::length(1.L * pc)) == 30'856'775'814'913'673 * si::metre);

View File

@ -83,7 +83,7 @@ static_assert(Magnitude<decltype(mag<2>)>);
static_assert(Magnitude<mag_2_>);
// is_named_magnitude
static_assert(!is_named_magnitude<std::remove_cvref_t<decltype(mag<2>)>>);
static_assert(!is_named_magnitude<decltype(mag<2>)>);
static_assert(is_named_magnitude<mag_2_>);
// power_v

View File

@ -26,20 +26,25 @@
#include <mp-units/systems/isq.h>
#include <mp-units/systems/si.h>
#include <mp-units/systems/usc.h>
#include <chrono>
#include <concepts>
#include <cstdint>
#include <limits>
#include <type_traits>
#include <utility>
#if MP_UNITS_HOSTED
#include <chrono>
#endif
namespace {
using namespace mp_units;
using namespace mp_units::si::unit_symbols;
using namespace mp_units::usc::unit_symbols;
#if MP_UNITS_HOSTED
using namespace std::chrono_literals;
using sys_seconds = std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>;
#endif
inline constexpr struct zeroth_length : absolute_point_origin<zeroth_length, isq::length> {
} zeroth_length;
@ -444,6 +449,7 @@ static_assert(!std::convertible_to<quantity<isq::length[m]>, quantity_point<isq:
static_assert(!std::constructible_from<quantity_point<special_height[m]>, quantity<isq::height[m]>>);
static_assert(!std::convertible_to<quantity<special_height[m]>, quantity_point<isq::height[m]>>);
#if MP_UNITS_HOSTED
// quantity-like
static_assert(!std::constructible_from<quantity_point<si::second>, std::chrono::seconds>);
static_assert(!std::convertible_to<std::chrono::seconds, quantity_point<si::second>>);
@ -453,7 +459,7 @@ static_assert(!std::convertible_to<std::chrono::seconds, quantity_point<isq::tim
static_assert(!std::constructible_from<quantity_point<isq::period_duration[s]>, std::chrono::seconds>);
static_assert(!std::convertible_to<std::chrono::seconds, quantity_point<isq::period_duration[s]>>);
#endif
// ----------------------
// explicit point origins
@ -499,6 +505,7 @@ static_assert(!std::convertible_to<quantity<special_height[m]>, quantity_point<i
static_assert(!std::constructible_from<quantity_point<si::metre, mean_sea_level>, quantity<isq::length[m]>>);
static_assert(!std::convertible_to<quantity<isq::length[m]>, quantity_point<si::metre, mean_sea_level>>);
#if MP_UNITS_HOSTED
// quantity-like
static_assert(!std::constructible_from<quantity_point<si::second, chrono_point_origin<std::chrono::system_clock>>,
std::chrono::seconds>);
@ -516,6 +523,7 @@ static_assert(
static_assert(
!std::convertible_to<std::chrono::seconds,
quantity_point<isq::period_duration[s], chrono_point_origin<std::chrono::system_clock>>>);
#endif
///////////////////////////////////////
@ -775,6 +783,7 @@ static_assert(!std::constructible_from<quantity_point<isq::height[m], other_abso
static_assert(!std::convertible_to<quantity_point<isq::height[m], ground_level>,
quantity_point<isq::height[m], other_absolute_level>>);
#if MP_UNITS_HOSTED
// quantity-point-like
static_assert(
std::constructible_from<quantity_point<isq::time[s], chrono_point_origin<std::chrono::system_clock>>, sys_seconds>);
@ -786,6 +795,7 @@ static_assert(
!std::constructible_from<quantity_point<isq::time[s], chrono_point_origin<std::chrono::steady_clock>>, sys_seconds>);
static_assert(
!std::convertible_to<sys_seconds, quantity_point<isq::time[s], chrono_point_origin<std::chrono::steady_clock>>>);
#endif
//////////////////////////////////
@ -889,6 +899,7 @@ static_assert(std::is_same_v<std::remove_const_t<decltype(quantity_point{20 * de
static_assert(quantity_point{20 * deg_C}.unit == si::degree_Celsius);
static_assert(quantity_point{20 * deg_C}.quantity_spec == kind_of<isq::thermodynamic_temperature>);
#if MP_UNITS_HOSTED
using namespace std::chrono_literals;
static_assert(std::is_same_v<decltype(quantity_point{sys_seconds{123s}})::rep, std::chrono::seconds::rep>);
static_assert(std::is_same_v<std::remove_const_t<decltype(quantity_point{sys_seconds{123s}}.point_origin)>,
@ -897,6 +908,7 @@ static_assert(std::is_same_v<std::remove_const_t<decltype(quantity_point{sys_sec
chrono_point_origin_<std::chrono::system_clock>>);
static_assert(quantity_point{sys_seconds{24h}}.unit == si::second);
static_assert(quantity_point{sys_seconds{24h}}.quantity_spec == kind_of<isq::time>);
#endif
////////////

View File

@ -121,10 +121,10 @@ static_assert(!detail::NamedQuantitySpec<decltype(inverse(time))>);
static_assert(detail::IntermediateDerivedQuantitySpec<decltype(inverse(time))>);
static_assert(!detail::QuantityKindSpec<decltype(inverse(time))>);
static_assert(QuantitySpec<kind_of_<std::remove_const_t<decltype(length / time)>>>);
static_assert(!detail::NamedQuantitySpec<kind_of_<std::remove_const_t<decltype(length / time)>>>);
static_assert(detail::IntermediateDerivedQuantitySpec<kind_of_<std::remove_const_t<decltype(length / time)>>>);
static_assert(detail::QuantityKindSpec<kind_of_<std::remove_const_t<decltype(length / time)>>>);
static_assert(QuantitySpec<kind_of_<decltype(length / time)>>);
static_assert(!detail::NamedQuantitySpec<kind_of_<decltype(length / time)>>);
static_assert(detail::IntermediateDerivedQuantitySpec<kind_of_<decltype(length / time)>>);
static_assert(detail::QuantityKindSpec<kind_of_<decltype(length / time)>>);
static_assert(QuantitySpec<decltype(kind_of<length> / kind_of<time>)>);
static_assert(!detail::NamedQuantitySpec<decltype(kind_of<length> / kind_of<time>)>);

View File

@ -27,12 +27,14 @@
#include <mp-units/systems/isq/mechanics.h>
#include <mp-units/systems/isq/space_and_time.h>
#include <mp-units/systems/si.h>
#include <chrono>
#include <concepts>
#include <cstdint>
#include <limits>
#include <type_traits>
#include <utility>
#if MP_UNITS_HOSTED
#include <chrono>
#endif
template<>
inline constexpr bool mp_units::is_vector<int> = true;
@ -311,12 +313,13 @@ static_assert(quantity{123. * m}.quantity_spec == kind_of<isq::length>);
static_assert(quantity{123. * h}.unit == si::hour);
static_assert(quantity{123. * h}.quantity_spec == kind_of<isq::time>);
#if MP_UNITS_HOSTED
using namespace std::chrono_literals;
static_assert(std::is_same_v<decltype(quantity{123s})::rep, std::chrono::seconds::rep>);
static_assert(std::is_same_v<decltype(quantity{123.s})::rep, long double>);
static_assert(quantity{24h}.unit == si::hour);
static_assert(quantity{24h}.quantity_spec == kind_of<isq::time>);
#endif
////////////////////////
// assignment operator
@ -403,9 +406,6 @@ static_assert((std::uint8_t{255}* m %= 257 * m).numerical_value_in(m) != [] {
return ui %= 257;
}());
// TODO ICE
// (https://developercommunity2.visualstudio.com/t/ICE-on-a-constexpr-operator-in-mp-unit/1302907)
#ifndef MP_UNITS_COMP_MSVC
// clang-17 with modules build on ignores disabling conversion warnings
#if !(defined MP_UNITS_COMP_CLANG && MP_UNITS_COMP_CLANG < 18 && defined MP_UNITS_MODULES)
// next two lines trigger conversions warnings
@ -415,7 +415,6 @@ static_assert((22 * m /= 3.33).numerical_value_in(m) == 6);
static_assert((22 * m *= 33.33 * one).numerical_value_in(m) == 733);
static_assert((22 * m /= 3.33 * one).numerical_value_in(m) == 6);
#endif
#endif
template<template<auto, typename> typename Q>
concept invalid_compound_assignments = requires() {
@ -745,6 +744,8 @@ static_assert(4 / (2 * one) == 2 * one);
static_assert(4 * one / 2 == 2 * one);
static_assert(4 * one % (2 * one) == 0 * one);
static_assert(2 * rad * (2 * rad) == 4 * pow<2>(rad));
// modulo arithmetics
static_assert(5 * h % (120 * min) == 60 * min);
static_assert(300 * min % (2 * h) == 60 * min);
@ -844,8 +845,8 @@ static_assert(10 * isq::mechanical_energy[J] == 5 * isq::force[N] * (2 * isq::le
static_assert(1 * si::si2019::speed_of_light_in_vacuum == 299'792'458 * isq::speed[m / s]);
// Different named dimensions
template</*Reference*/ auto R1, /*Reference*/ auto R2> // TODO Use `Reference` when Clang supports it.
concept invalid_comparison = !requires { 2 * R1 == 2 * R2; } && !requires { 2 * R2 == 2 * R1; };
template<Reference auto R1, Reference auto R2>
inline constexpr bool invalid_comparison = !requires { 2 * R1 == 2 * R2; } && !requires { 2 * R2 == 2 * R1; };
static_assert(invalid_comparison<isq::activity[Bq], isq::frequency[Hz]>);

View File

@ -98,8 +98,7 @@ static_assert(is_of_type<length[metre], reference<length_, metre_>>);
static_assert(is_of_type<kind_of<length>[metre], metre_>);
static_assert(
is_of_type<(length / time)[metre / second],
reference<std::remove_const_t<decltype(length / time)>, std::remove_const_t<decltype(metre / second)>>>);
is_of_type<(length / time)[metre / second], reference<decltype(length / time), decltype(metre / second)>>);
static_assert(is_of_type<(kind_of<length> / kind_of<time>)[metre / second], derived_unit<metre_, per<second_>>>);
// Unit as a reference

View File

@ -57,8 +57,7 @@ static_assert(1 * Qm == 1'000'000'000'000'000'000 * Tm);
// check for invalid prefixes
template<template<typename U> typename prefix, auto V1>
concept can_not_be_prefixed =
Unit<std::remove_const_t<decltype(V1)>> && !requires { typename prefix<std::remove_const_t<decltype(V1)>>; };
concept can_not_be_prefixed = Unit<decltype(V1)> && !requires { typename prefix<decltype(V1)>; };
static_assert(can_not_be_prefixed<si::milli_, si::degree_Celsius>);
static_assert(can_not_be_prefixed<si::milli_, si::minute>);

View File

@ -27,7 +27,7 @@
#include <type_traits>
template<auto V, typename T>
inline constexpr bool is_of_type = std::is_same_v<std::remove_cvref_t<decltype(V)>, T>;
inline constexpr bool is_of_type = std::is_same_v<MP_UNITS_REMOVE_CONST(decltype(V)), T>;
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
#ifdef MP_UNITS_API_NO_CRTP

View File

@ -210,7 +210,6 @@ static_assert(kilojoule.symbol == "kJ");
static_assert(is_of_type<si::kilo<metre>, si::kilo_<metre_>>);
static_assert(is_of_type<si::kilo<joule>, si::kilo_<joule_>>);
// TODO Should the below be a scaled version of metre^2?
static_assert(is_of_type<kilometre * metre, derived_unit<kilometre_, metre_>>); // !!!
static_assert(is_of_type<kilometre / metre, derived_unit<kilometre_, per<metre_>>>); // !!!
@ -241,7 +240,6 @@ static_assert(si::yotta<metre>.symbol == "Ym");
static_assert(si::ronna<metre>.symbol == "Rm");
static_assert(si::quetta<metre>.symbol == "Qm");
// scaled_unit
constexpr auto m_1 = mag<1> * metre;
static_assert(is_of_type<m_1, metre_>);

View File

@ -28,3 +28,11 @@ find_package(mp-units REQUIRED)
add_executable(test_package test_package.cpp)
target_link_libraries(test_package PRIVATE mp-units::mp-units)
target_compile_definitions(test_package PRIVATE MP_UNITS_API_STD_FORMAT=$<BOOL:${MP_UNITS_API_STD_FORMAT}>)
if(MP_UNITS_API_CONTRACTS STREQUAL "NONE")
target_compile_definitions(test_package PRIVATE MP_UNITS_API_CONTRACTS=0)
elseif(MP_UNITS_API_CONTRACTS STREQUAL "GSL-LITE")
target_compile_definitions(test_package PRIVATE MP_UNITS_API_CONTRACTS=2)
elseif(MP_UNITS_API_CONTRACTS STREQUAL "MS-GSL")
target_compile_definitions(test_package PRIVATE MP_UNITS_API_CONTRACTS=3)
endif()