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",
os: windows-2019,
compiler:
{ type: VISUAL, version: 16, std: 20, cc: "cl", cxx: "cl" },
compiler: { type: VISUAL, version: 16, cc: "", cxx: "", std: 20 },
}
- {
name: "Windows MSVC 14.3",
os: windows-2022,
compiler:
{ type: MSVC, version: 193, std: 23, cc: "cl", cxx: "cl" },
compiler: { type: MSVC, version: 193, cc: "", cxx: "", std: 23 },
}
- {
name: "Ubuntu GCC-10",
@@ -140,17 +138,26 @@ jobs:
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"]
downcast_mode: ["on", "auto"]
env:
CC: ${{ matrix.config.compiler.cc }}
CXX: ${{ matrix.config.compiler.cxx }}
steps:
- 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
if: runner.os == 'Linux'
with:
@@ -180,8 +187,6 @@ jobs:
run: |
sudo xcode-select -s "/Applications/Xcode_13.0.app"
- name: Install Ninja
# TODO Find a proper syntax to make the below work
# if: !matrix.config.docker_image
shell: bash
run: |
if [ $RUNNER_OS == 'Linux' ]; then
@@ -199,20 +204,21 @@ jobs:
uses: actions/setup-python@v2
with:
python-version: "3.8"
- name: Install Conan Package Tools
- name: Install Conan
shell: bash
run: |
pip install -U conan_package_tools
pip install -U conan
- name: Configure Conan
# TODO Find a proper syntax to make the below work
# if: !matrix.config.docker_image
shell: bash
run: |
conan config init
conan remote add artifactory https://mpusz.jfrog.io/artifactory/api/conan/conan-oss
if [[ "${{ matrix.config.compiler.type }}" == "GCC" || "${{ matrix.config.compiler.type }}" == "CLANG" ]]; then
conan profile update settings.compiler.libcxx=${{ matrix.config.lib }} default
fi
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
# - name: Add support for clang-13 to Conan's settings.yml
# # TODO Remove when Conan will support clang-13
@@ -220,21 +226,22 @@ jobs:
# shell: bash
# run: |
# 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
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: |
if [ ! -z "${{ matrix.config.docker_image }}" ]; then
export CONAN_DOCKER_IMAGE=${{ matrix.config.docker_image }}
fi
python build.py
[[ `git tag --contains ${{ github.sha }}` =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] && CHANNEL=stable || CHANNEL=testing
echo "CHANNEL=${CHANNEL}" >> ${GITHUB_ENV}
- name: Create Conan package
shell: bash
run: |
conan create . mpusz/testing -o mp-units:downcast_mode=${{ matrix.downcast_mode }} -c user.build:all=True -c user.build:skip_docs=True -b mp-units -b outdated -u
- name: Upload mp-units Conan package
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:
config:
- {
name: "Windows MSVC 2019",
os: windows-latest,
compiler: { type: VISUAL, version: 16, cc: "", cxx: "" },
name: "Windows MSVC 14.2",
os: windows-2019,
compiler: { type: VISUAL, version: 16, cc: "", cxx: "", std: 20 },
}
- {
name: "Ubuntu GCC 10.3.0",
os: ubuntu-20.04,
compiler: { type: GCC, version: 10, cc: "gcc-10", cxx: "g++-10" },
lib: "libstdc++11",
name: "Windows MSVC 14.3",
os: windows-2022,
compiler: { type: MSVC, version: 193, cc: "", cxx: "", std: 23 },
}
- {
name: "Ubuntu GCC 11.1.0",
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",
name: "Ubuntu GCC-10",
os: ubuntu-20.04,
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",
}
- {
name: "Ubuntu Clang 12.0.0 + libc++",
name: "Ubuntu GCC-11",
os: ubuntu-20.04,
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++",
}
- {
name: "Ubuntu Clang 13.0.0 + libc++",
name: "Ubuntu Clang-13 + libc++",
os: ubuntu-20.04,
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++",
}
- {
name: "Ubuntu Clang 14.0.0 + libc++",
name: "Ubuntu Clang-14 + libc++",
os: ubuntu-20.04,
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++",
}
- {
@@ -96,23 +139,21 @@ jobs:
version: "13.0",
cc: "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"]
env:
CC: ${{ matrix.config.compiler.cc }}
CXX: ${{ matrix.config.compiler.cxx }}
CMAKE_GENERATOR: Ninja
steps:
- name: Downcase 'build_type'
id: build_type
uses: ASzc/change-string-case-action@v2
with:
string: ${{ matrix.build_type }}
- uses: actions/checkout@v2
- name: Cache Conan data
uses: actions/cache@v2
@@ -149,8 +190,6 @@ jobs:
run: |
sudo xcode-select -s "/Applications/Xcode_13.0.app"
- name: Install Ninja
# TODO Find a proper syntax to make the below work
# if: !matrix.config.docker_image
shell: bash
run: |
if [ $RUNNER_OS == 'Linux' ]; then
@@ -176,13 +215,13 @@ jobs:
shell: bash
run: |
conan config init
conan remote add upload 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
conan remote add artifactory https://mpusz.jfrog.io/artifactory/api/conan/conan-oss
if [[ "${{ matrix.config.compiler.type }}" == "GCC" || "${{ matrix.config.compiler.type }}" == "CLANG" ]]; then
conan profile update settings.compiler.libcxx=${{ matrix.config.lib }} default
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
# - name: Add support for clang-13 to Conan's settings.yml
# # TODO Remove when Conan will support clang-13
@@ -193,63 +232,70 @@ jobs:
- name: Install Conan dependencies
shell: bash
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
if: matrix.config.compiler.type == 'VISUAL'
if: matrix.config.compiler.type == 'VISUAL' || matrix.config.compiler.type == 'MSVC'
shell: cmd
working-directory: build/${{ matrix.build_type }}
working-directory: src
run: |
call conanvcvars.bat
cmake ../../src -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --version
call ..\build\generators\conanvcvars.bat
cmake --preset default -DCMAKE_INSTALL_PREFIX=../out
- name: Configure mp-units CMake
if: matrix.config.compiler.type != 'VISUAL'
if: matrix.config.compiler.type != 'VISUAL' && matrix.config.compiler.type != 'MSVC'
shell: bash
working-directory: build/${{ matrix.build_type }}
working-directory: src
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
shell: bash
working-directory: build/${{ matrix.build_type }}
working-directory: src
run: |
cmake --install . --prefix test_package
- name: Install dependencies for test_package
cmake --build --preset ${{ steps.build_type.outputs.lowercase }} --target install
- name: Provide dependencies for test_package
shell: bash
working-directory: test_package
run: |
mkdir -p test_package/build/${{ matrix.build_type }} && cd test_package/build/${{ matrix.build_type }}
conan install ../../..
cp ../src/CMakeUserPresets.json .
- 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
working-directory: test_package/build/${{ matrix.build_type }}
working-directory: test_package
run: |
call 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 --build .
call ..\build\generators\conanvcvars.bat
cmake --preset default -Dmp-units_DIR=../build -Bbuild/local
cmake --build build/local --config ${{ matrix.build_type }}
- name: Build test_package CMake (local build)
if: matrix.config.compiler.type != 'VISUAL'
if: matrix.config.compiler.type != 'VISUAL' && matrix.config.compiler.type != 'MSVC'
shell: bash
working-directory: test_package/build/${{ matrix.build_type }}
working-directory: test_package
run: |
cmake ../.. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -Dmp-units_DIR=${{ github.workspace }}/build/${{ matrix.build_type }}
cmake --build .
- name: Build test_package CMake (installation)
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'
cmake --preset default -Dmp-units_DIR=../build -Bbuild/local
cmake --build build/local --config ${{ matrix.build_type }}
- name: Run test_package (local build)
shell: bash
working-directory: test_package/build/${{ matrix.build_type }}
run: |
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: Run test_package
shell: bash
working-directory: test_package/build/${{ matrix.build_type }}
working-directory: test_package/build/local/${{ matrix.build_type }}
run: |
./test_package
- name: Build test_package CMake (installation)
if: matrix.config.compiler.type == 'VISUAL' || matrix.config.compiler.type == 'MSVC'
shell: cmd
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: |
./test_package

View File

@@ -14,13 +14,13 @@ name: "CodeQL"
on:
push:
branches: [ master ]
branches: [master]
paths-ignore:
- 'docs/**'
- "docs/**"
pull_request:
branches: [ master ]
branches: [master]
paths-ignore:
- 'docs/**'
- "docs/**"
jobs:
analyze:
@@ -33,7 +33,7 @@ jobs:
strategy:
fail-fast: false
matrix:
language: [ 'cpp', 'python' ]
language: ["cpp", "python"]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more...
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
@@ -44,7 +44,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# 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)
- name: Autobuild
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.
# 📚 https://git.io/JvXDl
@@ -77,7 +77,7 @@ jobs:
if: matrix.language == 'cpp'
uses: actions/setup-python@v2
with:
python-version: '3.8'
python-version: "3.8"
- name: Conan build
if: matrix.language == 'cpp'
run: |
@@ -85,7 +85,7 @@ jobs:
conan config init
conan remote add upload https://mpusz.jfrog.io/artifactory/api/conan/conan-oss
mkdir _lgtm_build_dir && cd _lgtm_build_dir
conan install .. -s compiler.cppstd=20 -s compiler.libcxx=libstdc++11 -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 ..
- name: Perform CodeQL Analysis
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
- name: Install Conan dependencies
run: |
mkdir build && cd build
conan install .. -s compiler.cppstd=20 -s compiler.libcxx=libstdc++11 -e mp-units:CONAN_RUN_TESTS=True -b outdated -u
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
- name: Configure CMake
working-directory: build
run: |
cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
cmake --preset default
- name: Generate documentation
working-directory: build
run: |
cmake --build . --target documentation --config Release
cmake --build --preset release --target documentation
- name: Deploy documentation
if: github.ref == 'refs/heads/master'
uses: peaceiris/actions-gh-pages@v3

8
.gitignore vendored
View File

@@ -46,3 +46,11 @@ CMakeUserPresets.json
# Conan
*.pyc
/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)
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)
#

