Merge branch 'master' into chiphogg/docs

This commit is contained in:
Chip Hogg
2022-08-04 18:29:44 +00:00
74 changed files with 821 additions and 1423 deletions

View File

@@ -41,14 +41,12 @@ jobs:
- { - {
name: "Windows MSVC 14.2", name: "Windows MSVC 14.2",
os: windows-2019, os: windows-2019,
compiler: compiler: { type: VISUAL, version: 16, cc: "", cxx: "", std: 20 },
{ type: VISUAL, version: 16, std: 20, cc: "cl", cxx: "cl" },
} }
- { - {
name: "Windows MSVC 14.3", name: "Windows MSVC 14.3",
os: windows-2022, os: windows-2022,
compiler: compiler: { type: MSVC, version: 193, cc: "", cxx: "", std: 23 },
{ type: MSVC, version: 193, std: 23, cc: "cl", cxx: "cl" },
} }
- { - {
name: "Ubuntu GCC-10", name: "Ubuntu GCC-10",
@@ -140,17 +138,26 @@ jobs:
std: 20, std: 20,
}, },
} }
# In case a Conan docker image will be needed to provide a specific configuration we can use a Docker image as follows
# - {
# name: "Ubuntu GCC 10.2.0",
# os: ubuntu-20.04,
# compiler: { type: GCC, version: 10, cc: "gcc-10", cxx: "g++-10" },
# docker_image: conanio/gcc10
# }
build_type: ["Release", "Debug"] build_type: ["Release", "Debug"]
downcast_mode: ["on", "auto"] downcast_mode: ["on", "auto"]
env:
CC: ${{ matrix.config.compiler.cc }}
CXX: ${{ matrix.config.compiler.cxx }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Cache Conan data
uses: actions/cache@v2
env:
cache-name: cache-conan-data
with:
path: ~/.conan/data
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/metadata.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- uses: hendrikmuhs/ccache-action@v1 - uses: hendrikmuhs/ccache-action@v1
if: runner.os == 'Linux' if: runner.os == 'Linux'
with: with:
@@ -180,8 +187,6 @@ jobs:
run: | run: |
sudo xcode-select -s "/Applications/Xcode_13.0.app" sudo xcode-select -s "/Applications/Xcode_13.0.app"
- name: Install Ninja - name: Install Ninja
# TODO Find a proper syntax to make the below work
# if: !matrix.config.docker_image
shell: bash shell: bash
run: | run: |
if [ $RUNNER_OS == 'Linux' ]; then if [ $RUNNER_OS == 'Linux' ]; then
@@ -199,20 +204,21 @@ jobs:
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:
python-version: "3.8" python-version: "3.8"
- name: Install Conan Package Tools - name: Install Conan
shell: bash
run: | run: |
pip install -U conan_package_tools pip install -U conan
- name: Configure Conan - name: Configure Conan
# TODO Find a proper syntax to make the below work
# if: !matrix.config.docker_image
shell: bash shell: bash
run: | run: |
conan config init conan config init
conan remote add artifactory https://mpusz.jfrog.io/artifactory/api/conan/conan-oss
if [[ "${{ matrix.config.compiler.type }}" == "GCC" || "${{ matrix.config.compiler.type }}" == "CLANG" ]]; then if [[ "${{ matrix.config.compiler.type }}" == "GCC" || "${{ matrix.config.compiler.type }}" == "CLANG" ]]; then
conan profile update settings.compiler.libcxx=${{ matrix.config.lib }} default conan profile update settings.compiler.libcxx=${{ matrix.config.lib }} default
fi fi
conan profile update settings.compiler.cppstd=${{ matrix.config.compiler.std }} default conan profile update settings.compiler.cppstd=${{ matrix.config.compiler.std }} default
conan profile update conf.tools.cmake.cmaketoolchain:generator=Ninja default conan profile update settings.build_type=${{ matrix.build_type }} default
conan profile update conf.tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" default
conan profile show default conan profile show default
# - name: Add support for clang-13 to Conan's settings.yml # - name: Add support for clang-13 to Conan's settings.yml
# # TODO Remove when Conan will support clang-13 # # TODO Remove when Conan will support clang-13
@@ -220,21 +226,22 @@ jobs:
# shell: bash # shell: bash
# run: | # run: |
# sed -i -e 's/"8", "9", "10", "11", "12"]/"8", "9", "10", "11", "12", "13"]/' ~/.conan/settings.yml # sed -i -e 's/"8", "9", "10", "11", "12"]/"8", "9", "10", "11", "12", "13"]/' ~/.conan/settings.yml
- name: Run Conan Package Tools - name: Set channel
shell: bash shell: bash
env:
CONAN_USERNAME: mpusz
CONAN_OPTIONS: mp-units:build_docs=False,mp-units:downcast_mode=${{ matrix.downcast_mode }}
CONAN_UPLOAD: https://mpusz.jfrog.io/artifactory/api/conan/conan-oss
CONAN_LOGIN_USERNAME: ${{ secrets.CONAN_LOGIN_USERNAME }}
CONAN_PASSWORD: ${{ secrets.CONAN_PASSWORD }}
CONAN_CMAKE_GENERATOR: Ninja
CONAN_BUILD_TYPES: ${{ matrix.build_type }}
CC: ${{ matrix.config.compiler.cc }}
CXX: ${{ matrix.config.compiler.cxx }}
CONAN_${{ matrix.config.compiler.type }}_VERSIONS: ${{ matrix.config.compiler.version }}
run: | run: |
if [ ! -z "${{ matrix.config.docker_image }}" ]; then [[ `git tag --contains ${{ github.sha }}` =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] && CHANNEL=stable || CHANNEL=testing
export CONAN_DOCKER_IMAGE=${{ matrix.config.docker_image }} echo "CHANNEL=${CHANNEL}" >> ${GITHUB_ENV}
fi - name: Create Conan package
python build.py shell: bash
run: |
conan create . mpusz/testing -o mp-units:downcast_mode=${{ matrix.downcast_mode }} -c user.build:all=True -c user.build:skip_docs=True -b mp-units -b outdated -u
- name: Upload mp-units Conan package
if: github.ref == 'refs/heads/master' || env.CHANNEL == 'stable'
shell: bash
run: |
conan user ${{ secrets.CONAN_LOGIN_USERNAME }} -r artifactory -p ${{ secrets.CONAN_PASSWORD }}
conan upload "mp-units*" --all -r artifactory --confirm
- name: Remove mp-units package from Conan local cache
shell: bash
run: |
conan remove mp-units -f

View File

@@ -43,48 +43,91 @@ jobs:
matrix: matrix:
config: config:
- { - {
name: "Windows MSVC 2019", name: "Windows MSVC 14.2",
os: windows-latest, os: windows-2019,
compiler: { type: VISUAL, version: 16, cc: "", cxx: "" }, compiler: { type: VISUAL, version: 16, cc: "", cxx: "", std: 20 },
} }
- { - {
name: "Ubuntu GCC 10.3.0", name: "Windows MSVC 14.3",
os: ubuntu-20.04, os: windows-2022,
compiler: { type: GCC, version: 10, cc: "gcc-10", cxx: "g++-10" }, compiler: { type: MSVC, version: 193, cc: "", cxx: "", std: 23 },
lib: "libstdc++11",
} }
- { - {
name: "Ubuntu GCC 11.1.0", name: "Ubuntu GCC-10",
os: ubuntu-20.04,
compiler: { type: GCC, version: 11, cc: "gcc-11", cxx: "g++-11" },
lib: "libstdc++11",
}
- {
name: "Ubuntu Clang 12.0.0 + libstdc++11",
os: ubuntu-20.04, os: ubuntu-20.04,
compiler: compiler:
{ type: CLANG, version: 12, cc: "clang-12", cxx: "clang++-12" }, {
type: GCC,
version: 10,
cc: "gcc-10",
cxx: "g++-10",
std: 20,
},
lib: "libstdc++11", lib: "libstdc++11",
} }
- { - {
name: "Ubuntu Clang 12.0.0 + libc++", name: "Ubuntu GCC-11",
os: ubuntu-20.04, os: ubuntu-20.04,
compiler: compiler:
{ type: CLANG, version: 12, cc: "clang-12", cxx: "clang++-12" }, {
type: GCC,
version: 11,
cc: "gcc-11",
cxx: "g++-11",
std: 20,
},
lib: "libstdc++11",
}
- {
name: "Ubuntu Clang-12 + libstdc++11",
os: ubuntu-20.04,
compiler:
{
type: CLANG,
version: 12,
cc: "clang-12",
cxx: "clang++-12",
std: 20,
},
lib: "libstdc++11",
}
- {
name: "Ubuntu Clang-12 + libc++",
os: ubuntu-20.04,
compiler:
{
type: CLANG,
version: 12,
cc: "clang-12",
cxx: "clang++-12",
std: 20,
},
lib: "libc++", lib: "libc++",
} }
- { - {
name: "Ubuntu Clang 13.0.0 + libc++", name: "Ubuntu Clang-13 + libc++",
os: ubuntu-20.04, os: ubuntu-20.04,
compiler: compiler:
{ type: CLANG, version: 13, cc: "clang-13", cxx: "clang++-13" }, {
type: CLANG,
version: 13,
cc: "clang-13",
cxx: "clang++-13",
std: 20,
},
lib: "libc++", lib: "libc++",
} }
- { - {
name: "Ubuntu Clang 14.0.0 + libc++", name: "Ubuntu Clang-14 + libc++",
os: ubuntu-20.04, os: ubuntu-20.04,
compiler: compiler:
{ type: CLANG, version: 14, cc: "clang-14", cxx: "clang++-14" }, {
type: CLANG,
version: 14,
cc: "clang-14",
cxx: "clang++-14",
std: 20,
},
lib: "libc++", lib: "libc++",
} }
- { - {
@@ -96,23 +139,21 @@ jobs:
version: "13.0", version: "13.0",
cc: "clang", cc: "clang",
cxx: "clang++", cxx: "clang++",
std: 20,
}, },
} }
# In case a Conan docker image will be needed to provide a specific configuration we can use a Docker image as follows
# - {
# name: "Ubuntu GCC 10.2.0",
# os: ubuntu-20.04,
# compiler: { type: GCC, version: 10, cc: "gcc-10", cxx: "g++-10" },
# docker_image: conanio/gcc10
# }
build_type: ["Release", "Debug"] build_type: ["Release", "Debug"]
env: env:
CC: ${{ matrix.config.compiler.cc }} CC: ${{ matrix.config.compiler.cc }}
CXX: ${{ matrix.config.compiler.cxx }} CXX: ${{ matrix.config.compiler.cxx }}
CMAKE_GENERATOR: Ninja
steps: steps:
- name: Downcase 'build_type'
id: build_type
uses: ASzc/change-string-case-action@v2
with:
string: ${{ matrix.build_type }}
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Cache Conan data - name: Cache Conan data
uses: actions/cache@v2 uses: actions/cache@v2
@@ -149,8 +190,6 @@ jobs:
run: | run: |
sudo xcode-select -s "/Applications/Xcode_13.0.app" sudo xcode-select -s "/Applications/Xcode_13.0.app"
- name: Install Ninja - name: Install Ninja
# TODO Find a proper syntax to make the below work
# if: !matrix.config.docker_image
shell: bash shell: bash
run: | run: |
if [ $RUNNER_OS == 'Linux' ]; then if [ $RUNNER_OS == 'Linux' ]; then
@@ -176,13 +215,13 @@ jobs:
shell: bash shell: bash
run: | run: |
conan config init conan config init
conan remote add upload https://mpusz.jfrog.io/artifactory/api/conan/conan-oss conan remote add artifactory https://mpusz.jfrog.io/artifactory/api/conan/conan-oss
conan profile update settings.build_type=${{ matrix.build_type }} default
conan profile update settings.compiler.cppstd=20 default
if [[ "${{ matrix.config.compiler.type }}" == "GCC" || "${{ matrix.config.compiler.type }}" == "CLANG" ]]; then if [[ "${{ matrix.config.compiler.type }}" == "GCC" || "${{ matrix.config.compiler.type }}" == "CLANG" ]]; then
conan profile update settings.compiler.libcxx=${{ matrix.config.lib }} default conan profile update settings.compiler.libcxx=${{ matrix.config.lib }} default
fi fi
conan profile update conf.tools.cmake.cmaketoolchain:generator=Ninja default conan profile update settings.compiler.cppstd=${{ matrix.config.compiler.std }} default
conan profile update settings.build_type=${{ matrix.build_type }} default
conan profile update conf.tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" default
conan profile show default conan profile show default
# - name: Add support for clang-13 to Conan's settings.yml # - name: Add support for clang-13 to Conan's settings.yml
# # TODO Remove when Conan will support clang-13 # # TODO Remove when Conan will support clang-13
@@ -193,63 +232,70 @@ jobs:
- name: Install Conan dependencies - name: Install Conan dependencies
shell: bash shell: bash
run: | run: |
mkdir -p build/${{ matrix.build_type }} && cd build/${{ matrix.build_type }} conan install . -b outdated -u
conan install ../.. -b outdated -u mv CMakeUserPresets.json src
- name: Configure mp-units CMake - name: Configure mp-units CMake
if: matrix.config.compiler.type == 'VISUAL' if: matrix.config.compiler.type == 'VISUAL' || matrix.config.compiler.type == 'MSVC'
shell: cmd shell: cmd
working-directory: build/${{ matrix.build_type }} working-directory: src
run: | run: |
call conanvcvars.bat cmake --version
cmake ../../src -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} call ..\build\generators\conanvcvars.bat
cmake --preset default -DCMAKE_INSTALL_PREFIX=../out
- name: Configure mp-units CMake - name: Configure mp-units CMake
if: matrix.config.compiler.type != 'VISUAL' if: matrix.config.compiler.type != 'VISUAL' && matrix.config.compiler.type != 'MSVC'
shell: bash shell: bash
working-directory: build/${{ matrix.build_type }} working-directory: src
run: | run: |
cmake ../../src -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} cmake --version
cmake --preset default -DCMAKE_INSTALL_PREFIX=../out
- name: Install mp-units - name: Install mp-units
shell: bash shell: bash
working-directory: build/${{ matrix.build_type }} working-directory: src
run: | run: |
cmake --install . --prefix test_package cmake --build --preset ${{ steps.build_type.outputs.lowercase }} --target install
- name: Install dependencies for test_package - name: Provide dependencies for test_package
shell: bash shell: bash
working-directory: test_package
run: | run: |
mkdir -p test_package/build/${{ matrix.build_type }} && cd test_package/build/${{ matrix.build_type }} cp ../src/CMakeUserPresets.json .
conan install ../../..
- name: Build test_package CMake (local build) - name: Build test_package CMake (local build)
if: matrix.config.compiler.type == 'VISUAL' if: matrix.config.compiler.type == 'VISUAL' || matrix.config.compiler.type == 'MSVC'
shell: cmd shell: cmd
working-directory: test_package/build/${{ matrix.build_type }} working-directory: test_package
run: | run: |
call conanvcvars.bat call ..\build\generators\conanvcvars.bat
cmake ../.. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -Dmp-units_DIR=${{ github.workspace }}/build/${{ matrix.build_type }} cmake --preset default -Dmp-units_DIR=../build -Bbuild/local
cmake --build . cmake --build build/local --config ${{ matrix.build_type }}
- name: Build test_package CMake (local build) - name: Build test_package CMake (local build)
if: matrix.config.compiler.type != 'VISUAL' if: matrix.config.compiler.type != 'VISUAL' && matrix.config.compiler.type != 'MSVC'
shell: bash shell: bash
working-directory: test_package/build/${{ matrix.build_type }} working-directory: test_package
run: | run: |
cmake ../.. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -Dmp-units_DIR=${{ github.workspace }}/build/${{ matrix.build_type }} cmake --preset default -Dmp-units_DIR=../build -Bbuild/local
cmake --build . cmake --build build/local --config ${{ matrix.build_type }}
- name: Build test_package CMake (installation) - name: Run test_package (local build)
if: matrix.config.compiler.type == 'VISUAL'
shell: cmd
working-directory: test_package/build/${{ matrix.build_type }}
run: |
call conanvcvars.bat
cmake ../.. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_INSTALL_PREFIX=../../../build/${{ matrix.build_type }}/test_package
cmake --build .
- name: Build test_package CMake (installation)
if: matrix.config.compiler.type != 'VISUAL'
shell: bash shell: bash
working-directory: test_package/build/${{ matrix.build_type }} working-directory: test_package/build/local/${{ matrix.build_type }}
run: | run: |
cmake ../.. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_INSTALL_PREFIX=../../../build/${{ matrix.build_type }}/test_package ./test_package
cmake --build . - name: Build test_package CMake (installation)
- name: Run test_package if: matrix.config.compiler.type == 'VISUAL' || matrix.config.compiler.type == 'MSVC'
shell: bash shell: cmd
working-directory: test_package/build/${{ matrix.build_type }} working-directory: test_package
run: |
call ..\build\generators\conanvcvars.bat
cmake --preset default -DCMAKE_INSTALL_PREFIX=../out -Bbuild/install
cmake --build build/install --config ${{ matrix.build_type }}
- name: Build test_package CMake (installation)
if: matrix.config.compiler.type != 'VISUAL' && matrix.config.compiler.type != 'MSVC'
shell: bash
working-directory: test_package
run: |
cmake --preset default -DCMAKE_INSTALL_PREFIX=../out -Bbuild/install
cmake --build build/install --config ${{ matrix.build_type }}
- name: Run test_package (installation)
shell: bash
working-directory: test_package/build/install/${{ matrix.build_type }}
run: | run: |
./test_package ./test_package

View File

@@ -14,13 +14,13 @@ name: "CodeQL"
on: on:
push: push:
branches: [ master ] branches: [master]
paths-ignore: paths-ignore:
- 'docs/**' - "docs/**"
pull_request: pull_request:
branches: [ master ] branches: [master]
paths-ignore: paths-ignore:
- 'docs/**' - "docs/**"
jobs: jobs:
analyze: analyze:
@@ -33,7 +33,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
language: [ 'cpp', 'python' ] language: ["cpp", "python"]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more... # Learn more...
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
@@ -44,7 +44,7 @@ jobs:
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v1 uses: github/codeql-action/init@v2
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file. # If you wish to specify custom queries, you can do so here or in a config file.
@@ -56,7 +56,7 @@ jobs:
# If this step fails, then you should remove it and run the build manually (see below) # If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild - name: Autobuild
if: matrix.language != 'cpp' if: matrix.language != 'cpp'
uses: github/codeql-action/autobuild@v1 uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell. # Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl # 📚 https://git.io/JvXDl
@@ -77,7 +77,7 @@ jobs:
if: matrix.language == 'cpp' if: matrix.language == 'cpp'
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:
python-version: '3.8' python-version: "3.8"
- name: Conan build - name: Conan build
if: matrix.language == 'cpp' if: matrix.language == 'cpp'
run: | run: |
@@ -85,7 +85,7 @@ jobs:
conan config init conan config init
conan remote add upload https://mpusz.jfrog.io/artifactory/api/conan/conan-oss conan remote add upload https://mpusz.jfrog.io/artifactory/api/conan/conan-oss
mkdir _lgtm_build_dir && cd _lgtm_build_dir mkdir _lgtm_build_dir && cd _lgtm_build_dir
conan install .. -s compiler.cppstd=20 -s compiler.libcxx=libstdc++11 -o mp-units:build_docs=False -e mp-units:CONAN_RUN_TESTS=True -b outdated -u conan install .. -s compiler.cppstd=20 -s compiler.libcxx=libstdc++11 -c mp-units:user.build:all=True -c user.build:skip_docs=True -b outdated -u
conan build .. conan build ..
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1 uses: github/codeql-action/analyze@v1

View File

@@ -78,16 +78,13 @@ jobs:
conan remote add -i 0 upload https://mpusz.jfrog.io/artifactory/api/conan/conan-oss conan remote add -i 0 upload https://mpusz.jfrog.io/artifactory/api/conan/conan-oss
- name: Install Conan dependencies - name: Install Conan dependencies
run: | run: |
mkdir build && cd build conan install . -s compiler.cppstd=20 -s compiler.libcxx=libstdc++11 -c user.build:all=True -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" -b outdated -u
conan install .. -s compiler.cppstd=20 -s compiler.libcxx=libstdc++11 -e mp-units:CONAN_RUN_TESTS=True -b outdated -u
- name: Configure CMake - name: Configure CMake
working-directory: build
run: | run: |
cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release cmake --preset default
- name: Generate documentation - name: Generate documentation
working-directory: build
run: | run: |
cmake --build . --target documentation --config Release cmake --build --preset release --target documentation
- name: Deploy documentation - name: Deploy documentation
if: github.ref == 'refs/heads/master' if: github.ref == 'refs/heads/master'
uses: peaceiris/actions-gh-pages@v3 uses: peaceiris/actions-gh-pages@v3

8
.gitignore vendored
View File

@@ -46,3 +46,11 @@ CMakeUserPresets.json
# Conan # Conan
*.pyc *.pyc
/test_package/build/ /test_package/build/
# Conan internal files
# TODO Remove for Conan 2.0
conan_paths.cmake
conan.lock
conanbuildinfo.txt
conaninfo.txt
graph_info.json

View File

@@ -1,46 +0,0 @@
# The MIT License (MIT)
#
# Copyright (c) 2018 Mateusz Pusz
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# The following environment variables are required
# - CONAN_USERNAME
# - CONAN_UPLOAD
# - CONAN_LOGIN_USERNAME
# - CONAN_PASSWORD
from cpt.packager import ConanMultiPackager
if __name__ == "__main__":
builder = ConanMultiPackager(
# package id
channel="testing",
stable_branch_pattern=r"v\d+\.\d+\.\d+.*",
# dependencies
build_policy=["mp-units", "outdated"],
upload_dependencies="all",
pip_install=["sphinx", "recommonmark", "breathe"],
# build configurations
archs=["x86_64"], # limit to 64-bit only
)
builder.add_common_builds(pure_c=True)
for settings, options, env_vars, build_requires, reference in builder.items:
env_vars["mp-units:CONAN_RUN_TESTS"] = "True"
builder.run()

View File

@@ -22,8 +22,8 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
find_package(Doxygen MODULE REQUIRED # TODO Switch to CONFIG when Conan will start supporting imported executables in CMakeDeps
)# TODO Switch to CONFIG when Conan will start supporting imported executables in CMakeDeps find_package(Doxygen MODULE REQUIRED)
find_package(Sphinx REQUIRED) find_package(Sphinx REQUIRED)
# #

View File