View File

@@ -24,15 +24,13 @@ import os
import re
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.scm import Version
from conans.errors import ConanInvalidConfiguration
# TODO replace with new tools for Conan 2.0
from conans.tools import check_min_cppstd, get_env
required_conan_version = ">=1.48.0"
required_conan_version = ">=1.50.0"
class MPUnitsConan(ConanFile):
@@ -56,8 +54,8 @@ class MPUnitsConan(ConanFile):
url = "https://github.com/mpusz/units"
settings = "os", "compiler", "build_type", "arch"
requires = "gsl-lite/0.40.0"
options = {"downcast_mode": ["off", "on", "auto"], "build_docs": [True, False]}
default_options = {"downcast_mode": "on", "build_docs": True}
options = {"downcast_mode": ["off", "on", "auto"]}
default_options = {"downcast_mode": "on"}
exports = ["LICENSE.md"]
exports_sources = [
"docs/*",
@@ -71,8 +69,12 @@ class MPUnitsConan(ConanFile):
generators = "cmake_paths"
@property
def _run_tests(self):
return get_env("CONAN_RUN_TESTS", False)
def _build_all(self):
return bool(self.conf["user.build:all"])
@property
def _skip_docs(self):
return bool(self.conf["user.build:skip_docs"])
@property
def _use_libfmt(self):
@@ -112,10 +114,10 @@ class MPUnitsConan(ConanFile):
self.requires("range-v3/0.11.0")
def build_requirements(self):
if self._run_tests:
if self._build_all:
self.test_requires("catch2/2.13.9")
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")
# 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")
check_min_cppstd(self, 20)
# TODO Uncomment this when environment is supported in the Conan toolchain
# def config_options(self):
# if not self._run_tests:
# # build_docs has sense only in a development or CI build
# del self.options.build_docs
def layout(self):
cmake_layout(self)
def generate(self):
tc = CMakeToolchain(self)
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"] = bool(self.options.build_docs)
tc.variables["UNITS_BUILD_DOCS"] = self._build_all and not self._skip_docs
tc.variables["UNITS_USE_LIBFMT"] = self._use_libfmt
tc.generate()
deps = CMakeDeps(self)
@@ -163,13 +161,13 @@ class MPUnitsConan(ConanFile):
def build(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()
if self._run_tests:
if self._build_all:
cmake.test()
def package_id(self):
self.info.header_only()
self.info.clear()
def package(self):
copy(
@@ -187,7 +185,6 @@ class MPUnitsConan(ConanFile):
# core
self.cpp_info.components["core"].requires = ["gsl-lite::gsl-lite"]
self.cpp_info.components["core"].includedirs = ["include"]
if compiler == "Visual Studio":
self.cpp_info.components["core"].cxxflags = ["/utf-8"]
if self._use_range_v3:
@@ -195,49 +192,21 @@ class MPUnitsConan(ConanFile):
# rest
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"].includedirs = ["include"]
if self._use_libfmt:
self.cpp_info.components["core-fmt"].requires.append("fmt::fmt")
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"].includedirs = ["include"]
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"].includedirs = ["include"]
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"].includedirs = ["include"]
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"].includedirs = ["include"]
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"].includedirs = ["include"]
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"].includedirs = ["include"]
self.cpp_info.components["systems"].requires = [
"isq",
"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.
#
# This file only contains a selection of the most common options.
# For a full list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
import re
def get_version():
try:
@@ -598,29 +121,9 @@ html_css_files = ["css/custom.css"]
# -- 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
# are paths to the folder containing the doxygen output for that project.
breathe_projects = {}
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"
breathe_projects = {"mp-units": "build/xml"}
# 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

View File

@@ -188,9 +188,6 @@ and define units like::
struct electronvolt : named_scaled_unit<electronvolt, "eV", prefix,
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
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``,

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
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
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
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
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
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.
- [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.
- *.*
@@ -36,9 +36,13 @@ This repository contains three independent CMake-based projects:
- `linear algebra <https://github.com/BobSteagall/wg21/tree/master/include>`_
library based on proposal `P1385 <https://wg21.link/P1385>`_ used in some examples
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
code.
- `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>`_.
- `Breathe <https://breathe.readthedocs.io/>`_ as a bridge between the Sphinx and Doxygen
documentation systems.
@@ -82,47 +86,47 @@ in *~/.conan/profiles* directory. An example profile can look as follows:
arch=x86_64
arch_build=x86_64
compiler=gcc
compiler.version=10
compiler.version=12
compiler.cppstd=20
compiler.libcxx=libstdc++11
build_type=Release
[options]
[build_requires]
[conf]
tools.cmake.cmaketoolchain:generator=Ninja
[env]
CC=/usr/bin/gcc-10
CXX=/usr/bin/g++-10
CC=/usr/bin/gcc-12
CXX=/usr/bin/g++-12
.. tip::
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``
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
-------------
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
^^^^^^^^^^^^^
@@ -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
- ``automatic`` - downcasting automatically enabled if no collisions are present
build_docs
++++++++++
Conan Configuration Properties
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
build_all
+++++++++
**Values**: ``True``/``False``
**Defaulted to**: ``True``
**Defaulted to**: ``False``
If enabled, Conan installs the documentation generation dependencies (i.e. doxygen).
Additionally, enables project documentation generation when the project is being built by Conan.
Enables compilation of all the source code (tests and examples) and generating 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 (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
^^^^^^^^^^^^^
@@ -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.
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
----------------------
@@ -294,8 +328,8 @@ library release the following steps may be performed:
mkdir my_project/build && cd my_project/build
conan install .. -pr <your_conan_profile> -s compiler.cppstd=20 -b=missing
cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
cmake --build .
cmake .. -G "Ninja Multi-Config" -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake
cmake --build . --config Release
Conan + CMake (Live At Head)
@@ -329,6 +363,9 @@ differences:
[requires]
mp-units/0.8.0@mpusz/testing
[layout]
cmake_layout
[generators]
CMakeToolchain
CMakeDeps
@@ -343,10 +380,9 @@ differences:
.. code-block:: shell
mkdir my_project/build && cd my_project/build
conan install .. -pr <your_conan_profile> -s compiler.cppstd=20 -b=outdated -u
cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
cmake --build .
conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -b=outdated -u
cmake --preset default
cmake --build --preset release
Install
@@ -358,43 +394,60 @@ to find it, it is enough to perform the following steps:
.. code-block:: shell
mkdir units/build && cd units/build
conan install .. -pr <your_conan_profile> -s compiler.cppstd=20 -b=missing
cmake ../src -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
cmake --install . --prefix <install_dir>
conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -b=missing
mv CMakeUserPresets.json src
cd src
cmake --preset default -DCMAKE_INSTALL_PREFIX=<your_installation_path>
cmake --build --preset release --target install
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 **mp-units** repository, you should:
In case you would like to build all the source code (with unit tests and examples) in **mp-units** repository,
you should:
1. Add remotes of additional Conan dependencies.
2. Use the *CMakeLists.txt* from the top-level directory.
3. Obtain Python dependencies.
4. Run Conan with `CONAN_RUN_TESTS`_ = ``True``.
1. Use the *CMakeLists.txt* from the top-level directory.
2. Run Conan with `build_all`_ = ``True``
(use ``-o build_docs=False`` if you want to skip the documentation generation).
.. 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
git clone https://github.com/mpusz/units.git && cd units
pip3 install -r docs/requirements.txt
mkdir units/build && cd units/build
conan install .. -pr <your_conan_profile> -s compiler.cppstd=20 -e mp-units:CONAN_RUN_TESTS=True -b outdated -u
conan build ..
conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -c mp-units:user.build:all=True -b missing
cmake --preset default
cmake --build --preset release --target documentation
The above will download and install all of the dependencies needed for the development of the library,
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
The above will download and install all of the dependencies needed and build the documentation.
Packaging
@@ -404,7 +457,7 @@ To test CMake installation and Conan packaging or create a Conan package run:
.. code-block:: shell
conan create . <username>/<channel> -pr <your_conan_profile> -s compiler.cppstd=20 -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.

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::
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_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_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_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
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
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`::
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,
unknown_dimension<exponent<dim_length, 1>, exponent<dim_time, -1>>>);
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::
@@ -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
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'
.. seealso::

View File

@@ -33,7 +33,7 @@ using namespace units;
namespace fps {
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> {};
@@ -54,8 +54,8 @@ using length = quantity<dim_length, U, Rep>;
namespace fps {
struct foot : named_scaled_unit<foot, "ft", as_magnitude<ratio(3'048, 1'000, -1)>(), metre> {};
struct yard : named_scaled_unit<yard, "yd", as_magnitude<3>(), foot> {};
struct foot : named_scaled_unit<foot, "ft", mag<ratio{3'048, 10'000}>(), metre> {};
struct yard : named_scaled_unit<yard, "yd", mag<3>(), foot> {};
struct dim_length : base_dimension<"L", foot> {};
@@ -84,20 +84,17 @@ void conversions()
void unknown_dimensions()
{
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_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_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_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)
{
return os << "ratio{" << r.num << ", " << r.den << ", " << r.exp << "}";
}
std::ostream& operator<<(std::ostream& os, const ratio& r) { return os << "ratio{" << r.num << ", " << r.den << "}"; }
template<Unit U>
std::ostream& operator<<(std::ostream& os, const U& u)

View File

@@ -157,9 +157,8 @@ public:
using legs = std::vector<leg>;
template<std::ranges::input_range R>
requires std::same_as<std::ranges::range_value_t<R>,
waypoint> explicit task(const R& r) :
waypoints_(std::ranges::begin(r), std::ranges::end(r))
requires std::same_as<std::ranges::range_value_t<R>, waypoint>
explicit task(const R& 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)
{
// TODO support for hypot(q, q)
return sqrt(pow<2>(dist.common()) + pow<2>(h.common()));
return hypot(dist.common(), h.common());
}
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 {
static constexpr auto symbol = Symbol; ///< Unique base dimension identifier
using base_unit = U; ///< Base unit adopted for this dimension
static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = magnitude{};
};
// base_dimension_less

View File

@@ -42,7 +42,7 @@ namespace units::detail {
template<typename... 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

View File

@@ -29,6 +29,7 @@
#include <units/bits/external/type_traits.h>
#include <units/customization_points.h>
#include <units/magnitude.h>
#include <units/symbol_text.h>
// IWYU pragma: end_exports
#include <cstdint>
@@ -86,6 +87,20 @@ inline constexpr bool is_named = false;
template<typename 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
template<basic_fixed_string Symbol, NamedUnit U>
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>
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>;
static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto m1 = D1::base_units_ratio * U1::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 unit = downcast_unit<dimension, mag / dimension::mag>;
using type = reference<dimension, unit>;
};
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
using common_quantity_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>
constexpr Magnitude auto derived_mag(exponent_list<Es...>)
{
return (as_magnitude<1>() * ... *
pow<ratio{Es::num, Es::den}>(Us::mag / dimension_unit<typename Es::dimension>::mag));
return (magnitude<>{} * ... * pow<ratio{Es::num, Es::den}>(Us::mag / dimension_unit<typename Es::dimension>::mag));
}
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
* 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
*/
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 {

View File

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

View File

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

View File

@@ -39,108 +39,20 @@ template<typename T>
return v < 0 ? -v : v;
}
// the following functions enable gcd and related computations on ratios
// with exponents. They avoid overflow. Further information here:
// 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
// Computes the rational gcd of n1/d1 and n2/d2
[[nodiscard]] constexpr auto gcd_frac(std::intmax_t n1, std::intmax_t d1, std::intmax_t n2, std::intmax_t d2) noexcept
{
// Short cut for equal ratios
if (n1 == n2 && d1 == d2 && e1 == e2) {
return std::array{n1, d1, e1};
if (n1 == n2 && d1 == d2) {
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
assert(std::numeric_limits<std::intmax_t>::max() / n1 > d2);
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);
@@ -148,29 +60,19 @@ constexpr void cwap(std::intmax_t& lhs, std::intmax_t& rhs)
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) {
den = 1;
exp = 0;
return;
}
std::intmax_t gcd = std::gcd(num, den);
num = num * (den < 0 ? -1 : 1) / 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)

View File

@@ -37,7 +37,7 @@ constexpr auto magnitude_text()
{
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 den = denominator(base);
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>
constexpr auto prefix_or_magnitude_text()
{
if constexpr (M == as_magnitude<1>()) {
if constexpr (M == mag<1>()) {
// no ratio/prefix
return basic_fixed_string("");
} else {

View File

@@ -34,7 +34,7 @@ namespace units {
template<typename Rep, typename Period>
struct quantity_like_traits<std::chrono::duration<Rep, Period>> {
private:
static constexpr auto mag = as_magnitude<ratio(Period::num, Period::den)>();
static constexpr auto mag = ::units::mag<ratio(Period::num, Period::den)>();
public:
using dimension = isq::si::dim_time;
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>
struct quantity_point_like_traits<std::chrono::time_point<C, std::chrono::duration<Rep, Period>>> {
private:
static constexpr auto mag = as_magnitude<ratio(Period::num, Period::den)>();
static constexpr auto mag = ::units::mag<ratio(Period::num, Period::den)>();
public:
using origin = clock_origin<C>;
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>
constexpr auto to_std_ratio_impl()
{
if constexpr (R.exp == 0)
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

View File

@@ -23,7 +23,7 @@
#pragma once
#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/dim_consolidate.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
* 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
* such quantities of different systems base_units_ratio 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
* such quantities of different systems mag is being used. The result of the division of two
* 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
* versa.
*
@@ -85,8 +85,8 @@ template<typename Child, Unit U, Exponent... Es>
struct derived_dimension : downcast_dispatch<Child, typename detail::make_dimension<Es...>> {
using recipe = exponent_list<Es...>;
using coherent_unit = U;
static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto base_units_ratio =
detail::absolute_magnitude(typename derived_dimension::exponents());
static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag =
detail::absolute_magnitude(typename derived_dimension::exponents()) / U::mag;
};
} // namespace units

View File

@@ -31,7 +31,7 @@
namespace units {
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

View File

@@ -189,9 +189,6 @@ constexpr widen_t<T> compute_base_power(BasePower auto bp)
if (bp.power.den != 1) {
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 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);
}
@@ -344,7 +341,7 @@ inline constexpr bool is_base_power_pack_valid = all_base_powers_valid<BPs...> &
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; }
@@ -498,8 +495,8 @@ namespace detail {
template<auto BP>
constexpr auto integer_part(magnitude<BP>)
{
constexpr auto power_num = numerator(BP.power);
constexpr auto power_den = denominator(BP.power);
constexpr auto power_num = BP.power.num;
constexpr auto power_den = BP.power.den;
if constexpr (std::is_integral_v<decltype(BP.get_base())> && (power_num >= power_den)) {
constexpr auto largest_integer_power = [=](BasePower auto bp) {
@@ -556,7 +553,7 @@ namespace detail {
template<auto BP>
constexpr auto remove_positive_power(magnitude<BP> m)
{
if constexpr (numerator(BP.power) < 0) {
if constexpr (BP.power.num < 0) {
return m;
} else {
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
// _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>
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> /
detail::prime_factorization_v<R.den>;
return detail::prime_factorization_v<R.num> / 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 {
@@ -663,7 +669,7 @@ constexpr ratio get_power(T base, magnitude<BPs...>)
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)
{

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);
}
/**
* @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

View File

@@ -405,11 +405,7 @@ public:
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)
{
gsl_ExpectsAudit(q.number() != quantity_values<rep>::zero());
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());
return detail::make_quantity<::units::reference<dim_one, ::units::one>{} / reference>(v / q.number());
}
template<typename Value>
@@ -444,7 +440,7 @@ public:
};
// CTAD
#if !UNITS_COMP_CLANG || UNITS_COMP_CLANG > 15
#if !UNITS_COMP_CLANG || UNITS_COMP_CLANG > 16
template<typename D, typename U, typename Rep>
explicit(false) quantity(Rep&&)->quantity<D, U, Rep>;
#endif

View File

@@ -28,6 +28,7 @@
#include <units/concepts.h>
#include <units/customization_points.h>
#include <units/magnitude.h>
#include <units/reference.h>
UNITS_DIAGNOSTIC_PUSH
// 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 {
template<typename T>
inline constexpr Magnitude auto quantity_magnitude = std::enable_if_t<!Quantity<T>, magnitude<>>{};
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>
inline constexpr Magnitude auto quantity_magnitude = decltype(Q::reference)::mag;
template<Quantity Q>
inline constexpr ratio quantity_ratio = as_ratio(quantity_magnitude<Q>);
@@ -79,8 +71,8 @@ template<typename From, typename To>
struct cast_traits;
template<typename From, typename To>
requires common_type_with_<std::common_type_t<From, To>,
std::intmax_t> struct cast_traits<From, To> {
requires common_type_with_<std::common_type_t<From, To>, std::intmax_t>
struct cast_traits<From, To> {
using ratio_type = std::common_type_t<std::common_type_t<From, To>, std::intmax_t>;
using rep_type = ratio_type;
};

View File

@@ -43,42 +43,28 @@ constexpr ratio inverse(const ratio& r);
/**
* @brief Provides compile-time rational arithmetic support.
*
* This class is really similar to @c std::ratio but gets an additional `Exp`
* template parameter that defines the exponent of the ratio. Another important
* difference is the fact that the objects of that class are used as class NTTPs
* rather then a type template parameter kind.
* This class is really similar to @c std::ratio. An important difference is the fact that the objects of that class
* are used as class NTTPs rather then a type template parameter kind.
*/
struct ratio {
std::intmax_t num;
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);
detail::normalize(num, den, exp);
detail::normalize(num, den);
}
[[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 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)
{
// First, get the inputs into a common exponent.
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};
return ratio{lhs.num * rhs.den + lhs.den * rhs.num, lhs.den * rhs.den};
}
[[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 gcd2 = std::gcd(rhs.num, lhs.den);
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 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)
{
if (r.exp < 0) {
return false;
} else {
return detail::gcdpow(r.num, r.exp, r.den) == r.den;
}
}
[[nodiscard]] constexpr bool is_integral(const ratio& r) { return r.num % r.den == 0; }
namespace detail {
[[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>
template<std::intmax_t Num>
[[nodiscard]] constexpr ratio pow(const ratio& r)
{
if constexpr (Num == 0) {
return ratio(1);
} else if constexpr (Num == Den) {
} else if constexpr (Num == 1) {
return r;
} else {
// simplify factors first and compute power for positive exponent
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);
const ratio result = detail::pow_impl<Num>(r);
// integer root loses precision so do pow first
const ratio result = detail::root<den>(detail::pow_impl<num>(r));
if constexpr (Num * Den < 0) { // account for negative exponent
if constexpr (Num < 0) { // account for negative exponent
return inverse(result);
} else {
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
[[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);
return ratio(res[0], res[1], res[2]);
const auto res = detail::gcd_frac(r1.num, r1.den, r2.num, r2.den);
return ratio(res[0], res[1]);
}
} // namespace units

View File

@@ -35,27 +35,21 @@ struct reference;
namespace detail {
template<typename D, typename D1, typename U1, typename D2, typename U2>
using reference_multiply_impl =
reference<D, downcast_unit<D, (U1::mag / dimension_unit<D1>::mag) * (U2::mag / dimension_unit<D2>::mag) *
dimension_unit<D>::mag>>;
template<Dimension D, Reference R1, Reference R2>
using reference_multiply_impl = reference<D, downcast_unit<D, R1::mag * R2::mag / D::mag>>;
template<typename D, typename D1, typename U1, typename D2, typename U2>
using reference_divide_impl =
reference<D, downcast_unit<D, (U1::mag / dimension_unit<D1>::mag) / (U2::mag / dimension_unit<D2>::mag) *
dimension_unit<D>::mag>>;
template<typename D, Reference R1, Reference R2>
using reference_divide_impl = reference<D, downcast_unit<D, R1::mag / R2::mag / D::mag>>;
} // namespace detail
template<Reference R1, Reference R2>
using reference_multiply =
detail::reference_multiply_impl<dimension_multiply<typename R1::dimension, typename R2::dimension>,
typename R1::dimension, typename R1::unit, typename R2::dimension, typename R2::unit>;
detail::reference_multiply_impl<dimension_multiply<typename R1::dimension, typename R2::dimension>, R1, R2>;
template<Reference R1, Reference R2>
using reference_divide =
detail::reference_divide_impl<dimension_divide<typename R1::dimension, typename R2::dimension>,
typename R1::dimension, typename R1::unit, typename R2::dimension, typename R2::unit>;
detail::reference_divide_impl<dimension_divide<typename R1::dimension, typename R2::dimension>, R1, R2>;
/**
* @brief The type for quantity references
@@ -100,6 +94,7 @@ template<Dimension D, UnitOf<D> U>
struct reference {
using dimension = D;
using unit = U;
static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = dimension::mag * unit::mag;
// Hidden Friends
// 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/external/downcasting.h>
#include <units/bits/external/type_traits.h>
// IWYU pragma: begin_exports
#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 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>
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
*/
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;
};
@@ -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)
*/
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
@@ -173,12 +177,8 @@ struct alias_unit : U {
* @tparam P prefix to be appied to the reference unit
* @tparam AU reference alias unit
*/
// TODO gcc bug: 95015
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95015
// template<Unit U, Prefix P, AliasUnit AU>
// requires (!AliasUnit<U>)
template<Unit U, Prefix P, NamedUnit AU>
requires detail::can_be_prefixed<AU>
template<Unit U, Prefix P, AliasUnit AU>
requires(!AliasUnit<U>)
struct prefixed_alias_unit : U {
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.
*/
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 {

View File

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

View File

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

View File

@@ -40,7 +40,7 @@ namespace units::isq::si::fps {
struct poundal : named_unit<poundal, "pdl"> {};
// 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> {};

View File

@@ -48,7 +48,7 @@ struct thousandth : alias_unit<thou, "thou"> {};
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> {};

View File

@@ -35,28 +35,28 @@
namespace units::isq::si::fps {
// 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> {};
template<UnitOf<dim_mass> U, Representation Rep = double>
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

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 :
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>
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>;
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)>(),
poundal_per_foot_sq> {};
named_scaled_unit<pound_force_per_foot_sq, "lbf ft2", mag<ratio(32'174'049, 1'000'000)>(), poundal_per_foot_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> {};

View File

@@ -37,7 +37,7 @@ namespace units::isq::si::hep {
// 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://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 zepto_barn : prefixed_unit<zepto_barn, zepto, 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 :
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 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> {};
@@ -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 YeV_per_c2 : prefixed_unit<YeV_per_c2, yotta, eV_per_c2> {};
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 :
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 :
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> {};

View File

@@ -41,7 +41,7 @@ namespace units::isq::si::hep {
struct kilogram_metre_per_second : derived_unit<kilogram_metre_per_second> {};
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> {};
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> {};

View File

@@ -36,13 +36,13 @@
namespace units::isq::si::iau {
// 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
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
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

View File

@@ -35,10 +35,10 @@
namespace units::isq::si::imperial {
// 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)
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

View File

@@ -37,30 +37,30 @@ namespace units::isq::si::international {
// si::international yard
// 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
// 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
struct fathom : named_scaled_unit<fathom, "fathom", as_magnitude<2>(), yard> {};
struct fathom : named_scaled_unit<fathom, "fathom", mag<2>(), yard> {};
// si::international inch
// 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
// 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
// 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
// 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
// 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
// and provide hyperlinks to definitions
struct pica_comp :
named_scaled_unit<pica_comp, "pica(comp)", as_magnitude<ratio(4233333, 1000000, -3)>(), si::metre> {};
struct pica_prn : named_scaled_unit<pica_prn, "pica(prn)", as_magnitude<ratio(2108759, 500000, -3)>(), si::metre> {};
struct pica_comp : named_scaled_unit<pica_comp, "pica(comp)", mag<4'233'333>() * mag_power<10, -9>(), si::metre> {};
struct pica_prn :
named_scaled_unit<pica_prn, "pica(prn)", mag<ratio(2108759, 500000)>() * mag_power<10, -3>(), si::metre> {};
struct point_comp :
named_scaled_unit<point_comp, "point(comp)", as_magnitude<ratio(1763889, 500000, -4)>(), si::metre> {};
struct point_prn : named_scaled_unit<point_prn, "point(prn)", as_magnitude<ratio(1757299, 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)", mag<ratio(1757299, 500000)>() * mag_power<10, -4>(), si::metre> {};
#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://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
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://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

View File

@@ -58,7 +58,7 @@ struct exakatal : prefixed_unit<exakatal, exa, katal> {};
struct zettakatal : prefixed_unit<zettakatal, zetta, 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 :
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:
// https://www.bipm.org/documents/20126/41483022/SI-Brochure-9.pdf#page=147
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 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 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> {};

View File

@@ -36,7 +36,7 @@ namespace units::isq::si {
// 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;

View File

@@ -56,7 +56,7 @@ struct exatesla : prefixed_unit<exatesla, exa, tesla> {};
struct zettatesla : prefixed_unit<zettatesla, zetta, 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 :
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 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> {};

View File

@@ -27,26 +27,26 @@
namespace units::isq::si {
// clang-format off
struct yocto : prefix<yocto, "y", pow<-24>(as_magnitude<10>())> {};
struct zepto : prefix<zepto, "z", pow<-21>(as_magnitude<10>())> {};
struct atto : prefix<atto, "a", pow<-18>(as_magnitude<10>())> {};
struct femto : prefix<femto, "f", pow<-15>(as_magnitude<10>())> {};
struct pico : prefix<pico, "p", pow<-12>(as_magnitude<10>())> {};
struct nano : prefix<nano, "n", pow<-9>(as_magnitude<10>())> {};
struct micro : prefix<micro, basic_symbol_text{"\u00b5", "u"}, pow<-6>(as_magnitude<10>())> {};
struct milli : prefix<milli, "m", pow<-3>(as_magnitude<10>())> {};
struct centi : prefix<centi, "c", pow<-2>(as_magnitude<10>())> {};
struct deci : prefix<deci, "d", pow<-1>(as_magnitude<10>())> {};
struct deca : prefix<deca, "da", pow<1>(as_magnitude<10>())> {};
struct hecto : prefix<hecto, "h", pow<2>(as_magnitude<10>())> {};
struct kilo : prefix<kilo, "k", pow<3>(as_magnitude<10>())> {};
struct mega : prefix<mega, "M", pow<6>(as_magnitude<10>())> {};
struct giga : prefix<giga, "G", pow<9>(as_magnitude<10>())> {};
struct tera : prefix<tera, "T", pow<12>(as_magnitude<10>())> {};
struct peta : prefix<peta, "P", pow<15>(as_magnitude<10>())> {};
struct exa : prefix<exa, "E", pow<18>(as_magnitude<10>())> {};
struct zetta : prefix<zetta, "Z", pow<21>(as_magnitude<10>())> {};
struct yotta : prefix<yotta, "Y", pow<24>(as_magnitude<10>())> {};
struct yocto : prefix<yocto, "y", pow<-24>(mag<10>())> {};
struct zepto : prefix<zepto, "z", pow<-21>(mag<10>())> {};
struct atto : prefix<atto, "a", pow<-18>(mag<10>())> {};
struct femto : prefix<femto, "f", pow<-15>(mag<10>())> {};
struct pico : prefix<pico, "p", pow<-12>(mag<10>())> {};
struct nano : prefix<nano, "n", pow<-9>(mag<10>())> {};
struct micro : prefix<micro, basic_symbol_text{"\u00b5", "u"}, pow<-6>(mag<10>())> {};
struct milli : prefix<milli, "m", pow<-3>(mag<10>())> {};
struct centi : prefix<centi, "c", pow<-2>(mag<10>())> {};
struct deci : prefix<deci, "d", pow<-1>(mag<10>())> {};
struct deca : prefix<deca, "da", pow<1>(mag<10>())> {};
struct hecto : prefix<hecto, "h", pow<2>(mag<10>())> {};
struct kilo : prefix<kilo, "k", pow<3>(mag<10>())> {};
struct mega : prefix<mega, "M", pow<6>(mag<10>())> {};
struct giga : prefix<giga, "G", pow<9>(mag<10>())> {};
struct tera : prefix<tera, "T", pow<12>(mag<10>())> {};
struct peta : prefix<peta, "P", pow<15>(mag<10>())> {};
struct exa : prefix<exa, "E", pow<18>(mag<10>())> {};
struct zetta : prefix<zetta, "Z", pow<21>(mag<10>())> {};
struct yotta : prefix<yotta, "Y", pow<24>(mag<10>())> {};
// clang-format on
} // 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 microsecond : prefixed_unit<microsecond, micro, second> {};
struct millisecond : prefixed_unit<millisecond, milli, second> {};
struct minute : named_scaled_unit<minute, "min", as_magnitude<60>(), second> {};
struct hour : named_scaled_unit<hour, "h", as_magnitude<60>(), minute> {};
struct day : named_scaled_unit<day, "d", as_magnitude<24>(), hour> {};
struct minute : named_scaled_unit<minute, "min", mag<60>(), second> {};
struct hour : named_scaled_unit<hour, "h", mag<60>(), minute> {};
struct day : named_scaled_unit<day, "d", mag<24>(), hour> {};
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")
{
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;
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")
{
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;
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")
{
// 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("{:%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>
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{
get_value<std::intmax_t>(numerator(m)),
get_value<std::intmax_t>(denominator(m)),
@@ -125,10 +125,10 @@ TEST_CASE("base_power")
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(as_magnitude<ratio{4, 17}>());
check_product_with_inverse_is_identity(mag<3>());
check_product_with_inverse_is_identity(mag<ratio{4, 17}>());
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")
{
CHECK(as_magnitude<1>() == magnitude<>{});
CHECK(as_magnitude<2>() == magnitude<base_power{2}>{});
CHECK(as_magnitude<3>() == magnitude<base_power{3}>{});
CHECK(as_magnitude<4>() == magnitude<base_power{2, 2}>{});
CHECK(mag<1>() == magnitude<>{});
CHECK(mag<2>() == magnitude<base_power{2}>{});
CHECK(mag<3>() == magnitude<base_power{3}>{});
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 nonzero exp")
{
constexpr ratio r{3, 1, 2};
REQUIRE(r.exp == 2);
CHECK(as_magnitude<r>() == as_magnitude<300>());
}
SECTION("Supports fractions") { CHECK(mag<ratio{5, 8}>() == magnitude<base_power{2, -3}, base_power{5}>{}); }
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.
// 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>")
@@ -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
// test can pass is that we have provided the answer, by specializing the `known_first_factor` variable template
// 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")
{
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<std::size_t>(mag_412), std::size_t{412});
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")
{
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<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
// 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>:
// get_value<int64_t>(pow<63>(as_magnitude<2>()));
// get_value<int64_t>(pow<63>(mag<2>()));
// 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<309>(as_magnitude<10>()));
// get_value<double>(pow<3099>(as_magnitude<10>()));
// get_value<double>(pow<3099999>(as_magnitude<10>()));
get_value<double>(pow<308>(mag<10>())); // Compiles, correctly.
// get_value<double>(pow<309>(mag<10>()));
// get_value<double>(pow<3099>(mag<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));
// get_value<int>(sqrt_2);
}
@@ -249,46 +242,40 @@ TEST_CASE("Equality works for magnitudes")
{
SECTION("Equivalent ratios are equal")
{
CHECK(as_magnitude<1>() == as_magnitude<1>());
CHECK(as_magnitude<3>() == as_magnitude<3>());
CHECK(as_magnitude<ratio{3, 4}>() == as_magnitude<ratio{9, 12}>());
CHECK(mag<1>() == mag<1>());
CHECK(mag<3>() == mag<3>());
CHECK(mag<ratio{3, 4}>() == mag<ratio{9, 12}>());
}
SECTION("Different ratios are unequal")
{
CHECK(as_magnitude<3>() != as_magnitude<5>());
CHECK(as_magnitude<3>() != as_magnitude<ratio{3, 2}>());
CHECK(mag<3>() != mag<5>());
CHECK(mag<3>() != mag<ratio{3, 2}>());
}
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);
}
}
TEST_CASE("Multiplication works for magnitudes")
{
SECTION("Reciprocals reduce to null magnitude")
{
CHECK(as_magnitude<ratio{3, 4}>() * as_magnitude<ratio{4, 3}>() == as_magnitude<1>());
}
SECTION("Reciprocals reduce to null magnitude") { CHECK(mag<ratio{3, 4}>() * mag<ratio{4, 3}>() == mag<1>()); }
SECTION("Products work as expected")
{
CHECK(as_magnitude<ratio{4, 5}>() * as_magnitude<ratio{4, 3}>() == as_magnitude<ratio{16, 15}>());
}
SECTION("Products work as expected") { CHECK(mag<ratio{4, 5}>() * mag<ratio{4, 3}>() == mag<ratio{16, 15}>()); }
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}}>{});
}
SECTION("Supports constexpr")
{
constexpr auto p = as_magnitude<ratio{4, 5}>() * as_magnitude<ratio{4, 3}>();
CHECK(p == as_magnitude<ratio{16, 15}>());
constexpr auto p = mag<ratio{4, 5}>() * mag<ratio{4, 3}>();
CHECK(p == mag<ratio{16, 15}>());
}
}
@@ -296,20 +283,20 @@ TEST_CASE("Common Magnitude")
{
SECTION("Identity for identical magnitudes")
{
CHECK(common_magnitude(as_magnitude<1>(), as_magnitude<1>()) == as_magnitude<1>());
CHECK(common_magnitude(as_magnitude<15>(), as_magnitude<15>()) == as_magnitude<15>());
CHECK(common_magnitude(mag<1>(), mag<1>()) == mag<1>());
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}>());
}
SECTION("Greatest Common Factor for integers")
{
CHECK(common_magnitude(as_magnitude<24>(), as_magnitude<36>()) == as_magnitude<12>());
CHECK(common_magnitude(as_magnitude<24>(), as_magnitude<37>()) == as_magnitude<1>());
CHECK(common_magnitude(mag<24>(), mag<36>()) == mag<12>());
CHECK(common_magnitude(mag<24>(), mag<37>()) == mag<1>());
}
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")
{
CHECK(as_magnitude<ratio{3, 4}>() / as_magnitude<ratio{3, 4}>() == as_magnitude<1>());
CHECK(as_magnitude<15>() / as_magnitude<15>() == as_magnitude<1>());
CHECK(mag<ratio{3, 4}>() / mag<ratio{3, 4}>() == mag<1>());
CHECK(mag<15>() / mag<15>() == mag<1>());
}
SECTION("Quotients work as expected")
{
CHECK(as_magnitude<ratio{4, 5}>() / as_magnitude<ratio{4, 3}>() == as_magnitude<ratio{3, 5}>());
}
SECTION("Quotients work as expected") { CHECK(mag<ratio{4, 5}>() / mag<ratio{4, 3}>() == mag<ratio{3, 5}>()); }
SECTION("Supports constexpr")
{
constexpr auto q = as_magnitude<ratio{4, 5}>() / as_magnitude<ratio{4, 3}>();
CHECK(q == as_magnitude<ratio{3, 5}>());
constexpr auto q = mag<ratio{4, 5}>() / mag<ratio{4, 3}>();
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")
{
CHECK(pow<0>(as_magnitude<1>()) == as_magnitude<1>());
CHECK(pow<0>(as_magnitude<123>()) == as_magnitude<1>());
CHECK(pow<0>(as_magnitude<ratio{3, 4}>()) == as_magnitude<1>());
CHECK(pow<0>(pi_to_the<ratio{-1, 2}>()) == as_magnitude<1>());
CHECK(pow<0>(mag<1>()) == mag<1>());
CHECK(pow<0>(mag<123>()) == mag<1>());
CHECK(pow<0>(mag<ratio{3, 4}>()) == mag<1>());
CHECK(pow<0>(pi_to_the<ratio{-1, 2}>()) == mag<1>());
}
SECTION("Anything to the 1 is itself")
{
CHECK(pow<1>(as_magnitude<1>()) == as_magnitude<1>());
CHECK(pow<1>(as_magnitude<123>()) == as_magnitude<123>());
CHECK(pow<1>(as_magnitude<ratio{3, 4}>()) == as_magnitude<ratio{3, 4}>());
CHECK(pow<1>(mag<1>()) == mag<1>());
CHECK(pow<1>(mag<123>()) == mag<123>());
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}>());
}
@@ -366,11 +350,11 @@ TEST_CASE("can distinguish integral, rational, and irrational magnitudes")
CHECK(is_rational(m));
};
check_rational_and_integral(magnitude<>{});
check_rational_and_integral(as_magnitude<1>());
check_rational_and_integral(as_magnitude<3>());
check_rational_and_integral(as_magnitude<8>());
check_rational_and_integral(as_magnitude<412>());
check_rational_and_integral(as_magnitude<ratio{1, 1}>());
check_rational_and_integral(mag<1>());
check_rational_and_integral(mag<3>());
check_rational_and_integral(mag<8>());
check_rational_and_integral(mag<412>());
check_rational_and_integral(mag<ratio{1, 1}>());
}
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_rational(m));
};
check_rational_but_not_integral(as_magnitude<ratio{1, 2}>());
check_rational_but_not_integral(as_magnitude<ratio{5, 8}>());
check_rational_but_not_integral(mag<ratio{1, 2}>());
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")
{
constexpr ratio r = as_ratio(as_magnitude<ratio{22, 7}>());
constexpr ratio r = as_ratio(mag<ratio{22, 7}>());
CHECK(r == ratio{22, 7});
}
SECTION("Irrational magnitude does not convert to ratio")
{
// 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.
// 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")
{
CHECK(extract_power_of_10(as_magnitude<10>()) == 1);
CHECK(extract_power_of_10(as_magnitude<20>()) == 1);
CHECK(extract_power_of_10(as_magnitude<40>()) == 1);
CHECK(extract_power_of_10(as_magnitude<50>()) == 1);
CHECK(extract_power_of_10(as_magnitude<100>()) == 2);
CHECK(extract_power_of_10(mag<10>()) == 1);
CHECK(extract_power_of_10(mag<20>()) == 1);
CHECK(extract_power_of_10(mag<40>()) == 1);
CHECK(extract_power_of_10(mag<50>()) == 1);
CHECK(extract_power_of_10(mag<100>()) == 2);
}
SECTION("Picks out negative powers")
{
constexpr auto ONE = as_magnitude<1>();
CHECK(extract_power_of_10(ONE / as_magnitude<10>()) == -1);
CHECK(extract_power_of_10(ONE / as_magnitude<20>()) == -1);
CHECK(extract_power_of_10(ONE / as_magnitude<40>()) == -1);
CHECK(extract_power_of_10(ONE / as_magnitude<50>()) == -1);
CHECK(extract_power_of_10(ONE / as_magnitude<100>()) == -2);
constexpr auto ONE = mag<1>();
CHECK(extract_power_of_10(ONE / mag<10>()) == -1);
CHECK(extract_power_of_10(ONE / mag<20>()) == -1);
CHECK(extract_power_of_10(ONE / mag<40>()) == -1);
CHECK(extract_power_of_10(ONE / mag<50>()) == -1);
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

View File

@@ -22,6 +22,7 @@
#include <catch2/catch.hpp>
#include <units/isq/si/area.h>
#include <units/isq/si/cgs/length.h>
#include <units/isq/si/length.h>
#include <units/isq/si/time.h>
#include <units/isq/si/volume.h>
@@ -30,7 +31,7 @@
using namespace units;
using namespace units::isq;
using namespace units::isq::si;
using namespace units::isq::si::literals;
// classical
@@ -186,10 +187,10 @@ TEST_CASE("ceil functions", "[ceil]")
{
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") {
// REQUIRE(ceil<si::second>(-1000._q_ms) == -1_q_s);
// }
SECTION("ceil -1000. milliseconds with target unit second should be -1 second")
{
REQUIRE(ceil<si::second>(-1000._q_ms) == -1_q_s);
}
SECTION("ceil -999. milliseconds with target unit second should be 0 seconds")
{
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(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::area<si::square_metre>>);
#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
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>>);
#if UNITS_DOWNCAST_MODE == 0
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
static_assert(Force<si::force<si::newton>>);
static_assert(!Force<si::time<si::second>>);
#if UNITS_DOWNCAST_MODE == 0
// 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
static_assert(Energy<si::energy<si::joule>>);
static_assert(!Energy<si::time<si::second>>);
#if UNITS_DOWNCAST_MODE == 0
// 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
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 *= 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<as_magnitude<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<mag<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 %= quantity(1.0); };
requires !requires { w %= 1.0 * (w / w); };

View File

@@ -287,7 +287,7 @@ static_assert(get_length_derived_quantity() == 1_q_m);
// 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, double>);
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(quantity{1} / 1_q_s), frequency<hertz, std::int64_t>>);
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&&>);
@@ -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(quantity{1} / 1._q_s), frequency<hertz, long double>>);
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 % quantity{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(quantity{1.L} / 1_q_s), frequency<hertz, long double>>);
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
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
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(
compare<decltype(1_q_m_per_s * 1_q_h), length<scaled_unit<as_magnitude<ratio(36, 1, 2)>(), metre>, std::int64_t>>);
static_assert(
compare<decltype(1_q_m * 1_q_min), quantity<unknown_dimension<exponent<dim_length, 1>, exponent<dim_time, 1>>,
scaled_unit<as_magnitude<60>(), unknown_coherent_unit>, std::int64_t>>);
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>>>,
std::int64_t>>);
static_assert(compare<decltype(1_q_s * 1_q_Hz), dimensionless<one, std::int64_t>>);
static_assert(
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_min), frequency<scaled_unit<mag<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_km),
static_assert(
compare<decltype(1 / 1_q_km),
quantity<unknown_dimension<exponent<dim_length, -1>>,
scaled_unit<as_magnitude<ratio(1, 1, -3)>(), unknown_coherent_unit>, std::int64_t>>);
static_assert(compare<decltype(1_q_km / 1_q_m), dimensionless<scaled_unit<as_magnitude<1000>(), one>, 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<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_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(
compare<decltype(1_q_min / 1_q_m), quantity<unknown_dimension<exponent<dim_length, -1>, exponent<dim_time, 1>>,
scaled_unit<as_magnitude<60>(), unknown_coherent_unit>, std::int64_t>>);
compare<decltype(1_q_min / 1_q_m),
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_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
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(
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
@@ -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 * 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, dimensionless<scaled_unit<as_magnitude<ratio(1, 1000)>(), one>, int>(400)),
static_assert(comp(quotient_remainder_theorem(3 * km, dimensionless<scaled_unit<mag<ratio(1, 1000)>(), one>, int>(400)),
3 * km));
} // namespace

View File

@@ -28,11 +28,6 @@ using namespace units;
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(3, 8) * ratio(1) == ratio(3, 8));
static_assert(ratio(4) * ratio(1, 8) == ratio(1, 2));
@@ -45,21 +40,12 @@ static_assert(-ratio(3, 8) == ratio(-3, 8));
// ratio addition
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(2) / ratio(8) == ratio(1, 4));
static_assert(ratio(1, 8) / ratio(2) == ratio(1, 16));
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<1>(ratio(2)) == ratio(2));
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<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
static_assert(common_ratio(ratio(1), ratio(1000)) == 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(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
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 <=> 0));
static_assert((ratio(1, 1, 1) <=> ratio(10)) == (0 <=> 0));
} // 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(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(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
// static_assert(200_q_cm * si::length<si::metre>(2) == si::area<si::square_metre>(4)); // TODO Add support for
// comparing of an unknown_dimension
// TODO Add support for 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(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
// static_assert(si::area<si::square_metre>(4) / 200_q_cm == si::length<si::metre>(2)); // TODO Add support for
// comparing of an unknown_dimension
// TODO Add support for 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) ==
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 ==
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

View File

@@ -157,8 +157,8 @@ static_assert(quantity_cast<si::fps::length<si::fps::foot>>(si::length<si::metre
// multiplication
// static_assert(2 * ft * si::length<si::metre>(2) == si::area<si::square_metre>(1.2192)); // TODO Add support for
// comparing of an unknown_dimension
// TODO Add support for 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) ==
si::area<si::square_metre>(1.2192));
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
// static_assert(si::area<si::square_metre>(4) / 200_q_cm == si::length<si::metre>(2)); // TODO Add support for
// comparing of an unknown_dimension
// TODO Add support for 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) ==
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) ==

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(10_q_km / 5_q_km == 2);
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(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(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 == 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(2 / 1_q_Hz == 2_q_s);

View File

@@ -36,12 +36,12 @@ using namespace units::isq;
struct metre : named_unit<metre, "m"> {};
struct centimetre : prefixed_unit<centimetre, si::centi, 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 foot : named_scaled_unit<foot, "ft", as_magnitude<ratio(1, 3)>(), yard> {};
struct yard : named_scaled_unit<yard, "yd", mag<ratio{9'144, 10'000}>(), metre> {};
struct foot : named_scaled_unit<foot, "ft", mag<ratio(1, 3)>(), yard> {};
struct dim_length : base_dimension<"length", metre> {};
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 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::scaled_unit, metre>);
static_assert(compare<downcast<scaled_unit<as_magnitude<1>(), metre>>, metre>);
static_assert(compare<downcast<scaled_unit<as_magnitude<ratio(1, 1, -2)>(), metre>>, centimetre>);
static_assert(compare<downcast<scaled_unit<mag<1>(), metre>>, metre>);
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 / 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(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
# SOFTWARE.
import os
from conan import ConanFile
from conan.tools.build import cross_building
from conan.tools.cmake import CMake
from conan.tools.cmake import CMake, cmake_layout
class TestPackageConan(ConanFile):
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
def requirements(self):
@@ -38,6 +41,10 @@ class TestPackageConan(ConanFile):
cmake.configure()
cmake.build()
def layout(self):
cmake_layout(self)
def test(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")