@@ -24,15 +24,13 @@ import os
import re import re
from conan import ConanFile from conan import ConanFile
from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain from conan.tools.build import check_min_cppstd
from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout
from conan.tools.files import copy, load, rmdir from conan.tools.files import copy, load, rmdir
from conan.tools.scm import Version from conan.tools.scm import Version
from conans.errors import ConanInvalidConfiguration from conans.errors import ConanInvalidConfiguration
# TODO replace with new tools for Conan 2.0 required_conan_version = ">=1.50.0"
from conans.tools import check_min_cppstd, get_env
required_conan_version = ">=1.48.0"
class MPUnitsConan(ConanFile): class MPUnitsConan(ConanFile):
@@ -56,8 +54,8 @@ class MPUnitsConan(ConanFile):
url = "https://github.com/mpusz/units" url = "https://github.com/mpusz/units"
settings = "os", "compiler", "build_type", "arch" settings = "os", "compiler", "build_type", "arch"
requires = "gsl-lite/0.40.0" requires = "gsl-lite/0.40.0"
options = {"downcast_mode": ["off", "on", "auto"], "build_docs": [True, False]} options = {"downcast_mode": ["off", "on", "auto"]}
default_options = {"downcast_mode": "on", "build_docs": True} default_options = {"downcast_mode": "on"}
exports = ["LICENSE.md"] exports = ["LICENSE.md"]
exports_sources = [ exports_sources = [
"docs/*", "docs/*",
@@ -71,8 +69,12 @@ class MPUnitsConan(ConanFile):
generators = "cmake_paths" generators = "cmake_paths"
@property @property
def _run_tests(self): def _build_all(self):
return get_env("CONAN_RUN_TESTS", False) return bool(self.conf["user.build:all"])
@property
def _skip_docs(self):
return bool(self.conf["user.build:skip_docs"])
@property @property
def _use_libfmt(self): def _use_libfmt(self):
@@ -112,10 +114,10 @@ class MPUnitsConan(ConanFile):
self.requires("range-v3/0.11.0") self.requires("range-v3/0.11.0")
def build_requirements(self): def build_requirements(self):
if self._run_tests: if self._build_all:
self.test_requires("catch2/2.13.9") self.test_requires("catch2/2.13.9")
self.test_requires("wg21-linear_algebra/0.7.2") self.test_requires("wg21-linear_algebra/0.7.2")
if self.options.build_docs: if not self._skip_docs:
self.tool_requires("doxygen/1.9.4") self.tool_requires("doxygen/1.9.4")
# TODO Replace with `valdate()` for Conan 2.0 (https://github.com/conan-io/conan/issues/10723) # TODO Replace with `valdate()` for Conan 2.0 (https://github.com/conan-io/conan/issues/10723)
@@ -145,17 +147,13 @@ class MPUnitsConan(ConanFile):
raise ConanInvalidConfiguration("Unsupported compiler") raise ConanInvalidConfiguration("Unsupported compiler")
check_min_cppstd(self, 20) check_min_cppstd(self, 20)
# TODO Uncomment this when environment is supported in the Conan toolchain def layout(self):
# def config_options(self): cmake_layout(self)
# if not self._run_tests:
# # build_docs has sense only in a development or CI build
# del self.options.build_docs
def generate(self): def generate(self):
tc = CMakeToolchain(self) tc = CMakeToolchain(self)
tc.variables["UNITS_DOWNCAST_MODE"] = str(self.options.downcast_mode).upper() tc.variables["UNITS_DOWNCAST_MODE"] = str(self.options.downcast_mode).upper()
# if self._run_tests: # TODO Enable this when environment is supported in the Conan toolchain tc.variables["UNITS_BUILD_DOCS"] = self._build_all and not self._skip_docs
tc.variables["UNITS_BUILD_DOCS"] = bool(self.options.build_docs)
tc.variables["UNITS_USE_LIBFMT"] = self._use_libfmt tc.variables["UNITS_USE_LIBFMT"] = self._use_libfmt
tc.generate() tc.generate()
deps = CMakeDeps(self) deps = CMakeDeps(self)
@@ -163,13 +161,13 @@ class MPUnitsConan(ConanFile):
def build(self): def build(self):
cmake = CMake(self) cmake = CMake(self)
cmake.configure(build_script_folder=None if self._run_tests else "src") cmake.configure(build_script_folder=None if self._build_all else "src")
cmake.build() cmake.build()
if self._run_tests: if self._build_all:
cmake.test() cmake.test()
def package_id(self): def package_id(self):
self.info.header_only() self.info.clear()
def package(self): def package(self):
copy( copy(
@@ -187,7 +185,6 @@ class MPUnitsConan(ConanFile):
# core # core
self.cpp_info.components["core"].requires = ["gsl-lite::gsl-lite"] self.cpp_info.components["core"].requires = ["gsl-lite::gsl-lite"]
self.cpp_info.components["core"].includedirs = ["include"]
if compiler == "Visual Studio": if compiler == "Visual Studio":
self.cpp_info.components["core"].cxxflags = ["/utf-8"] self.cpp_info.components["core"].cxxflags = ["/utf-8"]
if self._use_range_v3: if self._use_range_v3:
@@ -195,49 +192,21 @@ class MPUnitsConan(ConanFile):
# rest # rest
self.cpp_info.components["core-io"].requires = ["core"] self.cpp_info.components["core-io"].requires = ["core"]
self.cpp_info.components["core-io"].includedirs = ["include"]
self.cpp_info.components["core-fmt"].requires = ["core"] self.cpp_info.components["core-fmt"].requires = ["core"]
self.cpp_info.components["core-fmt"].includedirs = ["include"]
if self._use_libfmt: if self._use_libfmt:
self.cpp_info.components["core-fmt"].requires.append("fmt::fmt") self.cpp_info.components["core-fmt"].requires.append("fmt::fmt")
self.cpp_info.components["isq"].requires = ["core"] self.cpp_info.components["isq"].requires = ["core"]
self.cpp_info.components["isq"].includedirs = ["include"]
self.cpp_info.components["isq-natural"].requires = ["isq"] self.cpp_info.components["isq-natural"].requires = ["isq"]
self.cpp_info.components["isq-natural"].includedirs = ["include"]
self.cpp_info.components["si"].requires = ["isq"] self.cpp_info.components["si"].requires = ["isq"]
self.cpp_info.components["si"].includedirs = ["include"]
self.cpp_info.components["si-cgs"].requires = ["si"] self.cpp_info.components["si-cgs"].requires = ["si"]
self.cpp_info.components["si-cgs"].includedirs = ["include"]
self.cpp_info.components["si-fps"].requires = ["si-international"] self.cpp_info.components["si-fps"].requires = ["si-international"]
self.cpp_info.components["si-fps"].includedirs = ["include"]
self.cpp_info.components["si-hep"].requires = ["si"] self.cpp_info.components["si-hep"].requires = ["si"]
self.cpp_info.components["si-hep"].includedirs = ["include"]
self.cpp_info.components["si-iau"].requires = ["si"] self.cpp_info.components["si-iau"].requires = ["si"]
self.cpp_info.components["si-iau"].includedirs = ["include"]
self.cpp_info.components["si-imperial"].requires = ["si"] self.cpp_info.components["si-imperial"].requires = ["si"]
self.cpp_info.components["si-imperial"].includedirs = ["include"]
self.cpp_info.components["si-international"].requires = ["si"] self.cpp_info.components["si-international"].requires = ["si"]
self.cpp_info.components["si-international"].includedirs = ["include"]
self.cpp_info.components["si-typographic"].requires = ["si"] self.cpp_info.components["si-typographic"].requires = ["si"]
self.cpp_info.components["si-typographic"].includedirs = ["include"]
self.cpp_info.components["si-uscs"].requires = ["si"] self.cpp_info.components["si-uscs"].requires = ["si"]
self.cpp_info.components["si-uscs"].includedirs = ["include"]
self.cpp_info.components["isq-iec80000"].requires = ["si"] self.cpp_info.components["isq-iec80000"].requires = ["si"]
self.cpp_info.components["isq-iec80000"].includedirs = ["include"]
self.cpp_info.components["systems"].requires = [ self.cpp_info.components["systems"].requires = [
"isq", "isq",
"isq-natural", "isq-natural",

View File

@@ -1,488 +1,11 @@
# -*- coding: utf-8 -*-
"""
pygments.lexers.c_cpp
~~~~~~~~~~~~~~~~~~~~~
Lexers for C/C++ languages.
:copyright: Copyright 2006-2019 by the Pygments team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import os
import re
import subprocess
from pygments.lexer import (
RegexLexer,
bygroups,
default,
include,
inherit,
this,
using,
words,
)
from pygments.token import (
Comment,
Error,
Keyword,
Name,
Number,
Operator,
Punctuation,
String,
Text,
)
from pygments.util import get_bool_opt
from sphinx.highlighting import lexers
class MyCFamilyLexer(RegexLexer):
#: optional Comment or Whitespace
_ws = r"(?:\s|//.*?\n|/[*].*?[*]/)+"
# The trailing ?, rather than *, avoids a geometric performance drop here.
#: only one /* */ style comment
_ws1 = r"\s*(?:/[*].*?[*]/\s*)?"
tokens = {
"whitespace": [
# preprocessor directives: without whitespace
(r"^#if\s+0", Comment.Preproc, "if0"),
("^#", Comment.Preproc, "macro"),
# or with whitespace
(
"^(" + _ws1 + r")(#if\s+0)",
bygroups(using(this), Comment.Preproc),
"if0",
),
("^(" + _ws1 + ")(#)", bygroups(using(this), Comment.Preproc), "macro"),
(r"\n", Text),
(r"\s+", Text),
(r"\\\n", Text), # line continuation
(r"//(\n|[\w\W]*?[^\\]\n)", Comment.Single),
(r"/(\\\n)?[*][\w\W]*?[*](\\\n)?/", Comment.Multiline),
# Open until EOF, so no ending delimeter
(r"/(\\\n)?[*][\w\W]*", Comment.Multiline),
],
"statements": [
(r'(L?)(")', bygroups(String.Affix, String), "string"),
(
r"(L?)(')(\\.|\\[0-7]{1,3}|\\x[a-fA-F0-9]{1,2}|[^\\\'\n])(')",
bygroups(String.Affix, String.Char, String.Char, String.Char),
),
(r"\*/", Error),
(r"[~!%^&*+=|?:<>/-]", Operator),
(r"[()\[\],.]", Punctuation),
(
words(
(
"asm",
"auto",
"break",
"case",
"const",
"continue",
"default",
"do",
"else",
"enum",
"extern",
"for",
"goto",
"if",
"register",
"restricted",
"return",
"sizeof",
"static",
"struct",
"switch",
"typedef",
"union",
"volatile",
"while",
),
suffix=r"\b",
),
Keyword,
),
(
r"(bool|int|long|float|short|double|char|unsigned|signed|void)\b",
Keyword.Type,
),
(
words(
(
"inline",
"_inline",
"__inline",
"naked",
"restrict",
"thread",
"typename",
),
suffix=r"\b",
),
Keyword.Reserved,
),
# Vector intrinsics
(r"(__m(128i|128d|128|64))\b", Keyword.Reserved),
# Microsoft-isms
(
words(
(
"asm",
"int8",
"based",
"except",
"int16",
"stdcall",
"cdecl",
"fastcall",
"int32",
"declspec",
"finally",
"int64",
"try",
"leave",
"wchar_t",
"w64",
"unaligned",
"raise",
"noop",
"identifier",
"forceinline",
"assume",
),
prefix=r"__",
suffix=r"\b",
),
Keyword.Reserved,
),
(r"(true|false|NULL)\b", Name.Builtin),
(r"([a-zA-Z_]\w*)(\s*)(:)(?!:)", bygroups(Name.Label, Text, Punctuation)),
(r"[a-zA-Z_]\w*", Name),
],
"root": [
include("whitespace"),
# functions
(
r"((?:[\w*\s])+?(?:\s|[*]))" # return arguments
r"([a-zA-Z_]\w*)" # method name
r"(\s*\([^;]*?\))" # signature
r"([^;{]*)(\{)",
bygroups(
using(this), Name.Function, using(this), using(this), Punctuation
),
"function",
),
# function declarations
(
r"((?:[\w*\s])+?(?:\s|[*]))" # return arguments
r"([a-zA-Z_]\w*)" # method name
r"(\s*\([^;]*?\))" # signature
r"([^;]*)(;)",
bygroups(
using(this), Name.Function, using(this), using(this), Punctuation
),
),
default("statement"),
],
"statement": [
include("whitespace"),
include("statements"),
("[{}]", Punctuation),
(";", Punctuation, "#pop"),
],
"function": [
include("whitespace"),
include("statements"),
(";", Punctuation),
(r"\{", Punctuation, "#push"),
(r"\}", Punctuation, "#pop"),
],
"string": [
(r'"', String, "#pop"),
(
r'\\([\\abfnrtv"\']|x[a-fA-F0-9]{2,4}|'
r"u[a-fA-F0-9]{4}|U[a-fA-F0-9]{8}|[0-7]{1,3})",
String.Escape,
),
(r'[^\\"\n]+', String), # all other characters
(r"\\\n", String), # line continuation
(r"\\", String), # stray backslash
],
"macro": [
(
r"(include)(" + _ws1 + r")([^\n]+)",
bygroups(Comment.Preproc, Text, Comment.PreprocFile),
),
(r"[^/\n]+", Comment.Preproc),
(r"/[*](.|\n)*?[*]/", Comment.Multiline),
(r"//.*?\n", Comment.Single, "#pop"),
(r"/", Comment.Preproc),
(r"(?<=\\)\n", Comment.Preproc),
(r"\n", Comment.Preproc, "#pop"),
],
"if0": [
(r"^\s*#if.*?(?<!\\)\n", Comment.Preproc, "#push"),
(r"^\s*#el(?:se|if).*\n", Comment.Preproc, "#pop"),
(r"^\s*#endif.*?(?<!\\)\n", Comment.Preproc, "#pop"),
(r".*?\n", Comment),
],
}
stdlib_types = {
"size_t",
"ssize_t",
"off_t",
"wchar_t",
"ptrdiff_t",
"sig_atomic_t",
"fpos_t",
"clock_t",
"time_t",
"va_list",
"jmp_buf",
"FILE",
"DIR",
"div_t",
"ldiv_t",
"mbstate_t",
"wctrans_t",
"wint_t",
"wctype_t",
}
c99_types = {
"_Bool",
"_Complex",
"int8_t",
"int16_t",
"int32_t",
"int64_t",
"uint8_t",
"uint16_t",
"uint32_t",
"uint64_t",
"int_least8_t",
"int_least16_t",
"int_least32_t",
"int_least64_t",
"uint_least8_t",
"uint_least16_t",
"uint_least32_t",
"uint_least64_t",
"int_fast8_t",
"int_fast16_t",
"int_fast32_t",
"int_fast64_t",
"uint_fast8_t",
"uint_fast16_t",
"uint_fast32_t",
"uint_fast64_t",
"intptr_t",
"uintptr_t",
"intmax_t",
"uintmax_t",
}
linux_types = {
"clockid_t",
"cpu_set_t",
"cpumask_t",
"dev_t",
"gid_t",
"id_t",
"ino_t",
"key_t",
"mode_t",
"nfds_t",
"pid_t",
"rlim_t",
"sig_t",
"sighandler_t",
"siginfo_t",
"sigset_t",
"sigval_t",
"socklen_t",
"timer_t",
"uid_t",
}
def __init__(self, **options):
self.stdlibhighlighting = get_bool_opt(options, "stdlibhighlighting", True)
self.c99highlighting = get_bool_opt(options, "c99highlighting", True)
self.platformhighlighting = get_bool_opt(options, "platformhighlighting", True)
RegexLexer.__init__(self, **options)
def get_tokens_unprocessed(self, text):
for index, token, value in RegexLexer.get_tokens_unprocessed(self, text):
if token is Name:
if self.stdlibhighlighting and value in self.stdlib_types:
token = Keyword.Type
elif self.c99highlighting and value in self.c99_types:
token = Keyword.Type
elif self.platformhighlighting and value in self.linux_types:
token = Keyword.Type
yield index, token, value
class MyCppLexer(MyCFamilyLexer):
"""
For C++ source code with preprocessor directives.
"""
name = "My C++"
aliases = ["cpp", "c++"]
filenames = [
"*.cpp",
"*.hpp",
"*.c++",
"*.h++",
"*.cc",
"*.hh",
"*.cxx",
"*.hxx",
"*.C",
"*.H",
"*.cp",
"*.CPP",
]
mimetypes = ["text/x-c++hdr", "text/x-c++src"]
priority = 0.1
tokens = {
"statements": [
(
words(
(
"catch",
"const_cast",
"delete",
"dynamic_cast",
"explicit",
"export",
"friend",
"mutable",
"namespace",
"new",
"operator",
"private",
"protected",
"public",
"reinterpret_cast",
"restrict",
"static_cast",
"template",
"this",
"throw",
"throws",
"try",
"typeid",
"typename",
"using",
"virtual",
"constexpr",
"nullptr",
"decltype",
"thread_local",
"alignas",
"alignof",
"static_assert",
"noexcept",
"override",
"final",
"constinit",
"consteval",
"constinit",
"concept",
"co_await",
"co_return",
"co_yield",
"requires",
"import",
"module",
),
suffix=r"\b",
),
Keyword,
),
(r"char(16_t|32_t|8_t)\b", Keyword.Type),
(r"(class)(\s+)", bygroups(Keyword, Text), "classname"),
# C++11 raw strings
(
r'(R)(")([^\\()\s]{,16})(\()((?:.|\n)*?)(\)\3)(")',
bygroups(
String.Affix,
String,
String.Delimiter,
String.Delimiter,
String,
String.Delimiter,
String,
),
),
# C++11 UTF-8/16/32 strings
(r'(u8|u|U)(")', bygroups(String.Affix, String), "string"),
# C++14 number separators
(
r"(\d[\d\']*\.[\d\']*|\.\d[\d\']*|\d[\d\']*)[eE][+-]?\d[\d\']*[LlUu]*",
Number.Float,
),
(r"(\d[\d\']*\.[\d\']*|\.\d[\d\']*|\d[\d\']*[fF])[fF]?", Number.Float),
(r"0x[0-9a-fA-F\']+[LlUu]*", Number.Hex),
(r"0[0-7\']+[LlUu]*", Number.Oct),
(r"\d[\d\']*[LlUu]*", Number.Integer),
inherit,
],
"root": [
inherit,
# C++ Microsoft-isms
(
words(
(
"virtual_inheritance",
"uuidof",
"super",
"single_inheritance",
"multiple_inheritance",
"interface",
"event",
),
prefix=r"__",
suffix=r"\b",
),
Keyword.Reserved,
),
# Offload C++ extensions, http://offload.codeplay.com/
(r"__(offload|blockingoffload|outer)\b", Keyword.Pseudo),
],
"classname": [
(r"[a-zA-Z_]\w*", Name.Class, "#pop"),
# template specification
(r"\s*(?=>)", Text, "#pop"),
],
}
def analyse_text(text):
if re.search("#include <[a-z_]+>", text):
return 0.2
if re.search("using namespace ", text):
return 0.4
lexers["cpp"] = MyCppLexer(startinline=True)
# TODO Remove the above when Pygments release with fixes is available
# Configuration file for the Sphinx documentation builder. # Configuration file for the Sphinx documentation builder.
# #
# This file only contains a selection of the most common options. # This file only contains a selection of the most common options.
# For a full list see the documentation: # For a full list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html # https://www.sphinx-doc.org/en/master/usage/configuration.html
import re
def get_version(): def get_version():
try: try:
@@ -598,29 +121,9 @@ html_css_files = ["css/custom.css"]
# -- Breathe configuration --------------------------------------------------- # -- Breathe configuration ---------------------------------------------------
def configureDoxyfile(input_dir, output_dir):
with open("Doxyfile.in", "r") as file:
filedata = file.read()
filedata = filedata.replace("@DOXYGEN_INPUT_DIR@", input_dir)
filedata = filedata.replace("@DOXYGEN_OUTPUT_DIR@", output_dir)
with open("Doxyfile", "w") as file:
file.write(filedata)
# Check if we're running on Read the Docs' servers
read_the_docs_build = os.environ.get("READTHEDOCS", None) == "True"
# This should be a dictionary in which the keys are project names and the values # This should be a dictionary in which the keys are project names and the values
# are paths to the folder containing the doxygen output for that project. # are paths to the folder containing the doxygen output for that project.
breathe_projects = {} breathe_projects = {"mp-units": "build/xml"}
if read_the_docs_build:
input_dir = "../src"
output_dir = "build"
configureDoxyfile(input_dir, output_dir)
subprocess.call("doxygen", shell=True)
breathe_projects["mp-units"] = output_dir + "/xml"
# This should match one of the keys in the breathe_projects dictionary and # This should match one of the keys in the breathe_projects dictionary and
# indicates which project should be used when the project is not specified on # indicates which project should be used when the project is not specified on

View File

@@ -188,9 +188,6 @@ and define units like::
struct electronvolt : named_scaled_unit<electronvolt, "eV", prefix, struct electronvolt : named_scaled_unit<electronvolt, "eV", prefix,
ratio(1'602'176'634, 1'000'000'000, -19), joule> {}; ratio(1'602'176'634, 1'000'000'000, -19), joule> {};
..
TODO Submit a bug for above lexing problem
Finally, the last of the `named_scaled_unit` class template parameters Finally, the last of the `named_scaled_unit` class template parameters
provide a reference unit for scaling. Please note that it can be a dimension's provide a reference unit for scaling. Please note that it can be a dimension's
base/coherent unit (like ``si::second``) or any other unit (i.e. ``si::minute``, base/coherent unit (like ``si::second``) or any other unit (i.e. ``si::minute``,

View File

@@ -45,6 +45,11 @@ Concepts
A concept matching all unit types that have an atomic text symbol that can be used to aggregate it with A concept matching all unit types that have an atomic text symbol that can be used to aggregate it with
other named units to form a final symbol of a derived unit. other named units to form a final symbol of a derived unit.
.. concept:: template<typename T> AliasUnit
A concept matching all alias unit types in the library. Satisfied by all unit types derived
from the instantiation of :class:`alias_unit`.
.. concept:: template<typename U, typename D> UnitOf .. concept:: template<typename U, typename D> UnitOf
A concept matching only units of a specified dimension. Satisfied by all unit types that A concept matching only units of a specified dimension. Satisfied by all unit types that
@@ -63,6 +68,11 @@ Concepts
A concept matching all quantity-like types other than specialization of :class:`quantity`. Satisfied by A concept matching all quantity-like types other than specialization of :class:`quantity`. Satisfied by
all types for which a correct specialization of :class:`quantity_like_traits` type trait is provided. all types for which a correct specialization of :class:`quantity_like_traits` type trait is provided.
.. concept:: template<typename T> QuantityPointLike
A concept matching all quantity-point-like types other than specialization of :class:`quantity_point`.
Satisfied by all types for which a correct specialization of :class:`quantity_point_like_traits` type trait is provided.
.. concept:: template<typename T> WrappedQuantity .. concept:: template<typename T> WrappedQuantity
A concept matching types that wrap quantity objects. Satisfied by all wrapper types that A concept matching types that wrap quantity objects. Satisfied by all wrapper types that

View File

@@ -22,8 +22,8 @@ This repository contains three independent CMake-based projects:
- when this library will become part of the C++ standard it will have no external dependencies - when this library will become part of the C++ standard it will have no external dependencies
but until then it depends on: but until then it depends on:
- `{fmt} <https://github.com/fmtlib/fmt>`_ to provide text formatting of quantities.
- `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>`_ to verify runtime contracts with the ``gsl_Expects`` macro.
- [for compilers other than VS2022] `{fmt} <https://github.com/fmtlib/fmt>`_ to provide text formatting of quantities.
- [only for clang < 14 with libc++] `range-v3 <https://github.com/ericniebler/range-v3>`_ to provide needed C++20 concepts and utilities. - [only for clang < 14 with libc++] `range-v3 <https://github.com/ericniebler/range-v3>`_ to provide needed C++20 concepts and utilities.
- *.* - *.*
@@ -36,9 +36,13 @@ This repository contains three independent CMake-based projects:
- `linear algebra <https://github.com/BobSteagall/wg21/tree/master/include>`_ - `linear algebra <https://github.com/BobSteagall/wg21/tree/master/include>`_
library based on proposal `P1385 <https://wg21.link/P1385>`_ used in some examples library based on proposal `P1385 <https://wg21.link/P1385>`_ used in some examples
and tests. and tests.
- in case you also want to build the project's documentation you will need:
- `Doxygen <http://www.doxygen.nl>`_ to extract C++ entities information from the source - `Doxygen <http://www.doxygen.nl>`_ to extract C++ entities information from the source
code. code.
- `Sphinx <https://www.sphinx-doc.org>`_ to build the documentation. - `Sphinx <https://www.sphinx-doc.org>`_ to build the documentation.
- `Sphinx ReadTheDocs Theme <https://sphinx-rtd-theme.readthedocs.io/>`_
- `Sphinx recommonmark <https://recommonmark.readthedocs.io>`_. - `Sphinx recommonmark <https://recommonmark.readthedocs.io>`_.
- `Breathe <https://breathe.readthedocs.io/>`_ as a bridge between the Sphinx and Doxygen - `Breathe <https://breathe.readthedocs.io/>`_ as a bridge between the Sphinx and Doxygen
documentation systems. documentation systems.
@@ -82,47 +86,47 @@ in *~/.conan/profiles* directory. An example profile can look as follows:
arch=x86_64 arch=x86_64
arch_build=x86_64 arch_build=x86_64
compiler=gcc compiler=gcc
compiler.version=10 compiler.version=12
compiler.cppstd=20 compiler.cppstd=20
compiler.libcxx=libstdc++11 compiler.libcxx=libstdc++11
build_type=Release build_type=Release
[options]
[build_requires]
[conf]
tools.cmake.cmaketoolchain:generator=Ninja
[env] [env]
CC=/usr/bin/gcc-10 CC=/usr/bin/gcc-12
CXX=/usr/bin/g++-10 CXX=/usr/bin/g++-12
.. tip:: .. tip::
Please note that **mp-units** library requires C++20 to be set either in a Conan profile or forced Please note that **mp-units** library requires C++20 to be set either in a Conan profile or forced
via Conan command line. If you do the former, you will not need to provide ``-s compiler.cppstd=20`` via Conan command line. If you do the former, you will not need to provide ``-s compiler.cppstd=20``
every time your run a Conan command line (as it is suggested below). every time your run a Conan command line (as provided in the command line instructions below).
Additionally, it is recommended to set Ninja as a CMake generator for Conan. To do so you should create
a *~/.conan/global.conf* file that will set ``tools.cmake.cmaketoolchain:generator`` to one of Ninja
generators. For example:
.. code-block:: text
tools.cmake.cmaketoolchain:generator="Ninja Multi-Config"
.. note::
*~/.conan/global.conf* file may also set ``tools.cmake.cmake_layout:build_folder_vars``` which
`makes working with several compilers or build configurations easier
<https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmake_layout.html#multi-setting-option-cmake-layout>`_.
For example the below line will force Conan to generate separate CMake presets and folders for each compiler:
.. code-block:: text
tools.cmake.cmake_layout:build_folder_vars=["settings.compiler", "settings.compiler.version"]
In such a case you will need to use a configuration specific preset name in the Conan instructions provided below
rather then just ``default`` and ``release`` (i.e. ``gcc-11`` and ``gcc-11-release``)
Build Options Build Options
------------- -------------
Environment Variables
^^^^^^^^^^^^^^^^^^^^^
CONAN_RUN_TESTS
+++++++++++++++
**Values**: ``True``/``False``
**Defaulted to**: ``False``
Enables compilation of all the source code (tests and examples) and building the documentation.
To support this it requires some additional Conan build dependencies described in
`Repository Structure and Dependencies`_.
It also runs unit tests during Conan build.
Conan Options Conan Options
^^^^^^^^^^^^^ ^^^^^^^^^^^^^
@@ -139,15 +143,30 @@ Specifies how :ref:`design/downcasting:The Downcasting Facility` works:
- ``on`` - downcasting always forced -> compile-time errors in case of duplicated definitions - ``on`` - downcasting always forced -> compile-time errors in case of duplicated definitions
- ``automatic`` - downcasting automatically enabled if no collisions are present - ``automatic`` - downcasting automatically enabled if no collisions are present
build_docs Conan Configuration Properties
++++++++++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
build_all
+++++++++
**Values**: ``True``/``False`` **Values**: ``True``/``False``
**Defaulted to**: ``True`` **Defaulted to**: ``False``
If enabled, Conan installs the documentation generation dependencies (i.e. doxygen). Enables compilation of all the source code (tests and examples) and generating the documentation.
Additionally, enables project documentation generation when the project is being built by Conan. To support this it requires some additional Conan build dependencies described in
`Repository Structure and Dependencies`_.
It also runs unit tests during Conan build (unless ``tools.build:skip_test`` configuration property is set to ``True``)
skip_docs
+++++++++
**Values**: ``True``/``False``
**Defaulted to**: ``False``
If `build_all`_ is enabled, among others, Conan installs the documentation generation dependencies (i.e. doxygen) and
turns on the project documentation generation. Such behavior can be disabled with this option.
CMake Options CMake Options
^^^^^^^^^^^^^ ^^^^^^^^^^^^^
@@ -203,6 +222,21 @@ UNITS_USE_LIBFMT
Enables usage of `{fmt} <https://github.com/fmtlib/fmt>`_ library instead of the C++20 Standard Library feature. Enables usage of `{fmt} <https://github.com/fmtlib/fmt>`_ library instead of the C++20 Standard Library feature.
CMake with Presets Support
--------------------------
It is recommended to use at least CMake 3.23 to build this project as this version introduced a support
for CMake Presets schema version 4 used now by Conan to generate presets files. All build instructions
below assume that you have such a support. If not, your CMake invocations have to be replaced to something
like:
.. code-block:: shell
mkdir build && cd build
cmake .. -G "Ninja Multi-Config" -DCMAKE_TOOLCHAIN_FILE=<path_to_generators_dir>/conan_toolchain.cmake
cmake --build . --config Release
Installation and Reuse Installation and Reuse
---------------------- ----------------------
@@ -294,8 +328,8 @@ library release the following steps may be performed:
mkdir my_project/build && cd my_project/build mkdir my_project/build && cd my_project/build
conan install .. -pr <your_conan_profile> -s compiler.cppstd=20 -b=missing conan install .. -pr <your_conan_profile> -s compiler.cppstd=20 -b=missing
cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release cmake .. -G "Ninja Multi-Config" -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake
cmake --build . cmake --build . --config Release
Conan + CMake (Live At Head) Conan + CMake (Live At Head)
@@ -329,6 +363,9 @@ differences:
[requires] [requires]
mp-units/0.8.0@mpusz/testing mp-units/0.8.0@mpusz/testing
[layout]
cmake_layout
[generators] [generators]
CMakeToolchain CMakeToolchain
CMakeDeps CMakeDeps
@@ -343,10 +380,9 @@ differences:
.. code-block:: shell .. code-block:: shell
mkdir my_project/build && cd my_project/build conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -b=outdated -u
conan install .. -pr <your_conan_profile> -s compiler.cppstd=20 -b=outdated -u cmake --preset default
cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release cmake --build --preset release
cmake --build .
Install Install
@@ -358,43 +394,60 @@ to find it, it is enough to perform the following steps:
.. code-block:: shell .. code-block:: shell
mkdir units/build && cd units/build conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -b=missing
conan install .. -pr <your_conan_profile> -s compiler.cppstd=20 -b=missing mv CMakeUserPresets.json src
cmake ../src -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release cd src
cmake --install . --prefix <install_dir> cmake --preset default -DCMAKE_INSTALL_PREFIX=<your_installation_path>
cmake --build --preset release --target install
Contributing (or just building all the tests, examples, and documentation) Contributing (or just building all the tests and examples)
-------------------------------------------------------------------------- ----------------------------------------------------------
In case you would like to build all the source code (with unit tests and examples) and documentation In case you would like to build all the source code (with unit tests and examples) in **mp-units** repository,
in **mp-units** repository, you should: you should:
1. Add remotes of additional Conan dependencies. 1. Use the *CMakeLists.txt* from the top-level directory.
2. Use the *CMakeLists.txt* from the top-level directory. 2. Run Conan with `build_all`_ = ``True``
3. Obtain Python dependencies. (use ``-o build_docs=False`` if you want to skip the documentation generation).
4. Run Conan with `CONAN_RUN_TESTS`_ = ``True``.
.. code-block:: shell
git clone https://github.com/mpusz/units.git && cd units
conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -c mp-units:user.build:all=True -c user.build:skip_docs=True -b outdated -u
conan build .
The above will download and install all of the dependencies needed for the development of the library,
build all of the source code and run unit tests.
If you prefer to build the project via CMake rather then Conan, then you should replace the last ``conan build .``
step with the explicit CMake build:
.. code-block:: shell
cmake --preset default
cmake --build --preset release
cmake --build --preset release --target test
Building documentation
----------------------
In case you would like to build the project's documentation, you should:
1. Use the *CMakeLists.txt* from the top-level directory.
2. Obtain Python dependencies.
3. Run Conan with `build_all`_ = ``True``.
.. code-block:: shell .. code-block:: shell
git clone https://github.com/mpusz/units.git && cd units git clone https://github.com/mpusz/units.git && cd units
pip3 install -r docs/requirements.txt pip3 install -r docs/requirements.txt
mkdir units/build && cd units/build conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -c mp-units:user.build:all=True -b missing
conan install .. -pr <your_conan_profile> -s compiler.cppstd=20 -e mp-units:CONAN_RUN_TESTS=True -b outdated -u cmake --preset default
conan build .. cmake --build --preset release --target documentation
The above will download and install all of the dependencies needed for the development of the library, The above will download and install all of the dependencies needed and build the documentation.
build all of the source code and documentation, and run unit tests.
If you prefer to build the project via CMake rather then Conan, then you should replace the last ``conan build ..``
step with the CMake build:
.. code-block:: shell
# ...
cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
cmake --build .
ctest
Packaging Packaging
@@ -404,7 +457,7 @@ To test CMake installation and Conan packaging or create a Conan package run:
.. code-block:: shell .. code-block:: shell
conan create . <username>/<channel> -pr <your_conan_profile> -s compiler.cppstd=20 -e mp-units:CONAN_RUN_TESTS=True -b outdated -u conan create . <username>/<channel> -pr <your_conan_profile> -s compiler.cppstd=20 -c mp-units:user.build:all=True -c user.build:skip_docs=True -b outdated -u
The above will create a Conan package and run tests provided in *./test_package* directory. The above will create a Conan package and run tests provided in *./test_package* directory.

View File

@@ -258,14 +258,14 @@ Even though the base dimension of ``si::fps`` is defined in terms of
``si::metre`` foot is preserved as the base unit of length in both systems:: ``si::metre`` foot is preserved as the base unit of length in both systems::
constexpr auto fps_yard = fps::length<fps::yard>(1.); constexpr auto fps_yard = fps::length<fps::yard>(1.);
constexpr auto fps_area = quantity_cast<unknown_coherent_unit>(fps_yard * fps_yard); constexpr auto fps_area = fps_yard * fps_yard;
std::cout << fps_yard << "\n"; // 1 yd std::cout << fps_yard << "\n"; // 1 yd
std::cout << fps_area << "\n"; // 9 ft² std::cout << quantity_cast<decltype(fps_area)::dimension::coherent_unit>(fps_area) << "\n"; // 9 ft²
constexpr auto si_fps_yard = si::fps::length<si::fps::yard>(1.); constexpr auto si_fps_yard = si::fps::length<si::fps::yard>(1.);
constexpr auto si_fps_area = quantity_cast<unknown_coherent_unit>(si_fps_yard * si_fps_yard); constexpr auto si_fps_area = si_fps_yard * si_fps_yard;
std::cout << si_fps_yard << "\n"; // 1 yd std::cout << si_fps_yard << "\n"; // 1 yd
std::cout << si_fps_area << "\n"; // 9 ft² std::cout << quantity_cast<decltype(si_fps_area)::dimension::coherent_unit>(si_fps_area) << "\n"; // 9 ft²
In most cases we want conversions between systems and that is why nearly all In most cases we want conversions between systems and that is why nearly all
systems provided with this library are implemented in terms on the :term:`SI` systems provided with this library are implemented in terms on the :term:`SI`

View File

@@ -59,7 +59,7 @@ It works just like `quantity_like_traits`, except that
``number(T)`` is replaced with ``relative(T)`` that returns the `QuantityLike` value ``number(T)`` is replaced with ``relative(T)`` that returns the `QuantityLike` value
and ``dimension`` is replaced with ``origin``. and ``dimension`` is replaced with ``origin``.
Similar to `quantity` and `quantity_kind`, `quantity_point` and `quantity_kind_point` Similar to `quantity` and `quantity_kind`, `quantity_point` and `quantity_point_kind`
provide a deduction guide from `QuantityPointLike`:: provide a deduction guide from `QuantityPointLike`::
using namespace std::chrono_literals; using namespace std::chrono_literals;

View File

@@ -52,7 +52,7 @@ dimensions used in the division operation:
static_assert(std::is_same_v<decltype(result)::dimension, static_assert(std::is_same_v<decltype(result)::dimension,
unknown_dimension<exponent<dim_length, 1>, exponent<dim_time, -1>>>); unknown_dimension<exponent<dim_length, 1>, exponent<dim_time, -1>>>);
static_assert(std::is_same_v<decltype(result)::unit, static_assert(std::is_same_v<decltype(result)::unit,
scaled_unit<ratio(1, 36, 1), unknown_coherent_unit>>); scaled_unit<ratio(1, 36, 1), unknown_coherent_unit<exponent<dim_length, 1>, exponent<dim_time, -1>>>>>);
.. important:: .. important::
@@ -81,7 +81,7 @@ this particular unknown derived dimension.
In case we would like to print the result in terms of base units we can simply do the In case we would like to print the result in terms of base units we can simply do the
following:: following::
auto s = quantity_cast<unknown_coherent_unit>(result); auto s = quantity_cast<decltype(result)::dimension::coherent_unit>(result);
std::cout << "Speed: " << s << '\n'; // prints 'Speed: 20 m/s' std::cout << "Speed: " << s << '\n'; // prints 'Speed: 20 m/s'
.. seealso:: .. seealso::

View File

@@ -33,7 +33,7 @@ using namespace units;
namespace fps { namespace fps {
struct foot : named_unit<foot, "ft"> {}; struct foot : named_unit<foot, "ft"> {};
struct yard : named_scaled_unit<yard, "yd", as_magnitude<3>(), foot> {}; struct yard : named_scaled_unit<yard, "yd", mag<3>(), foot> {};
struct dim_length : base_dimension<"L", foot> {}; struct dim_length : base_dimension<"L", foot> {};
@@ -54,8 +54,8 @@ using length = quantity<dim_length, U, Rep>;
namespace fps { namespace fps {
struct foot : named_scaled_unit<foot, "ft", as_magnitude<ratio(3'048, 1'000, -1)>(), metre> {}; struct foot : named_scaled_unit<foot, "ft", mag<ratio{3'048, 10'000}>(), metre> {};
struct yard : named_scaled_unit<yard, "yd", as_magnitude<3>(), foot> {}; struct yard : named_scaled_unit<yard, "yd", mag<3>(), foot> {};
struct dim_length : base_dimension<"L", foot> {}; struct dim_length : base_dimension<"L", foot> {};
@@ -84,20 +84,17 @@ void conversions()
void unknown_dimensions() void unknown_dimensions()
{ {
constexpr auto fps_yard = fps::length<fps::yard>(1.); constexpr auto fps_yard = fps::length<fps::yard>(1.);
constexpr auto fps_area = quantity_cast<unknown_coherent_unit>(fps_yard * fps_yard); constexpr auto fps_area = fps_yard * fps_yard;
std::cout << fps_yard << "\n"; std::cout << fps_yard << "\n";
std::cout << fps_area << "\n"; std::cout << quantity_cast<decltype(fps_area)::dimension::coherent_unit>(fps_area) << "\n";
constexpr auto si_fps_yard = si::fps::length<si::fps::yard>(1.); constexpr auto si_fps_yard = si::fps::length<si::fps::yard>(1.);
constexpr auto si_fps_area = quantity_cast<unknown_coherent_unit>(si_fps_yard * si_fps_yard); constexpr auto si_fps_area = si_fps_yard * si_fps_yard;
std::cout << si_fps_yard << "\n"; std::cout << si_fps_yard << "\n";
std::cout << si_fps_area << "\n"; std::cout << quantity_cast<decltype(si_fps_area)::dimension::coherent_unit>(si_fps_area) << "\n";
} }
std::ostream& operator<<(std::ostream& os, const ratio& r) std::ostream& operator<<(std::ostream& os, const ratio& r) { return os << "ratio{" << r.num << ", " << r.den << "}"; }
{
return os << "ratio{" << r.num << ", " << r.den << ", " << r.exp << "}";
}
template<Unit U> template<Unit U>
std::ostream& operator<<(std::ostream& os, const U& u) std::ostream& operator<<(std::ostream& os, const U& u)

View File

@@ -157,9 +157,8 @@ public:
using legs = std::vector<leg>; using legs = std::vector<leg>;
template<std::ranges::input_range R> template<std::ranges::input_range R>
requires std::same_as<std::ranges::range_value_t<R>, requires std::same_as<std::ranges::range_value_t<R>, waypoint>
waypoint> explicit task(const R& r) : explicit task(const R& r) : waypoints_(std::ranges::begin(r), std::ranges::end(r))
waypoints_(std::ranges::begin(r), std::ranges::end(r))
{ {
} }
@@ -215,8 +214,7 @@ constexpr height agl(altitude glider_alt, altitude terrain_level) { return glide
inline units::isq::si::length<units::isq::si::kilometre> length_3d(distance dist, height h) inline units::isq::si::length<units::isq::si::kilometre> length_3d(distance dist, height h)
{ {
// TODO support for hypot(q, q) return hypot(dist.common(), h.common());
return sqrt(pow<2>(dist.common()) + pow<2>(h.common()));
} }
distance glide_distance(const flight_point& pos, const glider& g, const task& t, const safety& s, altitude ground_alt); distance glide_distance(const flight_point& pos, const glider& g, const task& t, const safety& s, altitude ground_alt);

View File

@@ -54,6 +54,7 @@ template<basic_fixed_string Symbol, NamedUnit U>
struct base_dimension { struct base_dimension {
static constexpr auto symbol = Symbol; ///< Unique base dimension identifier static constexpr auto symbol = Symbol; ///< Unique base dimension identifier
using base_unit = U; ///< Base unit adopted for this dimension using base_unit = U; ///< Base unit adopted for this dimension
static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = magnitude{};
}; };
// base_dimension_less // base_dimension_less

View File

@@ -42,7 +42,7 @@ namespace units::detail {
template<typename... Es> template<typename... Es>
constexpr Magnitude auto absolute_magnitude(exponent_list<Es...>) constexpr Magnitude auto absolute_magnitude(exponent_list<Es...>)
{ {
return (pow<ratio{Es::num, Es::den}>(Es::dimension::base_unit::mag) * ... * magnitude<>{}); return (magnitude<>{} * ... * pow<ratio{Es::num, Es::den}>(Es::dimension::base_unit::mag));
} }
} // namespace units::detail } // namespace units::detail

View File

@@ -29,6 +29,7 @@
#include <units/bits/external/type_traits.h> #include <units/bits/external/type_traits.h>
#include <units/customization_points.h> #include <units/customization_points.h>
#include <units/magnitude.h> #include <units/magnitude.h>
#include <units/symbol_text.h>
// IWYU pragma: end_exports // IWYU pragma: end_exports
#include <cstdint> #include <cstdint>
@@ -86,6 +87,20 @@ inline constexpr bool is_named = false;
template<typename T> template<typename T>
concept NamedUnit = Unit<T> && detail::is_named<T>; concept NamedUnit = Unit<T> && detail::is_named<T>;
template<Unit U, basic_symbol_text Symbol>
struct alias_unit;
// TODO: Remove when P1985 accepted
namespace detail {
template<Unit U, basic_symbol_text Symbol>
void to_base_alias_unit(const volatile alias_unit<U, Symbol>*);
} // namespace detail
template<typename T>
concept AliasUnit = requires(T* t) { detail::to_base_alias_unit(t); };
// BaseDimension // BaseDimension
template<basic_fixed_string Symbol, NamedUnit U> template<basic_fixed_string Symbol, NamedUnit U>
struct base_dimension; struct base_dimension;

View File

@@ -66,15 +66,13 @@ struct common_quantity_reference_impl<reference<D1, U1>, reference<D2, U2>> {
template<typename D1, typename U1, typename D2, typename U2> template<typename D1, typename U1, typename D2, typename U2>
struct common_quantity_reference_impl<reference<D1, U1>, reference<D2, U2>> { struct common_quantity_reference_impl<reference<D1, U1>, reference<D2, U2>> {
static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = common_magnitude(reference<D1, U1>::mag,
reference<D2, U2>::mag);
using dimension = conditional<is_specialization_of<D1, unknown_dimension>, D2, D1>; using dimension = conditional<is_specialization_of<D1, unknown_dimension>, D2, D1>;
static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto m1 = D1::base_units_ratio * U1::mag; using unit = downcast_unit<dimension, mag / dimension::mag>;
static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto m2 = D2::base_units_ratio * U2::mag;
static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto cm = common_magnitude(m1, m2);
using unit = downcast_unit<dimension, cm / dimension::base_units_ratio>;
using type = reference<dimension, unit>; using type = reference<dimension, unit>;
}; };
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2> template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
using common_quantity_reference = using common_quantity_reference =
TYPENAME detail::common_quantity_reference_impl<std::remove_const_t<decltype(Q1::reference)>, TYPENAME detail::common_quantity_reference_impl<std::remove_const_t<decltype(Q1::reference)>,

View File

@@ -39,8 +39,7 @@ inline constexpr bool compatible_units<exponent_list<Es...>, Us...> = (UnitOf<Us
template<Unit... Us, typename... Es> template<Unit... Us, typename... Es>
constexpr Magnitude auto derived_mag(exponent_list<Es...>) constexpr Magnitude auto derived_mag(exponent_list<Es...>)
{ {
return (as_magnitude<1>() * ... * return (magnitude<>{} * ... * pow<ratio{Es::num, Es::den}>(Us::mag / dimension_unit<typename Es::dimension>::mag));
pow<ratio{Es::num, Es::den}>(Us::mag / dimension_unit<typename Es::dimension>::mag));
} }
template<DerivedDimension D, Unit... Us> template<DerivedDimension D, Unit... Us>

View File

@@ -37,12 +37,12 @@ namespace units {
* *
* Sometimes a temporary partial result of a complex calculation may not result in a predefined * Sometimes a temporary partial result of a complex calculation may not result in a predefined
* dimension. In such a case an `unknown_dimension` is created with a coherent unit of `unknown_coherent_unit` * dimension. In such a case an `unknown_dimension` is created with a coherent unit of `unknown_coherent_unit`
* and ratio(1). * with a magnitude being the absolute one of all the exponents of such a dimension.
* *
* @tparam Es the list of exponents of ingredient dimensions * @tparam Es the list of exponents of ingredient dimensions
*/ */
template<Exponent... Es> template<Exponent... Es>
struct unknown_dimension : derived_dimension<unknown_dimension<Es...>, unknown_coherent_unit, Es...> {}; struct unknown_dimension : derived_dimension<unknown_dimension<Es...>, unknown_coherent_unit<Es...>, Es...> {};
namespace detail { namespace detail {

View File

@@ -23,6 +23,7 @@
#pragma once #pragma once
#include <units/bits/basic_concepts.h> #include <units/bits/basic_concepts.h>
#include <units/reference.h>
#include <units/unit.h> #include <units/unit.h>
namespace units { namespace units {
@@ -72,8 +73,7 @@ struct equivalent_impl<D1, D2> :
// additionally accounts for unknown dimensions // additionally accounts for unknown dimensions
template<Unit U1, Dimension D1, Unit U2, Dimension D2> template<Unit U1, Dimension D1, Unit U2, Dimension D2>
struct equivalent_unit : struct equivalent_unit :
std::disjunction<equivalent_impl<U1, U2>, std::disjunction<equivalent_impl<U1, U2>, std::bool_constant<reference<D1, U1>::mag == reference<D2, U2>::mag>> {};
std::bool_constant<U1::mag / dimension_unit<D1>::mag == U2::mag / dimension_unit<D2>::mag>> {};
// point origins // point origins

View File

@@ -37,8 +37,9 @@ inline constexpr bool same_exponents_of = false;
template<Exponent... Es, template<typename...> typename DimTemplate> template<Exponent... Es, template<typename...> typename DimTemplate>
inline constexpr bool same_exponents_of<unknown_dimension<Es...>, DimTemplate> = inline constexpr bool same_exponents_of<unknown_dimension<Es...>, DimTemplate> =
requires { typename DimTemplate<unknown_dimension<Es...>, unknown_coherent_unit, typename Es::dimension...>; } && requires {
std::same_as<exponent_list<Es...>, typename DimTemplate<unknown_dimension<Es...>, unknown_coherent_unit, typename DimTemplate<unknown_dimension<Es...>, unknown_coherent_unit<Es...>, typename Es::dimension...>;
}&& std::same_as<exponent_list<Es...>, typename DimTemplate<unknown_dimension<Es...>, unknown_coherent_unit<Es...>,
typename Es::dimension...>::recipe>; typename Es::dimension...>::recipe>;
} // namespace detail } // namespace detail

View File

@@ -39,108 +39,20 @@ template<typename T>
return v < 0 ? -v : v; return v < 0 ? -v : v;
} }
// the following functions enable gcd and related computations on ratios // Computes the rational gcd of n1/d1 and n2/d2
// with exponents. They avoid overflow. Further information here: [[nodiscard]] constexpr auto gcd_frac(std::intmax_t n1, std::intmax_t d1, std::intmax_t n2, std::intmax_t d2) noexcept
// https://github.com/mpusz/units/issues/62#issuecomment-588152833
// Computes (a * b) mod m relies on unsigned integer arithmetic, should not
// overflow
[[nodiscard]] constexpr std::uint64_t mulmod(std::uint64_t a, std::uint64_t b, std::uint64_t m)
{
std::uint64_t res = 0;
if (b >= m) {
if (m > UINT64_MAX / 2u) {
b -= m;
} else {
b %= m;
}
}
while (a != 0) {
if (a & 1) {
if (b >= m - res) {
res -= m;
}
res += b;
}
a >>= 1;
std::uint64_t temp_b = b;
if (b >= m - b) {
temp_b -= m;
}
b += temp_b;
}
return res;
}
// Calculates (a ^ e) mod m , should not overflow.
[[nodiscard]] constexpr std::uint64_t modpow(std::uint64_t a, std::uint64_t e, std::uint64_t m)
{
a %= m;
std::uint64_t result = 1;
while (e > 0) {
if (e & 1) {
result = mulmod(result, a, m);
}
a = mulmod(a, a, m);
e >>= 1;
}
return result;
}
// gcd(a * 10 ^ e, b), should not overflow
[[nodiscard]] constexpr std::intmax_t gcdpow(std::intmax_t a, std::intmax_t e, std::intmax_t b) noexcept
{
assert(a > 0);
assert(e >= 0);
assert(b > 0);
// gcd(i, j) = gcd(j, i mod j) for j != 0 Euclid;
//
// gcd(a 10^e, b) = gcd(b, a 10^e mod b)
//
// (a 10^e) mod b -> [ (a mod b) (10^e mod b) ] mod b
return std::gcd(
b, static_cast<std::intmax_t>(mulmod(static_cast<std::uint64_t>(a % b),
modpow(10, static_cast<std::uint64_t>(e), static_cast<std::uint64_t>(b)),
static_cast<std::uint64_t>(b))));
}
constexpr void cwap(std::intmax_t& lhs, std::intmax_t& rhs)
{
std::intmax_t tmp = lhs;
lhs = rhs;
rhs = tmp;
}
// Computes the rational gcd of n1/d1 x 10^e1 and n2/d2 x 10^e2
[[nodiscard]] constexpr auto gcd_frac(std::intmax_t n1, std::intmax_t d1, std::intmax_t e1, std::intmax_t n2,
std::intmax_t d2, std::intmax_t e2) noexcept
{ {
// Short cut for equal ratios // Short cut for equal ratios
if (n1 == n2 && d1 == d2 && e1 == e2) { if (n1 == n2 && d1 == d2) {
return std::array{n1, d1, e1}; return std::array{n1, d1};
} }
if (e2 > e1) {
detail::cwap(n1, n2);
detail::cwap(d1, d2);
detail::cwap(e1, e2);
}
std::intmax_t exp = e2; // minimum
// gcd(a/b,c/d) = gcd(a⋅d, c⋅b) / b⋅d // gcd(a/b,c/d) = gcd(a⋅d, c⋅b) / b⋅d
assert(std::numeric_limits<std::intmax_t>::max() / n1 > d2); assert(std::numeric_limits<std::intmax_t>::max() / n1 > d2);
assert(std::numeric_limits<std::intmax_t>::max() / n2 > d1); assert(std::numeric_limits<std::intmax_t>::max() / n2 > d1);
std::intmax_t num = detail::gcdpow(n1 * d2, e1 - e2, n2 * d1); std::intmax_t num = std::gcd(n1 * d2, n2 * d1);
assert(std::numeric_limits<std::intmax_t>::max() / d1 > d2); assert(std::numeric_limits<std::intmax_t>::max() / d1 > d2);
@@ -148,29 +60,19 @@ constexpr void cwap(std::intmax_t& lhs, std::intmax_t& rhs)
std::intmax_t gcd = std::gcd(num, den); std::intmax_t gcd = std::gcd(num, den);
return std::array{num / gcd, den / gcd, exp}; return std::array{num / gcd, den / gcd};
} }
constexpr void normalize(std::intmax_t& num, std::intmax_t& den, std::intmax_t& exp) constexpr void normalize(std::intmax_t& num, std::intmax_t& den)
{ {
if (num == 0) { if (num == 0) {
den = 1; den = 1;
exp = 0;
return; return;
} }
std::intmax_t gcd = std::gcd(num, den); std::intmax_t gcd = std::gcd(num, den);
num = num * (den < 0 ? -1 : 1) / gcd; num = num * (den < 0 ? -1 : 1) / gcd;
den = detail::abs(den) / gcd; den = detail::abs(den) / gcd;
while (num % 10 == 0) {
num /= 10;
++exp;
}
while (den % 10 == 0) {
den /= 10;
--exp;
}
} }
[[nodiscard]] constexpr std::intmax_t safe_multiply(std::intmax_t lhs, std::intmax_t rhs) [[nodiscard]] constexpr std::intmax_t safe_multiply(std::intmax_t lhs, std::intmax_t rhs)

View File

@@ -37,7 +37,7 @@ constexpr auto magnitude_text()
{ {
constexpr auto exp10 = extract_power_of_10(M); constexpr auto exp10 = extract_power_of_10(M);
constexpr Magnitude auto base = M / pow<exp10>(as_magnitude<10>()); constexpr Magnitude auto base = M / pow<exp10>(mag<10>());
constexpr Magnitude auto num = numerator(base); constexpr Magnitude auto num = numerator(base);
constexpr Magnitude auto den = denominator(base); constexpr Magnitude auto den = denominator(base);
static_assert(base == num / den, "Printing rational powers, or irrational bases, not yet supported"); static_assert(base == num / den, "Printing rational powers, or irrational bases, not yet supported");
@@ -72,7 +72,7 @@ constexpr auto magnitude_text()
template<Unit U, Magnitude auto M, std::size_t SymbolLen> template<Unit U, Magnitude auto M, std::size_t SymbolLen>
constexpr auto prefix_or_magnitude_text() constexpr auto prefix_or_magnitude_text()
{ {
if constexpr (M == as_magnitude<1>()) { if constexpr (M == mag<1>()) {
// no ratio/prefix // no ratio/prefix
return basic_fixed_string(""); return basic_fixed_string("");
} else { } else {

View File

@@ -34,7 +34,7 @@ namespace units {
template<typename Rep, typename Period> template<typename Rep, typename Period>
struct quantity_like_traits<std::chrono::duration<Rep, Period>> { struct quantity_like_traits<std::chrono::duration<Rep, Period>> {
private: private:
static constexpr auto mag = as_magnitude<ratio(Period::num, Period::den)>(); static constexpr auto mag = ::units::mag<ratio(Period::num, Period::den)>();
public: public:
using dimension = isq::si::dim_time; using dimension = isq::si::dim_time;
using unit = downcast_unit<dimension, mag>; using unit = downcast_unit<dimension, mag>;
@@ -48,7 +48,7 @@ struct clock_origin : point_origin<isq::si::dim_time> {};
template<typename C, typename Rep, typename Period> template<typename C, typename Rep, typename Period>
struct quantity_point_like_traits<std::chrono::time_point<C, std::chrono::duration<Rep, Period>>> { struct quantity_point_like_traits<std::chrono::time_point<C, std::chrono::duration<Rep, Period>>> {
private: private:
static constexpr auto mag = as_magnitude<ratio(Period::num, Period::den)>(); static constexpr auto mag = ::units::mag<ratio(Period::num, Period::den)>();
public: public:
using origin = clock_origin<C>; using origin = clock_origin<C>;
using unit = downcast_unit<typename origin::dimension, mag>; using unit = downcast_unit<typename origin::dimension, mag>;
@@ -75,12 +75,7 @@ constexpr std::intmax_t pow_10(std::intmax_t v)
template<ratio R> template<ratio R>
constexpr auto to_std_ratio_impl() constexpr auto to_std_ratio_impl()
{ {
if constexpr (R.exp == 0)
return std::ratio<R.num, R.den>{}; return std::ratio<R.num, R.den>{};
else if constexpr (R.exp > 0)
return std::ratio<R.num * pow_10(R.exp), R.den>{};
else
return std::ratio<R.num, R.den * pow_10(-R.exp)>{};
} }
} // namespace detail } // namespace detail

View File

@@ -23,7 +23,7 @@
#pragma once #pragma once
#include <units/base_dimension.h> #include <units/base_dimension.h>
#include <units/bits/base_units_ratio.h> #include <units/bits/absolute_magnitude.h>
#include <units/bits/derived_dimension_base.h> #include <units/bits/derived_dimension_base.h>
#include <units/bits/dim_consolidate.h> #include <units/bits/dim_consolidate.h>
#include <units/bits/dim_unpack.h> #include <units/bits/dim_unpack.h>
@@ -72,8 +72,8 @@ using make_dimension = TYPENAME to_derived_dimension_base<
* and in CGS barye. Those two units are not directly related with each other with some ratio. As they both are * and in CGS barye. Those two units are not directly related with each other with some ratio. As they both are
* coherent units of their dimensions, the ratio between them is directly determined by the ratios of base units * coherent units of their dimensions, the ratio between them is directly determined by the ratios of base units
* defined in base dimensions end their exponents in the derived dimension recipe. To provide interoperability of * defined in base dimensions end their exponents in the derived dimension recipe. To provide interoperability of
* such quantities of different systems base_units_ratio is being used. The result of the division of two * such quantities of different systems mag is being used. The result of the division of two
* base_units_ratio of two quantities of equivalent dimensions in two different systems gives a ratio between their * mag of two quantities of equivalent dimensions in two different systems gives a ratio between their
* coherent units. Alternatively, the user would always have to directly define a barye in terms of pascal or vice * coherent units. Alternatively, the user would always have to directly define a barye in terms of pascal or vice
* versa. * versa.
* *
@@ -85,8 +85,8 @@ template<typename Child, Unit U, Exponent... Es>
struct derived_dimension : downcast_dispatch<Child, typename detail::make_dimension<Es...>> { struct derived_dimension : downcast_dispatch<Child, typename detail::make_dimension<Es...>> {
using recipe = exponent_list<Es...>; using recipe = exponent_list<Es...>;
using coherent_unit = U; using coherent_unit = U;
static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto base_units_ratio = static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag =
detail::absolute_magnitude(typename derived_dimension::exponents()); detail::absolute_magnitude(typename derived_dimension::exponents()) / U::mag;
}; };
} // namespace units } // namespace units

View File

@@ -31,7 +31,7 @@
namespace units { namespace units {
struct one : derived_unit<one> {}; struct one : derived_unit<one> {};
struct percent : named_scaled_unit<percent, "%", as_magnitude<ratio(1, 100)>(), one> {}; struct percent : named_scaled_unit<percent, "%", mag<ratio(1, 100)>(), one> {};
/** /**
* @brief Dimension one * @brief Dimension one

View File

@@ -189,9 +189,6 @@ constexpr widen_t<T> compute_base_power(BasePower auto bp)
if (bp.power.den != 1) { if (bp.power.den != 1) {
throw std::invalid_argument{"Rational powers not yet supported"}; throw std::invalid_argument{"Rational powers not yet supported"};
} }
if (bp.power.exp < 0) {
throw std::invalid_argument{"Unsupported exp value"};
}
if (bp.power.num < 0) { if (bp.power.num < 0) {
if constexpr (std::is_integral_v<T>) { if constexpr (std::is_integral_v<T>) {
@@ -201,7 +198,7 @@ constexpr widen_t<T> compute_base_power(BasePower auto bp)
} }
} }
auto power = numerator(bp.power); auto power = bp.power.num;
return int_power(static_cast<widen_t<T>>(bp.get_base()), power); return int_power(static_cast<widen_t<T>>(bp.get_base()), power);
} }
@@ -344,7 +341,7 @@ inline constexpr bool is_base_power_pack_valid = all_base_powers_valid<BPs...> &
constexpr bool is_rational(BasePower auto bp) constexpr bool is_rational(BasePower auto bp)
{ {
return std::is_integral_v<decltype(bp.get_base())> && (bp.power.den == 1) && (bp.power.exp >= 0); return std::is_integral_v<decltype(bp.get_base())> && (bp.power.den == 1);
} }
constexpr bool is_integral(BasePower auto bp) { return is_rational(bp) && bp.power.num > 0; } constexpr bool is_integral(BasePower auto bp) { return is_rational(bp) && bp.power.num > 0; }
@@ -498,8 +495,8 @@ namespace detail {
template<auto BP> template<auto BP>
constexpr auto integer_part(magnitude<BP>) constexpr auto integer_part(magnitude<BP>)
{ {
constexpr auto power_num = numerator(BP.power); constexpr auto power_num = BP.power.num;
constexpr auto power_den = denominator(BP.power); constexpr auto power_den = BP.power.den;
if constexpr (std::is_integral_v<decltype(BP.get_base())> && (power_num >= power_den)) { if constexpr (std::is_integral_v<decltype(BP.get_base())> && (power_num >= power_den)) {
constexpr auto largest_integer_power = [=](BasePower auto bp) { constexpr auto largest_integer_power = [=](BasePower auto bp) {
@@ -556,7 +553,7 @@ namespace detail {
template<auto BP> template<auto BP>
constexpr auto remove_positive_power(magnitude<BP> m) constexpr auto remove_positive_power(magnitude<BP> m)
{ {
if constexpr (numerator(BP.power) < 0) { if constexpr (BP.power.num < 0) {
return m; return m;
} else { } else {
return magnitude<>{}; return magnitude<>{};
@@ -599,7 +596,7 @@ constexpr auto common_magnitude(magnitude<H1, T1...>, magnitude<H2, T2...>)
} }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// `as_magnitude()` implementation. // `mag()` implementation.
// Sometimes we need to give the compiler a "shortcut" when factorizing large numbers (specifically, numbers whose // Sometimes we need to give the compiler a "shortcut" when factorizing large numbers (specifically, numbers whose
// _first factor_ is very large). If we don't, we can run into limits on the number of constexpr steps or iterations. // _first factor_ is very large). If we don't, we can run into limits on the number of constexpr steps or iterations.
@@ -650,10 +647,19 @@ inline constexpr auto prime_factorization_v = prime_factorization<N>::value;
*/ */
template<ratio R> template<ratio R>
requires(R.num > 0) requires(R.num > 0)
constexpr Magnitude auto as_magnitude() constexpr Magnitude auto mag()
{ {
return pow<ratio{R.exp}>(detail::prime_factorization_v<10>) * detail::prime_factorization_v<R.num> / return detail::prime_factorization_v<R.num> / detail::prime_factorization_v<R.den>;
detail::prime_factorization_v<R.den>; }
/**
* @brief Create a Magnitude which is some rational number raised to a rational power.
*/
template<ratio Base, ratio Pow>
requires(Base.num > 0)
constexpr Magnitude auto mag_power()
{
return pow<Pow>(mag<Base>());
} }
namespace detail { namespace detail {
@@ -663,7 +669,7 @@ constexpr ratio get_power(T base, magnitude<BPs...>)
return ((BPs.get_base() == base ? BPs.power : ratio{0}) + ... + ratio{0}); return ((BPs.get_base() == base ? BPs.power : ratio{0}) + ... + ratio{0});
} }
constexpr std::intmax_t integer_part(ratio r) { return numerator(r) / denominator(r); } constexpr std::intmax_t integer_part(ratio r) { return r.num / r.den; }
constexpr std::intmax_t extract_power_of_10(Magnitude auto m) constexpr std::intmax_t extract_power_of_10(Magnitude auto m)
{ {

View File

@@ -301,4 +301,42 @@ template<Quantity To, std::same_as<typename To::dimension> D, typename U, std::s
return ::units::round<typename To::unit>(q); return ::units::round<typename To::unit>(q);
} }
/**
* @brief Computes the square root of the sum of the squares of x and y,
* without undue overflow or underflow at intermediate stages of the computation
*/
template<Quantity Q1, Quantity Q2>
[[nodiscard]] inline std::common_type_t<Q1, Q2> hypot(const Q1& x, const Q2& y) noexcept
requires requires { typename std::common_type_t<Q1, Q2>; } &&
requires(std::common_type_t<Q1, Q2> q) {
requires requires { hypot(q.number(), q.number()); } || requires { std::hypot(q.number(), q.number()); };
}
{
using type = std::common_type_t<Q1, Q2>;
type xx = x;
type yy = y;
using std::hypot;
return type(hypot(xx.number(), yy.number()));
}
/**
* @brief Computes the square root of the sum of the squares of x, y, and z,
* without undue overflow or underflow at intermediate stages of the computation
*/
template<Quantity Q1, Quantity Q2, Quantity Q3>
[[nodiscard]] inline std::common_type_t<Q1, Q2, Q3> hypot(const Q1& x, const Q2& y, const Q3& z) noexcept
requires requires { typename std::common_type_t<Q1, Q2, Q3>; } &&
requires(std::common_type_t<Q1, Q2, Q3> q) {
requires requires { hypot(q.number(), q.number(), q.number()); } ||
requires { std::hypot(q.number(), q.number(), q.number()); };
}
{
using type = std::common_type_t<Q1, Q2, Q3>;
type xx = x;
type yy = y;
type zz = z;
using std::hypot;
return type(hypot(xx.number(), yy.number(), zz.number()));
}
} // namespace units } // namespace units

View File

@@ -405,11 +405,7 @@ public:
requires(!Quantity<Value>) && (invoke_result_convertible_to_<rep, std::divides<>, const Value&, rep>) requires(!Quantity<Value>) && (invoke_result_convertible_to_<rep, std::divides<>, const Value&, rep>)
[[nodiscard]] friend constexpr Quantity auto operator/(const Value& v, const quantity& q) [[nodiscard]] friend constexpr Quantity auto operator/(const Value& v, const quantity& q)
{ {
gsl_ExpectsAudit(q.number() != quantity_values<rep>::zero()); return detail::make_quantity<::units::reference<dim_one, ::units::one>{} / reference>(v / q.number());
using dim = dim_invert<D>;
using ret_unit = downcast_unit<dim, pow<-1>(U::mag)>;
using ret = quantity<dim, ret_unit, std::invoke_result_t<std::divides<>, Value, rep>>;
return ret(v / q.number());
} }
template<typename Value> template<typename Value>
@@ -444,7 +440,7 @@ public:
}; };
// CTAD // CTAD
#if !UNITS_COMP_CLANG || UNITS_COMP_CLANG > 15 #if !UNITS_COMP_CLANG || UNITS_COMP_CLANG > 16
template<typename D, typename U, typename Rep> template<typename D, typename U, typename Rep>
explicit(false) quantity(Rep&&)->quantity<D, U, Rep>; explicit(false) quantity(Rep&&)->quantity<D, U, Rep>;
#endif #endif

View File

@@ -28,6 +28,7 @@
#include <units/concepts.h> #include <units/concepts.h>
#include <units/customization_points.h> #include <units/customization_points.h>
#include <units/magnitude.h> #include <units/magnitude.h>
#include <units/reference.h>
UNITS_DIAGNOSTIC_PUSH UNITS_DIAGNOSTIC_PUSH
// warning C4244: 'argument': conversion from 'intmax_t' to 'T', possible loss of data with T=int // warning C4244: 'argument': conversion from 'intmax_t' to 'T', possible loss of data with T=int
@@ -49,17 +50,8 @@ class quantity_point_kind;
namespace detail { namespace detail {
template<typename T> template<Quantity Q>
inline constexpr Magnitude auto quantity_magnitude = std::enable_if_t<!Quantity<T>, magnitude<>>{}; inline constexpr Magnitude auto quantity_magnitude = decltype(Q::reference)::mag;
template<typename D, typename U, typename Rep>
inline constexpr Magnitude auto quantity_magnitude<quantity<D, U, Rep>> = [] {
if constexpr (BaseDimension<D>) {
return U::mag;
} else {
return D::base_units_ratio * U::mag / D::coherent_unit::mag;
}
}();
template<Quantity Q> template<Quantity Q>
inline constexpr ratio quantity_ratio = as_ratio(quantity_magnitude<Q>); inline constexpr ratio quantity_ratio = as_ratio(quantity_magnitude<Q>);
@@ -79,8 +71,8 @@ template<typename From, typename To>
struct cast_traits; struct cast_traits;
template<typename From, typename To> template<typename From, typename To>
requires common_type_with_<std::common_type_t<From, To>, requires common_type_with_<std::common_type_t<From, To>, std::intmax_t>
std::intmax_t> struct cast_traits<From, To> { struct cast_traits<From, To> {
using ratio_type = std::common_type_t<std::common_type_t<From, To>, std::intmax_t>; using ratio_type = std::common_type_t<std::common_type_t<From, To>, std::intmax_t>;
using rep_type = ratio_type; using rep_type = ratio_type;
}; };

View File

@@ -43,42 +43,28 @@ constexpr ratio inverse(const ratio& r);
/** /**
* @brief Provides compile-time rational arithmetic support. * @brief Provides compile-time rational arithmetic support.
* *
* This class is really similar to @c std::ratio but gets an additional `Exp` * This class is really similar to @c std::ratio. An important difference is the fact that the objects of that class
* template parameter that defines the exponent of the ratio. Another important * are used as class NTTPs rather then a type template parameter kind.
* difference is the fact that the objects of that class are used as class NTTPs
* rather then a type template parameter kind.
*/ */
struct ratio { struct ratio {
std::intmax_t num; std::intmax_t num;
std::intmax_t den; std::intmax_t den;
std::intmax_t exp;
constexpr explicit(false) ratio(std::intmax_t n, std::intmax_t d = 1, std::intmax_t e = 0) : num(n), den(d), exp(e) constexpr explicit(false) ratio(std::intmax_t n, std::intmax_t d = 1) : num(n), den(d)
{ {
gsl_Expects(den != 0); gsl_Expects(den != 0);
detail::normalize(num, den, exp); detail::normalize(num, den);
} }
[[nodiscard]] friend constexpr bool operator==(const ratio&, const ratio&) = default; [[nodiscard]] friend constexpr bool operator==(const ratio&, const ratio&) = default;
[[nodiscard]] friend constexpr auto operator<=>(const ratio& lhs, const ratio& rhs) { return (lhs - rhs).num <=> 0; } [[nodiscard]] friend constexpr auto operator<=>(const ratio& lhs, const ratio& rhs) { return (lhs - rhs).num <=> 0; }
[[nodiscard]] friend constexpr ratio operator-(const ratio& r) { return ratio(-r.num, r.den, r.exp); } [[nodiscard]] friend constexpr ratio operator-(const ratio& r) { return ratio(-r.num, r.den); }
[[nodiscard]] friend constexpr ratio operator+(ratio lhs, ratio rhs) [[nodiscard]] friend constexpr ratio operator+(ratio lhs, ratio rhs)
{ {
// First, get the inputs into a common exponent. return ratio{lhs.num * rhs.den + lhs.den * rhs.num, lhs.den * rhs.den};
const auto common_exp = std::min(lhs.exp, rhs.exp);
auto commonify = [common_exp](ratio& r) {
while (r.exp > common_exp) {
r.num *= 10;
--r.exp;
}
};
commonify(lhs);
commonify(rhs);
return ratio{lhs.num * rhs.den + lhs.den * rhs.num, lhs.den * rhs.den, common_exp};
} }
[[nodiscard]] friend constexpr ratio operator-(const ratio& lhs, const ratio& rhs) { return lhs + (-rhs); } [[nodiscard]] friend constexpr ratio operator-(const ratio& lhs, const ratio& rhs) { return lhs + (-rhs); }
@@ -88,96 +74,27 @@ struct ratio {
const std::intmax_t gcd1 = std::gcd(lhs.num, rhs.den); const std::intmax_t gcd1 = std::gcd(lhs.num, rhs.den);
const std::intmax_t gcd2 = std::gcd(rhs.num, lhs.den); const std::intmax_t gcd2 = std::gcd(rhs.num, lhs.den);
return ratio(detail::safe_multiply(lhs.num / gcd1, rhs.num / gcd2), return ratio(detail::safe_multiply(lhs.num / gcd1, rhs.num / gcd2),
detail::safe_multiply(lhs.den / gcd2, rhs.den / gcd1), lhs.exp + rhs.exp); detail::safe_multiply(lhs.den / gcd2, rhs.den / gcd1));
} }
[[nodiscard]] friend constexpr ratio operator/(const ratio& lhs, const ratio& rhs) { return lhs * inverse(rhs); } [[nodiscard]] friend constexpr ratio operator/(const ratio& lhs, const ratio& rhs) { return lhs * inverse(rhs); }
[[nodiscard]] friend constexpr std::intmax_t numerator(const ratio& r)
{
std::intmax_t true_num = r.num;
for (auto i = r.exp; i > 0; --i) {
true_num *= 10;
}
return true_num;
}
[[nodiscard]] friend constexpr std::intmax_t denominator(const ratio& r)
{
std::intmax_t true_den = r.den;
for (auto i = r.exp; i < 0; ++i) {
true_den *= 10;
}
return true_den;
}
}; };
[[nodiscard]] constexpr ratio inverse(const ratio& r) { return ratio(r.den, r.num, -r.exp); } [[nodiscard]] constexpr ratio inverse(const ratio& r) { return ratio(r.den, r.num); }
[[nodiscard]] constexpr bool is_integral(const ratio& r) [[nodiscard]] constexpr bool is_integral(const ratio& r) { return r.num % r.den == 0; }
{
if (r.exp < 0) {
return false;
} else {
return detail::gcdpow(r.num, r.exp, r.den) == r.den;
}
}
namespace detail { template<std::intmax_t Num>
[[nodiscard]] constexpr auto make_exp_align(const ratio& r, std::intmax_t alignment)
{
gsl_Expects(alignment > 0);
const std::intmax_t rem = r.exp % alignment;
if (rem == 0) { // already aligned
return std::array{r.num, r.den, r.exp};
}
if (r.exp > 0) { // remainder is positive
return std::array{r.num * ipow10(rem), r.den, r.exp - rem};
}
// remainder is negative
return std::array{r.num, r.den * ipow10(-rem), r.exp - rem};
}
template<std::intmax_t N>
requires gt_zero<N>
[[nodiscard]] constexpr ratio root(const ratio& r)
{
if constexpr (N == 1) {
return r;
} else {
if (r.num == 0) {
return ratio(0);
}
const auto aligned = make_exp_align(r, N);
return ratio(iroot<N>(aligned[0]), iroot<N>(aligned[1]), aligned[2] / N);
}
}
} // namespace detail
template<std::intmax_t Num, std::intmax_t Den = 1>
requires detail::non_zero<Den>
[[nodiscard]] constexpr ratio pow(const ratio& r) [[nodiscard]] constexpr ratio pow(const ratio& r)
{ {
if constexpr (Num == 0) { if constexpr (Num == 0) {
return ratio(1); return ratio(1);
} else if constexpr (Num == Den) { } else if constexpr (Num == 1) {
return r; return r;
} else { } else {
// simplify factors first and compute power for positive exponent const ratio result = detail::pow_impl<Num>(r);
constexpr std::intmax_t gcd = std::gcd(Num, Den);
constexpr std::intmax_t num = detail::abs(Num / gcd);
constexpr std::intmax_t den = detail::abs(Den / gcd);
// integer root loses precision so do pow first if constexpr (Num < 0) { // account for negative exponent
const ratio result = detail::root<den>(detail::pow_impl<num>(r));
if constexpr (Num * Den < 0) { // account for negative exponent
return inverse(result); return inverse(result);
} else { } else {
return result; return result;
@@ -185,15 +102,11 @@ template<std::intmax_t Num, std::intmax_t Den = 1>
} }
} }
[[nodiscard]] constexpr ratio sqrt(const ratio& r) { return pow<1, 2>(r); }
[[nodiscard]] constexpr ratio cbrt(const ratio& r) { return pow<1, 3>(r); }
// common_ratio // common_ratio
[[nodiscard]] constexpr ratio common_ratio(const ratio& r1, const ratio& r2) [[nodiscard]] constexpr ratio common_ratio(const ratio& r1, const ratio& r2)
{ {
const auto res = detail::gcd_frac(r1.num, r1.den, r1.exp, r2.num, r2.den, r2.exp); const auto res = detail::gcd_frac(r1.num, r1.den, r2.num, r2.den);
return ratio(res[0], res[1], res[2]); return ratio(res[0], res[1]);
} }
} // namespace units } // namespace units

View File

@@ -35,27 +35,21 @@ struct reference;
namespace detail { namespace detail {
template<typename D, typename D1, typename U1, typename D2, typename U2> template<Dimension D, Reference R1, Reference R2>
using reference_multiply_impl = using reference_multiply_impl = reference<D, downcast_unit<D, R1::mag * R2::mag / D::mag>>;
reference<D, downcast_unit<D, (U1::mag / dimension_unit<D1>::mag) * (U2::mag / dimension_unit<D2>::mag) *
dimension_unit<D>::mag>>;
template<typename D, typename D1, typename U1, typename D2, typename U2> template<typename D, Reference R1, Reference R2>
using reference_divide_impl = using reference_divide_impl = reference<D, downcast_unit<D, R1::mag / R2::mag / D::mag>>;
reference<D, downcast_unit<D, (U1::mag / dimension_unit<D1>::mag) / (U2::mag / dimension_unit<D2>::mag) *
dimension_unit<D>::mag>>;
} // namespace detail } // namespace detail
template<Reference R1, Reference R2> template<Reference R1, Reference R2>
using reference_multiply = using reference_multiply =
detail::reference_multiply_impl<dimension_multiply<typename R1::dimension, typename R2::dimension>, detail::reference_multiply_impl<dimension_multiply<typename R1::dimension, typename R2::dimension>, R1, R2>;
typename R1::dimension, typename R1::unit, typename R2::dimension, typename R2::unit>;
template<Reference R1, Reference R2> template<Reference R1, Reference R2>
using reference_divide = using reference_divide =
detail::reference_divide_impl<dimension_divide<typename R1::dimension, typename R2::dimension>, detail::reference_divide_impl<dimension_divide<typename R1::dimension, typename R2::dimension>, R1, R2>;
typename R1::dimension, typename R1::unit, typename R2::dimension, typename R2::unit>;
/** /**
* @brief The type for quantity references * @brief The type for quantity references
@@ -100,6 +94,7 @@ template<Dimension D, UnitOf<D> U>
struct reference { struct reference {
using dimension = D; using dimension = D;
using unit = U; using unit = U;
static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = dimension::mag * unit::mag;
// Hidden Friends // Hidden Friends
// Below friend functions are to be found via argument-dependent lookup only // Below friend functions are to be found via argument-dependent lookup only

View File

@@ -24,6 +24,7 @@
#include <units/bits/derived_symbol_text.h> #include <units/bits/derived_symbol_text.h>
#include <units/bits/external/downcasting.h> #include <units/bits/external/downcasting.h>
#include <units/bits/external/type_traits.h>
// IWYU pragma: begin_exports // IWYU pragma: begin_exports
#include <units/bits/derived_scaled_unit.h> #include <units/bits/derived_scaled_unit.h>
@@ -57,6 +58,9 @@ inline constexpr bool can_be_prefixed = false;
* *
* @tparam M a Magnitude representing the (relative) size of this unit * @tparam M a Magnitude representing the (relative) size of this unit
* @tparam U a unit to use as a reference for this dimension * @tparam U a unit to use as a reference for this dimension
*
* @note U cannot be constrained with Unit as for some specializations (i.e. named_unit)
* it gets the incomplete child's type with the CRTP idiom.
*/ */
template<Magnitude auto M, typename U> template<Magnitude auto M, typename U>
struct scaled_unit : downcast_base<scaled_unit<M, U>> { struct scaled_unit : downcast_base<scaled_unit<M, U>> {
@@ -80,7 +84,7 @@ struct same_unit_reference : is_same<typename U1::reference, typename U2::refere
* @tparam Symbol a short text representation of the unit * @tparam Symbol a short text representation of the unit
*/ */
template<typename Child, basic_symbol_text Symbol> template<typename Child, basic_symbol_text Symbol>
struct named_unit : downcast_dispatch<Child, scaled_unit<as_magnitude<1>(), Child>> { struct named_unit : downcast_dispatch<Child, scaled_unit<mag<1>(), Child>> {
static constexpr auto symbol = Symbol; static constexpr auto symbol = Symbol;
}; };
@@ -126,7 +130,7 @@ struct prefixed_unit : downcast_dispatch<Child, scaled_unit<P::mag * U::mag, typ
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
*/ */
template<typename Child> template<typename Child>
struct derived_unit : downcast_dispatch<Child, scaled_unit<as_magnitude<1>(), Child>> {}; struct derived_unit : downcast_dispatch<Child, scaled_unit<mag<1>(), Child>> {};
/** /**
* @brief A unit with a deduced ratio and symbol * @brief A unit with a deduced ratio and symbol
@@ -173,12 +177,8 @@ struct alias_unit : U {
* @tparam P prefix to be appied to the reference unit * @tparam P prefix to be appied to the reference unit
* @tparam AU reference alias unit * @tparam AU reference alias unit
*/ */
// TODO gcc bug: 95015 template<Unit U, Prefix P, AliasUnit AU>
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95015 requires(!AliasUnit<U>)
// template<Unit U, Prefix P, AliasUnit AU>
// requires (!AliasUnit<U>)
template<Unit U, Prefix P, NamedUnit AU>
requires detail::can_be_prefixed<AU>
struct prefixed_alias_unit : U { struct prefixed_alias_unit : U {
static constexpr auto symbol = P::symbol + AU::symbol; static constexpr auto symbol = P::symbol + AU::symbol;
}; };
@@ -188,7 +188,10 @@ struct prefixed_alias_unit : U {
* *
* Used as a coherent unit of an unknown dimension. * Used as a coherent unit of an unknown dimension.
*/ */
struct unknown_coherent_unit : derived_unit<unknown_coherent_unit> {}; template<Exponent... Es>
struct unknown_coherent_unit :
downcast_dispatch<unknown_coherent_unit<Es...>,
scaled_unit<detail::absolute_magnitude(exponent_list<Es...>()), unknown_coherent_unit<Es...>>> {};
namespace detail { namespace detail {

View File

@@ -30,6 +30,7 @@ function(__check_libcxx_in_use variable)
set(${variable} ${${variable}} PARENT_SCOPE) set(${variable} ${${variable}} PARENT_SCOPE)
list(POP_BACK CMAKE_MESSAGE_INDENT) list(POP_BACK CMAKE_MESSAGE_INDENT)
if(${variable}) if(${variable})
message(CHECK_PASS "found") message(CHECK_PASS "found")
else() else()
@@ -39,15 +40,21 @@ function(__check_libcxx_in_use variable)
endfunction() endfunction()
include(CMakeFindDependencyMacro) include(CMakeFindDependencyMacro)
find_dependency(fmt)
if(UNITS_USE_LIBFMT)
find_dependency(fmt)
endif()
find_dependency(gsl-lite) find_dependency(gsl-lite)
# add range-v3 dependency only for clang + libc++ # add range-v3 dependency only for clang + libc++
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
__check_libcxx_in_use(__units_libcxx) __check_libcxx_in_use(__units_libcxx)
if(__units_libcxx AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "14") if(__units_libcxx AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "14")
find_dependency(range-v3) find_dependency(range-v3)
endif() endif()
unset(__units_libcxx) unset(__units_libcxx)
endif() endif()

View File

@@ -26,13 +26,13 @@
namespace units::isq::iec80000 { namespace units::isq::iec80000 {
struct kibi : prefix<kibi, "Ki", pow<10>(as_magnitude<2>())> {}; struct kibi : prefix<kibi, "Ki", pow<10>(mag<2>())> {};
struct mebi : prefix<mebi, "Mi", pow<20>(as_magnitude<2>())> {}; struct mebi : prefix<mebi, "Mi", pow<20>(mag<2>())> {};
struct gibi : prefix<gibi, "Gi", pow<30>(as_magnitude<2>())> {}; struct gibi : prefix<gibi, "Gi", pow<30>(mag<2>())> {};
struct tebi : prefix<tebi, "Ti", pow<40>(as_magnitude<2>())> {}; struct tebi : prefix<tebi, "Ti", pow<40>(mag<2>())> {};
struct pebi : prefix<pebi, "Pi", pow<50>(as_magnitude<2>())> {}; struct pebi : prefix<pebi, "Pi", pow<50>(mag<2>())> {};
struct exbi : prefix<exbi, "Ei", pow<60>(as_magnitude<2>())> {}; struct exbi : prefix<exbi, "Ei", pow<60>(mag<2>())> {};
struct zebi : prefix<zebi, "Zi", pow<70>(as_magnitude<2>())> {}; struct zebi : prefix<zebi, "Zi", pow<70>(mag<2>())> {};
struct yobi : prefix<yobi, "Yi", pow<80>(as_magnitude<2>())> {}; struct yobi : prefix<yobi, "Yi", pow<80>(mag<2>())> {};
} // namespace units::isq::iec80000 } // namespace units::isq::iec80000

View File

@@ -53,7 +53,7 @@ struct tebibit : prefixed_unit<tebibit, tebi, bit> {};
struct pebibit : prefixed_unit<pebibit, pebi, bit> {}; struct pebibit : prefixed_unit<pebibit, pebi, bit> {};
struct exbibit : prefixed_unit<exbibit, exbi, bit> {}; struct exbibit : prefixed_unit<exbibit, exbi, bit> {};
struct byte : named_scaled_unit<byte, "B", as_magnitude<8>(), bit> {}; struct byte : named_scaled_unit<byte, "B", mag<8>(), bit> {};
struct kilobyte : prefixed_unit<kilobyte, si::kilo, byte> {}; struct kilobyte : prefixed_unit<kilobyte, si::kilo, byte> {};
struct megabyte : prefixed_unit<megabyte, si::mega, byte> {}; struct megabyte : prefixed_unit<megabyte, si::mega, byte> {};

View File

@@ -40,7 +40,7 @@ namespace units::isq::si::fps {
struct poundal : named_unit<poundal, "pdl"> {}; struct poundal : named_unit<poundal, "pdl"> {};
// https://en.wikipedia.org/wiki/Pound_(force) // https://en.wikipedia.org/wiki/Pound_(force)
struct pound_force : named_scaled_unit<pound_force, "lbf", as_magnitude<ratio(32'174'049, 1'000'000)>(), poundal> {}; struct pound_force : named_scaled_unit<pound_force, "lbf", mag<ratio(32'174'049, 1'000'000)>(), poundal> {};
struct kilopound_force : prefixed_unit<kilopound_force, si::kilo, pound_force> {}; struct kilopound_force : prefixed_unit<kilopound_force, si::kilo, pound_force> {};

View File

@@ -48,7 +48,7 @@ struct thousandth : alias_unit<thou, "thou"> {};
struct kiloyard : prefixed_unit<kiloyard, si::kilo, yard> {}; struct kiloyard : prefixed_unit<kiloyard, si::kilo, yard> {};
struct nautical_mile : named_scaled_unit<nautical_mile, "nmi", as_magnitude<2'000>(), yard> {}; struct nautical_mile : named_scaled_unit<nautical_mile, "nmi", mag<2'000>(), yard> {};
struct dim_length : isq::dim_length<foot> {}; struct dim_length : isq::dim_length<foot> {};

View File

@@ -35,28 +35,28 @@
namespace units::isq::si::fps { namespace units::isq::si::fps {
// https://en.wikipedia.org/wiki/Pound_(mass) // https://en.wikipedia.org/wiki/Pound_(mass)
struct pound : named_scaled_unit<pound, "lb", as_magnitude<ratio(45'359'237, 100'000'000)>(), si::kilogram> {}; struct pound : named_scaled_unit<pound, "lb", mag<ratio(45'359'237, 100'000'000)>(), si::kilogram> {};
struct dim_mass : isq::dim_mass<pound> {}; struct dim_mass : isq::dim_mass<pound> {};
template<UnitOf<dim_mass> U, Representation Rep = double> template<UnitOf<dim_mass> U, Representation Rep = double>
using mass = quantity<dim_mass, U, Rep>; using mass = quantity<dim_mass, U, Rep>;
struct grain : named_scaled_unit<grain, "gr", as_magnitude<ratio(1, 7000)>(), pound> {}; struct grain : named_scaled_unit<grain, "gr", mag<ratio(1, 7000)>(), pound> {};
struct dram : named_scaled_unit<dram, "dr", as_magnitude<ratio(1, 256)>(), pound> {}; struct dram : named_scaled_unit<dram, "dr", mag<ratio(1, 256)>(), pound> {};
struct ounce : named_scaled_unit<ounce, "oz", as_magnitude<ratio(1, 16)>(), pound> {}; struct ounce : named_scaled_unit<ounce, "oz", mag<ratio(1, 16)>(), pound> {};
struct stone : named_scaled_unit<stone, "st", as_magnitude<14>(), pound> {}; struct stone : named_scaled_unit<stone, "st", mag<14>(), pound> {};
struct quarter : named_scaled_unit<quarter, "qr", as_magnitude<28>(), pound> {}; struct quarter : named_scaled_unit<quarter, "qr", mag<28>(), pound> {};
struct hundredweight : named_scaled_unit<hundredweight, "cwt", as_magnitude<112>(), pound> {}; struct hundredweight : named_scaled_unit<hundredweight, "cwt", mag<112>(), pound> {};
struct short_ton : named_scaled_unit<short_ton, "ton (short)", as_magnitude<2'000>(), pound> {}; struct short_ton : named_scaled_unit<short_ton, "ton (short)", mag<2'000>(), pound> {};
struct long_ton : named_scaled_unit<long_ton, "ton (long)", as_magnitude<2'240>(), pound> {}; struct long_ton : named_scaled_unit<long_ton, "ton (long)", mag<2'240>(), pound> {};
#ifndef UNITS_NO_LITERALS #ifndef UNITS_NO_LITERALS

View File

@@ -42,7 +42,7 @@ struct dim_power : isq::dim_power<dim_power, foot_poundal_per_second, dim_length
struct foot_pound_force_per_second : struct foot_pound_force_per_second :
derived_scaled_unit<foot_pound_force_per_second, dim_power, foot, pound_force, second> {}; derived_scaled_unit<foot_pound_force_per_second, dim_power, foot, pound_force, second> {};
struct horse_power : named_scaled_unit<horse_power, "hp", as_magnitude<550>(), foot_pound_force_per_second> {}; struct horse_power : named_scaled_unit<horse_power, "hp", mag<550>(), foot_pound_force_per_second> {};
template<UnitOf<dim_power> U, Representation Rep = double> template<UnitOf<dim_power> U, Representation Rep = double>
using power = quantity<dim_power, U, Rep>; using power = quantity<dim_power, U, Rep>;

View File

@@ -44,11 +44,10 @@ template<UnitOf<dim_pressure> U, Representation Rep = double>
using pressure = quantity<dim_pressure, U, Rep>; using pressure = quantity<dim_pressure, U, Rep>;
struct pound_force_per_foot_sq : struct pound_force_per_foot_sq :
named_scaled_unit<pound_force_per_foot_sq, "lbf ft2", as_magnitude<ratio(32'174'049, 1'000'000)>(), named_scaled_unit<pound_force_per_foot_sq, "lbf ft2", mag<ratio(32'174'049, 1'000'000)>(), poundal_per_foot_sq> {};
poundal_per_foot_sq> {};
struct pound_force_per_inch_sq : struct pound_force_per_inch_sq :
named_scaled_unit<pound_force_per_inch_sq, "psi", as_magnitude<ratio(1, 144)>(), pound_force_per_foot_sq> {}; named_scaled_unit<pound_force_per_inch_sq, "psi", mag<ratio(1, 144)>(), pound_force_per_foot_sq> {};
struct kilopound_force_per_inch_sq : prefixed_unit<kilopound_force_per_inch_sq, si::kilo, pound_force_per_inch_sq> {}; struct kilopound_force_per_inch_sq : prefixed_unit<kilopound_force_per_inch_sq, si::kilo, pound_force_per_inch_sq> {};

View File

@@ -37,7 +37,7 @@ namespace units::isq::si::hep {
// effective cross-sectional area according to EU council directive 80/181/EEC // effective cross-sectional area according to EU council directive 80/181/EEC
// https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:01980L0181-20090527#page=10 // https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:01980L0181-20090527#page=10
// https://www.fedlex.admin.ch/eli/cc/1994/3109_3109_3109/de // https://www.fedlex.admin.ch/eli/cc/1994/3109_3109_3109/de
struct barn : named_scaled_unit<barn, "b", as_magnitude<ratio(1, 1, -28)>(), square_metre> {}; struct barn : named_scaled_unit<barn, "b", mag_power<10, -28>(), square_metre> {};
struct yocto_barn : prefixed_unit<yocto_barn, yocto, barn> {}; struct yocto_barn : prefixed_unit<yocto_barn, yocto, barn> {};
struct zepto_barn : prefixed_unit<zepto_barn, zepto, barn> {}; struct zepto_barn : prefixed_unit<zepto_barn, zepto, barn> {};
struct atto_barn : prefixed_unit<atto_barn, atto, barn> {}; struct atto_barn : prefixed_unit<atto_barn, atto, barn> {};

View File

@@ -44,7 +44,7 @@ namespace units::isq::si::hep {
struct eV_per_c2 : struct eV_per_c2 :
named_scaled_unit<eV_per_c2, basic_symbol_text{"eV/c²", "eV/c^2"}, named_scaled_unit<eV_per_c2, basic_symbol_text{"eV/c²", "eV/c^2"},
as_magnitude<ratio(17'826'619'216'279, 1'000'000'000'000, -35)>(), kilogram> {}; mag<ratio(17'826'619'216'279, 1'000'000'000'000)>() * mag_power<10, -35>(), kilogram> {};
struct feV_per_c2 : prefixed_unit<feV_per_c2, femto, eV_per_c2> {}; struct feV_per_c2 : prefixed_unit<feV_per_c2, femto, eV_per_c2> {};
struct peV_per_c2 : prefixed_unit<peV_per_c2, pico, eV_per_c2> {}; struct peV_per_c2 : prefixed_unit<peV_per_c2, pico, eV_per_c2> {};
struct neV_per_c2 : prefixed_unit<neV_per_c2, nano, eV_per_c2> {}; struct neV_per_c2 : prefixed_unit<neV_per_c2, nano, eV_per_c2> {};
@@ -60,11 +60,14 @@ struct PeV_per_c2 : prefixed_unit<PeV_per_c2, peta, eV_per_c2> {};
struct EeV_per_c2 : prefixed_unit<EeV_per_c2, exa, eV_per_c2> {}; struct EeV_per_c2 : prefixed_unit<EeV_per_c2, exa, eV_per_c2> {};
struct YeV_per_c2 : prefixed_unit<YeV_per_c2, yotta, eV_per_c2> {}; struct YeV_per_c2 : prefixed_unit<YeV_per_c2, yotta, eV_per_c2> {};
struct electron_mass : struct electron_mass :
named_scaled_unit<eV_per_c2, "m_e", as_magnitude<ratio(9'109'383'701'528, 1'000'000'000'000, -31)>(), kilogram> {}; named_scaled_unit<eV_per_c2, "m_e", mag<ratio(9'109'383'701'528, 1'000'000'000'000)>() * mag_power<10, -31>(),
kilogram> {};
struct proton_mass : struct proton_mass :
named_scaled_unit<eV_per_c2, "m_p", as_magnitude<ratio(1'672'621'923'695, 1'000'000'000'000, -27)>(), kilogram> {}; named_scaled_unit<eV_per_c2, "m_p", mag<ratio(1'672'621'923'695, 1'000'000'000'000)>() * mag_power<10, -27>(),
kilogram> {};
struct neutron_mass : struct neutron_mass :
named_scaled_unit<eV_per_c2, "m_n", as_magnitude<ratio(1'674'927'498'049, 1'000'000'000'000, -27)>(), kilogram> {}; named_scaled_unit<eV_per_c2, "m_n", mag<ratio(1'674'927'498'049, 1'000'000'000'000)>() * mag_power<10, -27>(),
kilogram> {};
struct dim_mass : isq::dim_mass<eV_per_c2> {}; struct dim_mass : isq::dim_mass<eV_per_c2> {};

View File

@@ -41,7 +41,7 @@ namespace units::isq::si::hep {
struct kilogram_metre_per_second : derived_unit<kilogram_metre_per_second> {}; struct kilogram_metre_per_second : derived_unit<kilogram_metre_per_second> {};
struct eV_per_c : struct eV_per_c :
named_scaled_unit<eV_per_c, "eV/c", as_magnitude<ratio(5'344'285'992'678, 1'000'000'000'000, -35)>(), named_scaled_unit<eV_per_c, "eV/c", mag<ratio(5'344'285'992'678, 1'000'000'000'000)>() * mag_power<10, -35>(),
kilogram_metre_per_second> {}; kilogram_metre_per_second> {};
struct feV_per_c : prefixed_unit<feV_per_c, femto, eV_per_c> {}; struct feV_per_c : prefixed_unit<feV_per_c, femto, eV_per_c> {};
struct peV_per_c : prefixed_unit<peV_per_c, pico, eV_per_c> {}; struct peV_per_c : prefixed_unit<peV_per_c, pico, eV_per_c> {};

View File

@@ -36,13 +36,13 @@
namespace units::isq::si::iau { namespace units::isq::si::iau {
// https://en.wikipedia.org/wiki/Light-year // https://en.wikipedia.org/wiki/Light-year
struct light_year : named_scaled_unit<light_year, "ly", as_magnitude<9460730472580800>(), si::metre> {}; struct light_year : named_scaled_unit<light_year, "ly", mag<9460730472580800>(), si::metre> {};
// https://en.wikipedia.org/wiki/Parsec // https://en.wikipedia.org/wiki/Parsec
struct parsec : named_scaled_unit<parsec, "pc", as_magnitude<30'856'775'814'913'673>(), si::metre> {}; struct parsec : named_scaled_unit<parsec, "pc", mag<30'856'775'814'913'673>(), si::metre> {};
// https://en.wikipedia.org/wiki/Angstrom // https://en.wikipedia.org/wiki/Angstrom
struct angstrom : named_scaled_unit<angstrom, "angstrom", as_magnitude<ratio(1, 1, -10)>(), si::metre> {}; struct angstrom : named_scaled_unit<angstrom, "angstrom", mag_power<10, -10>(), si::metre> {};
#ifndef UNITS_NO_LITERALS #ifndef UNITS_NO_LITERALS

View File

@@ -35,10 +35,10 @@
namespace units::isq::si::imperial { namespace units::isq::si::imperial {
// https://en.wikipedia.org/wiki/Chain_(unit) // https://en.wikipedia.org/wiki/Chain_(unit)
struct chain : named_scaled_unit<chain, "ch", as_magnitude<22>(), si::international::yard> {}; struct chain : named_scaled_unit<chain, "ch", mag<22>(), si::international::yard> {};
// https://en.wikipedia.org/wiki/Rod_(unit) // https://en.wikipedia.org/wiki/Rod_(unit)
struct rod : named_scaled_unit<rod, "rd", as_magnitude<ratio(1, 4)>(), chain> {}; struct rod : named_scaled_unit<rod, "rd", mag<ratio(1, 4)>(), chain> {};
#ifndef UNITS_NO_LITERALS #ifndef UNITS_NO_LITERALS

View File

@@ -37,30 +37,30 @@ namespace units::isq::si::international {
// si::international yard // si::international yard
// https://en.wikipedia.org/wiki/International_yard_and_pound // https://en.wikipedia.org/wiki/International_yard_and_pound
struct yard : named_scaled_unit<yard, "yd", as_magnitude<ratio(9'144, 1'000, -1)>(), si::metre> {}; struct yard : named_scaled_unit<yard, "yd", mag<ratio{9'144, 10'000}>(), si::metre> {};
// si::international foot // si::international foot
// https://en.wikipedia.org/wiki/Foot_(unit)#International_foot // https://en.wikipedia.org/wiki/Foot_(unit)#International_foot
struct foot : named_scaled_unit<foot, "ft", as_magnitude<ratio(1, 3)>(), yard> {}; struct foot : named_scaled_unit<foot, "ft", mag<ratio(1, 3)>(), yard> {};
// https://en.wikipedia.org/wiki/Fathom#International_fathom // https://en.wikipedia.org/wiki/Fathom#International_fathom
struct fathom : named_scaled_unit<fathom, "fathom", as_magnitude<2>(), yard> {}; struct fathom : named_scaled_unit<fathom, "fathom", mag<2>(), yard> {};
// si::international inch // si::international inch
// https://en.wikipedia.org/wiki/Inch#Equivalences // https://en.wikipedia.org/wiki/Inch#Equivalences
struct inch : named_scaled_unit<inch, "in", as_magnitude<ratio(1, 36)>(), yard> {}; struct inch : named_scaled_unit<inch, "in", mag<ratio(1, 36)>(), yard> {};
// intrnational mile // intrnational mile
// https://en.wikipedia.org/wiki/Mile#International_mile // https://en.wikipedia.org/wiki/Mile#International_mile
struct mile : named_scaled_unit<mile, "mi", as_magnitude<ratio(25'146, 15'625)>(), si::kilometre> {}; struct mile : named_scaled_unit<mile, "mi", mag<ratio(25'146, 15'625)>(), si::kilometre> {};
// si::international nautical mile // si::international nautical mile
// https://en.wikipedia.org/wiki/Nautical_mile // https://en.wikipedia.org/wiki/Nautical_mile
struct nautical_mile : named_scaled_unit<nautical_mile, "mi(naut)", as_magnitude<1852>(), si::metre> {}; struct nautical_mile : named_scaled_unit<nautical_mile, "mi(naut)", mag<1852>(), si::metre> {};
// thou // thou
// https://en.wikipedia.org/wiki/Thousandth_of_an_inch // https://en.wikipedia.org/wiki/Thousandth_of_an_inch
struct thou : named_scaled_unit<thou, "thou", as_magnitude<ratio(1, 1000)>(), inch> {}; struct thou : named_scaled_unit<thou, "thou", mag<ratio(1, 1000)>(), inch> {};
// mil - different name for thou // mil - different name for thou
// https://en.wikipedia.org/wiki/Thousandth_of_an_inch // https://en.wikipedia.org/wiki/Thousandth_of_an_inch

View File

@@ -37,12 +37,13 @@ namespace units::isq::si::typographic {
// TODO Conflicts with (https://en.wikipedia.org/wiki/Pica_(typography)), verify correctness of below conversion factors // TODO Conflicts with (https://en.wikipedia.org/wiki/Pica_(typography)), verify correctness of below conversion factors
// and provide hyperlinks to definitions // and provide hyperlinks to definitions
struct pica_comp : struct pica_comp : named_scaled_unit<pica_comp, "pica(comp)", mag<4'233'333>() * mag_power<10, -9>(), si::metre> {};
named_scaled_unit<pica_comp, "pica(comp)", as_magnitude<ratio(4233333, 1000000, -3)>(), si::metre> {}; struct pica_prn :
struct pica_prn : named_scaled_unit<pica_prn, "pica(prn)", as_magnitude<ratio(2108759, 500000, -3)>(), si::metre> {}; named_scaled_unit<pica_prn, "pica(prn)", mag<ratio(2108759, 500000)>() * mag_power<10, -3>(), si::metre> {};
struct point_comp : struct point_comp :
named_scaled_unit<point_comp, "point(comp)", as_magnitude<ratio(1763889, 500000, -4)>(), si::metre> {}; named_scaled_unit<point_comp, "point(comp)", mag<ratio(1763889, 500000)>() * mag_power<10, -4>(), si::metre> {};
struct point_prn : named_scaled_unit<point_prn, "point(prn)", as_magnitude<ratio(1757299, 500000, -4)>(), si::metre> {}; struct point_prn :
named_scaled_unit<point_prn, "point(prn)", mag<ratio(1757299, 500000)>() * mag_power<10, -4>(), si::metre> {};
#ifndef UNITS_NO_LITERALS #ifndef UNITS_NO_LITERALS

View File

@@ -36,14 +36,14 @@ namespace units::isq::si::uscs {
// https://en.wikipedia.org/wiki/Foot_(unit)#US_survey_foot // https://en.wikipedia.org/wiki/Foot_(unit)#US_survey_foot
// https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6 // https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6
struct foot : named_scaled_unit<foot, "ft(us)", as_magnitude<ratio(1'200, 3'937)>(), si::metre> {}; struct foot : named_scaled_unit<foot, "ft(us)", mag<ratio(1'200, 3'937)>(), si::metre> {};
// https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6 // https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6
struct fathom : named_scaled_unit<fathom, "fathom(us)", as_magnitude<6>(), foot> {}; struct fathom : named_scaled_unit<fathom, "fathom(us)", mag<6>(), foot> {};
// https://en.wikipedia.org/wiki/Mile#U.S._survey_mile // https://en.wikipedia.org/wiki/Mile#U.S._survey_mile
// https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6 // https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6
struct mile : named_scaled_unit<mile, "mi(us)", as_magnitude<5280>(), foot> {}; struct mile : named_scaled_unit<mile, "mi(us)", mag<5280>(), foot> {};
#ifndef UNITS_NO_LITERALS #ifndef UNITS_NO_LITERALS

View File

@@ -58,7 +58,7 @@ struct exakatal : prefixed_unit<exakatal, exa, katal> {};
struct zettakatal : prefixed_unit<zettakatal, zetta, katal> {}; struct zettakatal : prefixed_unit<zettakatal, zetta, katal> {};
struct yottakatal : prefixed_unit<yottakatal, yotta, katal> {}; struct yottakatal : prefixed_unit<yottakatal, yotta, katal> {};
struct enzyme_unit : named_scaled_unit<enzyme_unit, "U", as_magnitude<ratio(1, 60, -6)>(), katal> {}; struct enzyme_unit : named_scaled_unit<enzyme_unit, "U", mag<ratio(1, 60)>() * mag_power<10, -6>(), katal> {};
struct dim_catalytic_activity : struct dim_catalytic_activity :
isq::dim_catalytic_activity<dim_catalytic_activity, katal, dim_time, dim_amount_of_substance> {}; isq::dim_catalytic_activity<dim_catalytic_activity, katal, dim_time, dim_amount_of_substance> {};

View File

@@ -56,7 +56,7 @@ struct yottajoule : prefixed_unit<yottajoule, yotta, joule> {};
// N.B. electron charge (and eV) is an exact constant: // N.B. electron charge (and eV) is an exact constant:
// https://www.bipm.org/documents/20126/41483022/SI-Brochure-9.pdf#page=147 // https://www.bipm.org/documents/20126/41483022/SI-Brochure-9.pdf#page=147
struct electronvolt : struct electronvolt :
named_scaled_unit<electronvolt, "eV", as_magnitude<ratio(1'602'176'634, 1'000'000'000, -19)>(), joule> {}; named_scaled_unit<electronvolt, "eV", mag<ratio(1'602'176'634, 1'000'000'000)>() * mag_power<10, -19>(), joule> {};
struct gigaelectronvolt : prefixed_unit<gigaelectronvolt, giga, electronvolt> {}; struct gigaelectronvolt : prefixed_unit<gigaelectronvolt, giga, electronvolt> {};
struct dim_energy : isq::dim_energy<dim_energy, joule, dim_force, dim_length> {}; struct dim_energy : isq::dim_energy<dim_energy, joule, dim_force, dim_length> {};

View File

@@ -56,7 +56,7 @@ struct exametre : prefixed_unit<exametre, exa, metre> {};
struct zettametre : prefixed_unit<zettametre, zetta, metre> {}; struct zettametre : prefixed_unit<zettametre, zetta, metre> {};
struct yottametre : prefixed_unit<yottametre, yotta, metre> {}; struct yottametre : prefixed_unit<yottametre, yotta, metre> {};
struct astronomical_unit : named_scaled_unit<astronomical_unit, "au", as_magnitude<149'597'870'700>(), metre> {}; struct astronomical_unit : named_scaled_unit<astronomical_unit, "au", mag<149'597'870'700>(), metre> {};
struct dim_length : isq::dim_length<metre> {}; struct dim_length : isq::dim_length<metre> {};

View File

@@ -36,7 +36,7 @@ namespace units::isq::si {
// TODO Is this correct? Should we account for steradian here? How? // TODO Is this correct? Should we account for steradian here? How?
struct lumen : named_scaled_unit<lumen, "lm", as_magnitude<ratio(1, 683)>(), watt> {}; struct lumen : named_scaled_unit<lumen, "lm", mag<ratio(1, 683)>(), watt> {};
using dim_luminous_flux = dim_power; using dim_luminous_flux = dim_power;

View File

@@ -56,7 +56,7 @@ struct exatesla : prefixed_unit<exatesla, exa, tesla> {};
struct zettatesla : prefixed_unit<zettatesla, zetta, tesla> {}; struct zettatesla : prefixed_unit<zettatesla, zetta, tesla> {};
struct yottatesla : prefixed_unit<yottatesla, yotta, tesla> {}; struct yottatesla : prefixed_unit<yottatesla, yotta, tesla> {};
struct gauss : named_scaled_unit<gauss, "G", as_magnitude<ratio(1, 10'000)>(), tesla> {}; struct gauss : named_scaled_unit<gauss, "G", mag<ratio(1, 10'000)>(), tesla> {};
struct dim_magnetic_induction : struct dim_magnetic_induction :
isq::dim_magnetic_induction<dim_magnetic_induction, tesla, dim_voltage, dim_time, dim_length> {}; isq::dim_magnetic_induction<dim_magnetic_induction, tesla, dim_voltage, dim_time, dim_length> {};

View File

@@ -79,7 +79,8 @@ struct zettatonne : prefixed_unit<zettatonne, zetta, tonne> {};
struct yottatonne : prefixed_unit<yottatonne, yotta, tonne> {}; struct yottatonne : prefixed_unit<yottatonne, yotta, tonne> {};
struct dalton : struct dalton :
named_scaled_unit<dalton, "Da", as_magnitude<ratio(16'605'390'666'050, 10'000'000'000'000, -27)>(), kilogram> {}; named_scaled_unit<dalton, "Da", mag<ratio(16'605'390'666'050, 10'000'000'000'000)>() * mag_power<10, -27>(),
kilogram> {};
struct dim_mass : isq::dim_mass<kilogram> {}; struct dim_mass : isq::dim_mass<kilogram> {};

View File

@@ -27,26 +27,26 @@
namespace units::isq::si { namespace units::isq::si {
// clang-format off // clang-format off
struct yocto : prefix<yocto, "y", pow<-24>(as_magnitude<10>())> {}; struct yocto : prefix<yocto, "y", pow<-24>(mag<10>())> {};
struct zepto : prefix<zepto, "z", pow<-21>(as_magnitude<10>())> {}; struct zepto : prefix<zepto, "z", pow<-21>(mag<10>())> {};
struct atto : prefix<atto, "a", pow<-18>(as_magnitude<10>())> {}; struct atto : prefix<atto, "a", pow<-18>(mag<10>())> {};
struct femto : prefix<femto, "f", pow<-15>(as_magnitude<10>())> {}; struct femto : prefix<femto, "f", pow<-15>(mag<10>())> {};
struct pico : prefix<pico, "p", pow<-12>(as_magnitude<10>())> {}; struct pico : prefix<pico, "p", pow<-12>(mag<10>())> {};
struct nano : prefix<nano, "n", pow<-9>(as_magnitude<10>())> {}; struct nano : prefix<nano, "n", pow<-9>(mag<10>())> {};
struct micro : prefix<micro, basic_symbol_text{"\u00b5", "u"}, pow<-6>(as_magnitude<10>())> {}; struct micro : prefix<micro, basic_symbol_text{"\u00b5", "u"}, pow<-6>(mag<10>())> {};
struct milli : prefix<milli, "m", pow<-3>(as_magnitude<10>())> {}; struct milli : prefix<milli, "m", pow<-3>(mag<10>())> {};
struct centi : prefix<centi, "c", pow<-2>(as_magnitude<10>())> {}; struct centi : prefix<centi, "c", pow<-2>(mag<10>())> {};
struct deci : prefix<deci, "d", pow<-1>(as_magnitude<10>())> {}; struct deci : prefix<deci, "d", pow<-1>(mag<10>())> {};
struct deca : prefix<deca, "da", pow<1>(as_magnitude<10>())> {}; struct deca : prefix<deca, "da", pow<1>(mag<10>())> {};
struct hecto : prefix<hecto, "h", pow<2>(as_magnitude<10>())> {}; struct hecto : prefix<hecto, "h", pow<2>(mag<10>())> {};
struct kilo : prefix<kilo, "k", pow<3>(as_magnitude<10>())> {}; struct kilo : prefix<kilo, "k", pow<3>(mag<10>())> {};
struct mega : prefix<mega, "M", pow<6>(as_magnitude<10>())> {}; struct mega : prefix<mega, "M", pow<6>(mag<10>())> {};
struct giga : prefix<giga, "G", pow<9>(as_magnitude<10>())> {}; struct giga : prefix<giga, "G", pow<9>(mag<10>())> {};
struct tera : prefix<tera, "T", pow<12>(as_magnitude<10>())> {}; struct tera : prefix<tera, "T", pow<12>(mag<10>())> {};
struct peta : prefix<peta, "P", pow<15>(as_magnitude<10>())> {}; struct peta : prefix<peta, "P", pow<15>(mag<10>())> {};
struct exa : prefix<exa, "E", pow<18>(as_magnitude<10>())> {}; struct exa : prefix<exa, "E", pow<18>(mag<10>())> {};
struct zetta : prefix<zetta, "Z", pow<21>(as_magnitude<10>())> {}; struct zetta : prefix<zetta, "Z", pow<21>(mag<10>())> {};
struct yotta : prefix<yotta, "Y", pow<24>(as_magnitude<10>())> {}; struct yotta : prefix<yotta, "Y", pow<24>(mag<10>())> {};
// clang-format on // clang-format on
} // namespace units::isq::si } // namespace units::isq::si

View File

@@ -43,9 +43,9 @@ struct picosecond : prefixed_unit<picosecond, pico, second> {};
struct nanosecond : prefixed_unit<nanosecond, nano, second> {}; struct nanosecond : prefixed_unit<nanosecond, nano, second> {};
struct microsecond : prefixed_unit<microsecond, micro, second> {}; struct microsecond : prefixed_unit<microsecond, micro, second> {};
struct millisecond : prefixed_unit<millisecond, milli, second> {}; struct millisecond : prefixed_unit<millisecond, milli, second> {};
struct minute : named_scaled_unit<minute, "min", as_magnitude<60>(), second> {}; struct minute : named_scaled_unit<minute, "min", mag<60>(), second> {};
struct hour : named_scaled_unit<hour, "h", as_magnitude<60>(), minute> {}; struct hour : named_scaled_unit<hour, "h", mag<60>(), minute> {};
struct day : named_scaled_unit<day, "d", as_magnitude<24>(), hour> {}; struct day : named_scaled_unit<day, "d", mag<24>(), hour> {};
struct dim_time : isq::dim_time<second> {}; struct dim_time : isq::dim_time<second> {};

View File

@@ -84,7 +84,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
{ {
SECTION("in terms of base units") SECTION("in terms of base units")
{ {
const length<scaled_unit<pow<6>(as_magnitude<10>()), metre>> q(123); const length<scaled_unit<pow<6>(mag<10>()), metre>> q(123);
os << q; os << q;
SECTION("iostream") { CHECK(os.str() == "123 Mm"); } SECTION("iostream") { CHECK(os.str() == "123 Mm"); }
@@ -96,7 +96,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
SECTION("in terms of derived units") SECTION("in terms of derived units")
{ {
const energy<scaled_unit<pow<-2>(as_magnitude<10>()), joule>> q(60); const energy<scaled_unit<pow<-2>(mag<10>()), joule>> q(60);
os << q; os << q;
SECTION("iostream") { CHECK(os.str() == "60 cJ"); } SECTION("iostream") { CHECK(os.str() == "60 cJ"); }

View File

@@ -327,7 +327,7 @@ TEST_CASE("std::format on synthesized unit symbols", "[text][fmt]")
SECTION("unknown scaled unit with reference different than the dimension's coherent unit") SECTION("unknown scaled unit with reference different than the dimension's coherent unit")
{ {
// TODO(chogg): Reinstate after format/Magnitude redesign. // TODO(chogg): Reinstate after format/Magnitude redesign.
// constexpr auto mag = units::as_magnitude<units::ratio{2, 3}>(); // constexpr auto mag = units::mag<units::ratio{2, 3}>();
// CHECK(STD_FMT::format("{}", mass<units::scaled_unit<mag, gram>>(1)) == "1 [2/3 × 10⁻³] kg"); // CHECK(STD_FMT::format("{}", mass<units::scaled_unit<mag, gram>>(1)) == "1 [2/3 × 10⁻³] kg");
// CHECK(STD_FMT::format("{:%Q %Aq}", mass<units::scaled_unit<mag, gram>>(1)) == "1 [2/3 x 10^-3] kg"); // CHECK(STD_FMT::format("{:%Q %Aq}", mass<units::scaled_unit<mag, gram>>(1)) == "1 [2/3 x 10^-3] kg");
} }

View File

@@ -66,7 +66,7 @@ void check_same_type_and_value(T actual, U expected)
template<ratio R> template<ratio R>
void check_ratio_round_trip_is_identity() void check_ratio_round_trip_is_identity()
{ {
constexpr Magnitude auto m = as_magnitude<R>(); constexpr Magnitude auto m = mag<R>();
constexpr ratio round_trip = ratio{ constexpr ratio round_trip = ratio{
get_value<std::intmax_t>(numerator(m)), get_value<std::intmax_t>(numerator(m)),
get_value<std::intmax_t>(denominator(m)), get_value<std::intmax_t>(denominator(m)),
@@ -125,10 +125,10 @@ TEST_CASE("base_power")
SECTION("product with inverse equals identity") SECTION("product with inverse equals identity")
{ {
auto check_product_with_inverse_is_identity = [](auto x) { CHECK(x * pow<-1>(x) == as_magnitude<1>()); }; auto check_product_with_inverse_is_identity = [](auto x) { CHECK(x * pow<-1>(x) == mag<1>()); };
check_product_with_inverse_is_identity(as_magnitude<3>()); check_product_with_inverse_is_identity(mag<3>());
check_product_with_inverse_is_identity(as_magnitude<ratio{4, 17}>()); check_product_with_inverse_is_identity(mag<ratio{4, 17}>());
check_product_with_inverse_is_identity(pi_to_the<ratio{-22, 7}>()); check_product_with_inverse_is_identity(pi_to_the<ratio{-22, 7}>());
} }
@@ -144,28 +144,21 @@ TEST_CASE("make_ratio performs prime factorization correctly")
{ {
SECTION("Performs prime factorization when denominator is 1") SECTION("Performs prime factorization when denominator is 1")
{ {
CHECK(as_magnitude<1>() == magnitude<>{}); CHECK(mag<1>() == magnitude<>{});
CHECK(as_magnitude<2>() == magnitude<base_power{2}>{}); CHECK(mag<2>() == magnitude<base_power{2}>{});
CHECK(as_magnitude<3>() == magnitude<base_power{3}>{}); CHECK(mag<3>() == magnitude<base_power{3}>{});
CHECK(as_magnitude<4>() == magnitude<base_power{2, 2}>{}); CHECK(mag<4>() == magnitude<base_power{2, 2}>{});
CHECK(as_magnitude<792>() == magnitude<base_power{2, 3}, base_power{3, 2}, base_power{11}>{}); CHECK(mag<792>() == magnitude<base_power{2, 3}, base_power{3, 2}, base_power{11}>{});
} }
SECTION("Supports fractions") { CHECK(as_magnitude<ratio{5, 8}>() == magnitude<base_power{2, -3}, base_power{5}>{}); } SECTION("Supports fractions") { CHECK(mag<ratio{5, 8}>() == magnitude<base_power{2, -3}, base_power{5}>{}); }
SECTION("Supports nonzero exp")
{
constexpr ratio r{3, 1, 2};
REQUIRE(r.exp == 2);
CHECK(as_magnitude<r>() == as_magnitude<300>());
}
SECTION("Can handle prime factor which would be large enough to overflow int") SECTION("Can handle prime factor which would be large enough to overflow int")
{ {
// This was taken from a case which failed when we used `int` for our base to store prime numbers. // This was taken from a case which failed when we used `int` for our base to store prime numbers.
// The failure was due to a prime factor which is larger than 2^31. // The failure was due to a prime factor which is larger than 2^31.
as_magnitude<ratio(16'605'390'666'050, 10'000'000'000'000)>(); mag<ratio(16'605'390'666'050, 10'000'000'000'000)>();
} }
SECTION("Can bypass computing primes by providing known_first_factor<N>") SECTION("Can bypass computing primes by providing known_first_factor<N>")
@@ -176,7 +169,7 @@ TEST_CASE("make_ratio performs prime factorization correctly")
// In this case, we test that we can represent the largest prime that fits in a signed 64-bit int. The reason this // In this case, we test that we can represent the largest prime that fits in a signed 64-bit int. The reason this
// test can pass is that we have provided the answer, by specializing the `known_first_factor` variable template // test can pass is that we have provided the answer, by specializing the `known_first_factor` variable template
// above in this file. // above in this file.
as_magnitude<9'223'372'036'854'775'783>(); mag<9'223'372'036'854'775'783>();
} }
} }
@@ -184,7 +177,7 @@ TEST_CASE("magnitude converts to numerical value")
{ {
SECTION("Positive integer powers of integer bases give integer values") SECTION("Positive integer powers of integer bases give integer values")
{ {
constexpr auto mag_412 = as_magnitude<412>(); constexpr auto mag_412 = mag<412>();
check_same_type_and_value(get_value<int>(mag_412), 412); check_same_type_and_value(get_value<int>(mag_412), 412);
check_same_type_and_value(get_value<std::size_t>(mag_412), std::size_t{412}); check_same_type_and_value(get_value<std::size_t>(mag_412), std::size_t{412});
check_same_type_and_value(get_value<float>(mag_412), 412.0f); check_same_type_and_value(get_value<float>(mag_412), 412.0f);
@@ -193,7 +186,7 @@ TEST_CASE("magnitude converts to numerical value")
SECTION("Negative integer powers of integer bases compute correct values") SECTION("Negative integer powers of integer bases compute correct values")
{ {
constexpr auto mag_0p125 = as_magnitude<ratio{1, 8}>(); constexpr auto mag_0p125 = mag<ratio{1, 8}>();
check_same_type_and_value(get_value<float>(mag_0p125), 0.125f); check_same_type_and_value(get_value<float>(mag_0p125), 0.125f);
check_same_type_and_value(get_value<double>(mag_0p125), 0.125); check_same_type_and_value(get_value<double>(mag_0p125), 0.125);
} }
@@ -226,20 +219,20 @@ TEST_CASE("magnitude converts to numerical value")
// Naturally, we cannot actually write a test to verify a compiler error. But any of these can // Naturally, we cannot actually write a test to verify a compiler error. But any of these can
// be uncommented if desired to verify that it breaks the build. // be uncommented if desired to verify that it breaks the build.
// get_value<int8_t>(as_magnitude<412>()); // get_value<int8_t>(mag<412>());
// Would work for pow<62>: // Would work for pow<62>:
// get_value<int64_t>(pow<63>(as_magnitude<2>())); // get_value<int64_t>(pow<63>(mag<2>()));
// Would work for pow<63>: // Would work for pow<63>:
// get_value<uint64_t>(pow<64>(as_magnitude<2>())); // get_value<uint64_t>(pow<64>(mag<2>()));
get_value<double>(pow<308>(as_magnitude<10>())); // Compiles, correctly. get_value<double>(pow<308>(mag<10>())); // Compiles, correctly.
// get_value<double>(pow<309>(as_magnitude<10>())); // get_value<double>(pow<309>(mag<10>()));
// get_value<double>(pow<3099>(as_magnitude<10>())); // get_value<double>(pow<3099>(mag<10>()));
// get_value<double>(pow<3099999>(as_magnitude<10>())); // get_value<double>(pow<3099999>(mag<10>()));
auto sqrt_2 = pow<ratio{1, 2}>(as_magnitude<2>()); auto sqrt_2 = pow<ratio{1, 2}>(mag<2>());
CHECK(!is_integral(sqrt_2)); CHECK(!is_integral(sqrt_2));
// get_value<int>(sqrt_2); // get_value<int>(sqrt_2);
} }
@@ -249,46 +242,40 @@ TEST_CASE("Equality works for magnitudes")
{ {
SECTION("Equivalent ratios are equal") SECTION("Equivalent ratios are equal")
{ {
CHECK(as_magnitude<1>() == as_magnitude<1>()); CHECK(mag<1>() == mag<1>());
CHECK(as_magnitude<3>() == as_magnitude<3>()); CHECK(mag<3>() == mag<3>());
CHECK(as_magnitude<ratio{3, 4}>() == as_magnitude<ratio{9, 12}>()); CHECK(mag<ratio{3, 4}>() == mag<ratio{9, 12}>());
} }
SECTION("Different ratios are unequal") SECTION("Different ratios are unequal")
{ {
CHECK(as_magnitude<3>() != as_magnitude<5>()); CHECK(mag<3>() != mag<5>());
CHECK(as_magnitude<3>() != as_magnitude<ratio{3, 2}>()); CHECK(mag<3>() != mag<ratio{3, 2}>());
} }
SECTION("Supports constexpr") SECTION("Supports constexpr")
{ {
constexpr auto eq = (as_magnitude<ratio{4, 5}>() == as_magnitude<ratio{4, 3}>()); constexpr auto eq = (mag<ratio{4, 5}>() == mag<ratio{4, 3}>());
CHECK(!eq); CHECK(!eq);
} }
} }
TEST_CASE("Multiplication works for magnitudes") TEST_CASE("Multiplication works for magnitudes")
{ {
SECTION("Reciprocals reduce to null magnitude") SECTION("Reciprocals reduce to null magnitude") { CHECK(mag<ratio{3, 4}>() * mag<ratio{4, 3}>() == mag<1>()); }
{
CHECK(as_magnitude<ratio{3, 4}>() * as_magnitude<ratio{4, 3}>() == as_magnitude<1>());
}
SECTION("Products work as expected") SECTION("Products work as expected") { CHECK(mag<ratio{4, 5}>() * mag<ratio{4, 3}>() == mag<ratio{16, 15}>()); }
{
CHECK(as_magnitude<ratio{4, 5}>() * as_magnitude<ratio{4, 3}>() == as_magnitude<ratio{16, 15}>());
}
SECTION("Products handle pi correctly") SECTION("Products handle pi correctly")
{ {
CHECK(pi_to_the<1>() * as_magnitude<ratio{2, 3}>() * pi_to_the<ratio{-1, 2}>() == CHECK(pi_to_the<1>() * mag<ratio{2, 3}>() * pi_to_the<ratio{-1, 2}>() ==
magnitude<base_power{2}, base_power{3, -1}, base_power<pi_base>{ratio{1, 2}}>{}); magnitude<base_power{2}, base_power{3, -1}, base_power<pi_base>{ratio{1, 2}}>{});
} }
SECTION("Supports constexpr") SECTION("Supports constexpr")
{ {
constexpr auto p = as_magnitude<ratio{4, 5}>() * as_magnitude<ratio{4, 3}>(); constexpr auto p = mag<ratio{4, 5}>() * mag<ratio{4, 3}>();
CHECK(p == as_magnitude<ratio{16, 15}>()); CHECK(p == mag<ratio{16, 15}>());
} }
} }
@@ -296,20 +283,20 @@ TEST_CASE("Common Magnitude")
{ {
SECTION("Identity for identical magnitudes") SECTION("Identity for identical magnitudes")
{ {
CHECK(common_magnitude(as_magnitude<1>(), as_magnitude<1>()) == as_magnitude<1>()); CHECK(common_magnitude(mag<1>(), mag<1>()) == mag<1>());
CHECK(common_magnitude(as_magnitude<15>(), as_magnitude<15>()) == as_magnitude<15>()); CHECK(common_magnitude(mag<15>(), mag<15>()) == mag<15>());
CHECK(common_magnitude(pi_to_the<ratio{3, 4}>(), pi_to_the<ratio{3, 4}>()) == pi_to_the<ratio{3, 4}>()); CHECK(common_magnitude(pi_to_the<ratio{3, 4}>(), pi_to_the<ratio{3, 4}>()) == pi_to_the<ratio{3, 4}>());
} }
SECTION("Greatest Common Factor for integers") SECTION("Greatest Common Factor for integers")
{ {
CHECK(common_magnitude(as_magnitude<24>(), as_magnitude<36>()) == as_magnitude<12>()); CHECK(common_magnitude(mag<24>(), mag<36>()) == mag<12>());
CHECK(common_magnitude(as_magnitude<24>(), as_magnitude<37>()) == as_magnitude<1>()); CHECK(common_magnitude(mag<24>(), mag<37>()) == mag<1>());
} }
SECTION("Handles fractions") SECTION("Handles fractions")
{ {
CHECK(common_magnitude(as_magnitude<ratio{3, 8}>(), as_magnitude<ratio{5, 6}>()) == as_magnitude<ratio{1, 24}>()); CHECK(common_magnitude(mag<ratio{3, 8}>(), mag<ratio{5, 6}>()) == mag<ratio{1, 24}>());
} }
} }
@@ -317,19 +304,16 @@ TEST_CASE("Division works for magnitudes")
{ {
SECTION("Dividing anything by itself reduces to null magnitude") SECTION("Dividing anything by itself reduces to null magnitude")
{ {
CHECK(as_magnitude<ratio{3, 4}>() / as_magnitude<ratio{3, 4}>() == as_magnitude<1>()); CHECK(mag<ratio{3, 4}>() / mag<ratio{3, 4}>() == mag<1>());
CHECK(as_magnitude<15>() / as_magnitude<15>() == as_magnitude<1>()); CHECK(mag<15>() / mag<15>() == mag<1>());
} }
SECTION("Quotients work as expected") SECTION("Quotients work as expected") { CHECK(mag<ratio{4, 5}>() / mag<ratio{4, 3}>() == mag<ratio{3, 5}>()); }
{
CHECK(as_magnitude<ratio{4, 5}>() / as_magnitude<ratio{4, 3}>() == as_magnitude<ratio{3, 5}>());
}
SECTION("Supports constexpr") SECTION("Supports constexpr")
{ {
constexpr auto q = as_magnitude<ratio{4, 5}>() / as_magnitude<ratio{4, 3}>(); constexpr auto q = mag<ratio{4, 5}>() / mag<ratio{4, 3}>();
CHECK(q == as_magnitude<ratio{3, 5}>()); CHECK(q == mag<ratio{3, 5}>());
} }
} }
@@ -337,17 +321,17 @@ TEST_CASE("Can raise Magnitudes to rational powers")
{ {
SECTION("Anything to the 0 is 1") SECTION("Anything to the 0 is 1")
{ {
CHECK(pow<0>(as_magnitude<1>()) == as_magnitude<1>()); CHECK(pow<0>(mag<1>()) == mag<1>());
CHECK(pow<0>(as_magnitude<123>()) == as_magnitude<1>()); CHECK(pow<0>(mag<123>()) == mag<1>());
CHECK(pow<0>(as_magnitude<ratio{3, 4}>()) == as_magnitude<1>()); CHECK(pow<0>(mag<ratio{3, 4}>()) == mag<1>());
CHECK(pow<0>(pi_to_the<ratio{-1, 2}>()) == as_magnitude<1>()); CHECK(pow<0>(pi_to_the<ratio{-1, 2}>()) == mag<1>());
} }
SECTION("Anything to the 1 is itself") SECTION("Anything to the 1 is itself")
{ {
CHECK(pow<1>(as_magnitude<1>()) == as_magnitude<1>()); CHECK(pow<1>(mag<1>()) == mag<1>());
CHECK(pow<1>(as_magnitude<123>()) == as_magnitude<123>()); CHECK(pow<1>(mag<123>()) == mag<123>());
CHECK(pow<1>(as_magnitude<ratio{3, 4}>()) == as_magnitude<ratio{3, 4}>()); CHECK(pow<1>(mag<ratio{3, 4}>()) == mag<ratio{3, 4}>());
CHECK(pow<1>(pi_to_the<ratio{-1, 2}>()) == pi_to_the<ratio{-1, 2}>()); CHECK(pow<1>(pi_to_the<ratio{-1, 2}>()) == pi_to_the<ratio{-1, 2}>());
} }
@@ -366,11 +350,11 @@ TEST_CASE("can distinguish integral, rational, and irrational magnitudes")
CHECK(is_rational(m)); CHECK(is_rational(m));
}; };
check_rational_and_integral(magnitude<>{}); check_rational_and_integral(magnitude<>{});
check_rational_and_integral(as_magnitude<1>()); check_rational_and_integral(mag<1>());
check_rational_and_integral(as_magnitude<3>()); check_rational_and_integral(mag<3>());
check_rational_and_integral(as_magnitude<8>()); check_rational_and_integral(mag<8>());
check_rational_and_integral(as_magnitude<412>()); check_rational_and_integral(mag<412>());
check_rational_and_integral(as_magnitude<ratio{1, 1}>()); check_rational_and_integral(mag<ratio{1, 1}>());
} }
SECTION("Fractional magnitudes are rational, but not integral") SECTION("Fractional magnitudes are rational, but not integral")
@@ -379,8 +363,8 @@ TEST_CASE("can distinguish integral, rational, and irrational magnitudes")
CHECK(!is_integral(m)); CHECK(!is_integral(m));
CHECK(is_rational(m)); CHECK(is_rational(m));
}; };
check_rational_but_not_integral(as_magnitude<ratio{1, 2}>()); check_rational_but_not_integral(mag<ratio{1, 2}>());
check_rational_but_not_integral(as_magnitude<ratio{5, 8}>()); check_rational_but_not_integral(mag<ratio{5, 8}>());
} }
} }
@@ -397,17 +381,17 @@ TEST_CASE("Constructing ratio from rational magnitude")
SECTION("Rational magnitude converts to ratio") SECTION("Rational magnitude converts to ratio")
{ {
constexpr ratio r = as_ratio(as_magnitude<ratio{22, 7}>()); constexpr ratio r = as_ratio(mag<ratio{22, 7}>());
CHECK(r == ratio{22, 7}); CHECK(r == ratio{22, 7});
} }
SECTION("Irrational magnitude does not convert to ratio") SECTION("Irrational magnitude does not convert to ratio")
{ {
// The following code should not compile. // The following code should not compile.
// as_ratio(pow<ratio{1, 2}>(as_magnitude<2>())); // as_ratio(pow<ratio{1, 2}>(mag<2>()));
// The following code should not compile. // The following code should not compile.
// as_ratio(as_magnitude<180>() / pi_to_the<1>()); // as_ratio(mag<180>() / pi_to_the<1>());
} }
} }
@@ -614,26 +598,26 @@ TEST_CASE("extract_power_of_10")
{ {
SECTION("Picks out positive powers") SECTION("Picks out positive powers")
{ {
CHECK(extract_power_of_10(as_magnitude<10>()) == 1); CHECK(extract_power_of_10(mag<10>()) == 1);
CHECK(extract_power_of_10(as_magnitude<20>()) == 1); CHECK(extract_power_of_10(mag<20>()) == 1);
CHECK(extract_power_of_10(as_magnitude<40>()) == 1); CHECK(extract_power_of_10(mag<40>()) == 1);
CHECK(extract_power_of_10(as_magnitude<50>()) == 1); CHECK(extract_power_of_10(mag<50>()) == 1);
CHECK(extract_power_of_10(as_magnitude<100>()) == 2); CHECK(extract_power_of_10(mag<100>()) == 2);
} }
SECTION("Picks out negative powers") SECTION("Picks out negative powers")
{ {
constexpr auto ONE = as_magnitude<1>(); constexpr auto ONE = mag<1>();
CHECK(extract_power_of_10(ONE / as_magnitude<10>()) == -1); CHECK(extract_power_of_10(ONE / mag<10>()) == -1);
CHECK(extract_power_of_10(ONE / as_magnitude<20>()) == -1); CHECK(extract_power_of_10(ONE / mag<20>()) == -1);
CHECK(extract_power_of_10(ONE / as_magnitude<40>()) == -1); CHECK(extract_power_of_10(ONE / mag<40>()) == -1);
CHECK(extract_power_of_10(ONE / as_magnitude<50>()) == -1); CHECK(extract_power_of_10(ONE / mag<50>()) == -1);
CHECK(extract_power_of_10(ONE / as_magnitude<100>()) == -2); CHECK(extract_power_of_10(ONE / mag<100>()) == -2);
} }
SECTION("Zero if signs disagree") { CHECK(extract_power_of_10(as_magnitude<2>() / as_magnitude<5>()) == 0); } SECTION("Zero if signs disagree") { CHECK(extract_power_of_10(mag<2>() / mag<5>()) == 0); }
SECTION("Handles rational powers") { CHECK(extract_power_of_10(sqrt(as_magnitude<1000>())) == 1); } SECTION("Handles rational powers") { CHECK(extract_power_of_10(sqrt(mag<1000>())) == 1); }
} }
} // namespace } // namespace

View File

@@ -22,6 +22,7 @@
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
#include <units/isq/si/area.h> #include <units/isq/si/area.h>
#include <units/isq/si/cgs/length.h>
#include <units/isq/si/length.h> #include <units/isq/si/length.h>
#include <units/isq/si/time.h> #include <units/isq/si/time.h>
#include <units/isq/si/volume.h> #include <units/isq/si/volume.h>
@@ -30,7 +31,7 @@
using namespace units; using namespace units;
using namespace units::isq; using namespace units::isq;
using namespace units::isq::si; using namespace units::isq::si::literals;
// classical // classical
@@ -186,10 +187,10 @@ TEST_CASE("ceil functions", "[ceil]")
{ {
REQUIRE(ceil<si::second>(1999._q_ms) == 2_q_s); REQUIRE(ceil<si::second>(1999._q_ms) == 2_q_s);
} }
// TODO does not work, probably due to a bug in fpow10() see #311 SECTION("ceil -1000. milliseconds with target unit second should be -1 second")
// SECTION ("ceil -1000. milliseconds with target unit second should be -1 second") { {
// REQUIRE(ceil<si::second>(-1000._q_ms) == -1_q_s); REQUIRE(ceil<si::second>(-1000._q_ms) == -1_q_s);
// } }
SECTION("ceil -999. milliseconds with target unit second should be 0 seconds") SECTION("ceil -999. milliseconds with target unit second should be 0 seconds")
{ {
REQUIRE(ceil<si::second>(-999._q_ms) == 0_q_s); REQUIRE(ceil<si::second>(-999._q_ms) == 0_q_s);
@@ -363,3 +364,24 @@ TEMPLATE_PRODUCT_TEST_CASE_SIG("detail::iroot<N>()", "[math][pow][iroot]", (std:
ROOT_TEST_CASE(CompileRoot) ROOT_TEST_CASE(CompileRoot)
ROOT_TEST_CASE(RuntimeRoot) ROOT_TEST_CASE(RuntimeRoot)
TEST_CASE("hypot functions", "[hypot]")
{
using namespace units::aliases::isq::si;
SECTION("hypot should work on the same quantities")
{
REQUIRE(hypot(km<>(3.), km<>(4.)) == km<>(5.));
REQUIRE(hypot(km<>(2.), km<>(3.), km<>(6.)) == km<>(7.));
}
SECTION("hypot should work with different units of the same dimension")
{
REQUIRE(hypot(km<>(3.), m<>(4000.)) == km<>(5.));
REQUIRE(hypot(km<>(2.), m<>(3000.), km<>(6.)) == km<>(7.));
}
SECTION("hypot should work with different but equivalent dimensions")
{
REQUIRE(hypot(km<>(3.), cgs::length::cm<>(400'000.)) == km<>(5.));
REQUIRE(hypot(km<>(2.), cgs::length::cm<>(300'000.), km<>(6.)) == km<>(7.));
}
}

View File

@@ -58,7 +58,8 @@ static_assert(!Area<si::time<si::second>>);
static_assert(Volume<si::volume<si::cubic_metre>>); static_assert(Volume<si::volume<si::cubic_metre>>);
static_assert(!Volume<si::area<si::square_metre>>); static_assert(!Volume<si::area<si::square_metre>>);
#if UNITS_DOWNCAST_MODE == 0 #if UNITS_DOWNCAST_MODE == 0
static_assert(Volume<quantity<unknown_dimension<exponent<si::dim_length, 3>>, unknown_coherent_unit>>); static_assert(
Volume<quantity<unknown_dimension<exponent<si::dim_length, 3>>, unknown_coherent_unit<exponent<si::dim_length, 3>>>>);
#endif #endif
static_assert(Speed<si::speed<si::metre_per_second>>); static_assert(Speed<si::speed<si::metre_per_second>>);
@@ -68,21 +69,23 @@ static_assert(Acceleration<si::acceleration<si::metre_per_second_sq>>);
static_assert(!Acceleration<si::time<si::second>>); static_assert(!Acceleration<si::time<si::second>>);
#if UNITS_DOWNCAST_MODE == 0 #if UNITS_DOWNCAST_MODE == 0
static_assert(Acceleration<quantity<unknown_dimension<exponent<si::dim_length, 1>, exponent<si::dim_time, -2>>, static_assert(Acceleration<quantity<unknown_dimension<exponent<si::dim_length, 1>, exponent<si::dim_time, -2>>,
unknown_coherent_unit>>); unknown_coherent_unit<exponent<si::dim_length, 1>, exponent<si::dim_time, -2>>>>);
#endif #endif
static_assert(Force<si::force<si::newton>>); static_assert(Force<si::force<si::newton>>);
static_assert(!Force<si::time<si::second>>); static_assert(!Force<si::time<si::second>>);
#if UNITS_DOWNCAST_MODE == 0 #if UNITS_DOWNCAST_MODE == 0
// static_assert(Force<quantity<unknown_dimension<exponent<si::dim_length, 1>, exponent<si::dim_time, -2>, // static_assert(Force<quantity<unknown_dimension<exponent<si::dim_length, 1>, exponent<si::dim_time, -2>,
// exponent<si::dim_mass, 1>>, unknown_coherent_unit>>); // exponent<si::dim_mass, 1>>, unknown_coherent_unit<exponent<si::dim_length, 1>,
// exponent<si::dim_time, -2>>>);
#endif #endif
static_assert(Energy<si::energy<si::joule>>); static_assert(Energy<si::energy<si::joule>>);
static_assert(!Energy<si::time<si::second>>); static_assert(!Energy<si::time<si::second>>);
#if UNITS_DOWNCAST_MODE == 0 #if UNITS_DOWNCAST_MODE == 0
// static_assert(Energy<quantity<unknown_dimension<exponent<si::dim_mass, 1>, exponent<si::dim_length, 2>, // static_assert(Energy<quantity<unknown_dimension<exponent<si::dim_mass, 1>, exponent<si::dim_length, 2>,
// exponent<si::dim_time, -3>>, unknown_coherent_unit>>); // exponent<si::dim_time, -3>>, unknown_coherent_unit<exponent<si::dim_mass, 1>,
// exponent<si::dim_length, 2>>>);
#endif #endif
static_assert(Power<si::power<si::watt>>); static_assert(Power<si::power<si::watt>>);

View File

@@ -456,9 +456,9 @@ concept invalid_compound_assignments =
requires !requires { w *= m; }; requires !requires { w *= m; };
requires !requires { w /= m; }; requires !requires { w /= m; };
requires !requires { w %= m; }; requires !requires { w %= m; };
requires !requires { w *= quantity_kind<downcast_kind<Width, dim_one>, scaled_unit<as_magnitude<1000>(), one>, int>{1}; }; requires !requires { w *= quantity_kind<downcast_kind<Width, dim_one>, scaled_unit<mag<1000>(), one>, int>{1}; };
requires !requires { w /= quantity_kind<downcast_kind<Width, dim_one>, scaled_unit<as_magnitude<1000>(), one>, int>{1}; }; requires !requires { w /= quantity_kind<downcast_kind<Width, dim_one>, scaled_unit<mag<1000>(), one>, int>{1}; };
requires !requires { w %= quantity_kind<downcast_kind<Width, dim_one>, scaled_unit<as_magnitude<1000>(), one>, int>{1}; }; requires !requires { w %= quantity_kind<downcast_kind<Width, dim_one>, scaled_unit<mag<1000>(), one>, int>{1}; };
requires !requires { w %= 1.0; }; requires !requires { w %= 1.0; };
requires !requires { w %= quantity(1.0); }; requires !requires { w %= quantity(1.0); };
requires !requires { w %= 1.0 * (w / w); }; requires !requires { w %= 1.0 * (w / w); };

View File

@@ -287,7 +287,7 @@ static_assert(get_length_derived_quantity() == 1_q_m);
// CTAD // CTAD
///////// /////////
#if UNITS_COMP_GCC >= 11 || UNITS_COMP_CLANG > 15 #if UNITS_COMP_GCC >= 11 || UNITS_COMP_CLANG > 16
static_assert(std::is_same_v<decltype(aliases::isq::si::m(123))::rep, int>); static_assert(std::is_same_v<decltype(aliases::isq::si::m(123))::rep, int>);
static_assert(std::is_same_v<decltype(aliases::isq::si::m(123.))::rep, double>); static_assert(std::is_same_v<decltype(aliases::isq::si::m(123.))::rep, double>);
static_assert( static_assert(
@@ -498,7 +498,7 @@ static_assert(compare<decltype(1_q_m / 1_q_m), dimensionless<one, std::int64_t>>
static_assert(compare<decltype(1 / 1_q_s), frequency<hertz, std::int64_t>>); static_assert(compare<decltype(1 / 1_q_s), frequency<hertz, std::int64_t>>);
static_assert(compare<decltype(quantity{1} / 1_q_s), frequency<hertz, std::int64_t>>); static_assert(compare<decltype(quantity{1} / 1_q_s), frequency<hertz, std::int64_t>>);
static_assert(compare<decltype(dimensionless<percent, std::int64_t>(1) / 1_q_s), static_assert(compare<decltype(dimensionless<percent, std::int64_t>(1) / 1_q_s),
frequency<scaled_unit<as_magnitude<ratio(1, 100)>(), hertz>, std::int64_t>>); frequency<scaled_unit<mag<ratio(1, 100)>(), hertz>, std::int64_t>>);
static_assert(is_same_v<decltype((std::uint8_t(0) * m + std::uint8_t(0) * m).number()), int&&>); static_assert(is_same_v<decltype((std::uint8_t(0) * m + std::uint8_t(0) * m).number()), int&&>);
static_assert(is_same_v<decltype((std::uint8_t(0) * m - std::uint8_t(0) * m).number()), int&&>); static_assert(is_same_v<decltype((std::uint8_t(0) * m - std::uint8_t(0) * m).number()), int&&>);
@@ -529,7 +529,7 @@ static_assert(compare<decltype(1_q_m / 1._q_m), dimensionless<one, long double>>
static_assert(compare<decltype(1 / 1._q_s), frequency<hertz, long double>>); static_assert(compare<decltype(1 / 1._q_s), frequency<hertz, long double>>);
static_assert(compare<decltype(quantity{1} / 1._q_s), frequency<hertz, long double>>); static_assert(compare<decltype(quantity{1} / 1._q_s), frequency<hertz, long double>>);
static_assert(compare<decltype(dimensionless<percent, std::int64_t>(1) / 1._q_s), static_assert(compare<decltype(dimensionless<percent, std::int64_t>(1) / 1._q_s),
frequency<scaled_unit<as_magnitude<ratio(1, 100)>(), hertz>, long double>>); frequency<scaled_unit<mag<ratio(1, 100)>(), hertz>, long double>>);
static_assert(compare<decltype(1_q_m % short(1)), length<metre, std::int64_t>>); static_assert(compare<decltype(1_q_m % short(1)), length<metre, std::int64_t>>);
static_assert(compare<decltype(1_q_m % quantity{short(1)}), length<metre, std::int64_t>>); static_assert(compare<decltype(1_q_m % quantity{short(1)}), length<metre, std::int64_t>>);
static_assert(compare<decltype(1_q_m % dimensionless<percent, short>(1)), length<metre, std::int64_t>>); static_assert(compare<decltype(1_q_m % dimensionless<percent, short>(1)), length<metre, std::int64_t>>);
@@ -551,7 +551,7 @@ static_assert(compare<decltype(1._q_m / 1_q_m), dimensionless<one, long double>>
static_assert(compare<decltype(1.L / 1_q_s), frequency<hertz, long double>>); static_assert(compare<decltype(1.L / 1_q_s), frequency<hertz, long double>>);
static_assert(compare<decltype(quantity{1.L} / 1_q_s), frequency<hertz, long double>>); static_assert(compare<decltype(quantity{1.L} / 1_q_s), frequency<hertz, long double>>);
static_assert(compare<decltype(dimensionless<percent, long double>(1) / 1_q_s), static_assert(compare<decltype(dimensionless<percent, long double>(1) / 1_q_s),
frequency<scaled_unit<as_magnitude<ratio(1, 100)>(), hertz>, long double>>); frequency<scaled_unit<mag<ratio(1, 100)>(), hertz>, long double>>);
// different units // different units
static_assert(compare<decltype(1_q_m + 1_q_km), length<metre, std::int64_t>>); static_assert(compare<decltype(1_q_m + 1_q_km), length<metre, std::int64_t>>);
@@ -579,25 +579,28 @@ static_assert(is_same_v<decltype(1_q_km % 1_q_m), length<kilometre, std::int64_t
// different dimensions // different dimensions
static_assert(compare<decltype(1_q_m_per_s * 1_q_s), length<metre, std::int64_t>>); static_assert(compare<decltype(1_q_m_per_s * 1_q_s), length<metre, std::int64_t>>);
static_assert(compare<decltype(1_q_m_per_s * 1_q_h), length<scaled_unit<mag<3600>(), metre>, std::int64_t>>);
static_assert( static_assert(
compare<decltype(1_q_m_per_s * 1_q_h), length<scaled_unit<as_magnitude<ratio(36, 1, 2)>(), metre>, std::int64_t>>); compare<decltype(1_q_m * 1_q_min),
static_assert( quantity<unknown_dimension<exponent<dim_length, 1>, exponent<dim_time, 1>>,
compare<decltype(1_q_m * 1_q_min), quantity<unknown_dimension<exponent<dim_length, 1>, exponent<dim_time, 1>>, scaled_unit<mag<60>(), unknown_coherent_unit<exponent<dim_length, 1>, exponent<dim_time, 1>>>,
scaled_unit<as_magnitude<60>(), unknown_coherent_unit>, std::int64_t>>); std::int64_t>>);
static_assert(compare<decltype(1_q_s * 1_q_Hz), dimensionless<one, std::int64_t>>); static_assert(compare<decltype(1_q_s * 1_q_Hz), dimensionless<one, std::int64_t>>);
static_assert( static_assert(compare<decltype(1 / 1_q_min), frequency<scaled_unit<mag<ratio(1, 60)>(), hertz>, std::int64_t>>);
compare<decltype(1 / 1_q_min), frequency<scaled_unit<as_magnitude<ratio(1, 60)>(), hertz>, std::int64_t>>);
static_assert(compare<decltype(1 / 1_q_Hz), isq::si::time<second, std::int64_t>>); static_assert(compare<decltype(1 / 1_q_Hz), isq::si::time<second, std::int64_t>>);
static_assert(compare<decltype(1 / 1_q_km), static_assert(
compare<decltype(1 / 1_q_km),
quantity<unknown_dimension<exponent<dim_length, -1>>, quantity<unknown_dimension<exponent<dim_length, -1>>,
scaled_unit<as_magnitude<ratio(1, 1, -3)>(), unknown_coherent_unit>, std::int64_t>>); scaled_unit<mag<ratio(1, 1000)>(), unknown_coherent_unit<exponent<dim_length, -1>>>, std::int64_t>>);
static_assert(compare<decltype(1_q_km / 1_q_m), dimensionless<scaled_unit<as_magnitude<1000>(), one>, std::int64_t>>); static_assert(compare<decltype(1_q_km / 1_q_m), dimensionless<scaled_unit<mag<1000>(), one>, std::int64_t>>);
static_assert(compare<decltype(1_q_m / 1_q_s), speed<metre_per_second, std::int64_t>>); static_assert(compare<decltype(1_q_m / 1_q_s), speed<metre_per_second, std::int64_t>>);
static_assert( static_assert(
compare<decltype(1_q_m / 1_q_min), speed<scaled_unit<as_magnitude<ratio(1, 60)>(), metre_per_second>, std::int64_t>>); compare<decltype(1_q_m / 1_q_min), speed<scaled_unit<mag<ratio(1, 60)>(), metre_per_second>, std::int64_t>>);
static_assert( static_assert(
compare<decltype(1_q_min / 1_q_m), quantity<unknown_dimension<exponent<dim_length, -1>, exponent<dim_time, 1>>, compare<decltype(1_q_min / 1_q_m),
scaled_unit<as_magnitude<60>(), unknown_coherent_unit>, std::int64_t>>); quantity<unknown_dimension<exponent<dim_length, -1>, exponent<dim_time, 1>>,
scaled_unit<mag<60>(), unknown_coherent_unit<exponent<dim_length, -1>, exponent<dim_time, 1>>>,
std::int64_t>>);
static_assert((1_q_m + 1_q_m).number() == 2); static_assert((1_q_m + 1_q_m).number() == 2);
static_assert((1_q_m + 1_q_km).number() == 1001); static_assert((1_q_m + 1_q_km).number() == 1001);
@@ -885,11 +888,13 @@ static_assert(!is_same_v<decltype(quantity_cast<litre>(2_q_dm3)), volume<cubic_d
#if UNITS_DOWNCAST_MODE == 0 #if UNITS_DOWNCAST_MODE == 0
static_assert(is_same_v<decltype(10_q_m / 5_q_s),
quantity<unknown_dimension<units::exponent<dim_length, 1>, units::exponent<dim_time, -1>>,
scaled_unit<as_magnitude<1>(), unknown_coherent_unit>, std::int64_t>>);
static_assert( static_assert(
is_same_v<decltype(1_q_mm + 1_q_km), length<scaled_unit<as_magnitude<ratio(1, 1, -3)>(), metre>, std::int64_t>>); is_same_v<decltype(10_q_m / 5_q_s),
quantity<unknown_dimension<units::exponent<dim_length, 1>, units::exponent<dim_time, -1>>,
scaled_unit<mag<1>(),
unknown_coherent_unit<units::exponent<dim_length, 1>, units::exponent<dim_time, -1>>>,
std::int64_t>>);
static_assert(is_same_v<decltype(1_q_mm + 1_q_km), length<scaled_unit<mag<ratio(1, 1000)>(), metre>, std::int64_t>>);
#else #else
@@ -919,8 +924,7 @@ static_assert(same(quotient_remainder_theorem(3'000 * m, 400), 3'000 * m));
static_assert(comp(quotient_remainder_theorem(3'000 * m, quantity(400)), 3'000 * m)); static_assert(comp(quotient_remainder_theorem(3'000 * m, quantity(400)), 3'000 * m));
static_assert(comp(quotient_remainder_theorem(3 * km, quantity(400)), 3 * km)); static_assert(comp(quotient_remainder_theorem(3 * km, quantity(400)), 3 * km));
static_assert(comp(quotient_remainder_theorem(3 * km, quantity(2)), 3 * km)); static_assert(comp(quotient_remainder_theorem(3 * km, quantity(2)), 3 * km));
static_assert( static_assert(comp(quotient_remainder_theorem(3 * km, dimensionless<scaled_unit<mag<ratio(1, 1000)>(), one>, int>(400)),
comp(quotient_remainder_theorem(3 * km, dimensionless<scaled_unit<as_magnitude<ratio(1, 1000)>(), one>, int>(400)),
3 * km)); 3 * km));
} // namespace } // namespace

View File

@@ -28,11 +28,6 @@ using namespace units;
static_assert(ratio(2, 4) == ratio(1, 2)); static_assert(ratio(2, 4) == ratio(1, 2));
// basic exponents tests
static_assert(ratio(2, 40, 1) == ratio(1, 20, 1));
static_assert(ratio(20, 4, -1) == ratio(10, 2, -1));
static_assert(ratio(200, 5) == ratio(20'000, 50, -1));
static_assert(ratio(1) * ratio(3, 8) == ratio(3, 8)); static_assert(ratio(1) * ratio(3, 8) == ratio(3, 8));
static_assert(ratio(3, 8) * ratio(1) == ratio(3, 8)); static_assert(ratio(3, 8) * ratio(1) == ratio(3, 8));
static_assert(ratio(4) * ratio(1, 8) == ratio(1, 2)); static_assert(ratio(4) * ratio(1, 8) == ratio(1, 2));
@@ -45,21 +40,12 @@ static_assert(-ratio(3, 8) == ratio(-3, 8));
// ratio addition // ratio addition
static_assert(ratio(1, 2) + ratio(1, 3) == ratio(5, 6)); static_assert(ratio(1, 2) + ratio(1, 3) == ratio(5, 6));
static_assert(ratio(1, 3, 2) + ratio(11, 6) == ratio(211, 6)); // 100/3 + 11/6
// multiply with exponents
static_assert(ratio(1, 8, 2) * ratio(2, 1, 4) == ratio(1, 4, 6));
static_assert(ratio(1, 2, -4) * ratio(8, 1, 3) == ratio(4, 1, -1));
static_assert(ratio(4) / ratio(2) == ratio(2)); static_assert(ratio(4) / ratio(2) == ratio(2));
static_assert(ratio(2) / ratio(8) == ratio(1, 4)); static_assert(ratio(2) / ratio(8) == ratio(1, 4));
static_assert(ratio(1, 8) / ratio(2) == ratio(1, 16)); static_assert(ratio(1, 8) / ratio(2) == ratio(1, 16));
static_assert(ratio(6) / ratio(3) == ratio(2)); static_assert(ratio(6) / ratio(3) == ratio(2));
// divide with exponents
static_assert(ratio(1, 8, -6) / ratio(2, 1, -8) == ratio(1, 16, 2));
static_assert(ratio(6, 1, 4) / ratio(3) == ratio(2, 1, 4));
static_assert(pow<0>(ratio(2)) == ratio(1)); static_assert(pow<0>(ratio(2)) == ratio(1));
static_assert(pow<1>(ratio(2)) == ratio(2)); static_assert(pow<1>(ratio(2)) == ratio(2));
static_assert(pow<2>(ratio(2)) == ratio(4)); static_assert(pow<2>(ratio(2)) == ratio(4));
@@ -69,27 +55,6 @@ static_assert(pow<1>(ratio(1, 2)) == ratio(1, 2));
static_assert(pow<2>(ratio(1, 2)) == ratio(1, 4)); static_assert(pow<2>(ratio(1, 2)) == ratio(1, 4));
static_assert(pow<3>(ratio(1, 2)) == ratio(1, 8)); static_assert(pow<3>(ratio(1, 2)) == ratio(1, 8));
// pow with exponents
static_assert(pow<2>(ratio(1, 2, 3)) == ratio(1, 4, 6));
static_assert(pow<4, 2>(ratio(1, 2, 3)) == ratio(1, 4, 6));
static_assert(pow<3>(ratio(1, 2, -6)) == ratio(1, 8, -18));
static_assert(sqrt(ratio(9)) == ratio(3));
static_assert(cbrt(ratio(27)) == ratio(3));
static_assert(sqrt(ratio(4)) == ratio(2));
static_assert(cbrt(ratio(8)) == ratio(2));
static_assert(sqrt(ratio(1)) == ratio(1));
static_assert(cbrt(ratio(1)) == ratio(1));
static_assert(sqrt(ratio(0)) == ratio(0));
static_assert(cbrt(ratio(0)) == ratio(0));
static_assert(sqrt(ratio(1, 4)) == ratio(1, 2));
static_assert(cbrt(ratio(1, 8)) == ratio(1, 2));
// sqrt with exponents
static_assert(sqrt(ratio(9, 1, 2)) == ratio(3, 1, 1));
static_assert(cbrt(ratio(27, 1, 3)) == ratio(3, 1, 1));
static_assert(cbrt(ratio(27, 1, 2)) == ratio(13, 1, 0));
// common_ratio // common_ratio
static_assert(common_ratio(ratio(1), ratio(1000)) == ratio(1)); static_assert(common_ratio(ratio(1), ratio(1000)) == ratio(1));
static_assert(common_ratio(ratio(1000), ratio(1)) == ratio(1)); static_assert(common_ratio(ratio(1000), ratio(1)) == ratio(1));
@@ -98,20 +63,9 @@ static_assert(common_ratio(ratio(1, 1000), ratio(1)) == ratio(1, 1000));
static_assert(common_ratio(ratio(100, 1), ratio(10, 1)) == ratio(10, 1)); static_assert(common_ratio(ratio(100, 1), ratio(10, 1)) == ratio(10, 1));
static_assert(common_ratio(ratio(100, 1), ratio(1, 10)) == ratio(1, 10)); static_assert(common_ratio(ratio(100, 1), ratio(1, 10)) == ratio(1, 10));
// common ratio with exponents
static_assert(common_ratio(ratio(1), ratio(1, 1, 3)) == ratio(1));
static_assert(common_ratio(ratio(10, 1, -1), ratio(1, 1, -3)) == ratio(1, 1, -3));
// numerator and denominator
static_assert(numerator(ratio(3, 4)) == 3);
static_assert(numerator(ratio(3, 7, 2)) == 300);
static_assert(denominator(ratio(3, 4)) == 4);
static_assert(denominator(ratio(3, 7, -2)) == 700);
// comparison // comparison
static_assert((ratio(3, 4) <=> ratio(6, 8)) == (0 <=> 0)); static_assert((ratio(3, 4) <=> ratio(6, 8)) == (0 <=> 0));
static_assert((ratio(3, 4) <=> ratio(-3, 4)) == (0 <=> -1)); static_assert((ratio(3, 4) <=> ratio(-3, 4)) == (0 <=> -1));
static_assert((ratio(-3, 4) <=> ratio(3, -4)) == (0 <=> 0)); static_assert((ratio(-3, 4) <=> ratio(3, -4)) == (0 <=> 0));
static_assert((ratio(1, 1, 1) <=> ratio(10)) == (0 <=> 0));
} // namespace } // namespace

View File

@@ -116,7 +116,7 @@ static_assert(si::length<si::metre>(1) + quantity_cast<si::length<si::metre>>(10
static_assert(100_q_cm + quantity_cast<si::cgs::length<si::cgs::centimetre>>(si::length<si::metre>(1)) == 200_q_cm); static_assert(100_q_cm + quantity_cast<si::cgs::length<si::cgs::centimetre>>(si::length<si::metre>(1)) == 200_q_cm);
static_assert(quantity_cast<si::cgs::length<si::cgs::centimetre>>(si::length<si::metre>(1)) + 100_q_cm == 200_q_cm); static_assert(quantity_cast<si::cgs::length<si::cgs::centimetre>>(si::length<si::metre>(1)) + 100_q_cm == 200_q_cm);
// substraction // subtraction
static_assert(500_q_cm - si::length<si::metre>(1) == si::length<si::metre>(4)); static_assert(500_q_cm - si::length<si::metre>(1) == si::length<si::metre>(4));
static_assert(si::length<si::metre>(5) - 100_q_cm == si::length<si::metre>(4)); static_assert(si::length<si::metre>(5) - 100_q_cm == si::length<si::metre>(4));
@@ -127,8 +127,8 @@ static_assert(quantity_cast<si::cgs::length<si::cgs::centimetre>>(si::length<si:
// multiplication // multiplication
// static_assert(200_q_cm * si::length<si::metre>(2) == si::area<si::square_metre>(4)); // TODO Add support for // TODO Add support for comparing of an unknown_dimension
// comparing of an unknown_dimension // static_assert(200._q_cm * si::length<si::metre>(2) == si::area<si::square_metre>(4));
static_assert(quantity_cast<si::dim_length>(200._q_cm) * si::length<si::metre>(2) == si::area<si::square_metre>(4)); static_assert(quantity_cast<si::dim_length>(200._q_cm) * si::length<si::metre>(2) == si::area<si::square_metre>(4));
static_assert(200._q_cm * quantity_cast<si::cgs::dim_length>(si::length<si::metre>(2)) == 40'000_q_cm2); static_assert(200._q_cm * quantity_cast<si::cgs::dim_length>(si::length<si::metre>(2)) == 40'000_q_cm2);
@@ -142,14 +142,32 @@ static_assert(200._q_cm * quantity_cast<si::cgs::dim_length>(si::length<si::metr
// division // division
// static_assert(si::area<si::square_metre>(4) / 200_q_cm == si::length<si::metre>(2)); // TODO Add support for // TODO Add support for comparing of an unknown_dimension
// comparing of an unknown_dimension // static_assert(si::area<si::square_metre>(4) / 200_q_cm == si::length<si::metre>(2));
// static_assert(400._q_cm / si::length<si::metre>(2) == 2);
static_assert(si::area<si::square_metre>(4) / quantity_cast<si::length<si::metre>>(200_q_cm) == static_assert(si::area<si::square_metre>(4) / quantity_cast<si::length<si::metre>>(200_q_cm) ==
si::length<si::metre>(2)); si::length<si::metre>(2));
static_assert(quantity_cast<si::cgs::area<si::cgs::square_centimetre>>(si::area<si::square_metre>(4)) / 200._q_cm == static_assert(quantity_cast<si::cgs::area<si::cgs::square_centimetre>>(si::area<si::square_metre>(4)) / 200._q_cm ==
200_q_cm); 200_q_cm);
static_assert(si::cgs::length<si::cgs::centimetre>(50) == si::length<si::centimetre>(50));
static_assert(si::cgs::mass<si::cgs::gram>(50) == si::mass<si::gram>(50));
static_assert(1 / si::cgs::length<si::cgs::centimetre>(50) == 1 / si::length<si::centimetre>(50));
static_assert(1 / si::cgs::length<si::metre>(50) == 1 / si::length<si::metre>(50));
static_assert(1 / si::cgs::mass<si::cgs::gram>(50) == 1 / si::mass<si::gram>(50));
static_assert(1 / si::cgs::mass<si::kilogram>(50) == 1 / si::mass<si::kilogram>(50));
static_assert(si::cgs::length<si::cgs::centimetre>(50) * si::cgs::mass<si::cgs::gram>(50) ==
si::length<si::centimetre>(50) * si::mass<si::gram>(50));
static_assert(si::cgs::length<si::metre>(50) * si::cgs::mass<si::kilogram>(50) ==
si::length<si::metre>(50) * si::mass<si::kilogram>(50));
static_assert(si::cgs::length<si::cgs::centimetre>(50) / si::cgs::mass<si::cgs::gram>(50) ==
si::length<si::centimetre>(50) / si::mass<si::gram>(50));
} // namespace cgs_test } // namespace cgs_test
} // namespace } // namespace

View File

@@ -157,8 +157,8 @@ static_assert(quantity_cast<si::fps::length<si::fps::foot>>(si::length<si::metre
// multiplication // multiplication
// static_assert(2 * ft * si::length<si::metre>(2) == si::area<si::square_metre>(1.2192)); // TODO Add support for // TODO Add support for comparing of an unknown_dimension
// comparing of an unknown_dimension // static_assert(2 * ft * si::length<si::metre>(2) == si::area<si::square_metre>(1.2192));
static_assert(quantity_cast<si::length<si::metre>>(2. * ft) * si::length<si::metre>(2) == static_assert(quantity_cast<si::length<si::metre>>(2. * ft) * si::length<si::metre>(2) ==
si::area<si::square_metre>(1.2192)); si::area<si::square_metre>(1.2192));
static_assert(quantity_cast<si::length<si::metre>>(2. * ft) * si::length<si::metre>(0.6096) == static_assert(quantity_cast<si::length<si::metre>>(2. * ft) * si::length<si::metre>(0.6096) ==
@@ -167,8 +167,10 @@ static_assert(2. * ft * quantity_cast<si::fps::length<si::fps::foot>>(si::length
// division // division
// static_assert(si::area<si::square_metre>(4) / 200_q_cm == si::length<si::metre>(2)); // TODO Add support for // TODO Add support for comparing of an unknown_dimension
// comparing of an unknown_dimension // static_assert(si::area<si::square_metre>(4) / 200_q_cm == si::length<si::metre>(2));
// static_assert(400._q_cm / si::length<si::metre>(2) == 2);
static_assert(si::area<si::square_metre>(1.48644864) / quantity_cast<si::length<si::metre>>(4 * ft) == static_assert(si::area<si::square_metre>(1.48644864) / quantity_cast<si::length<si::metre>>(4 * ft) ==
si::length<si::metre>(1.2192)); // 16 ft2 / 4 ft = 4 ft si::length<si::metre>(1.2192)); // 16 ft2 / 4 ft = 4 ft
static_assert(quantity_cast<si::fps::area<si::fps::square_foot>>(si::area<si::square_metre>(1.48644864)) / (4. * ft) == static_assert(quantity_cast<si::fps::area<si::fps::square_foot>>(si::area<si::square_metre>(1.48644864)) / (4. * ft) ==

View File

@@ -43,7 +43,7 @@ static_assert(1_q_au == 149'597'870'700_q_m);
static_assert(1_q_km + 1_q_m == 1001_q_m); static_assert(1_q_km + 1_q_m == 1001_q_m);
static_assert(10_q_km / 5_q_km == 2); static_assert(10_q_km / 5_q_km == 2);
static_assert(10_q_km / 5_q_km < 3); static_assert(10_q_km / 5_q_km < 3);
static_assert(100_q_mm / 5_q_cm == dimensionless<scaled_unit<as_magnitude<ratio(1, 10)>(), one>>(20)); static_assert(100_q_mm / 5_q_cm == dimensionless<scaled_unit<mag<ratio(1, 10)>(), one>>(20));
static_assert(100_q_mm / 5_q_cm == dimensionless<one>(2)); static_assert(100_q_mm / 5_q_cm == dimensionless<one>(2));
static_assert(10_q_km / 2 == 5_q_km); static_assert(10_q_km / 2 == 5_q_km);
@@ -107,7 +107,7 @@ static_assert(1000 / 1_q_s == 1_q_kHz);
static_assert(1 / 1_q_ms == 1_q_kHz); static_assert(1 / 1_q_ms == 1_q_kHz);
static_assert(3.2_q_GHz == 3'200'000'000_q_Hz); static_assert(3.2_q_GHz == 3'200'000'000_q_Hz);
static_assert((10_q_Hz * 1_q_min).number() == 10); static_assert((10_q_Hz * 1_q_min).number() == 10);
static_assert(10_q_Hz * 1_q_min == dimensionless<scaled_unit<as_magnitude<60>(), one>>(10)); static_assert(10_q_Hz * 1_q_min == dimensionless<scaled_unit<mag<60>(), one>>(10));
static_assert(10_q_Hz * 1_q_min == dimensionless<one>(600)); static_assert(10_q_Hz * 1_q_min == dimensionless<one>(600));
static_assert(2 / 1_q_Hz == 2_q_s); static_assert(2 / 1_q_Hz == 2_q_s);

View File

@@ -36,12 +36,12 @@ using namespace units::isq;
struct metre : named_unit<metre, "m"> {}; struct metre : named_unit<metre, "m"> {};
struct centimetre : prefixed_unit<centimetre, si::centi, metre> {}; struct centimetre : prefixed_unit<centimetre, si::centi, metre> {};
struct kilometre : prefixed_unit<kilometre, si::kilo, metre> {}; struct kilometre : prefixed_unit<kilometre, si::kilo, metre> {};
struct yard : named_scaled_unit<yard, "yd", as_magnitude<ratio(9'144, 1, -4)>(), metre> {}; struct yard : named_scaled_unit<yard, "yd", mag<ratio{9'144, 10'000}>(), metre> {};
struct foot : named_scaled_unit<foot, "ft", as_magnitude<ratio(1, 3)>(), yard> {}; struct foot : named_scaled_unit<foot, "ft", mag<ratio(1, 3)>(), yard> {};
struct dim_length : base_dimension<"length", metre> {}; struct dim_length : base_dimension<"length", metre> {};
struct second : named_unit<second, "s"> {}; struct second : named_unit<second, "s"> {};
struct hour : named_scaled_unit<hour, "h", as_magnitude<ratio(36, 1, 2)>(), second> {}; struct hour : named_scaled_unit<hour, "h", mag<3600>(), second> {};
struct dim_time : base_dimension<"time", second> {}; struct dim_time : base_dimension<"time", second> {};
struct kelvin : named_unit<kelvin, "K"> {}; struct kelvin : named_unit<kelvin, "K"> {};
@@ -59,10 +59,10 @@ struct kilometre_per_hour : derived_scaled_unit<kilometre_per_hour, dim_speed, k
static_assert(equivalent<metre::named_unit, metre>); static_assert(equivalent<metre::named_unit, metre>);
static_assert(equivalent<metre::scaled_unit, metre>); static_assert(equivalent<metre::scaled_unit, metre>);
static_assert(compare<downcast<scaled_unit<as_magnitude<1>(), metre>>, metre>); static_assert(compare<downcast<scaled_unit<mag<1>(), metre>>, metre>);
static_assert(compare<downcast<scaled_unit<as_magnitude<ratio(1, 1, -2)>(), metre>>, centimetre>); static_assert(compare<downcast<scaled_unit<mag<ratio(1, 100)>(), metre>>, centimetre>);
static_assert(compare<downcast<scaled_unit<yard::mag, metre>>, yard>); static_assert(compare<downcast<scaled_unit<yard::mag, metre>>, yard>);
static_assert(compare<downcast<scaled_unit<yard::mag / as_magnitude<3>(), metre>>, foot>); static_assert(compare<downcast<scaled_unit<yard::mag / mag<3>(), metre>>, foot>);
static_assert(compare<downcast<scaled_unit<kilometre::mag / hour::mag, metre_per_second>>, kilometre_per_hour>); static_assert(compare<downcast<scaled_unit<kilometre::mag / hour::mag, metre_per_second>>, kilometre_per_hour>);
static_assert(centimetre::symbol == "cm"); static_assert(centimetre::symbol == "cm");

View File

@@ -20,14 +20,17 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.
import os
from conan import ConanFile from conan import ConanFile
from conan.tools.build import cross_building from conan.tools.build import cross_building
from conan.tools.cmake import CMake from conan.tools.cmake import CMake, cmake_layout
class TestPackageConan(ConanFile): class TestPackageConan(ConanFile):
settings = "os", "compiler", "build_type", "arch" settings = "os", "compiler", "build_type", "arch"
generators = "CMakeToolchain", "CMakeDeps" generators = "CMakeDeps", "CMakeToolchain", "VirtualBuildEnv", "VirtualRunEnv"
apply_env = False
test_type = "explicit" # TODO Remove for Conan 2.0 test_type = "explicit" # TODO Remove for Conan 2.0
def requirements(self): def requirements(self):
@@ -38,6 +41,10 @@ class TestPackageConan(ConanFile):
cmake.configure() cmake.configure()
cmake.build() cmake.build()
def layout(self):
cmake_layout(self)
def test(self): def test(self):
if not cross_building(self): if not cross_building(self):
self.run("test_package", run_environment=True) cmd = os.path.join(self.cpp.build.bindirs[0], "test_package")
self.run(cmd, env="conanrun")