Merge remote-tracking branch 'mpusz/master' into master-msvc-194

This commit is contained in:
Jonas Hoppe
2024-09-09 12:05:19 +02:00
78 changed files with 1750 additions and 1017 deletions

View File

@@ -1,3 +1,4 @@
include: ["cmake/.cmake-format-additional_commands-jegp.cmake_modules.yaml"]
parse:
additional_commands:
add_mp_units_module:

View File

@@ -46,14 +46,36 @@ jobs:
path: .cache
restore-keys: |
mkdocs-material-
- name: Installing pip packages
run: |
pip install conan mkdocs-material mkdocs-rss-plugin mkdocs-material[imaging] mike
- name: Prepare git
run: |
git config --global user.name github-actions
git config --global user.email github-actions@github.com
git fetch origin gh-pages --depth=1
- name: Installing API reference dependencies
run: |
sudo apt install haskell-stack graphviz nodejs npm ghc cabal-install
npm install split mathjax-full mathjax-node-sre
cabal update
- name: Installing MathJax-Node-CLI
run: |
git clone https://github.com/mathjax/mathjax-node-cli --depth=1
echo "${{ github.workspace }}/mathjax-node-cli/bin" >> $GITHUB_PATH
- name: Get git repos with API reference tools
run: |
git clone https://github.com/JohelEGP/jegp.cmake_modules.git --depth=1
git clone https://github.com/JohelEGP/draft.git --branch=standardese_sources_base --depth=1
git clone https://github.com/JohelEGP/cxxdraft-htmlgen.git --branch=standardese_sources_base --depth=1
- name: Generate API reference
run: |
cmake -S docs/api_reference/src -B build \
-DCMAKE_MODULE_PATH="${{ github.workspace }}/jegp.cmake_modules/modules" \
-DJEGP_STANDARDESE_SOURCES_GIT_REPOSITORY="${{ github.workspace }}/draft" \
-DJEGP_CXXDRAFT_HTMLGEN_GIT_REPOSITORY="${{ github.workspace }}/cxxdraft-htmlgen"
cmake --build build
mv build/mp-units.html docs/api_reference/gen
- name: Installing pip dependencies
run: |
pip install conan mkdocs-material mkdocs-rss-plugin mkdocs-material[imaging] mkdocs-exclude mike
- name: Building docs
run: |
mike deploy --push --update-aliases `conan inspect . | sed -n -r 's/version: ([0-9]+.[0-9]+).[0-9]+/\1/p'` latest

4
.gitignore vendored
View File

@@ -46,3 +46,7 @@ CMakeUserPresets.json
# Conan
*.pyc
/test_package/build/
# cxxdraft-htmlgen
docs/api_reference/src/source/
docs/api_reference/gen/

View File

@@ -36,3 +36,5 @@ repos:
rev: 5.0.4
hooks:
- id: flake8
exclude: ^docs/javascripts/

View File

@@ -1,93 +1,289 @@
# Contributing to `mp-units`
# Contributing
👍🎉 First off, thanks for taking the time to contribute! 🎉👍
## Gitpod
The easiest way to start coding is to jump straight into [Gitpod](https://www.gitpod.io). You can either click the button
below or prefix any `mp-units` URL (main branch, other branches, issues, PRs, ...) in your web browser with `gitpod.io/#`
(e.g., <https://gitpod.io/#https://github.com/mpusz/mp-units>).
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/mpusz/mp-units)
The above environment provides you with:
- all supported compilers for Linux development and the latest version of build tools like `cmake` and `conan`
- all Conan dependencies preinstalled on the machine
- all documentation generation tools ready to use
- completed prebuilds for all targets (Debug and Release builds for each compiler)
- VSCode preconfigured to benefit from all the above
## Download, Build, Install
Alternatively, please refer to our official docs for
[download, build, and install instructions](https://mpusz.github.io/mp-units/latest/getting_started/installation_and_usage)
if you want to setup a development environment on your local machine.
## Before Committing git Changes
There are a few steps recommended to check before committing and pushing your changes to the git repository.
### Naming Conventions
Here are the main rules for naming things in this repo:
- types, functions, variables naming in a `standard_case`
- template parameters in a `PascalCase`
- C++20 concepts names for now in a `PascalCase` but we plan to change it (see <https://github.com/mpusz/mp-units/issues/93>
for more details)
### Unified Code Formatting
There is a formatting standard enforced with the `pre-commit` script. Before committing your changes please do the following:
```bash
pip3 install -U pre-commit
pre-commit run --all-files
```
This will run `clang-format` for code formatting with the `.clang-format` file provided in the repo, `cmake-format` to format
the CMake files, and some other check as well.
The script will run on all the files in the repo and will apply the changes in-place when needed.
After the script is done please make sure to stage all those changes to git commit.
### Build All CMake Targets And Run Unit Tests
The simplest way to verify if all targets build correctly and all unit tests pass is to run:
```bash
conan build . -pr <your_conan_profile> -s compiler.cppstd=23 -o cxx_modules=True -c user.mp-units.build:all=True -b missing
```
as described in the
[Installation and Usage](https://mpusz.github.io/mp-units/latest/getting_started/installation_and_usage/#contributing-or-just-building-all-the-tests-and-examples)
chapter of our documentation.
_Hint:_ To ensure that that we always build all the targets and to save some typing of the Conan commands,
it is a good practice to set the following in the `~/.conan2/global.conf`:
```text
user.mp-units.build:all=True
```
Non-Conan users should:
- build `all` and `all_verify_interface_header_sets` CMake targets,
- run all unit tests.
### Backward Compatibility
Before submission, please remember to check if the code compiles fine on the supported compilers.
The CI will check it anyway but it is good to check at least some of the configurations before pushing changes.
Especially older compilers can be tricky as those do not support all the C++20 features well enough. The official
list of supported compilers can be always found in the
[Installation And Usage](https://mpusz.github.io/mp-units/latest/getting_started/cpp_compiler_support)
chapter of our documentation.
## Where To Start?
## Where to start?
If you are looking for a good issue to start with, please check the following:
- [good first issue](https://github.com/mpusz/mp-units/labels/good%20first%20issue) - issues that should be pretty simple to implement.
- [help wanted](https://github.com/mpusz/mp-units/labels/help%20wanted) - issues that typically are a bit more involved than beginner issues.
- [high priority](https://github.com/mpusz/mp-units/labels/high%20priority) - things to fix ASAP but often of higher complexity.
- [good first issue](https://github.com/mpusz/mp-units/labels/good%20first%20issue) - issues that
should be pretty simple to implement,
- [help wanted](https://github.com/mpusz/mp-units/labels/help%20wanted) - issues that typically are
a bit more involved than beginner issues,
- [high priority](https://github.com/mpusz/mp-units/labels/high%20priority) - things to fix ASAP
but often of higher complexity.
## Gitpod
The easiest way to start coding is to jump straight into [Gitpod](https://www.gitpod.io)
environment. You can either click the button below
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/mpusz/mp-units)
or prefix any `mp-units` URL (main branch, other branches, issues, PRs, ...) in your web browser
with `gitpod.io/#` (e.g., <https://gitpod.io/#https://github.com/mpusz/mp-units>).
The above environment provides you with:
- all supported compilers for Linux development and the latest version of build tools like `cmake`
and `conan`,
- all Conan dependencies preinstalled on the machine,
- all documentation generation tools ready to use,
- completed prebuilds for all targets (Debug and Release builds for each compiler),
- VSCode preconfigured to benefit from all the above.
## Building, testing, and packaging
Alternatively, please refer to our official docs for
[download, build, and install instructions](https://mpusz.github.io/mp-units/latest/getting_started/installation_and_usage) with the below changes
if you want to set up a development environment on your local machine.
### Conan configuration properties
[`user.mp-units.build:all`](#user.mp-units.build-all){ #user.mp-units.build-all }
Enables compilation of all the source code, including tests and examples. To support this, it requires some additional Conan build dependencies described in
[Repository directory tree and dependencies](https://mpusz.github.io/mp-units/latest/getting_started/project_structure#cmake-projects-and-dependencies).
It also runs unit tests during the Conan build (unless
[`tools.build:skip_test`](https://docs.conan.io/2/reference/commands/config.html?highlight=tools.build:skip_test#conan-config-list)
configuration property is set to `True`).
[conan build all support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[`user.mp-units.build:skip_la`](#user-skip-la){ #user-skip-la }
If `user.mp-units.build:all` is enabled, among others, Conan installs the external
[wg21-linear_algebra](https://conan.io/center/recipes/wg21-linear_algebra)
dependency and enables the compilation of linear algebra-based tests and usage examples.
Such behavior can be disabled with this option.
[conan skip la support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[`user.mp-units.analyze:clang-tidy`](#user.mp-units.analyze-clang-tidy){ #user.mp-units.analyze-clang-tidy }
Enables clang-tidy analysis.
[conan clang-tidy support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
### CMake options for mp-units project developers
[`MP_UNITS_DEV_BUILD_LA`](#MP_UNITS_DEV_BUILD_LA){ #MP_UNITS_DEV_BUILD_LA }
:   [:octicons-tag-24: 2.2.0][cmake build la support] · :octicons-milestone-24: `ON`/`OFF` (Default: `ON`)
Enables building code depending on the linear algebra library.
[cmake build la support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[`MP_UNITS_DEV_IWYU`](#MP_UNITS_DEV_IWYU){ #MP_UNITS_DEV_IWYU }
:   [:octicons-tag-24: 2.2.0][cmake iwyu support] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`)
Enables include-what-you-use analysis.
[cmake iwyu support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[`MP_UNITS_DEV_CLANG_TIDY`](#MP_UNITS_DEV_CLANG_TIDY){ #MP_UNITS_DEV_CLANG_TIDY }
:   [:octicons-tag-24: 2.2.0][cmake clang-tidy support] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`)
Enables clang-tidy analysis.
[cmake clang-tidy support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
### Building the entire repository
To build all the **mp-units** source code (with unit tests and examples), you should:
1. Use the _CMakeLists.txt_ from the top-level directory.
2. Run Conan with [`user.mp-units.build:all`](#user.mp-units.build-all) = `True`.
```shell
git clone https://github.com/mpusz/mp-units.git && cd units
conan build . -pr <your_conan_profile> -s compiler.cppstd=23 -c user.mp-units.build:all=True -b missing
```
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 than Conan, then you should replace
the `conan build` with `conan install` command and then follow with a regular CMake build and testing:
```shell
conan install . -pr <your_conan_profile> -s compiler.cppstd=23 -c user.mp-units.build:all=True -b missing
cmake --preset conan-default
cmake --build --preset conan-release
cmake --build --preset conan-release --target all_verify_interface_header_sets
cmake --build --preset conan-release --target test
```
!!! hint
To ensure that we always build all the targets and to save some typing of the Conan commands,
we can set the following in the `~/.conan2/global.conf`:
```text
user.mp-units.build:all=True
```
### Packaging
To test CMake installation and Conan packaging run:
```shell
conan create . --user <username> --channel <channel> -pr <your_conan_profile> -s compiler.cppstd=23 \
-c user.mp-units.build:all=True -b missing
```
The above will create a Conan package and run tests provided in _./test_package_ directory.
In case you would like to upload **mp-units** package to the Conan server, do the following:
```shell
conan upload -r <remote-name> --all mp-units/2.2.0@<user>/<channel>
```
## Building documentation
We are building our documentation using [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/).
The easiest way to install all the required dependencies is with `pip`:
```shell
pip install -U mkdocs-material mkdocs-rss-plugin
```
Additionally, a [Cairo Graphics library](https://www.cairographics.org/) is required by
Material for MkDocs. Please follow the
[official MkDocs documentation to install it](https://squidfunk.github.io/mkdocs-material/plugins/requirements/image-processing/#cairo-graphics).
After that, you can either:
- easily [start a live server to preview the documentation as you write](https://squidfunk.github.io/mkdocs-material/creating-your-site/#previewing-as-you-write)
```shell
mkdocs serve
```
- [build the documentation](https://squidfunk.github.io/mkdocs-material/creating-your-site/#building-your-site)
```shell
mkdocs build
```
### Generating API reference
We need to take a few steps to set up our environment so that we are ready to generate API reference
documents.
First, we need to satisfy the requirements described in <https://github.com/Eelis/cxxdraft-htmlgen>.
On the Ubuntu platform, this is equivalent to the following instructions run from the user's home
directory:
```bash
sudo apt install haskell-stack graphviz nodejs npm ghc cabal-install
npm install split mathjax-full mathjax-node-sre
cabal update
```
Also, installing `mathjax-node-cli` through npm does not help because `tex2html` is not called within
node.js. This is why we need to download `mathjax-node-cli` and add its `bin` folder to the `PATH`
environment variable:
```bash
git clone https://github.com/mathjax/mathjax-node-cli
echo "export PATH=\"$PWD/mathjax-node-cli/bin:\$PATH\"" >> ~/.bashrc && source ~/.bashrc
```
Next, we need to clone the following git repositories:
- <https://github.com/JohelEGP/jegp.cmake_modules>
- `standardese_sources_base` branch of <https://github.com/JohelEGP/draft>
- `standardese_sources_base` branch of <https://github.com/JohelEGP/cxxdraft-htmlgen>
For example:
```bash
git clone https://github.com/JohelEGP/jegp.cmake_modules.git --depth=1
git clone https://github.com/JohelEGP/draft.git --branch=standardese_sources_base --depth=1
git clone https://github.com/JohelEGP/cxxdraft-htmlgen.git --branch=standardese_sources_base --depth=1
```
Now, we are ready to start building our API reference. First, we need to configure CMake with the
following:
```bash
cmake -S docs/api_reference/src -B build/docs/api_reference \
-DCMAKE_MODULE_PATH="<path to gh:JohelEGP/jegp.cmake_modules>/modules" \
      -DJEGP_STANDARDESE_SOURCES_GIT_REPOSITORY="<path to gh:JohelEGP/draft>" \
-DJEGP_CXXDRAFT_HTMLGEN_GIT_REPOSITORY="<path to gh:JohelEGP/cxxdraft-htmlgen>"
```
Then we need to build the docs with CMake:
```bash
cmake --build build/docs/api_reference
```
In the end, we need to move the generated documentation to the `docs/api_reference/gen` subdirectory:
```bash
mv build/docs/api_reference/mp-units.html docs/api_reference/gen
```
or just link the entire directory:
```bash
ln -sf ../../build/docs/api_reference/mp-units.html docs/api_reference/gen
```
## Before committing git changes
There are a few steps recommended to check before committing and pushing your changes to the git
repository.
### Naming conventions
Here are the main rules for naming things in this repo:
- types, functions, variables use `standard_case`,
- template parameters use `PascalCase`,
- C++ concept names, for now, use `PascalCase`, but we plan to change it
(see [GitHub Issue #93](https://github.com/mpusz/mp-units/issues/93) for more details).
### Unified code formatting
A formatting standard is enforced with the `pre-commit` script. Before committing your changes,
please do the following:
```bash
pip install -U pre-commit
pre-commit run --all-files
```
This will run:
- `clang-format` for code formatting with the `.clang-format` file provided in the repo,
- `cmake-format` to format the CMake files,
- some other checks (e.g., python script checkers, whitespaces, etc.).
The script will run on all the files in the repo and will apply the changes in place when needed.
After the script is done, please make sure to review and stage all those changes for the git commit.
### Backward compatibility
Before submission, please remember to check if the code compiles fine on the supported compilers.
The CI will check it anyway, but it is good to check at least some of the configurations before
pushing changes.
Especially older compilers can be tricky as those do not have full C++20 conformance.
The official list of supported compilers can always be found in the
[C++ compiler support (API/ABI)](https://mpusz.github.io/mp-units/latest/getting_started/cpp_compiler_support)
chapter of our documentation.

View File

@@ -0,0 +1,82 @@
parse:
additional_commands:
_jegp_common_yaml_anchors:
kwargs:
PUBLIC_INTERFACE_PRIVATE: &public_interface_private
kwargs:
PUBLIC: +
INTERFACE: +
PRIVATE: +
jegp_add_standardese_sources:
pargs:
nargs: 1
flags:
- EXCLUDE_FROM_ALL
kwargs:
LIBRARIES: +
APPENDICES: +
EXTENSIONS: +
CHECKED: 1
PDF: &standardese_pdf
pargs:
flags:
- EXCLUDE_FROM_MAIN
kwargs:
PATH: 1
HTML:
<<: *standardese_pdf
kwargs:
SECTION_FILE_STYLE: 1
LATEX_REGEX_REPLACE: +
HTML_REGEX_REPLACE: +
jegp_add_module:
pargs: &jegp_add_module_pargs
nargs: 1
flags:
- IMPORTABLE_HEADER
kwargs:
SOURCES: +
COMPILE_OPTIONS: *public_interface_private
LINK_LIBRARIES: *public_interface_private
jegp_cpp_module:
pargs: *jegp_add_module_pargs
jegp_target_link_header_units:
pargs:
nargs: 1+
jegp_cpp2_target_sources:
pargs:
nargs: 1
kwargs:
JEGP_FILE_SET_KWARGS: &file_set
kwargs:
FILE_SET: 1
TYPE: 1
BASE_DIRS: +
FILES: +
PUBLIC: *file_set
INTERFACE: *file_set
PRIVATE: *file_set
jegp_add_headers_test:
pargs:
nargs: 1+
kwargs:
PRIVATE_REGEXES: +
jegp_add_test:
pargs:
nargs: 1+
flags:
- COMPILE_ONLY
kwargs:
TYPE: 1
SOURCES: +
COMPILE_OPTIONS: +
LINK_LIBRARIES: +
jegp_add_build_error:
pargs:
nargs: 1+
kwargs:
AS: 1
TYPE: 1
SOURCE: 1
COMPILE_OPTIONS: +
LINK_LIBRARIES: +

74
docs/api_reference.md Normal file
View File

@@ -0,0 +1,74 @@
---
hide:
- navigation
- toc
---
<script src="../javascripts/jquery.min.js"></script>
<script src="../javascripts/iframeResizer.min.js"></script>
<iframe id="apiIframe" style="width: 100%; border: none;"></iframe>
<script>
// injects scripts to the iframe source code
$('#apiIframe').on('load', function() {
body = $(this).contents().find('body');
body.append('<scr' + 'ipt src="../../javascripts/iframeResizer.contentWindow.min.js"></scr' + 'ipt>');
body.append('<scr' + 'ipt>' +
'var links = document.querySelectorAll("a");' +
'for (var i = 0; i < links.length; i++) {' +
' links[i].addEventListener("click", whichLinkWasClicked);' +
'}' +
'' +
'function whichLinkWasClicked(evt) {' +
' window.parentIFrame.sendMessage(evt.target.href);' +
' evt.preventDefault();' +
'}' +
'</scr' + 'ipt>');
});
// reloads the window with the content when a user navigates over the browser's history
function locationHashChanged() {
window.location.reload();
}
window.onhashchange = locationHashChanged;
apiIframe = document.getElementById('apiIframe');
hash = window.location.hash;
if (hash.length == 0) {
// sets default hash for the API Reference
hash = "#full";
history.pushState(null, "", hash);
}
// set the iframe source based on the hash in the URL
if (hash.startsWith("#wg21.link/")) {
apiIframe.src = "https://" + hash.slice(1);
apiIframe.height = 900;
}
else
apiIframe.src = "gen/" + hash.slice(1) + ".html";
// receives content height from the subpage displayed in the iframe
// works only for the pages in the same domain as the main docs
iFrameResize({
scrolling: "yes",
// obtains the link URL clicked in the subpage
onMessage: function(messageData) {
url = messageData.message;
if (url.startsWith("https://wg21.link/")) {
hash = '#' + messageData.message.replace("https://", "");
}
else {
pos = messageData.message.indexOf('#');
if(pos == -1) {
pos = messageData.message.lastIndexOf('/');
}
hash = '#' + messageData.message.slice(pos + 1);
}
history.pushState(null, "", hash);
window.location.reload();
}
},'#apiIframe')
</script>

View File

@@ -0,0 +1,41 @@
cmake_minimum_required(VERSION 3.24.0)
project(mp-units_reference_documentations LANGUAGES NONE)
include(JEGPAddStandardeseSources)
set(pdf_title "mp-units Library")
set(page_license "MIT License")
set(first_library_chapter "qties")
set(last_library_chapter "qties")
set(cover_title "mp-units Library Reference Documentations")
set(reply_to "\\href{${PROJECT_HOMEPAGE_URL}/discussions}{Discussions}, \\href{${PROJECT_HOMEPAGE_URL}/issues}{issues}")
jegp_add_standardese_sources(
mp-units_reference_documentations
LIBRARIES intro quantities
EXTENSIONS macros_extensions
CHECKED TRUE
# PDF PATH "mp-units.pdf" #[[EXCLUDE_FROM_MAIN]]
HTML PATH "mp-units.html" #[[EXCLUDE_FROM_MAIN]]
SECTION_FILE_STYLE "WithExtension"
LATEX_REGEX_REPLACE
# Latex commands.
[[\\href{([^}]+)}{([^}]+)};HREF(\1)(\2)]]
# Macros extensions.
[[\\refcpp{([^}]+)};REFCPP(\1)]]
[[\\irefcpp{([^}]+)};~(REFCPP(\1))]]
[[\\refcppx{([^}]+)}{([^}]+)};REFCPPX(\1)(\2)]]
[[\\irefcppx{([^}]+)}{([^}]+)};~(REFCPPX(\1)(\2))]]
[[\\refiev{([^}]+)};REFIEV(\1)]]
[[\\irefiev{([^}]+)};~(REFIEV(\1))]]
# Main matter and annexes.
[[\\\"{o};ö]]
HTML_REGEX_REPLACE
# Latex commands.
[[HREF\(([^)]+)\)\(([^)]+)\);<a href="\1">\2</a>]]
# Macros extensions.
[[REFCPP\(([^)]+)\);<a href="https://wg21.link/\1">ISOCPP, [\1]</a>]]
[[REFCPPX\(([^)]+)\)\(([^)]+)\);<a href="https://wg21.link/\1\2">ISOCPP, [\1]</a>]] #
[[ISOCPP;N4971]]
[[REFIEV\(([^)]+)\);<a href="https://www.electropedia.org/iev/iev.nsf/display?openform&ievref=\1">IEC 60050, \1</a>]]
# Main matter and annexes.
)

View File

@@ -0,0 +1,134 @@
%!TEX root = std.tex
\rSec0[scope]{Scope}
\pnum
\indextext{scope|(}%
This document describes the contents of the \defn{mp-units library}.
\indextext{scope|)}
\rSec0[refs]{References}
\pnum
\indextext{references|(}%
The following documents are referred to in the text
in such a way that some or all of their content
constitutes requirements of this document.
For dated references, only the edition cited applies.
For undated references,
the latest edition of the referenced document
(including any amendments) applies.
\begin{itemize}
\item
IEC 60050-102:2007/AMD3:2021,
\doccite{Amendment 3 --- International Electrotechnical Vocabulary (IEV) ---
Part 102: Mathematics --- General concepts and linear algebra}
\item
IEC 60050-112:2010/AMD2:2020,
\doccite{Amendment 2 --- International Electrotechnical Vocabulary (IEV) ---
Part 112: Quantities and units}
\item
ISO 80000 (all parts), \doccite{Quantities and units}
\item
The \Cpp{} Standards Committee.
\IsoCpp{}: \doccite{Working Draft, Standard for Programming Language \Cpp{}}.
Edited by Thomas K\"{o}ppe.
Available from: \url{https://wg21.link/\IsoCpp{}}
\item
The \Cpp{} Standards Committee.
SD-8: \doccite{Standard Library Compatibility}.
Edited by Bryce Lelbach.
Available from: \url{https://wg21.link/SD8}
\end{itemize}
\indextext{references|)}
\rSec0[defs]{Terms and definitions}
\pnum
\indextext{definitions|(}%
For the purposes of this document,
the terms and definitions given in
IEC 60050-102:2007/AMD3:2021,
IEC 60050-112:2010/AMD2:2020,
ISO 80000-2:2019,
and
\IsoCpp{},
and the following apply.
\pnum
ISO and IEC maintain terminology databases
for use in standardization
at the following addresses:
\begin{itemize}
\item ISO Online browsing platform: available at \url{https://www.iso.org/obp}
\item IEC Electropedia: available at \url{http://www.electropedia.org}
\end{itemize}
\indextext{definitions|)}
\rSec0[spec]{Specification}
\rSec1[spec.ext]{External}
\pnum
The specification of the mp-units library subsumes
\refcpp{description}, \refcpp{requirements}, \refcpp{concepts.equality}, and SD-8,
all assumingly amended for the context of this library.
\begin{note}
This means that, non exhaustively,
\begin{itemize}
\item \tcode{::mp_units2} is a reserved namespace, and
\item
\tcode{std::vector<mp_units::\placeholdernc{type}>}
is a program-defined specialization and a library-defined specialization
from the point of view of the \Cpp{} standard library and the mp-units library, respectively.
\end{itemize}
\end{note}
\pnum
The mp-units library is not part of the \Cpp{} implementation.
\rSec1[spec.cats]{Categories}
\pnum
Detailed specifications for each of the components in the library are in
\ref{\firstlibchapter}--\ref{\lastlibchapter},
as shown in \tref{lib.cats}.
\begin{floattable}{Library categories}{lib.cats}
{ll}
\topline
\hdstyle{Clause} & \hdstyle{Category} \\ \capsep
\ref{qties} & Quantities library \\
\end{floattable}
\pnum
The quantities library\iref{qties}
describes components for dealing with quantities.
\rSec1[spec.mods]{Modules}
\pnum
The mp-units library provides the
\defnx{mp-units modules}{module!mp-units},
shown in \tref{modules}.
\begin{multicolfloattable}{mp-units modules}{modules}
{lll}
\tcode{mp_units} \\
\columnbreak
\tcode{mp_units.core} \\
\columnbreak
\tcode{mp_units.systems} \\
\end{multicolfloattable}
\rSec1[spec.reqs]{Library-wide requirements}
\rSec2[spec.res.names]{Reserved names}
\pnum
The mp-units library reserves macro names that start with
\tcode{MP_UNITS\opt{\gterm{digit-sequence}}_}.

View File

@@ -0,0 +1,11 @@
\newcommand{\IsoCpp}{N4971}
%% Inline non-parenthesized C++ reference
\newcommand{\refcpp}[1]{\href{https://wg21.link/#1}{\IsoCpp{}, [#1]}}
\newcommand{\irefcpp}[1]{\nolinebreak[3] (\refcpp{#1})}
\newcommand{\refcppx}[2]{\href{https://wg21.link/#1\##2}{\IsoCpp{}, [#1]}}
\newcommand{\irefcppx}[2]{\nolinebreak[3] (\refcppx{#1}{#2})}
%% Inline IEV reference
\newcommand{\refiev}[1]{\href{https://www.electropedia.org/iev/iev.nsf/display?openform&ievref=#1}{IEC 60050, #1}}
\newcommand{\irefiev}[1]{\nolinebreak[3] (\refiev{#1})}

View File

@@ -0,0 +1,266 @@
%!TEX root = std.tex
\rSec0[qties]{Quantities library}
\rSec1[qties.summary]{Summary}
\pnum
This Clause describes components for dealing with quantities,
as summarized in \tref{qties.summary}.
\begin{modularlibsumtab}{Quantities library summary}{qties.summary}
\ref{qty.helpers} & Helpers & \tcode{mp_units.core} \\
\ref{qty.traits} & Traits & \\
\ref{qty.concepts} & Concepts & \\
\ref{qty.types} & Types & \\
\ref{qty.compat} & Compatibility & \\
\ref{qty.one} & Dimension one & \\ \rowsep
\ref{qty.systems} & Systems & \tcode{mp_units.systems} \\
\ref{qty.chrono} & \tcode{std::chrono} compatibility & \\
\end{modularlibsumtab}
\rSec1[mp.units.syn]{Module \tcode{mp_units} synopsis}
\indexmodule{mp_units}%
\begin{codeblock}
export module mp_units;
export import mp_units.core;
export import mp_units.systems;
\end{codeblock}
\rSec1[mp.units.core.syn]{Module \tcode{mp_units.core} synopsis}
\indexmodule{mp_units.core}%
\begin{codeblock}
export module mp_units.core;
import std;
export namespace mp_units {
export enum class quantity_character { scalar, vector, tensor };
// \ref{qty.traits}, traits
template<typename Rep>
constexpr bool treat_as_floating_point = std::is_floating_point_v<Rep>;
template<typename Rep>
constexpr bool is_scalar =
std::is_floating_point_v<Rep> || (std::is_integral_v<Rep> && !is_same_v<Rep, bool>);
template<typename Rep>
constexpr bool is_vector = false;
template<typename Rep>
constexpr bool is_tensor = false;
template<typename Rep>
struct quantity_values;
template<typename T>
struct quantity_like_traits;
template<typename T>
struct quantity_point_like_traits;
// \ref{qty.concepts}, concepts
template<typename T>
concept @\deflibconcept{some_reference}@ = template_of(^std::remove_cvref_t<T>) == ^reference;
template<typename T>
concept representation = @\seebelownc@;
template<typename T, quantity_character Ch>
concept representation_of = @\seebelownc@;
template<typename T>
concept some_quantity_spec = @\seebelownc@;
// \ref{qty.types}, types
template<auto...>
struct quantity_spec; // \notdef
template<auto Q>
struct kind_of_; // \notdef
template<@\unspec@... Expr>
struct derived_quantity_spec;
// \ref{qty.type}, class template \tcode{quantity}
export template<@\libconcept{some_reference}@ auto R,
@\libconcept{representation_of}@<get_quantity_spec(R).character> Rep = double>
class quantity;
// \ref{qty.point.type}, class template \tcode{quantity_point}
template<@\unspec@>
class quantity_point;
}
\end{codeblock}
\rSec1[mp.units.systems.syn]{Module \tcode{mp_units.systems} synopsis}
\indexmodule{mp_units.systems}%
\begin{codeblock}
export module mp_units.systems;
export import mp_units.core;
import std;
export namespace mp_units {
}
\end{codeblock}
\rSec1[qty.helpers]{Helpers}
\begin{itemdecl}
consteval bool @\exposidnc{converts-to-base-subobject-of}@(std::meta type, std::meta template_name);
\end{itemdecl}
\begin{itemdescr}
\pnum
\expects
\tcode{is_type(type) \&\& is_template(template_name)} is \tcode{true}.
\pnum
\returns
\tcode{true} if
\tcode{[:type:]} has an unambiguous and accessible base
that is a specialization of \tcode{[:template_name:]}, and
\tcode{false} otherwise.
\end{itemdescr}
\rSec1[qty.traits]{Traits}
\begin{itemdecl}
template<typename Rep>
constexpr bool @\libglobal{is_scalar}@ =
std::is_floating_point_v<Rep> || (std::is_integral_v<Rep> && !is_same_v<Rep, bool>);
template<typename Rep>
constexpr bool @\libglobal{is_vector}@ = false;
template<typename Rep>
constexpr bool @\libglobal{is_tensor}@ = false;
\end{itemdecl}
\begin{itemdescr}
\pnum
\remarks
Pursuant to \refcpp{namespace.std}\iref{spec.ext},
users may specialize \tcode{is_scalar}, \tcode{is_vector}, and \tcode{is_tensor} to \tcode{true}
for cv-unqualified program-defined types
which respectively represent
a scalar\irefiev{102-02-18},
a vector\irefiev{102-03-04}, and
% FIXME Undefined term.
a tensor,
and \tcode{false} for types which respectively do not.
\end{itemdescr}
\rSec1[qty.concepts]{Concepts}
\begin{itemdecl}
export template<typename T>
concept @\deflibconcept{representation}@ =
(is_scalar<T> || is_vector<T> || is_tensor<T>)&&std::regular<T> && @\exposidnc{scalable}@<T>;
\end{itemdecl}
\begin{itemdecl}
export template<typename T, quantity_character Ch>
concept @\deflibconcept{representation_of}@ =
@\libconcept{representation}@<T> && ((Ch == quantity_character::scalar && is_scalar<T>) ||
(Ch == quantity_character::vector && is_vector<T>) ||
(Ch == quantity_character::tensor && is_tensor<T>));
\end{itemdecl}
% FIXME Despite the `some_` prefix, it doesn't conform to the convention
% `template_of(^std::remove_cvref_t<T>) == ^template_name`.
\begin{itemdecl}
template<typename T>
concept @\defexposconceptnc{named-quantity-spec}@ =
(@\exposidnc{converts-to-base-subobject-of}@(^T, ^quantity_spec) && template_of(^T) != ^kind_of_);
template<typename T>
concept @\deflibconcept{some_quantity_spec}@ =
@\exposconceptnc{named-quantity-spec}@<T> ||
detail::IntermediateDerivedQuantitySpec<T> ||
template_of(^T) == ^kind_of;
\end{itemdecl}
\rSec1[qty.types]{Types}
\rSec2[qty.types.general]{General}
\pnum
A \defnadj{quantity}{type}
is a type \tcode{\placeholder{Q}}
that is a specialization of \tcode{quantity} or \tcode{quantity_point}.
\tcode{\placeholder{Q}} represents a quantity\irefiev{112-01-01}
with \tcode{\placeholder{Q}::rep} as its number
and \tcode{\placeholder{Q}::reference} as its reference.
\tcode{\placeholder{Q}} is a structural type\irefcppx{temp.param}{term.structural.type}
if \tcode{\placeholder{Q}::rep} is a structural type.
\pnum
Each class template defined in subclause \ref{qty.types}
has data members and special members specified below, and
has no base classes or members other than those specified.
\rSec2[qty.type]{Class template \tcode{quantity}}
\begin{codeblock}
namespace mp_units {
export template<@\libconcept{some_reference}@ auto R,
@\libconcept{representation_of}@<get_quantity_spec(R).character> Rep = double>
class quantity { @\unspec@ };
}
\end{codeblock}
Let \tcode{\placeholder{Q}} be a specialization of \tcode{quantity}.
\begin{itemize}
\item
If \tcode{Rep} is a scalar,
\tcode{\placeholder{Q}} represents a scalar quantity\irefiev{102-02-19}.
\item
If \tcode{Rep} is a vector,
\tcode{\placeholder{Q}} represents a vector\irefiev{102-03-04}.
% FIXME What if `Rep` is a tensor?
\end{itemize}
\rSec2[qty.point.type]{Class template \tcode{quantity_point}}
\begin{codeblock}
namespace mp_units {
export template<@\unspec@>
class quantity_point { @\unspec@ };
}
\end{codeblock}
A \defnadj{quantity point}{type} is a specialization of \tcode{quantity_point}.
Let \tcode{\placeholder{Q}} be a quantity point type.
\tcode{\placeholdernc{Q}::point_origin} represents
the origin point of a position vector\irefiev{102-03-15}.
\begin{itemize}
\item
If \tcode{Rep} is a scalar,
\tcode{\placeholder{Q}} represents the scalar quantity\irefiev{102-02-19}
of a position vector.
\item
If \tcode{Rep} is a vector,
\tcode{\placeholder{Q}} represents a position vector.
% FIXME What if `Rep` is a tensor?
\end{itemize}
\rSec1[qty.compat]{Compatibility}
\rSec1[qty.one]{Dimension one}
\rSec1[qty.systems]{Systems}
\rSec1[qty.chrono]{\tcode{std::chrono} compatibility}

View File

@@ -0,0 +1 @@
../../CONTRIBUTING.md

View File

@@ -1,155 +1,7 @@
# Installation And Usage
This chapter provides all the necessary information to obtain and build the code using **mp-units**.
It also describes how to build or distribute the library and generate its documentation.
## Project structure
### Repository directory tree and dependencies
The [GitHub repository](https://github.com/mpusz/mp-units) contains three independent CMake-based
projects:
- **_./src_**
- header-only project containing whole **mp-units** library
- _./src/CMakeList.txt_ file is intended as an **entry point for library users**
- in case this library becomes part of the C++ standard, it will have no external dependencies
but until then, it depends on the following:
- [gsl-lite](https://github.com/gsl-lite/gsl-lite) or [ms-gsl](https://github.com/microsoft/GSL)
to verify runtime contracts (if contract checking is enabled),
- [{fmt}](https://github.com/fmtlib/fmt) to provide text formatting of quantities
(if `std::format` is not supported yet on a specific compiler).
- **_._**
- project used as an **entry point for library development and CI/CD**
- it wraps _./src_ project together with usage examples and tests
- additionally to the dependencies of _./src_ project, it uses:
- [Catch2](https://github.com/catchorg/Catch2) library as a unit tests framework,
- [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.
- **_./test_package_**
- CMake library installation and Conan package verification.
!!! important "Important: Library users should not use the top-level CMake file"
Top level _CMakeLists.txt_ file should only be used by **mp-units** developers and contributors
as an entry point for the project's development. We want to ensure that everyone will build **ALL**
the code correctly before pushing a commit. Having such options would allow unintended issues to
leak to PRs and CI.
This is why our projects have two entry points:
- _./CMakeLists.txt_ is **to be used by projects developers** to build **ALL** the project code
with really restrictive compilation flags,
- _./src/CMakeLists.txt_ contains only a pure library definition and **should be used by the
customers** that prefer to use CMake's
[`add_subdirectory()`](https://cmake.org/cmake/help/latest/command/add_subdirectory.html) to
handle the dependencies.
To learn more about the rationale, please check our
[FAQ](faq.md#why-dont-we-have-cmake-options-to-disable-the-building-of-tests-and-examples).
### Modules
The **mp-units** library provides the following C++ modules:
```mermaid
flowchart TD
mp_units --- mp_units.systems --- mp_units.core
```
| C++ Module | CMake Target | Contents |
|--------------------|----------------------|----------------------------------------------------------|
| `mp_units.core` | `mp-units::core` | Core library framework and systems-independent utilities |
| `mp_units.systems` | `mp-units::systems` | All the systems of quantities and units |
| `mp_units` | `mp-units::mp-units` | Core + Systems |
!!! note
C++ modules are provided within the package only when:
- [`cxx_modules`](#cxx_modules) Conan option is set to `True`,
- [`MP_UNITS_BUILD_CXX_MODULES`](#MP_UNITS_BUILD_CXX_MODULES) CMake option is set to `ON`.
### Header files
All of the project's header files can be found in the `mp-units/...` subdirectory.
#### Core library
- `mp-units/framework.h` contains the entire library's framework definitions,
- `mp-units/concepts.h` exposes only the library's concepts for generic code needs,
- `mp-units/format.h` provides text formatting support,
- `mp-units/ostream.h` enables streaming of the library's objects to the text output,
- `mp-units/math.h` provides overloads of common math functions for quantities,
- `mp-units/random.h` provides C++ pseudo-random number generators for quantities,
- `mp-units/compat_macros.h` provides macros for [wide compatibility](../users_guide/use_cases/wide_compatibility.md).
??? info "More details"
More detailed header files can be found in subfolders which typically should not be
included by the end users:
- `mp-units/framework/...` provides all the public interfaces of the framework,
- `mp-units/bits/...` provides private implementation details only (no public definitions),
- `mp-units/ext/...` contains external dependencies that at some point in the future should
be replaced with C++ standard library facilities.
#### Systems and associated utilities
The systems definitions can be found in the `mp-units/systems/...` subdirectory:
##### Systems of quantities
- `mp-units/systems/isq.h` provides
[International System of Quantities (ISQ)](https://en.wikipedia.org/wiki/International_System_of_Quantities)
definitions,
??? tip "Tip: Improving compile times"
`mp-units/systems/isq.h` might be expensive to compile in every translation unit. There are
some smaller, domain targeted files available for explicit inclusion in the
`mp-units/systems/isq/...` subdirectory.
##### Systems of units
- `mp-units/systems/si.h` provides
[International System of Units (SI)](https://en.wikipedia.org/wiki/International_System_of_Units)
definitions and associated math functions,
- `mp-units/systems/angular.h` provides strong angular units and associated math functions,
- `mp-units/systems/international.h` provides
[international yard and pound](https://en.wikipedia.org/wiki/International_yard_and_pound) units,
- `mp-units/systems/imperial.h` includes `international.h` and extends it with
[imperial units](https://en.wikipedia.org/wiki/Imperial_units),
- `mp-units/systems/usc.h` includes `international.h` and extends it with
[United States customary system of units](https://en.wikipedia.org/wiki/United_States_customary_units),
- `mp-units/systems/cgs.h` provides
[centimetre-gram-second system of units](https://en.wikipedia.org/wiki/Centimetre%E2%80%93gram%E2%80%93second_system_of_units),
- `mp-units/systems/iau.h` provides
[astronomical system of units](https://en.wikipedia.org/wiki/Astronomical_system_of_units),
- `mp-units/systems/hep.h` provides units used in
[high-energy physics](https://en.wikipedia.org/wiki/Particle_physics),
- `mp-units/systems/typographic.h` provides units used in
[typography or typesetting](https://en.wikipedia.org/wiki/Typographic_unit),
- `mp-units/systems/natural.h` provides an example implementation of
[natural units](https://en.wikipedia.org/wiki/Natural_units).
??? tip "Tip: Improving compile times"
`mp-units/systems/si.h` might be expensive to compile in every translation unit.
There are some smaller files available for explicit inclusion in the
`mp-units/systems/si/...` subdirectory.
`mp-units/systems/si/unit_symbols.h` is the most expensive to include.
This chapter provides all the necessary information to obtain **mp-units** and build the user's
source code using it.
## Obtaining dependencies
@@ -157,66 +9,82 @@ The systems definitions can be found in the `mp-units/systems/...` subdirectory:
This library assumes that most of the dependencies will be provided by the
[Conan Package Manager](https://conan.io/). If you want to obtain required
dependencies by other means, some modifications to the library's CMake files might be needed.
The rest of the dependencies responsible for documentation generation are provided by
`python3-pip`.
### Conan quick intro
??? info "Conan quick intro"
In case you are not familiar with Conan, to install it (or upgrade) just do:
In case you are not familiar with Conan, to install it (or upgrade) just do:
```shell
pip install -U conan
```
After that, you might need to add a custom profile file for your development environment
in _~/.conan2/profiles_ directory. An example profile can look as follows:
```ini hl_lines="5" title="~/.conan2/profiles/gcc12"
[settings]
arch=x86_64
build_type=Release
compiler=gcc
compiler.cppstd=20
compiler.libcxx=libstdc++11
compiler.version=12
os=Linux
[conf]
tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"}
```
!!! tip "Setting the language version"
Please note that the **mp-units** library requires at least C++20 to be set in a Conan profile
or forced via the Conan command line. If we do the former, we will not need to provide
`-s compiler.cppstd=20` every time we run a Conan command line (as provided in the command
line instructions below).
!!! tip "Using Ninja as a CMake generator for Conan"
It is highly recommended to set Ninja as a CMake generator for Conan. To do so, we should
create a _~/.conan2/global.conf_ file that will set `tools.cmake.cmaketoolchain:generator`
to one of the Ninja generators. For example:
```text title="~/.conan2/global.conf"
tools.cmake.cmaketoolchain:generator="Ninja Multi-Config"
```shell
pip install -U conan
```
!!! tip "Separate build folders for different configurations"
After that, you might need to add a custom profile file for your development environment
in _~/.conan2/profiles_ directory. An example profile can look as follows:
_~/.conan2/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/2/reference/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 and C++ standard version:
```ini hl_lines="5" title="~/.conan2/profiles/gcc12"
[settings]
arch=x86_64
build_type=Release
compiler=gcc
compiler.cppstd=20
compiler.libcxx=libstdc++11
compiler.version=12
os=Linux
```text title="~/.conan2/global.conf"
tools.cmake.cmake_layout:build_folder_vars=["settings.compiler", "settings.compiler.version", "settings.compiler.cppstd"]
[conf]
tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"}
```
In such a case, we will need to use a configuration-specific preset name in the Conan instructions
provided below rather than just `conan-default` and `conan-release`
(e.g. `conan-gcc-13-23` and `conan-gcc-13-23-release`)
!!! tip "Setting the language version"
Please note that the **mp-units** library requires at least C++20 to be set in a Conan profile
or forced via the Conan command line. If we do the former, we will not need to provide
`-s compiler.cppstd=20` every time we run a Conan command line (as provided in the command
line instructions below).
!!! tip "Using Ninja as a CMake generator for Conan"
It is highly recommended to set Ninja as a CMake generator for Conan. To do so, we could
create a _~/.conan2/global.conf_ file that will set `tools.cmake.cmaketoolchain:generator`
to one of the Ninja generators. For example:
```text title="~/.conan2/global.conf"
tools.cmake.cmaketoolchain:generator="Ninja Multi-Config"
```
!!! tip "Separate build folders for different configurations"
_~/.conan2/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/2/reference/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 and C++ standard version:
```text title="~/.conan2/global.conf"
tools.cmake.cmake_layout:build_folder_vars=["settings.compiler", "settings.compiler.version", "settings.compiler.cppstd"]
```
In such a case, we will need to use a configuration-specific preset name in the Conan instructions
provided below rather than just `conan-default` and `conan-release`
(e.g., `conan-gcc-13-23` and `conan-gcc-13-23-release`)
??? info "CMake with presets support"
It is recommended to use at least CMake 3.23 to build this project to benefit from CMake Presets
generated by Conan. All build instructions below assume that you have such support. If not,
your CMake invocations have to be replaced with something like:
```shell
mkdir build && cd build
cmake .. -G "Ninja Multi-Config" -DCMAKE_TOOLCHAIN_FILE=<path_to_generators_dir>/conan_toolchain.cmake
cmake --build . --config Release
```
!!! tip
In case you can't use CMake 3.23 but you have access to CMake 3.20 or later, you can append
`-c tools.cmake.cmaketoolchain.presets:max_schema_version=2` to the `conan install` command
which will force Conan to use an older version of the CMake Presets schema.
## Build options
@@ -225,7 +93,7 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"}
Most of the below options are related to the C++ language features available in the compilers.
Please refer to the [C++ compiler support](cpp_compiler_support.md) chapter to learn more
about which C++ features are required and which compiler support them.
about which C++ features are required for each option and which compilers support them.
### Conan options
@@ -289,166 +157,92 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"}
: [:octicons-tag-24: 2.2.0][conan freestanding] · :octicons-milestone-24: `True`/`False` (Default: `False`)
Configures the library in the [freestanding](https://en.cppreference.com/w/cpp/freestanding)
mode. When enabled, the library's source code should build with the compiler's
mode. When enabled, the library's source code will build with the compiler's
[`-ffreestanding`](https://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html) compilation option
without any issues.
[conan freestanding]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
### Conan configuration properties
??? info "CMake options to set when Conan is not being used"
[`user.mp-units.build:all`](#user.mp-units.build-all){ #user.mp-units.build-all }
### CMake options
: [:octicons-tag-24: 2.2.0][conan build all support] · :octicons-milestone-24: `True`/`False` (Default: `False`)
Conan will automatically set all the below CMake options based on its configuration (described above).
Manual setting of the below CMake options is only needed when Conan is not being used.
Enables compilation of all the source code, including tests and examples. To support this, it requires some additional Conan build dependencies described in
[Repository directory tree and dependencies](#repository-directory-tree-and-dependencies).
It also runs unit tests during Conan build (unless
[`tools.build:skip_test`](https://docs.conan.io/2/reference/commands/config.html?highlight=tools.build:skip_test#conan-config-list)
configuration property is set to `True`).
[`MP_UNITS_BUILD_AS_SYSTEM_HEADERS`](#MP_UNITS_BUILD_AS_SYSTEM_HEADERS){ #MP_UNITS_BUILD_AS_SYSTEM_HEADERS }
[conan build all support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
: [:octicons-tag-24: 2.2.0][cmake as system headers support] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`)
[`user.mp-units.build:skip_la`](#user-skip-la){ #user-skip-la }
Exports library as system headers.
: [:octicons-tag-24: 2.2.0][conan skip la support] · :octicons-milestone-24: `True`/`False` (Default: `True`)
[cmake as system headers support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
If `user.mp-units.build:all` is enabled, among others, Conan installs the external
[wg21-linear_algebra](https://conan.io/center/recipes/wg21-linear_algebra)
dependency and enables the compilation of linear algebra-based tests and usage examples.
Such behavior can be disabled with this option.
[`MP_UNITS_BUILD_CXX_MODULES`](#MP_UNITS_BUILD_CXX_MODULES){ #MP_UNITS_BUILD_CXX_MODULES }
[conan skip la support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
: [:octicons-tag-24: 2.2.0][cmake build cxx modules support] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`)
[`user.mp-units.analyze:clang-tidy`](#user.mp-units.analyze-clang-tidy){ #user.mp-units.analyze-clang-tidy }
Adds C++ modules to the list of default targets.
: [:octicons-tag-24: 2.2.0][conan clang-tidy support] · :octicons-milestone-24: `True`/`False` (Default: `False`)
[cmake build cxx modules support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
Enables clang-tidy analysis.
[`MP_UNITS_BUILD_IMPORT_STD`](#MP_UNITS_BUILD_IMPORT_STD){ #MP_UNITS_BUILD_IMPORT_STD }
[conan clang-tidy support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
: [:octicons-tag-24: 2.3.0][cmake import std support] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`)
### CMake options
Enables `import std;` usage.
[`MP_UNITS_BUILD_AS_SYSTEM_HEADERS`](#MP_UNITS_BUILD_AS_SYSTEM_HEADERS){ #MP_UNITS_BUILD_AS_SYSTEM_HEADERS }
[cmake import std support]: https://github.com/mpusz/mp-units/releases/tag/v2.3.0
: [:octicons-tag-24: 2.2.0][cmake as system headers support] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`)
[`MP_UNITS_API_STD_FORMAT`](#MP_UNITS_API_STD_FORMAT){ #MP_UNITS_API_STD_FORMAT }
Exports library as system headers.
: [:octicons-tag-24: 2.2.0][cmake std::format support] · :octicons-milestone-24: `ON`/`OFF` (Default: automatically determined)
[cmake as system headers support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
Enables the usage of [`std::format`](https://en.cppreference.com/w/cpp/utility/format/format)
and associated facilities for text formatting. If it is not supported, then
the [{fmt}](https://github.com/fmtlib/fmt) library is used instead.
[`MP_UNITS_BUILD_CXX_MODULES`](#MP_UNITS_BUILD_CXX_MODULES){ #MP_UNITS_BUILD_CXX_MODULES }
[cmake std::format support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
: [:octicons-tag-24: 2.2.0][cmake build cxx modules support] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`)
[`MP_UNITS_API_STRING_VIEW_RET`](#MP_UNITS_API_STRING_VIEW_RET){ #MP_UNITS_API_STRING_VIEW_RET }
Adds C++ modules to the list of default targets.
: [:octicons-tag-24: 2.2.0][cmake returning string_view] · :octicons-milestone-24: `ON`/`OFF` (Default: automatically determined)
[cmake build cxx modules support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
Enables returning `std::string_view` from the
[`unit_symbol()`](../users_guide/framework_basics/text_output.md#unit_symbol)
and [`dimension_symbol()`](../users_guide/framework_basics/text_output.md#dimension_symbol)
functions. If this feature is not available, those functions will return
`mp_units::basic_fixed_string<CharT, N>` instead.
[`MP_UNITS_BUILD_IMPORT_STD`](#MP_UNITS_BUILD_IMPORT_STD){ #MP_UNITS_BUILD_IMPORT_STD }
[cmake returning string_view]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
: [:octicons-tag-24: 2.3.0][cmake import std support] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`)
[`MP_UNITS_API_NO_CRTP`](#MP_UNITS_API_NO_CRTP){ #MP_UNITS_API_NO_CRTP }
Enables `import std;` usage.
: [:octicons-tag-24: 2.2.0][cmake no crtp support] · :octicons-milestone-24: `ON`/`OFF` (Default: automatically determined)
[cmake import std support]: https://github.com/mpusz/mp-units/releases/tag/v2.3.0
Removes the need for the usage of the CRTP idiom in the
[`quantity_spec` definitions](../users_guide/framework_basics/systems_of_quantities.md#defining-quantities).
[`MP_UNITS_API_STD_FORMAT`](#MP_UNITS_API_STD_FORMAT){ #MP_UNITS_API_STD_FORMAT }
[cmake no crtp support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
: [:octicons-tag-24: 2.2.0][cmake std::format support] · :octicons-milestone-24: `ON`/`OFF` (Default: automatically determined)
[`MP_UNITS_API_CONTRACTS`](#MP_UNITS_API_CONTRACTS){ #MP_UNITS_API_CONTRACTS }
Enables the usage of [`std::format`](https://en.cppreference.com/w/cpp/utility/format/format)
and associated facilities for text formatting. If it is not supported, then
the [{fmt}](https://github.com/fmtlib/fmt) library is used instead.
: [:octicons-tag-24: 2.2.0][cmake contracts] · :octicons-milestone-24: `NONE`/`GSL-LITE`/`MS-GSL` (Default: `GSL-LITE`)
[cmake std::format support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
Enables checking of preconditions and additional asserts in the code.
[`MP_UNITS_API_STRING_VIEW_RET`](#MP_UNITS_API_STRING_VIEW_RET){ #MP_UNITS_API_STRING_VIEW_RET }
[cmake contracts]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
: [:octicons-tag-24: 2.2.0][cmake returning string_view] · :octicons-milestone-24: `ON`/`OFF` (Default: automatically determined)
[`MP_UNITS_API_FREESTANDING`](#MP_UNITS_API_FREESTANDING){ #MP_UNITS_API_FREESTANDING }
Enables returning `std::string_view` from the
[`unit_symbol()`](../users_guide/framework_basics/text_output.md#unit_symbol)
and [`dimension_symbol()`](../users_guide/framework_basics/text_output.md#dimension_symbol)
functions. If this feature is not available, those functions will return
`mp_units::basic_fixed_string<CharT, N>` instead.
: [:octicons-tag-24: 2.2.0][cmake freestanding] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`)
[cmake returning string_view]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
Configures the library in the [freestanding](https://en.cppreference.com/w/cpp/freestanding)
mode. When enabled, the library's source code should build with the compiler's
[`-ffreestanding`](https://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html) compilation option
without any issues.
[`MP_UNITS_API_NO_CRTP`](#MP_UNITS_API_NO_CRTP){ #MP_UNITS_API_NO_CRTP }
: [:octicons-tag-24: 2.2.0][cmake no crtp support] · :octicons-milestone-24: `ON`/`OFF` (Default: automatically determined)
Removes the need for the usage of the CRTP idiom in the
[`quantity_spec` definitions](../users_guide/framework_basics/systems_of_quantities.md#defining-quantities).
[cmake no crtp support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[`MP_UNITS_API_CONTRACTS`](#MP_UNITS_API_CONTRACTS){ #MP_UNITS_API_CONTRACTS }
: [:octicons-tag-24: 2.2.0][cmake contracts] · :octicons-milestone-24: `NONE`/`GSL-LITE`/`MS-GSL` (Default: `GSL-LITE`)
Enables checking of preconditions and additional asserts in the code.
[cmake contracts]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[`MP_UNITS_API_FREESTANDING`](#MP_UNITS_API_FREESTANDING){ #MP_UNITS_API_FREESTANDING }
: [:octicons-tag-24: 2.2.0][cmake freestanding] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`)
Configures the library in the [freestanding](https://en.cppreference.com/w/cpp/freestanding)
mode. When enabled, the library's source code should build with the compiler's
[`-ffreestanding`](https://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html) compilation option
without any issues.
[cmake freestanding]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
#### Options for mp-units project developers
[`MP_UNITS_DEV_BUILD_LA`](#MP_UNITS_DEV_BUILD_LA){ #MP_UNITS_DEV_BUILD_LA }
: [:octicons-tag-24: 2.2.0][cmake build la support] · :octicons-milestone-24: `ON`/`OFF` (Default: `ON`)
Enables building code depending on the linear algebra library.
[cmake build la support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[`MP_UNITS_DEV_IWYU`](#MP_UNITS_DEV_IWYU){ #MP_UNITS_DEV_IWYU }
: [:octicons-tag-24: 2.2.0][cmake iwyu support] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`)
Enables include-what-you-use analysis.
[cmake iwyu support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[`MP_UNITS_DEV_CLANG_TIDY`](#MP_UNITS_DEV_CLANG_TIDY){ #MP_UNITS_DEV_CLANG_TIDY }
: [:octicons-tag-24: 2.2.0][cmake clang-tidy support] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`)
Enables clang-tidy analysis.
[cmake clang-tidy support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
## CMake with presets support
It is recommended to use at least CMake 3.23 to build this project as this version introduced support
for CMake Presets schema version 4, used now by Conan to generate presets files. All build instructions
below assume that you have such support. If not, your CMake invocations have to be replaced with something
like:
```shell
mkdir build && cd build
cmake .. -G "Ninja Multi-Config" -DCMAKE_TOOLCHAIN_FILE=<path_to_generators_dir>/conan_toolchain.cmake
cmake --build . --config Release
```
!!! tip
In case you can't use CMake 3.23 but you have access to CMake 3.20 or later, you can append
`-c tools.cmake.cmaketoolchain.presets:max_schema_version=2` to the `conan install` command
which will force Conan to use an older version of the CMake Presets schema.
[cmake freestanding]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
## Installation and reuse
@@ -461,42 +255,12 @@ only a few of many options possible.
The easiest and most recommended way to obtain **mp-units** is with the Conan package manager.
See [Conan + CMake (release)](#conan-cmake-release) for a detailed instruction.
### Copy
As **mp-units** is a C++ header-only library you can simply copy all needed _src/*/include_ subdirectories
to your source tree.
!!! note
In such a case, you are on your own to ensure all the dependencies are installed and their header
files can be located during the build. Please also note that some compiler-specific flags are needed
to make the code compile without issues.
### Copy + CMake
If you copy the whole **mp-units** repository to your project's file tree, you can reuse CMake targets
defined by the library. To do so, **you should use _CMakeLists.txt_ file from the _./src_ directory**:
```cmake
add_subdirectory(<path_to_units_folder>/src)
# ...
target_link_libraries(<your_target> <PUBLIC|PRIVATE|INTERFACE> mp-units::mp-units)
```
!!! note
You are still on your own to make sure all the dependencies are installed and their header and CMake
configuration files can be located during the build.
### Conan + CMake (release)
!!! tip
If you are new to the Conan package manager, it is highly recommended to read
[Obtaining Dependencies](#obtaining-dependencies) and refer to
If you are new to the Conan package manager you may want to read
[Obtaining Dependencies](#obtaining-dependencies) and refer to the
[Consuming packages](https://docs.conan.io/2/tutorial/consuming_packages.html)
chapter of the official Conan documentation for more information.
@@ -512,7 +276,6 @@ The following steps may be performed to obtain an official library release:
mp-units/2.2.0
[options]
mp-units:cxx_modules=True
[layout]
cmake_layout
@@ -522,8 +285,7 @@ The following steps may be performed to obtain an official library release:
CMakeDeps
```
2. Import **mp-units** and its dependencies definitions to your project's build procedure
with `find_package`:
2. Import **mp-units** and its dependencies definitions with `find_package`:
```cmake
find_package(mp-units REQUIRED)
@@ -552,7 +314,7 @@ of **mp-units** all the time.
Please note that even though the Conan packages that you will be using are generated **ONLY**
for builds that are considered stable (passed our CI tests), some minor regressions may happen
(our CI and C++20 build environment is not perfect yet). Also, please expect that the library
(CI and C++ build environments are not perfect yet). Also, please expect that the library
interface might, and probably will, change occasionally. Even though we do our best, such
changes might not be reflected in the project's documentation right away.
@@ -572,7 +334,6 @@ with the following differences:
mp-units/2.3.0@mpusz/testing
[options]
mp-units:cxx_modules=True
[layout]
cmake_layout
@@ -594,91 +355,60 @@ with the following differences:
conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -b=missing -u
```
??? info "Alternative installation scenarios"
### Install
### Copy
In case you don't want to use Conan in your project and just want to install the **mp-units**
library on your file system and use `find_package(mp-units)` from another repository to find it;
it is enough to perform the following steps:
As **mp-units** is a C++ header-only library you can simply copy all needed _src/*/include_ subdirectories
to your source tree.
```shell
conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -b=missing
mv CMakeUserPresets.json src
cd src
cmake --preset conan-default -DCMAKE_INSTALL_PREFIX=<your_installation_path>
cmake --build --preset conan-release --target install
```
!!! note
In such a case, you are on your own to ensure all the dependencies are installed and their header
files can be located during the build. Please also note that some compiler-specific flags are needed
to make the code compile without issues.
## Contributing (or just building all the tests and examples)
### Copy + CMake
In case you would like to build all the **mp-units** source code (with unit tests and examples),
you should:
If you copy the **mp-units** library source code from **the project's _./src_ directory**
(not the entire repo from its root), you can reuse CMake targets defined by the library.
To do so, **you should use _CMakeLists.txt_ file from the _./src_ directory**:
1. Use the _CMakeLists.txt_ from the top-level directory.
2. Run Conan with [`user.mp-units.build:all`](#user.mp-units.build-all) = `True`.
```shell
git clone https://github.com/mpusz/mp-units.git && cd units
conan build . -pr <your_conan_profile> -s compiler.cppstd=23 -o '&:cxx_modules=True' -c user.mp-units.build:all=True -b missing
```
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 than Conan, then you should replace
the `conan build` with `conan install` command and then follow with a regular CMake build:
```shell
cmake --preset conan-default
cmake --build --preset conan-release
cmake --build --preset conan-release --target all_verify_interface_header_sets
cmake --build --preset conan-release --target test
```
## Building documentation
Starting from **mp-units 2.0** we are using [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/)
to build our documentation. The easiest way to install all the required dependencies
is with `pip`:
```shell
pip install -U mkdocs-material mkdocs-rss-plugin
```
Additionally, a [Cairo Graphics library](https://www.cairographics.org/) is required by
Material for MkDocs. Please follow the
[official MkDocs documentation to install it](https://squidfunk.github.io/mkdocs-material/plugins/requirements/image-processing/#cairo-graphics).
After that, you can either:
- easily [start a live server to preview the documentation as you write](https://squidfunk.github.io/mkdocs-material/creating-your-site/#previewing-as-you-write)
```shell
mkdocs serve
```cmake
add_subdirectory(<path_to_mp_units_lib_folder>)
# ...
target_link_libraries(<your_target> <PUBLIC|PRIVATE|INTERFACE> mp-units::mp-units)
```
- [build the documentation](https://squidfunk.github.io/mkdocs-material/creating-your-site/#building-your-site)
!!! note
You are still on your own to make sure all the dependencies are installed and their header and CMake
configuration files can be located during the build.
!!! important "Important: Library users should not use the top-level CMake file"
Top level _CMakeLists.txt_ file should only be used by **mp-units developers and contributors**
as an entry point for the project's development.
_./src/CMakeLists.txt_ contains only a pure library definition and **should be used by the
customers** that prefer to use CMake's
[`add_subdirectory()`](https://cmake.org/cmake/help/latest/command/add_subdirectory.html) to
handle the dependencies.
To learn more about the rationale, please check our
[FAQ](faq.md#why-dont-we-have-cmake-options-to-disable-the-building-of-tests-and-examples).
### Install
If you don't want to use Conan in your project and just want to install the **mp-units**
library on your file system, and use `find_package(mp-units)` from another repository to find it;
it is enough to perform the following steps:
```shell
mkdocs build
conan install . -pr <your_conan_profile> -s compiler.cppstd=20 -b=missing
mv CMakeUserPresets.json src
cd src
cmake --preset conan-default -DCMAKE_INSTALL_PREFIX=<your_installation_path>
cmake --build --preset conan-release --target install
```
## Packaging
To test CMake installation and Conan packaging or create a Conan package run:
```shell
conan create . --user <username> --channel <channel> -pr <your_conan_profile> -s compiler.cppstd=20 -o '&:cxx_modules=True' -c user.mp-units.build:all=True -b missing
```
The above will create a Conan package and run tests provided in _./test_package_ directory.
## Uploading **mp-units** package to the Conan server
```shell
conan upload -r <remote-name> --all mp-units/2.2.0@<user>/<channel>
```

View File

@@ -0,0 +1,150 @@
# Project structure
This chapter provides a high level overview of the project to make it easier to navigate, build,
and use.
## CMake projects and dependencies
The [GitHub repository](https://github.com/mpusz/mp-units) contains three independent CMake-based
projects:
- **_./src_**
- header-only project containing whole **mp-units** library
- _./src/CMakeLists.txt_ file is intended as an **entry point for library users**
- in case this library becomes part of the C++ standard, it will have no external dependencies
but until then, it depends on the following:
- [gsl-lite](https://github.com/gsl-lite/gsl-lite) or [ms-gsl](https://github.com/microsoft/GSL)
to verify runtime contracts (if contract checking is enabled),
- [{fmt}](https://github.com/fmtlib/fmt) to provide text formatting of quantities
(if `std::format` is not supported yet on a specific compiler).
- **_._**
- project used as an **entry point for library development and CI/CD**
- it wraps _./src_ project together with usage examples and tests
- additionally to the dependencies of _./src_ project, it uses:
- [Catch2](https://github.com/catchorg/Catch2) library as a unit tests framework,
- [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.
- **_./test_package_**
- CMake library installation and Conan package verification.
!!! important "Important: Library users should not use the top-level CMake file"
Top level _CMakeLists.txt_ file should only be used by **mp-units** developers and contributors
as an entry point for the project's development. We want to ensure that everyone will build **ALL**
the code correctly before pushing a commit. Having such options would allow unintended issues to
leak to PRs and CI.
This is why our projects have two entry points:
- _./CMakeLists.txt_ is **to be used by projects developers** to build **ALL** the project code
with really restrictive compilation flags,
- _./src/CMakeLists.txt_ contains only a pure library definition and **should be used by the
customers** that prefer to use CMake's
[`add_subdirectory()`](https://cmake.org/cmake/help/latest/command/add_subdirectory.html) to
handle the dependencies.
To learn more about the rationale, please check our
[FAQ](faq.md#why-dont-we-have-cmake-options-to-disable-the-building-of-tests-and-examples).
## Modules
The **mp-units** library provides the following C++ modules:
```mermaid
flowchart TD
mp_units --- mp_units.systems --- mp_units.core
```
| C++ Module | CMake Target | Contents |
|--------------------|----------------------|----------------------------------------------------------|
| `mp_units.core` | `mp-units::core` | Core library framework and systems-independent utilities |
| `mp_units.systems` | `mp-units::systems` | All the systems of quantities and units |
| `mp_units` | `mp-units::mp-units` | Core + Systems |
!!! note
C++ modules are provided within the package only when:
- [`cxx_modules`](installation_and_usage.md#cxx_modules) Conan option is set to `True`,
- [`MP_UNITS_BUILD_CXX_MODULES`](installation_and_usage.md#MP_UNITS_BUILD_CXX_MODULES) CMake option is set to `ON`.
## Header files
All of the project's header files can be found in the `mp-units/...` subdirectory.
### Core library
- `mp-units/framework.h` contains the entire library's framework definitions,
- `mp-units/concepts.h` exposes only the library's concepts for generic code needs,
- `mp-units/format.h` provides text formatting support,
- `mp-units/ostream.h` enables streaming of the library's objects to the text output,
- `mp-units/math.h` provides overloads of common math functions for quantities,
- `mp-units/random.h` provides C++ pseudo-random number generators for quantities,
- `mp-units/compat_macros.h` provides macros for [wide compatibility](../users_guide/use_cases/wide_compatibility.md).
??? info "More details"
More detailed header files can be found in subfolders which typically should not be
included by the end users:
- `mp-units/framework/...` provides all the public interfaces of the framework,
- `mp-units/bits/...` provides private implementation details only (no public definitions),
- `mp-units/ext/...` contains external dependencies that at some point in the future should
be replaced with C++ standard library facilities.
### Systems and associated utilities
The systems definitions can be found in the `mp-units/systems/...` subdirectory:
#### Systems of quantities
- `mp-units/systems/isq.h` provides
[International System of Quantities (ISQ)](https://en.wikipedia.org/wiki/International_System_of_Quantities)
definitions,
??? tip "Tip: Improving compile times"
`mp-units/systems/isq.h` might be expensive to compile in every translation unit. There are
some smaller, domain targeted files available for explicit inclusion in the
`mp-units/systems/isq/...` subdirectory.
#### Systems of units
- `mp-units/systems/si.h` provides
[International System of Units (SI)](https://en.wikipedia.org/wiki/International_System_of_Units)
definitions and associated math functions,
- `mp-units/systems/angular.h` provides strong angular units and associated math functions,
- `mp-units/systems/international.h` provides
[international yard and pound](https://en.wikipedia.org/wiki/International_yard_and_pound) units,
- `mp-units/systems/imperial.h` includes `international.h` and extends it with
[imperial units](https://en.wikipedia.org/wiki/Imperial_units),
- `mp-units/systems/usc.h` includes `international.h` and extends it with
[United States customary system of units](https://en.wikipedia.org/wiki/United_States_customary_units),
- `mp-units/systems/cgs.h` provides
[centimetre-gram-second system of units](https://en.wikipedia.org/wiki/Centimetre%E2%80%93gram%E2%80%93second_system_of_units),
- `mp-units/systems/iau.h` provides
[astronomical system of units](https://en.wikipedia.org/wiki/Astronomical_system_of_units),
- `mp-units/systems/hep.h` provides units used in
[high-energy physics](https://en.wikipedia.org/wiki/Particle_physics),
- `mp-units/systems/typographic.h` provides units used in
[typography or typesetting](https://en.wikipedia.org/wiki/Typographic_unit),
- `mp-units/systems/natural.h` provides an example implementation of
[natural units](https://en.wikipedia.org/wiki/Natural_units).
??? tip "Tip: Improving compile times"
`mp-units/systems/si.h` might be expensive to compile in every translation unit.
There are some smaller files available for explicit inclusion in the
`mp-units/systems/si/...` subdirectory.
`mp-units/systems/si/unit_symbols.h` is the most expensive to include.

File diff suppressed because one or more lines are too long

8
docs/javascripts/iframeResizer.min.js vendored Normal file

File diff suppressed because one or more lines are too long

2
docs/javascripts/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -179,7 +179,7 @@ For example, here is how it can be done for the [P1385](https://wg21.link/p1385)
using la_vector = STD_LA::fixed_size_column_vector<double, 3>;
template<>
inline constexpr bool mp_units::is_vector<la_vector> = true;
constexpr bool mp_units::is_vector<la_vector> = true;
```
With the above, we can use `la_vector` as a representation type for our quantity:
@@ -233,7 +233,7 @@ For example, we can do the following:
```cpp
template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
constexpr bool mp_units::is_vector<T> = true;
```
which says that every type that can be used as a scalar representation is also allowed for vector

View File

@@ -180,7 +180,7 @@ with `true` for one or more of the following variable templates:
```cpp
template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
constexpr bool mp_units::is_vector<T> = true;
```

View File

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

View File

@@ -138,7 +138,7 @@ Each prefix is implemented similarly to the following:
```cpp
template<PrefixableUnit U> struct quecto_ : prefixed_unit<"q", mag_power<10, -30>, U{}> {};
template<PrefixableUnit auto U> inline constexpr quecto_<decltype(U)> quecto;
template<PrefixableUnit auto U> constexpr quecto_<decltype(U)> quecto;
```
and then a [PrefixableUnit](concepts.md#PrefixableUnit) can be prefixed in the following
@@ -154,7 +154,7 @@ IT industry can be implemented as:
```cpp
template<PrefixableUnit U> struct yobi_ : prefixed_unit<"Yi", mag_power<2, 80>, U{}> {};
template<PrefixableUnit auto U> inline constexpr yobi_<decltype(U)> yobi;
template<PrefixableUnit auto U> constexpr yobi_<decltype(U)> yobi;
```
## Scaled units

View File

@@ -288,7 +288,7 @@ specialization for a specific unit:
```cpp
template<>
inline constexpr bool space_before_unit_symbol<non_si::degree> = false;
constexpr bool space_before_unit_symbol<non_si::degree> = false;
```
!!! note

View File

@@ -42,7 +42,7 @@ import mp_units.core;
template<std::movable T, MP_UNITS_CONSTRAINED_NTTP_WORKAROUND(std::convertible_to<T>) auto Min,
MP_UNITS_CONSTRAINED_NTTP_WORKAROUND(std::convertible_to<T>) auto Max>
inline constexpr auto is_in_range = [](const auto& v) { return std::clamp(v, T{Min}, T{Max}) == v; };
MP_UNITS_INLINE constexpr auto is_in_range = [](const auto& v) { return std::clamp(v, T{Min}, T{Max}) == v; };
template<std::movable T, MP_UNITS_CONSTRAINED_NTTP_WORKAROUND(std::convertible_to<T>) auto Min,
MP_UNITS_CONSTRAINED_NTTP_WORKAROUND(std::convertible_to<T>) auto Max>
@@ -63,10 +63,10 @@ public:
};
template<typename T, auto Min, auto Max>
inline constexpr bool mp_units::is_scalar<ranged_representation<T, Min, Max>> = mp_units::is_scalar<T>;
constexpr bool mp_units::is_scalar<ranged_representation<T, Min, Max>> = mp_units::is_scalar<T>;
template<typename T, auto Min, auto Max>
inline constexpr bool mp_units::treat_as_floating_point<ranged_representation<T, Min, Max>> =
constexpr bool mp_units::treat_as_floating_point<ranged_representation<T, Min, Max>> =
mp_units::treat_as_floating_point<T>;
template<typename T, auto Min, auto Max, typename Char>

View File

@@ -114,11 +114,10 @@ public:
};
template<typename T, typename Validator>
inline constexpr bool mp_units::is_scalar<validated_type<T, Validator>> = mp_units::is_scalar<T>;
constexpr bool mp_units::is_scalar<validated_type<T, Validator>> = mp_units::is_scalar<T>;
template<typename T, typename Validator>
inline constexpr bool mp_units::treat_as_floating_point<validated_type<T, Validator>> =
mp_units::treat_as_floating_point<T>;
constexpr bool mp_units::treat_as_floating_point<validated_type<T, Validator>> = mp_units::treat_as_floating_point<T>;
template<typename CharT, typename Traits, typename T, typename Validator>

View File

@@ -47,13 +47,13 @@ namespace kalman {
namespace detail {
template<mp_units::Dimension auto... Ds>
inline constexpr bool are_time_derivatives = false;
constexpr bool are_time_derivatives = false;
template<mp_units::Dimension auto D>
inline constexpr bool are_time_derivatives<D> = true;
constexpr bool are_time_derivatives<D> = true;
template<mp_units::Dimension auto D1, mp_units::Dimension auto D2, mp_units::Dimension auto... Ds>
inline constexpr bool are_time_derivatives<D1, D2, Ds...> =
constexpr bool are_time_derivatives<D1, D2, Ds...> =
(D1 / D2 == mp_units::isq::dim_time) && are_time_derivatives<D2, Ds...>;
} // namespace detail

View File

@@ -40,7 +40,7 @@ import mp_units;
template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
constexpr bool mp_units::is_vector<T> = true;
using namespace mp_units;

View File

@@ -40,7 +40,7 @@ import mp_units;
template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
constexpr bool mp_units::is_vector<T> = true;
using namespace mp_units;

View File

@@ -40,7 +40,7 @@ import mp_units;
template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
constexpr bool mp_units::is_vector<T> = true;
using namespace mp_units;

View File

@@ -132,12 +132,12 @@ private:
} // namespace
template<class T>
inline constexpr bool mp_units::treat_as_floating_point<measurement<T>> = mp_units::treat_as_floating_point<T>;
constexpr bool mp_units::treat_as_floating_point<measurement<T>> = mp_units::treat_as_floating_point<T>;
template<class T>
inline constexpr bool mp_units::is_scalar<measurement<T>> = true;
constexpr bool mp_units::is_scalar<measurement<T>> = true;
template<class T>
inline constexpr bool mp_units::is_vector<measurement<T>> = true;
constexpr bool mp_units::is_vector<measurement<T>> = true;
static_assert(mp_units::RepresentationOf<measurement<double>, mp_units::quantity_character::scalar>);

View File

@@ -41,7 +41,7 @@ import mp_units;
template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
constexpr bool mp_units::is_vector<T> = true;
int main()
{

View File

@@ -44,7 +44,7 @@ import mp_units;
// types instead of requiring the usage of Linear Algebra library for this simple example
template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
constexpr bool mp_units::is_vector<T> = true;
namespace {

View File

@@ -35,7 +35,7 @@ import mp_units;
template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
constexpr bool mp_units::is_vector<T> = true;
int main()
{

View File

@@ -39,7 +39,7 @@ import mp_units;
template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
constexpr bool mp_units::is_vector<T> = true;
namespace {

View File

@@ -53,7 +53,7 @@ struct height_above_ellipsoid_t final : absolute_point_origin<isq::altitude> {
static constexpr earth_gravity_model egm = M;
};
template<earth_gravity_model M>
inline constexpr height_above_ellipsoid_t<M> height_above_ellipsoid; // NOLINT(google-readability-casting)
constexpr height_above_ellipsoid_t<M> height_above_ellipsoid; // NOLINT(google-readability-casting)
template<earth_gravity_model M>
using hae_altitude = quantity_point<isq::altitude[si::metre], height_above_ellipsoid<M>>;

View File

@@ -64,6 +64,9 @@ theme:
# Plugins
plugins:
- blog
- exclude:
glob:
- api_reference/src/*
- rss:
match_path: blog/posts/.*
date_from_meta:
@@ -130,8 +133,10 @@ nav:
- Introduction: getting_started/introduction.md
- Quick Start: getting_started/quick_start.md
- Look and Feel: getting_started/look_and_feel.md
- Project Structure: getting_started/project_structure.md
- C++ compiler support (API/ABI): getting_started/cpp_compiler_support.md
- Installation and Usage: getting_started/installation_and_usage.md
- Contributing: getting_started/contributing.md
- FAQ: getting_started/faq.md
- User's Guide:
- Terms and Definitions: users_guide/terms_and_definitions.md
@@ -173,6 +178,7 @@ nav:
- Appendix:
- Glossary: appendix/glossary.md
- References: appendix/references.md
- API Reference: api_reference.md
- Blog:
- blog/index.md
- Release Notes: release_notes.md

View File

@@ -4,4 +4,5 @@ identify
mkdocs-material
mkdocs-rss-plugin
mkdocs-material[imaging]
mkdocs-exclude
mike

View File

@@ -122,7 +122,7 @@ public:
MP_UNITS_EXPORT_END
template<typename T>
inline constexpr bool is_integer =
constexpr bool is_integer =
std::is_integral_v<T> && !std::is_same_v<T, bool> && !std::is_same_v<T, char> && !std::is_same_v<T, wchar_t>;
// Converts a character to ASCII. Returns a number > 127 on conversion failure.

View File

@@ -76,6 +76,10 @@
#if !defined MP_UNITS_HOSTED && defined __STDC_HOSTED__
#define MP_UNITS_HOSTED __STDC_HOSTED__
#endif
// workarounds for https://cplusplus.github.io/CWG/issues/2387.html
#define MP_UNITS_INLINE inline
#if MP_UNITS_COMP_GCC
#define MP_UNITS_REMOVE_CONST(expr) std::remove_const_t<expr>

View File

@@ -42,36 +42,26 @@ using has_common_type = typename has_common_type_impl<void, Ts...>::type;
template<typename... Ts>
constexpr bool has_common_type_v = has_common_type_impl<void, Ts...>::value;
template<typename T, typename Other>
struct get_common_type : std::common_type<T, Other> {};
template<typename T, typename Other>
using maybe_common_type =
std::conditional_t<has_common_type_v<T, Other>, get_common_type<T, Other>, std::type_identity<T>>::type;
std::conditional_t<has_common_type_v<T, Other>, std::common_type<T, Other>, std::type_identity<T>>::type;
/**
* @brief Details about the conversion from one quantity to another.
* @brief Type-related details about the conversion from one quantity to another
*
* This struct calculates the conversion factor that needs to be applied to a number,
* in order to convert from one quantity to another. In addition to that, it also
* helps to determine what representations to use at which step in the conversion process,
* This trait helps to determine what representations to use at which step in the conversion process,
* in order to avoid overflow and underflow while not causing excessive computations.
*
* @note This is a low-level facility.
*
* @tparam To a target quantity type to cast to
* @tparam From a source quantity type to cast from
* @tparam M common magnitude between the two quantities
* @tparam Rep1 first quantity representation type
* @tparam Rep2 second quantity representation type
*/
template<Quantity To, Quantity From>
requires(castable(From::quantity_spec, To::quantity_spec))
struct magnitude_conversion_traits {
// scale the number
static constexpr Magnitude auto c_mag = get_canonical_unit(From::unit).mag / get_canonical_unit(To::unit).mag;
static constexpr Magnitude auto num = numerator(c_mag);
static constexpr Magnitude auto den = denominator(c_mag);
static constexpr Magnitude auto irr = c_mag * (den / num);
using c_rep_type = maybe_common_type<typename std::remove_reference_t<From>::rep, typename To::rep>;
using c_mag_type = common_magnitude_type<c_mag>;
template<Magnitude auto M, typename Rep1, typename Rep2>
struct conversion_type_traits {
using c_rep_type = maybe_common_type<Rep1, Rep2>;
using c_mag_type = common_magnitude_type<M>;
using multiplier_type = conditional<
treat_as_floating_point<c_rep_type>,
// ensure that the multiplier is also floating-point
@@ -80,11 +70,28 @@ struct magnitude_conversion_traits {
std::common_type_t<c_mag_type, value_type_t<c_rep_type>>, std::common_type_t<c_mag_type, double>>,
c_mag_type>;
using c_type = maybe_common_type<c_rep_type, multiplier_type>;
static constexpr auto val(Magnitude auto m) { return get_value<multiplier_type>(m); };
static constexpr multiplier_type num_mult = val(num);
static constexpr multiplier_type den_mult = val(den);
static constexpr multiplier_type irr_mult = val(irr);
static constexpr multiplier_type ratio = num_mult / den_mult * irr_mult;
};
/**
* @brief Value-related details about the conversion from one quantity to another
*
* This trait provide ingredients to calculate the conversion factor that needs to be applied
* to a number, in order to convert from one quantity to another.
*
* @note This is a low-level facility.
*
* @tparam M common magnitude between the two quantities
* @tparam T common multiplier representation type
*/
template<Magnitude auto M, typename T>
struct conversion_value_traits {
static constexpr Magnitude auto num = numerator(M);
static constexpr Magnitude auto den = denominator(M);
static constexpr Magnitude auto irr = M * (den / num);
static constexpr T num_mult = get_value<T>(num);
static constexpr T den_mult = get_value<T>(den);
static constexpr T irr_mult = get_value<T>(irr);
static constexpr T ratio = num_mult / den_mult * irr_mult;
};
@@ -96,34 +103,45 @@ struct magnitude_conversion_traits {
*
* @tparam To a target quantity type to cast to
*/
template<Quantity To, typename From>
requires Quantity<std::remove_cvref_t<From>> &&
(castable(std::remove_reference_t<From>::quantity_spec, To::quantity_spec)) &&
((std::remove_reference_t<From>::unit == To::unit &&
std::constructible_from<typename To::rep, typename std::remove_reference_t<From>::rep>) ||
(std::remove_reference_t<From>::unit != To::unit)) // && scalable_with_<typename To::rep>))
template<Quantity To, typename FwdFrom, Quantity From = std::remove_cvref_t<FwdFrom>>
requires(castable(From::quantity_spec, To::quantity_spec)) &&
((From::unit == To::unit && std::constructible_from<typename To::rep, typename From::rep>) ||
(From::unit != To::unit)) // && scalable_with_<typename To::rep>))
// TODO how to constrain the second part here?
[[nodiscard]] constexpr To sudo_cast(From&& q)
[[nodiscard]] constexpr To sudo_cast(FwdFrom&& q)
{
constexpr auto q_unit = std::remove_reference_t<From>::unit;
constexpr auto q_unit = From::unit;
if constexpr (q_unit == To::unit) {
// no scaling of the number needed
return {static_cast<To::rep>(std::forward<From>(q).numerical_value_is_an_implementation_detail_),
return {static_cast<To::rep>(std::forward<FwdFrom>(q).numerical_value_is_an_implementation_detail_),
To::reference}; // this is the only (and recommended) way to do a truncating conversion on a number, so we
// are using static_cast to suppress all the compiler warnings on conversions
} else {
constexpr Magnitude auto c_mag = get_canonical_unit(From::unit).mag / get_canonical_unit(To::unit).mag;
using type_traits = conversion_type_traits<c_mag, typename From::rep, typename To::rep>;
using multiplier_type = typename type_traits::multiplier_type;
// TODO the below crashed nearly every compiler I tried it on
// auto scale = [&](std::invocable<typename type_traits::c_type> auto func) {
auto scale = [&](auto func) {
auto res =
static_cast<To::rep>(func(static_cast<type_traits::c_type>(q.numerical_value_is_an_implementation_detail_)));
return To{res, To::reference};
};
// scale the number
using traits = magnitude_conversion_traits<To, std::remove_reference_t<From>>;
if constexpr (std::is_floating_point_v<typename traits::multiplier_type>) {
// this results in great assembly
auto res = static_cast<To::rep>(static_cast<traits::c_type>(q.numerical_value_is_an_implementation_detail_) *
traits::ratio);
return {res, To::reference};
} else {
// this is slower but allows conversions like 2000 m -> 2 km without loosing data
auto res = static_cast<To::rep>(static_cast<traits::c_type>(q.numerical_value_is_an_implementation_detail_) *
traits::num_mult / traits::den_mult * traits::irr_mult);
return {res, To::reference};
if constexpr (is_integral(c_mag))
return scale([&](auto value) { return value * get_value<multiplier_type>(numerator(c_mag)); });
else if constexpr (is_integral(pow<-1>(c_mag)))
return scale([&](auto value) { return value / get_value<multiplier_type>(denominator(c_mag)); });
else {
using value_traits = conversion_value_traits<c_mag, multiplier_type>;
if constexpr (std::is_floating_point_v<multiplier_type>)
// this results in great assembly
return scale([](auto value) { return value * value_traits::ratio; });
else
// this is slower but allows conversions like 2000 m -> 2 km without loosing data
return scale(
[](auto value) { return value * value_traits::num_mult / value_traits::den_mult * value_traits::irr_mult; });
}
}
}
@@ -137,21 +155,18 @@ template<Quantity To, typename From>
*
* @tparam ToQP a target quantity point type to which to cast to
*/
template<QuantityPoint ToQP, typename FromQP>
requires QuantityPoint<std::remove_cvref_t<FromQP>> &&
(castable(std::remove_reference_t<FromQP>::quantity_spec, ToQP::quantity_spec)) &&
(detail::same_absolute_point_origins(ToQP::point_origin, std::remove_reference_t<FromQP>::point_origin)) &&
((std::remove_reference_t<FromQP>::unit == ToQP::unit &&
std::constructible_from<typename ToQP::rep, typename std::remove_reference_t<FromQP>::rep>) ||
(std::remove_reference_t<FromQP>::unit != ToQP::unit))
[[nodiscard]] constexpr QuantityPoint auto sudo_cast(FromQP&& qp)
template<QuantityPoint ToQP, typename FwdFromQP, QuantityPoint FromQP = std::remove_cvref_t<FwdFromQP>>
requires(castable(FromQP::quantity_spec, ToQP::quantity_spec)) &&
(detail::same_absolute_point_origins(ToQP::point_origin, FromQP::point_origin)) &&
((FromQP::unit == ToQP::unit && std::constructible_from<typename ToQP::rep, typename FromQP::rep>) ||
(FromQP::unit != ToQP::unit))
[[nodiscard]] constexpr QuantityPoint auto sudo_cast(FwdFromQP&& qp)
{
using qp_type = std::remove_reference_t<FromQP>;
if constexpr (is_same_v<std::remove_const_t<decltype(ToQP::point_origin)>,
std::remove_const_t<decltype(qp_type::point_origin)>>) {
std::remove_const_t<decltype(FromQP::point_origin)>>) {
return quantity_point{
sudo_cast<typename ToQP::quantity_type>(std::forward<FromQP>(qp).quantity_from(qp_type::point_origin)),
qp_type::point_origin};
sudo_cast<typename ToQP::quantity_type>(std::forward<FwdFromQP>(qp).quantity_from(FromQP::point_origin)),
FromQP::point_origin};
} else {
// it's unclear how hard we should try to avoid truncation here. For now, the only corner case we cater for,
// is when the range of the quantity type of at most one of QP or ToQP doesn't cover the offset between the
@@ -163,23 +178,26 @@ template<QuantityPoint ToQP, typename FromQP>
// (c) add/subtract the origin difference
// In the following, we carefully select the order of these three operations: each of (a) and (b) is scheduled
// either before or after (c), such that (c) acts on the largest range possible among all combination of source
// and target unit and represenation.
using traits = magnitude_conversion_traits<typename ToQP::quantity_type, typename qp_type::quantity_type>;
using c_rep_type = typename traits::c_rep_type;
if constexpr (traits::num_mult * traits::irr_mult > traits::den_mult) {
// and target unit and representation.
constexpr Magnitude auto c_mag = get_canonical_unit(FromQP::unit).mag / get_canonical_unit(ToQP::unit).mag;
using type_traits = conversion_type_traits<c_mag, typename FromQP::rep, typename ToQP::rep>;
using value_traits = conversion_value_traits<c_mag, typename type_traits::multiplier_type>;
using c_rep_type = typename type_traits::c_rep_type;
if constexpr (value_traits::num_mult * value_traits::irr_mult > value_traits::den_mult) {
// original unit had a larger unit magnitude; if we first convert to the common representation but retain the
// unit, we obtain the largest possible range while not causing truncation of fractional values. This is optimal
// for the offset computation.
return sudo_cast<ToQP>(
sudo_cast<quantity_point<qp_type::reference, qp_type::point_origin, c_rep_type>>(std::forward<FromQP>(qp))
sudo_cast<quantity_point<FromQP::reference, FromQP::point_origin, c_rep_type>>(std::forward<FwdFromQP>(qp))
.point_for(ToQP::point_origin));
} else {
// new unit may have a larger unit magnitude; we first need to convert to the new unit (potentially causing
// truncation, but no more than if we did the conversion later), but make sure we keep the larger of the two
// representation types. Then, we can perform the offset computation.
return sudo_cast<ToQP>(sudo_cast<quantity_point<make_reference(qp_type::quantity_spec, ToQP::unit),
qp_type::point_origin, c_rep_type>>(std::forward<FromQP>(qp))
.point_for(ToQP::point_origin));
return sudo_cast<ToQP>(
sudo_cast<quantity_point<make_reference(FromQP::quantity_spec, ToQP::unit), FromQP::point_origin, c_rep_type>>(
std::forward<FwdFromQP>(qp))
.point_for(ToQP::point_origin));
}
}
}

View File

@@ -40,28 +40,28 @@ namespace mp_units::detail {
template<std::intmax_t Value>
requires(0 <= Value) && (Value < 10)
inline constexpr basic_fixed_string superscript_number = u8"";
constexpr basic_fixed_string superscript_number = u8"";
template<>
inline constexpr basic_fixed_string superscript_number<0> = u8"\u2070";
MP_UNITS_INLINE constexpr basic_fixed_string superscript_number<0> = u8"\u2070";
template<>
inline constexpr basic_fixed_string superscript_number<1> = u8"\u00b9";
MP_UNITS_INLINE constexpr basic_fixed_string superscript_number<1> = u8"\u00b9";
template<>
inline constexpr basic_fixed_string superscript_number<2> = u8"\u00b2";
MP_UNITS_INLINE constexpr basic_fixed_string superscript_number<2> = u8"\u00b2";
template<>
inline constexpr basic_fixed_string superscript_number<3> = u8"\u00b3";
MP_UNITS_INLINE constexpr basic_fixed_string superscript_number<3> = u8"\u00b3";
template<>
inline constexpr basic_fixed_string superscript_number<4> = u8"\u2074";
MP_UNITS_INLINE constexpr basic_fixed_string superscript_number<4> = u8"\u2074";
template<>
inline constexpr basic_fixed_string superscript_number<5> = u8"\u2075";
MP_UNITS_INLINE constexpr basic_fixed_string superscript_number<5> = u8"\u2075";
template<>
inline constexpr basic_fixed_string superscript_number<6> = u8"\u2076";
MP_UNITS_INLINE constexpr basic_fixed_string superscript_number<6> = u8"\u2076";
template<>
inline constexpr basic_fixed_string superscript_number<7> = u8"\u2077";
MP_UNITS_INLINE constexpr basic_fixed_string superscript_number<7> = u8"\u2077";
template<>
inline constexpr basic_fixed_string superscript_number<8> = u8"\u2078";
MP_UNITS_INLINE constexpr basic_fixed_string superscript_number<8> = u8"\u2078";
template<>
inline constexpr basic_fixed_string superscript_number<9> = u8"\u2079";
MP_UNITS_INLINE constexpr basic_fixed_string superscript_number<9> = u8"\u2079";
inline constexpr symbol_text superscript_minus(u8"\u207b", "-");

View File

@@ -40,10 +40,10 @@ MP_UNITS_DIAGNOSTIC_IGNORE_EXPR_ALWAYS_TF
namespace mp_units::detail {
template<typename T>
inline constexpr bool is_type_list = false;
constexpr bool is_type_list = false;
template<template<typename...> typename T, typename... Types>
inline constexpr bool is_type_list<T<Types...>> = true;
constexpr bool is_type_list<T<Types...>> = true;
template<typename T>
concept TypeList = is_type_list<T>;
@@ -56,7 +56,7 @@ template<template<typename...> typename List, typename... Types>
struct type_list_size_impl<List<Types...>> : std::integral_constant<std::size_t, sizeof...(Types)> {};
template<TypeList List>
inline constexpr std::size_t type_list_size = type_list_size_impl<List>::value;
constexpr std::size_t type_list_size = type_list_size_impl<List>::value;
// map
template<typename T, template<typename...> typename To>

View File

@@ -60,26 +60,26 @@ using conditional = detail::conditional_impl<B>::template type<T, F>;
// is_same
template<class T, class U>
inline constexpr bool is_same_v = false;
constexpr bool is_same_v = false;
template<class T>
inline constexpr bool is_same_v<T, T> = true;
constexpr bool is_same_v<T, T> = true;
template<class T, class U>
using is_same = std::bool_constant<is_same_v<T, U>>;
// is_specialization_of
template<typename T, template<typename...> typename Type>
inline constexpr bool is_specialization_of = false;
constexpr bool is_specialization_of = false;
template<typename... Params, template<typename...> typename Type>
inline constexpr bool is_specialization_of<Type<Params...>, Type> = true;
constexpr bool is_specialization_of<Type<Params...>, Type> = true;
template<typename T, template<auto...> typename Type>
inline constexpr bool is_specialization_of_v = false;
constexpr bool is_specialization_of_v = false;
template<auto... Params, template<auto...> typename Type>
inline constexpr bool is_specialization_of_v<Type<Params...>, Type> = true;
constexpr bool is_specialization_of_v<Type<Params...>, Type> = true;
MP_UNITS_EXPORT_END
@@ -92,8 +92,7 @@ void to_base_specialization_of(const volatile Type<Params...>*);
} // namespace detail
template<typename T, template<typename...> typename Type>
inline constexpr bool is_derived_from_specialization_of =
requires(T* t) { detail::to_base_specialization_of<Type>(t); };
constexpr bool is_derived_from_specialization_of = requires(T* t) { detail::to_base_specialization_of<Type>(t); };
namespace detail {

View File

@@ -39,34 +39,29 @@ namespace mp_units {
template<Reference R>
struct delta_ {
template<typename Rep>
requires RepresentationOf<std::remove_cvref_t<Rep>, get_quantity_spec(R{}).character>
[[nodiscard]] constexpr quantity<MP_UNITS_EXPRESSION_WORKAROUND(R{}), std::remove_cvref_t<Rep>> operator()(
Rep&& lhs) const
template<typename FwdRep, RepresentationOf<get_quantity_spec(R{}).character> Rep = std::remove_cvref_t<FwdRep>>
[[nodiscard]] constexpr quantity<MP_UNITS_EXPRESSION_WORKAROUND(R{}), Rep> operator()(FwdRep&& lhs) const
{
return quantity{std::forward<Rep>(lhs), R{}};
return quantity{std::forward<FwdRep>(lhs), R{}};
}
};
template<Reference R>
struct absolute_ {
template<typename Rep>
requires RepresentationOf<std::remove_cvref_t<Rep>, get_quantity_spec(R{}).character>
[[nodiscard]] constexpr quantity_point<MP_UNITS_EXPRESSION_WORKAROUND(R{}), default_point_origin(R{}),
std::remove_cvref_t<Rep>>
operator()(Rep&& lhs) const
template<typename FwdRep, RepresentationOf<get_quantity_spec(R{}).character> Rep = std::remove_cvref_t<FwdRep>>
[[nodiscard]] constexpr quantity_point<MP_UNITS_EXPRESSION_WORKAROUND(R{}), default_point_origin(R{}), Rep> operator()(FwdRep&& lhs) const
{
return quantity_point{quantity{std::forward<Rep>(lhs), R{}}};
return quantity_point{quantity{std::forward<FwdRep>(lhs), R{}}};
}
};
MP_UNITS_EXPORT_BEGIN
template<Reference auto R>
inline constexpr delta_<MP_UNITS_REMOVE_CONST(decltype(R))> delta{};
constexpr delta_<MP_UNITS_REMOVE_CONST(decltype(R))> delta{};
template<Reference auto R>
inline constexpr absolute_<MP_UNITS_REMOVE_CONST(decltype(R))> absolute{};
constexpr absolute_<MP_UNITS_REMOVE_CONST(decltype(R))> absolute{};
MP_UNITS_EXPORT_END

View File

@@ -50,11 +50,11 @@ MP_UNITS_EXPORT_BEGIN
* @tparam Rep a representation type for which a type trait is defined
*/
template<typename Rep>
inline constexpr bool treat_as_floating_point = std::is_floating_point_v<Rep>;
constexpr bool treat_as_floating_point = std::is_floating_point_v<Rep>;
template<typename Rep>
requires requires { typename wrapped_type_t<Rep>; }
inline constexpr bool treat_as_floating_point<Rep> = treat_as_floating_point<wrapped_type_t<Rep>>;
constexpr bool treat_as_floating_point<Rep> = treat_as_floating_point<wrapped_type_t<Rep>>;
/**
* @brief Specifies a type to have a scalar character
@@ -62,7 +62,7 @@ inline constexpr bool treat_as_floating_point<Rep> = treat_as_floating_point<wra
* A scalar is a physical quantity that has magnitude but no direction.
*/
template<typename Rep>
inline constexpr bool is_scalar = std::is_floating_point_v<Rep> || (std::is_integral_v<Rep> && !is_same_v<Rep, bool>);
constexpr bool is_scalar = std::is_floating_point_v<Rep> || (std::is_integral_v<Rep> && !is_same_v<Rep, bool>);
/**
* @brief Specifies a type to have a vector character
@@ -76,11 +76,11 @@ inline constexpr bool is_scalar = std::is_floating_point_v<Rep> || (std::is_inte
* @code{.cpp}
* template<class T>
* requires mp_units::is_scalar<T>
* inline constexpr bool mp_units::is_vector<T> = true;
* constexpr bool mp_units::is_vector<T> = true;
* @endcode
*/
template<typename Rep>
inline constexpr bool is_vector = false;
constexpr bool is_vector = false;
/**
* @brief Specifies a type to have a tensor character
@@ -91,7 +91,7 @@ inline constexpr bool is_vector = false;
* Similarly to `is_vector` a partial specialization is needed in such cases.
*/
template<typename Rep>
inline constexpr bool is_tensor = false;
constexpr bool is_tensor = false;
/**
* @brief A type trait that defines zero, one, min, and max for a representation type

View File

@@ -53,7 +53,7 @@ template<symbol_text Symbol>
void to_base_specialization_of_base_dimension(const volatile base_dimension<Symbol>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_base_dimension =
constexpr bool is_derived_from_specialization_of_base_dimension =
requires(T* type) { to_base_specialization_of_base_dimension(type); };
/**
@@ -68,16 +68,16 @@ template<typename T>
struct is_dimension_one : std::false_type {};
template<typename T>
inline constexpr bool is_power_of_dim = requires {
constexpr bool is_power_of_dim = requires {
requires is_specialization_of_power<T> &&
(BaseDimension<typename T::factor> || is_dimension_one<typename T::factor>::value);
};
template<typename T>
inline constexpr bool is_per_of_dims = false;
constexpr bool is_per_of_dims = false;
template<typename... Ts>
inline constexpr bool is_per_of_dims<per<Ts...>> =
constexpr bool is_per_of_dims<per<Ts...>> =
(... && (BaseDimension<Ts> || is_dimension_one<Ts>::value || is_power_of_dim<Ts>));
template<typename T>

View File

@@ -57,37 +57,37 @@ struct per {};
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_per = false;
constexpr bool is_specialization_of_per = false;
template<typename... Ts>
inline constexpr bool is_specialization_of_per<per<Ts...>> = true;
constexpr bool is_specialization_of_per<per<Ts...>> = true;
template<int Num, int... Den>
inline constexpr bool valid_ratio = true;
constexpr bool valid_ratio = true;
template<int... Den>
inline constexpr bool valid_ratio<0, Den...> = false;
constexpr bool valid_ratio<0, Den...> = false;
template<int Num>
inline constexpr bool valid_ratio<Num, 0> = false;
constexpr bool valid_ratio<Num, 0> = false;
template<>
inline constexpr bool valid_ratio<0, 0> = false;
MP_UNITS_INLINE constexpr bool valid_ratio<0, 0> = false;
template<int Num, int... Den>
inline constexpr bool positive_ratio = gt_zero<Num>;
constexpr bool positive_ratio = gt_zero<Num>;
template<int Num, int Den>
inline constexpr bool positive_ratio<Num, Den> = gt_zero<Num * Den>;
constexpr bool positive_ratio<Num, Den> = gt_zero<Num * Den>;
template<int Num, int... Den>
inline constexpr bool ratio_one = false;
constexpr bool ratio_one = false;
template<>
inline constexpr bool ratio_one<1> = true;
MP_UNITS_INLINE constexpr bool ratio_one<1> = true;
template<int N>
inline constexpr bool ratio_one<N, N> = true;
constexpr bool ratio_one<N, N> = true;
} // namespace detail
@@ -127,10 +127,10 @@ using expr_type = detail::expr_type_impl<T>::type;
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_power = false;
constexpr bool is_specialization_of_power = false;
template<typename F, int... Ints>
inline constexpr bool is_specialization_of_power<power<F, Ints...>> = true;
constexpr bool is_specialization_of_power<power<F, Ints...>> = true;
template<typename T, ratio R>
consteval auto power_or_T_impl()
@@ -508,10 +508,10 @@ concept expr_type_projectable = (requires { typename Proj<T>; } ||
(is_specialization_of_power<T> && requires { typename Proj<typename T::factor>; }));
template<typename T, template<typename> typename Proj>
inline constexpr bool expr_projectable_impl = false;
constexpr bool expr_projectable_impl = false;
template<typename... Ts, template<typename> typename Proj>
inline constexpr bool expr_projectable_impl<type_list<Ts...>, Proj> = (... && expr_type_projectable<Ts, Proj>);
constexpr bool expr_projectable_impl<type_list<Ts...>, Proj> = (... && expr_type_projectable<Ts, Proj>);
template<typename T, template<typename> typename Proj>
concept expr_projectable = requires {

View File

@@ -59,10 +59,10 @@ using factorizer = wheel_factorizer<4>;
namespace detail {
template<typename T>
inline constexpr bool is_magnitude = false;
constexpr bool is_magnitude = false;
template<typename T>
inline constexpr bool is_specialization_of_magnitude = false;
constexpr bool is_specialization_of_magnitude = false;
} // namespace detail
@@ -87,7 +87,7 @@ concept Magnitude = detail::is_magnitude<T>;
// void to_base_specialization_of_constant(const volatile constant<V>*);
// template<typename T>
// inline constexpr bool is_derived_from_specialization_of_constant =
// constexpr bool is_derived_from_specialization_of_constant =
// requires(T * t) { to_base_specialization_of_constant(t); };
// } // namespace detail
@@ -142,7 +142,7 @@ concept Magnitude = detail::is_magnitude<T>;
namespace detail {
template<typename T>
inline constexpr bool is_named_magnitude = Magnitude<T> && !detail::is_specialization_of_magnitude<T>;
constexpr bool is_named_magnitude = Magnitude<T> && !detail::is_specialization_of_magnitude<T>;
}
@@ -178,10 +178,10 @@ struct power_v {
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_power_v = false;
constexpr bool is_specialization_of_power_v = false;
template<auto V, int... Ints>
inline constexpr bool is_specialization_of_power_v<power_v<V, Ints...>> = true;
constexpr bool is_specialization_of_power_v<power_v<V, Ints...>> = true;
} // namespace detail
@@ -572,13 +572,13 @@ namespace detail {
// }
// template<MagnitudeSpec auto... Elements>
// inline constexpr bool all_elements_valid = (is_valid_element(Elements) && ...);
// constexpr bool all_elements_valid = (is_valid_element(Elements) && ...);
// template<MagnitudeSpec auto... Elements>
// inline constexpr bool all_elements_in_order = strictly_increasing(get_base_value(Elements)...);
// constexpr bool all_elements_in_order = strictly_increasing(get_base_value(Elements)...);
// template<MagnitudeSpec auto... Elements>
// inline constexpr bool is_element_pack_valid = all_elements_valid<Elements...> && all_elements_in_order<Elements...>;
// constexpr bool is_element_pack_valid = all_elements_valid<Elements...> && all_elements_in_order<Elements...>;
[[nodiscard]] consteval bool is_rational(MagnitudeSpec auto element)
{
@@ -707,15 +707,15 @@ template<auto... Ms>
void to_base_specialization_of_magnitude(const volatile magnitude<Ms...>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_magnitude =
constexpr bool is_derived_from_specialization_of_magnitude =
requires(T* type) { to_base_specialization_of_magnitude(type); };
template<typename T>
requires is_derived_from_specialization_of_magnitude<T>
inline constexpr bool is_magnitude<T> = true;
constexpr bool is_magnitude<T> = true;
template<auto... Ms>
inline constexpr bool is_specialization_of_magnitude<magnitude<Ms...>> = true;
constexpr bool is_specialization_of_magnitude<magnitude<Ms...>> = true;
} // namespace detail
@@ -882,7 +882,7 @@ using common_magnitude_type = decltype(common_magnitude_type_impl(M));
//
// WARNING: The program behaviour will be undefined if you provide a wrong answer, so check your math!
MP_UNITS_EXPORT template<std::intmax_t N>
inline constexpr std::optional<std::intmax_t> known_first_factor = std::nullopt;
constexpr std::optional<std::intmax_t> known_first_factor = std::nullopt;
namespace detail {
@@ -915,7 +915,7 @@ struct prime_factorization<1> {
};
template<std::intmax_t N>
inline constexpr auto prime_factorization_v = prime_factorization<N>::value;
constexpr auto prime_factorization_v = prime_factorization<N>::value;
} // namespace detail
@@ -927,18 +927,18 @@ inline constexpr auto prime_factorization_v = prime_factorization<N>::value;
*/
MP_UNITS_EXPORT template<std::intmax_t V>
requires detail::gt_zero<V>
inline constexpr Magnitude auto mag = detail::prime_factorization_v<V>;
constexpr Magnitude auto mag = detail::prime_factorization_v<V>;
MP_UNITS_EXPORT template<std::intmax_t N, std::intmax_t D>
requires detail::gt_zero<N>
inline constexpr Magnitude auto mag_ratio = detail::prime_factorization_v<N> / detail::prime_factorization_v<D>;
constexpr Magnitude auto mag_ratio = detail::prime_factorization_v<N> / detail::prime_factorization_v<D>;
/**
* @brief Create a Magnitude which is some rational number raised to a rational power.
*/
MP_UNITS_EXPORT template<std::intmax_t Base, std::intmax_t Pow>
requires detail::gt_zero<Base>
inline constexpr Magnitude auto mag_power = pow<Pow>(mag<Base>);
constexpr Magnitude auto mag_power = pow<Pow>(mag<Base>);
namespace detail {

View File

@@ -168,22 +168,23 @@ public:
quantity(quantity&&) = default;
~quantity() = default;
template<typename Value, Reference R2>
requires detail::SameValueAs<R2{}, R, std::remove_cvref_t<Value>, Rep>
constexpr quantity(Value&& v, R2) : numerical_value_is_an_implementation_detail_(std::forward<Value>(v))
template<typename FwdValue, Reference R2>
requires detail::SameValueAs<R2{}, R, std::remove_cvref_t<FwdValue>, Rep>
constexpr quantity(FwdValue&& v, R2) : numerical_value_is_an_implementation_detail_(std::forward<FwdValue>(v))
{
}
template<typename Value, Reference R2>
requires(!detail::SameValueAs<R2{}, R, std::remove_cvref_t<Value>, Rep>) &&
detail::QuantityConvertibleTo<quantity<R2{}, std::remove_cvref_t<Value>>, quantity>
constexpr quantity(Value&& v, R2) : quantity(quantity<R2{}, std::remove_cvref_t<Value>>{std::forward<Value>(v), R2{}})
template<typename FwdValue, Reference R2, typename Value = std::remove_cvref_t<FwdValue>>
requires(!detail::SameValueAs<R2{}, R, Value, Rep>) &&
detail::QuantityConvertibleTo<quantity<R2{}, Value>, quantity>
constexpr quantity(FwdValue&& v, R2) : quantity(quantity<R2{}, Value>{std::forward<FwdValue>(v), R2{}})
{
}
template<detail::ValuePreservingTo<Rep> Value>
template<detail::ValuePreservingTo<Rep> FwdValue>
requires(unit == ::mp_units::one)
constexpr explicit(false) quantity(Value&& v) : numerical_value_is_an_implementation_detail_(std::forward<Value>(v))
constexpr explicit(false) quantity(FwdValue&& v) :
numerical_value_is_an_implementation_detail_(std::forward<FwdValue>(v))
{
}
@@ -209,11 +210,11 @@ public:
quantity& operator=(const quantity&) = default;
quantity& operator=(quantity&&) = default;
template<detail::ValuePreservingTo<Rep> Value>
template<detail::ValuePreservingTo<Rep> FwdValue>
requires(unit == ::mp_units::one)
constexpr quantity& operator=(Value&& v)
constexpr quantity& operator=(FwdValue&& v)
{
numerical_value_is_an_implementation_detail_ = std::forward<Value>(v);
numerical_value_is_an_implementation_detail_ = std::forward<FwdValue>(v);
return *this;
}
@@ -355,16 +356,16 @@ public:
return ::mp_units::quantity{-numerical_value_is_an_implementation_detail_, reference};
}
template<typename Q>
friend constexpr decltype(auto) operator++(Q&& q)
requires std::derived_from<std::remove_cvref_t<Q>, quantity> && requires(rep v) {
template<typename FwdQ, std::derived_from<quantity> Q = std::remove_cvref_t<FwdQ>>
friend constexpr decltype(auto) operator++(FwdQ&& q)
requires requires(rep v) {
{
++v
} -> std::same_as<rep&>;
}
{
++q.numerical_value_is_an_implementation_detail_;
return std::forward<Q>(q);
return std::forward<FwdQ>(q);
}
[[nodiscard]] constexpr QuantityOf<quantity_spec> auto operator++(int)
@@ -377,16 +378,16 @@ public:
return ::mp_units::quantity{numerical_value_is_an_implementation_detail_++, reference};
}
template<typename Q>
friend constexpr decltype(auto) operator--(Q&& q)
requires std::derived_from<std::remove_cvref_t<Q>, quantity> && requires(rep v) {
template<typename FwdQ, std::derived_from<quantity> Q = std::remove_cvref_t<FwdQ>>
friend constexpr decltype(auto) operator--(FwdQ&& q)
requires requires(rep v) {
{
--v
} -> std::same_as<rep&>;
}
{
--q.numerical_value_is_an_implementation_detail_;
return std::forward<Q>(q);
return std::forward<FwdQ>(q);
}
[[nodiscard]] constexpr QuantityOf<quantity_spec> auto operator--(int)
@@ -400,99 +401,94 @@ public:
}
// compound assignment operators
template<typename Q>
requires std::derived_from<std::remove_cvref_t<Q>, quantity> && requires(rep a, rep b) {
template<typename FwdQ, std::derived_from<quantity> Q = std::remove_cvref_t<FwdQ>>
requires requires(rep a, rep b) {
{
a += b
} -> std::same_as<rep&>;
}
friend constexpr decltype(auto) operator+=(Q&& lhs, const quantity& rhs)
friend constexpr decltype(auto) operator+=(FwdQ&& lhs, const quantity& rhs)
{
lhs.numerical_value_is_an_implementation_detail_ += rhs.numerical_value_is_an_implementation_detail_;
return std::forward<Q>(lhs);
return std::forward<FwdQ>(lhs);
}
template<typename Q>
requires std::derived_from<std::remove_cvref_t<Q>, quantity> && requires(rep a, rep b) {
template<typename FwdQ, std::derived_from<quantity> Q = std::remove_cvref_t<FwdQ>>
requires requires(rep a, rep b) {
{
a -= b
} -> std::same_as<rep&>;
}
friend constexpr decltype(auto) operator-=(Q&& lhs, const quantity& rhs)
friend constexpr decltype(auto) operator-=(FwdQ&& lhs, const quantity& rhs)
{
lhs.numerical_value_is_an_implementation_detail_ -= rhs.numerical_value_is_an_implementation_detail_;
return std::forward<Q>(lhs);
return std::forward<FwdQ>(lhs);
}
template<typename Q>
requires std::derived_from<std::remove_cvref_t<Q>, quantity> && (!treat_as_floating_point<rep>) &&
requires(rep a, rep b) {
{
a %= b
} -> std::same_as<rep&>;
}
friend constexpr decltype(auto) operator%=(Q&& lhs, const quantity& rhs)
template<typename FwdQ, std::derived_from<quantity> Q = std::remove_cvref_t<FwdQ>>
requires(!treat_as_floating_point<rep>) && requires(rep a, rep b) {
{
a %= b
} -> std::same_as<rep&>;
}
friend constexpr decltype(auto) operator%=(FwdQ&& lhs, const quantity& rhs)
{
MP_UNITS_EXPECTS_DEBUG(rhs != zero());
lhs.numerical_value_is_an_implementation_detail_ %= rhs.numerical_value_is_an_implementation_detail_;
return std::forward<Q>(lhs);
return std::forward<FwdQ>(lhs);
}
template<typename Q, typename Value>
requires std::derived_from<std::remove_cvref_t<Q>, quantity> && (!Quantity<Value>) &&
requires(rep a, const Value b) {
{
a *= b
} -> std::same_as<rep&>;
}
friend constexpr decltype(auto) operator*=(Q&& lhs, const Value& v)
template<typename FwdQ, typename Value, std::derived_from<quantity> Q = std::remove_cvref_t<FwdQ>>
requires(!Quantity<Value>) && requires(rep a, const Value b) {
{
a *= b
} -> std::same_as<rep&>;
}
friend constexpr decltype(auto) operator*=(FwdQ&& lhs, const Value& v)
{
lhs.numerical_value_is_an_implementation_detail_ = lhs.numerical_value_is_an_implementation_detail_ * v;
return std::forward<Q>(lhs);
return std::forward<FwdQ>(lhs);
}
template<typename Q1, QuantityOf<dimensionless> Q2>
requires std::derived_from<std::remove_cvref_t<Q1>, quantity> && (Q2::unit == ::mp_units::one) &&
requires(rep a, const typename Q2::rep b) {
{
a *= b
} -> std::same_as<rep&>;
}
friend constexpr decltype(auto) operator*=(Q1&& lhs, const Q2& rhs)
template<typename FwdQ1, QuantityOf<dimensionless> Q2, std::derived_from<quantity> Q1 = std::remove_cvref_t<FwdQ1>>
requires(Q2::unit == ::mp_units::one) && requires(rep a, const typename Q2::rep b) {
{
a *= b
} -> std::same_as<rep&>;
}
friend constexpr decltype(auto) operator*=(FwdQ1&& lhs, const Q2& rhs)
{
lhs.numerical_value_is_an_implementation_detail_ =
lhs.numerical_value_is_an_implementation_detail_ * rhs.numerical_value_is_an_implementation_detail_;
return std::forward<Q1>(lhs);
return std::forward<FwdQ1>(lhs);
}
template<typename Q, typename Value>
requires std::derived_from<std::remove_cvref_t<Q>, quantity> && (!Quantity<Value>) &&
requires(rep a, const Value b) {
{
a /= b
} -> std::same_as<rep&>;
}
friend constexpr decltype(auto) operator/=(Q&& lhs, const Value& v)
template<typename FwdQ, typename Value, std::derived_from<quantity> Q = std::remove_cvref_t<FwdQ>>
requires(!Quantity<Value>) && requires(rep a, const Value b) {
{
a /= b
} -> std::same_as<rep&>;
}
friend constexpr decltype(auto) operator/=(FwdQ&& lhs, const Value& v)
{
MP_UNITS_EXPECTS_DEBUG(v != quantity_values<Value>::zero());
lhs.numerical_value_is_an_implementation_detail_ = lhs.numerical_value_is_an_implementation_detail_ / v;
return std::forward<Q>(lhs);
return std::forward<FwdQ>(lhs);
}
template<typename Q1, QuantityOf<dimensionless> Q2>
requires std::derived_from<std::remove_cvref_t<Q1>, quantity> && (Q2::unit == ::mp_units::one) &&
requires(rep a, const typename Q2::rep b) {
{
a /= b
} -> std::same_as<rep&>;
}
friend constexpr decltype(auto) operator/=(Q1&& lhs, const Q2& rhs)
template<typename FwdQ1, QuantityOf<dimensionless> Q2, std::derived_from<quantity> Q1 = std::remove_cvref_t<FwdQ1>>
requires(Q2::unit == ::mp_units::one) && requires(rep a, const typename Q2::rep b) {
{
a /= b
} -> std::same_as<rep&>;
}
friend constexpr decltype(auto) operator/=(FwdQ1&& lhs, const Q2& rhs)
{
MP_UNITS_EXPECTS_DEBUG(rhs != rhs.zero());
lhs.numerical_value_is_an_implementation_detail_ =
lhs.numerical_value_is_an_implementation_detail_ / rhs.numerical_value_is_an_implementation_detail_;
return std::forward<Q1>(lhs);
return std::forward<FwdQ1>(lhs);
}
// binary operators on quantities

View File

@@ -56,13 +56,11 @@ namespace mp_units {
*
* @tparam ToQS a quantity specification to use for a target quantity
*/
template<QuantitySpec auto ToQS, typename Q>
requires Quantity<std::remove_cvref_t<Q>> &&
detail::QuantitySpecCastableTo<std::remove_reference_t<Q>::quantity_spec, ToQS>
[[nodiscard]] constexpr Quantity auto quantity_cast(Q&& q)
template<QuantitySpec auto ToQS, typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>>
requires detail::QuantitySpecCastableTo<Q::quantity_spec, ToQS>
[[nodiscard]] constexpr Quantity auto quantity_cast(FwdQ&& q)
{
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
make_reference(ToQS, std::remove_reference_t<Q>::unit)};
return quantity{std::forward<FwdQ>(q).numerical_value_is_an_implementation_detail_, make_reference(ToQS, Q::unit)};
}
/**
@@ -82,13 +80,12 @@ template<QuantitySpec auto ToQS, typename Q>
*
* @tparam ToQS a quantity specification to use for a target quantity point
*/
template<QuantitySpec auto ToQS, typename QP>
requires QuantityPoint<std::remove_cvref_t<QP>> &&
detail::QuantitySpecCastableTo<std::remove_reference_t<QP>::quantity_spec, ToQS>
[[nodiscard]] constexpr QuantityPoint auto quantity_cast(QP&& qp)
template<QuantitySpec auto ToQS, typename FwdQP, QuantityPoint QP = std::remove_cvref_t<FwdQP>>
requires detail::QuantitySpecCastableTo<QP::quantity_spec, ToQS>
[[nodiscard]] constexpr QuantityPoint auto quantity_cast(FwdQP&& qp)
{
return QP{quantity_cast<ToQS>(std::forward<QP>(qp).quantity_from_origin_is_an_implementation_detail_),
std::remove_reference_t<QP>::point_origin};
return QP{quantity_cast<ToQS>(std::forward<FwdQP>(qp).quantity_from_origin_is_an_implementation_detail_),
QP::point_origin};
}
} // namespace mp_units

View File

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

View File

@@ -43,7 +43,7 @@ namespace mp_units {
namespace detail {
template<PointOrigin PO>
inline constexpr bool is_specialization_of_zeroth_point_origin = false;
constexpr bool is_specialization_of_zeroth_point_origin = false;
template<PointOrigin PO>
[[nodiscard]] consteval bool is_zeroth_point_origin(PO)
@@ -52,20 +52,16 @@ template<PointOrigin PO>
}
struct point_origin_interface {
template<PointOrigin PO, Quantity Q>
requires ReferenceOf<std::remove_const_t<decltype(Q::reference)>, PO::quantity_spec>
[[nodiscard]] friend constexpr quantity_point<Q::reference, MP_UNITS_EXPRESSION_WORKAROUND(PO{}), typename Q::rep>
operator+(PO, Q&& q)
template<PointOrigin PO, typename FwdQ, QuantityOf<PO::quantity_spec> Q = std::remove_cvref_t<FwdQ>>
[[nodiscard]] friend constexpr quantity_point<Q::reference, MP_UNITS_EXPRESSION_WORKAROUND(PO{}), typename Q::rep> operator+(PO, FwdQ&& q)
{
return quantity_point{std::forward<Q>(q), PO{}};
return quantity_point{std::forward<FwdQ>(q), PO{}};
}
template<Quantity Q, PointOrigin PO>
requires ReferenceOf<std::remove_const_t<decltype(Q::reference)>, PO::quantity_spec>
[[nodiscard]] friend constexpr quantity_point<Q::reference, MP_UNITS_EXPRESSION_WORKAROUND(PO{}), typename Q::rep>
operator+(Q&& q, PO po)
template<Quantity FwdQ, PointOrigin PO, QuantityOf<PO::quantity_spec> Q = std::remove_cvref_t<FwdQ>>
[[nodiscard]] friend constexpr quantity_point<Q::reference, MP_UNITS_EXPRESSION_WORKAROUND(PO{}), typename Q::rep> operator+(FwdQ&& q, PO po)
{
return po + std::forward<Q>(q);
return po + std::forward<FwdQ>(q);
}
template<PointOrigin PO, Quantity Q>
@@ -132,12 +128,12 @@ template<QuantitySpec auto QS>
struct zeroth_point_origin_ final : absolute_point_origin<QS> {};
MP_UNITS_EXPORT template<QuantitySpec auto QS>
inline constexpr zeroth_point_origin_<QS> zeroth_point_origin;
constexpr zeroth_point_origin_<QS> zeroth_point_origin;
namespace detail {
template<auto QS>
inline constexpr bool is_specialization_of_zeroth_point_origin<zeroth_point_origin_<QS>> = true;
constexpr bool is_specialization_of_zeroth_point_origin<zeroth_point_origin_<QS>> = true;
} // namespace detail
@@ -208,27 +204,26 @@ public:
quantity_point(quantity_point&&) = default;
~quantity_point() = default;
template<typename Q>
requires QuantityOf<std::remove_cvref_t<Q>, quantity_spec> && std::constructible_from<quantity_type, Q> &&
(point_origin == default_point_origin(R)) && (implicitly_convertible(Q::quantity_spec, quantity_spec))
constexpr explicit quantity_point(Q&& q) : quantity_from_origin_is_an_implementation_detail_(std::forward<Q>(q))
template<typename FwdQ, QuantityOf<quantity_spec> Q = std::remove_cvref_t<FwdQ>>
requires std::constructible_from<quantity_type, FwdQ> && (point_origin == default_point_origin(R)) &&
(implicitly_convertible(Q::quantity_spec, quantity_spec))
constexpr explicit quantity_point(FwdQ&& q) : quantity_from_origin_is_an_implementation_detail_(std::forward<FwdQ>(q))
{
}
template<typename Q>
requires QuantityOf<std::remove_cvref_t<Q>, quantity_spec> && std::constructible_from<quantity_type, Q>
constexpr quantity_point(Q&& q, decltype(PO)) : quantity_from_origin_is_an_implementation_detail_(std::forward<Q>(q))
template<typename FwdQ, QuantityOf<quantity_spec> Q = std::remove_cvref_t<FwdQ>>
requires std::constructible_from<quantity_type, FwdQ>
constexpr quantity_point(FwdQ&& q, decltype(PO)) :
quantity_from_origin_is_an_implementation_detail_(std::forward<FwdQ>(q))
{
}
template<typename Q, PointOrigin PO2>
requires Quantity<std::remove_cvref_t<Q>> && std::constructible_from<quantity_type, Q> &&
ReferenceOf<std::remove_const_t<decltype(std::remove_reference_t<Q>::reference)>, PO2::quantity_spec> &&
detail::SameAbsolutePointOriginAs<PO2, PO>
constexpr quantity_point(Q&& q, PO2) :
template<typename FwdQ, PointOrigin PO2, QuantityOf<PO2::quantity_spec> Q = std::remove_cvref_t<FwdQ>>
requires std::constructible_from<quantity_type, FwdQ> && detail::SameAbsolutePointOriginAs<PO2, PO>
constexpr quantity_point(FwdQ&& q, PO2) :
quantity_point(
quantity_point<std::remove_reference_t<Q>::reference, PO2{}, typename std::remove_reference_t<Q>::rep>{
std::forward<Q>(q), PO2{}})
std::forward<FwdQ>(q), PO2{}})
{
}
@@ -299,7 +294,7 @@ public:
#endif
template<PointOrigin PO2>
requires requires { quantity_point{} - PO2{}; }
requires requires(quantity_point qp) { qp - PO2{}; }
[[nodiscard]] constexpr Quantity auto quantity_from(PO2) const
{
return *this - PO2{};
@@ -414,13 +409,12 @@ public:
}
// member unary operators
template<typename QP>
friend constexpr decltype(auto) operator++(QP&& qp)
requires std::derived_from<std::remove_cvref_t<QP>, quantity_point> &&
requires { ++qp.quantity_from_origin_is_an_implementation_detail_; }
template<typename FwdQP, std::derived_from<quantity_point> QP = std::remove_cvref_t<FwdQP>>
friend constexpr decltype(auto) operator++(FwdQP&& qp)
requires requires { ++qp.quantity_from_origin_is_an_implementation_detail_; }
{
++qp.quantity_from_origin_is_an_implementation_detail_;
return std::forward<QP>(qp);
return std::forward<FwdQP>(qp);
}
[[nodiscard]] constexpr quantity_point operator++(int)
@@ -429,13 +423,12 @@ public:
return {quantity_from_origin_is_an_implementation_detail_++, PO};
}
template<typename QP>
friend constexpr decltype(auto) operator--(QP&& qp)
requires std::derived_from<std::remove_cvref_t<QP>, quantity_point> &&
requires { --qp.quantity_from_origin_is_an_implementation_detail_; }
template<typename FwdQP, std::derived_from<quantity_point> QP = std::remove_cvref_t<FwdQP>>
friend constexpr decltype(auto) operator--(FwdQP&& qp)
requires requires { --qp.quantity_from_origin_is_an_implementation_detail_; }
{
--qp.quantity_from_origin_is_an_implementation_detail_;
return std::forward<QP>(qp);
return std::forward<FwdQP>(qp);
}
[[nodiscard]] constexpr quantity_point operator--(int)
@@ -445,22 +438,20 @@ public:
}
// compound assignment operators
template<typename QP>
requires std::derived_from<std::remove_cvref_t<QP>, quantity_point> &&
requires(quantity_type q) { quantity_from_origin_is_an_implementation_detail_ += q; }
friend constexpr decltype(auto) operator+=(QP&& qp, const quantity_type& q)
template<typename FwdQP, std::derived_from<quantity_point> QP = std::remove_cvref_t<FwdQP>>
requires requires(quantity_type q) { quantity_from_origin_is_an_implementation_detail_ += q; }
friend constexpr decltype(auto) operator+=(FwdQP&& qp, const quantity_type& q)
{
qp.quantity_from_origin_is_an_implementation_detail_ += q;
return std::forward<QP>(qp);
return std::forward<FwdQP>(qp);
}
template<typename QP>
requires std::derived_from<std::remove_cvref_t<QP>, quantity_point> &&
requires(quantity_type q) { quantity_from_origin_is_an_implementation_detail_ -= q; }
friend constexpr decltype(auto) operator-=(QP&& qp, const quantity_type& q)
template<typename FwdQP, std::derived_from<quantity_point> QP = std::remove_cvref_t<FwdQP>>
requires requires(quantity_type q) { quantity_from_origin_is_an_implementation_detail_ -= q; }
friend constexpr decltype(auto) operator-=(FwdQP&& qp, const quantity_type& q)
{
qp.quantity_from_origin_is_an_implementation_detail_ -= q;
return std::forward<QP>(qp);
return std::forward<FwdQP>(qp);
}
// binary operators on quantity points

View File

@@ -38,13 +38,13 @@ struct absolute_point_origin;
namespace detail {
template<typename T>
inline constexpr bool is_quantity_point = false;
constexpr bool is_quantity_point = false;
template<auto Q>
void to_base_specialization_of_absolute_point_origin(const volatile absolute_point_origin<Q>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_absolute_point_origin =
constexpr bool is_derived_from_specialization_of_absolute_point_origin =
requires(T* type) { to_base_specialization_of_absolute_point_origin(type); };
} // namespace detail
@@ -66,7 +66,7 @@ template<auto QP>
void to_base_specialization_of_relative_point_origin(const volatile relative_point_origin<QP>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_relative_point_origin =
constexpr bool is_derived_from_specialization_of_relative_point_origin =
requires(T* type) { to_base_specialization_of_relative_point_origin(type); };
struct point_origin_interface;
@@ -99,12 +99,12 @@ template<auto R, auto PO, typename Rep>
void to_base_specialization_of_quantity_point(const volatile quantity_point<R, PO, Rep>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_quantity_point =
constexpr bool is_derived_from_specialization_of_quantity_point =
requires(T* type) { to_base_specialization_of_quantity_point(type); };
template<typename T>
requires is_derived_from_specialization_of_quantity_point<T>
inline constexpr bool is_quantity_point<T> = true;
constexpr bool is_quantity_point<T> = true;
template<PointOrigin PO1, PointOrigin PO2>
[[nodiscard]] consteval bool same_absolute_point_origins(PO1 po1, PO2 po2)

View File

@@ -159,13 +159,11 @@ struct quantity_spec_interface : quantity_spec_interface_base {
return make_reference(self, u);
}
template<typename Self, typename Q>
requires Quantity<std::remove_cvref_t<Q>> &&
QuantitySpecExplicitlyConvertibleTo<std::remove_reference_t<Q>::quantity_spec, Self{}>
[[nodiscard]] constexpr Quantity auto operator()(this Self self, Q&& q)
template<typename Self, typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>>
requires QuantitySpecExplicitlyConvertibleTo<Q::quantity_spec, Self{}>
[[nodiscard]] constexpr Quantity auto operator()(this Self self, FwdQ&& q)
{
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
make_reference(self, std::remove_cvref_t<Q>::unit)};
return quantity{std::forward<FwdQ>(q).numerical_value_is_an_implementation_detail_, make_reference(self, Q::unit)};
}
#else
template<typename Self_ = Self, UnitOf<Self_{}> U>
@@ -174,13 +172,12 @@ struct quantity_spec_interface : quantity_spec_interface_base {
return make_reference(Self{}, u);
}
template<typename Q, typename Self_ = Self>
requires Quantity<std::remove_cvref_t<Q>> &&
QuantitySpecExplicitlyConvertibleTo<std::remove_reference_t<Q>::quantity_spec, Self_{}>
[[nodiscard]] constexpr Quantity auto operator()(Q&& q) const
template<typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>, typename Self_ = Self>
requires QuantitySpecExplicitlyConvertibleTo<Q::quantity_spec, Self_{}>
[[nodiscard]] constexpr Quantity auto operator()(FwdQ&& q) const
{
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
make_reference(Self{}, std::remove_cvref_t<Q>::unit)};
return quantity{std::forward<FwdQ>(q).numerical_value_is_an_implementation_detail_,
make_reference(Self{}, Q::unit)};
}
#endif
};
@@ -371,13 +368,12 @@ struct quantity_spec<Self, QS, Args...> : detail::propagate_equation<QS>, detail
return detail::make_reference(Self{}, u);
}
template<typename Q, typename Self_ = Self>
requires Quantity<std::remove_cvref_t<Q>> &&
detail::QuantitySpecExplicitlyConvertibleTo<std::remove_reference_t<Q>::quantity_spec, Self_{}>
[[nodiscard]] constexpr Quantity auto operator()(Q&& q) const
template<typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>, typename Self_ = Self>
requires detail::QuantitySpecExplicitlyConvertibleTo<Q::quantity_spec, Self_{}>
[[nodiscard]] constexpr Quantity auto operator()(FwdQ&& q) const
{
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
detail::make_reference(Self{}, std::remove_cvref_t<Q>::unit)};
return quantity{std::forward<FwdQ>(q).numerical_value_is_an_implementation_detail_,
detail::make_reference(Self{}, Q::unit)};
}
#endif
};
@@ -556,7 +552,7 @@ struct kind_of_<Q> final : quantity_spec<kind_of_<Q>, Q{}>::_base_type_ {
MP_UNITS_EXPORT template<detail::QuantitySpecWithNoSpecifiers auto Q>
requires detail::SameQuantitySpec<detail::get_kind_tree_root(Q), Q>
inline constexpr kind_of_<MP_UNITS_REMOVE_CONST(decltype(Q))> kind_of;
constexpr kind_of_<MP_UNITS_REMOVE_CONST(decltype(Q))> kind_of;
namespace detail {

View File

@@ -53,10 +53,10 @@ struct kind_of_;
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_kind_of = false;
constexpr bool is_specialization_of_kind_of = false;
template<typename Q>
inline constexpr bool is_specialization_of_kind_of<kind_of_<Q>> = true;
constexpr bool is_specialization_of_kind_of<kind_of_<Q>> = true;
template<typename T>
concept QuantityKindSpec = is_specialization_of_kind_of<T>;
@@ -70,7 +70,7 @@ void to_base_specialization_of_quantity_spec(const volatile quantity_spec<T, Arg
#endif
template<typename T>
inline constexpr bool is_derived_from_specialization_of_quantity_spec =
constexpr bool is_derived_from_specialization_of_quantity_spec =
requires(T* type) { to_base_specialization_of_quantity_spec(type); };
/**
@@ -86,16 +86,16 @@ template<typename T>
struct is_dimensionless : std::false_type {};
template<typename T>
inline constexpr bool is_power_of_quantity_spec = requires {
constexpr bool is_power_of_quantity_spec = requires {
requires is_specialization_of_power<T> &&
(NamedQuantitySpec<typename T::factor> || is_dimensionless<typename T::factor>::value);
};
template<typename T>
inline constexpr bool is_per_of_quantity_specs = false;
constexpr bool is_per_of_quantity_specs = false;
template<typename... Ts>
inline constexpr bool is_per_of_quantity_specs<per<Ts...>> =
constexpr bool is_per_of_quantity_specs<per<Ts...>> =
(... && (NamedQuantitySpec<Ts> || is_dimensionless<Ts>::value || is_power_of_quantity_spec<Ts>));
template<typename T>

View File

@@ -190,36 +190,36 @@ struct reference {
};
template<typename Rep, Reference R>
requires(!detail::OffsetUnit<decltype(get_unit(R{}))>) &&
RepresentationOf<std::remove_cvref_t<Rep>, get_quantity_spec(R{}).character>
[[nodiscard]] constexpr quantity<R{}, std::remove_cvref_t<Rep>> operator*(Rep&& lhs, R)
template<typename FwdRep, Reference R,
RepresentationOf<get_quantity_spec(R{}).character> Rep = std::remove_cvref_t<FwdRep>>
requires(!detail::OffsetUnit<decltype(get_unit(R{}))>)
[[nodiscard]] constexpr quantity<R{}, Rep> operator*(FwdRep&& lhs, R)
{
return quantity{std::forward<Rep>(lhs), R{}};
return quantity{std::forward<FwdRep>(lhs), R{}};
}
template<typename Rep, Reference R>
requires(!detail::OffsetUnit<decltype(get_unit(R{}))>) &&
RepresentationOf<std::remove_cvref_t<Rep>, get_quantity_spec(R{}).character>
[[nodiscard]] constexpr quantity<inverse(R{}), std::remove_cvref_t<Rep>> operator/(Rep&& lhs, R)
template<typename FwdRep, Reference R,
RepresentationOf<get_quantity_spec(R{}).character> Rep = std::remove_cvref_t<FwdRep>>
requires(!detail::OffsetUnit<decltype(get_unit(R{}))>)
[[nodiscard]] constexpr quantity<inverse(R{}), Rep> operator/(FwdRep&& lhs, R)
{
return quantity{std::forward<Rep>(lhs), inverse(R{})};
return quantity{std::forward<FwdRep>(lhs), inverse(R{})};
}
template<typename Rep, Reference R>
requires detail::OffsetUnit<decltype(get_unit(R{}))> &&
RepresentationOf<std::remove_cvref_t<Rep>, get_quantity_spec(R{}).character>
[[noreturn]] constexpr auto operator*(Rep&&, R)
template<typename FwdRep, Reference R,
RepresentationOf<get_quantity_spec(R{}).character> Rep = std::remove_cvref_t<FwdRep>>
requires detail::OffsetUnit<decltype(get_unit(R{}))>
[[noreturn]] constexpr auto operator*(FwdRep&&, R)
{
static_assert(!detail::OffsetUnit<decltype(get_unit(R{}))>,
"References using offset units (e.g., temperatures) may be constructed only with the `delta` or "
"`absolute` helpers");
}
template<typename Rep, Reference R>
requires detail::OffsetUnit<decltype(get_unit(R{}))> &&
RepresentationOf<std::remove_cvref_t<Rep>, get_quantity_spec(R{}).character>
[[noreturn]] constexpr auto operator/(Rep&&, R)
template<typename FwdRep, Reference R,
RepresentationOf<get_quantity_spec(R{}).character> Rep = std::remove_cvref_t<FwdRep>>
requires detail::OffsetUnit<decltype(get_unit(R{}))>
[[noreturn]] constexpr auto operator/(FwdRep&&, R)
{
static_assert(!detail::OffsetUnit<decltype(get_unit(R{}))>,
"References using offset units (e.g., temperatures) may be constructed only with the `delta` or "
@@ -246,20 +246,16 @@ constexpr auto operator/(R, Rep&&)
= delete;
#endif
template<typename Q, Reference R>
requires Quantity<std::remove_cvref_t<Q>>
[[nodiscard]] constexpr Quantity auto operator*(Q&& q, R)
template<typename FwdQ, Reference R, Quantity Q = std::remove_cvref_t<FwdQ>>
[[nodiscard]] constexpr Quantity auto operator*(FwdQ&& q, R)
{
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
std::remove_cvref_t<Q>::reference * R{}};
return quantity{std::forward<FwdQ>(q).numerical_value_is_an_implementation_detail_, Q::reference * R{}};
}
template<typename Q, Reference R>
requires Quantity<std::remove_cvref_t<Q>>
[[nodiscard]] constexpr Quantity auto operator/(Q&& q, R)
template<typename FwdQ, Reference R, Quantity Q = std::remove_cvref_t<FwdQ>>
[[nodiscard]] constexpr Quantity auto operator/(FwdQ&& q, R)
{
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
std::remove_cvref_t<Q>::reference / R{}};
return quantity{std::forward<FwdQ>(q).numerical_value_is_an_implementation_detail_, Q::reference / R{}};
}
template<Reference R, typename Q>

View File

@@ -79,10 +79,14 @@ concept Scalable =
CastableNumber<T> || (requires { typename wrapped_type_t<T>; } && CastableNumber<wrapped_type_t<T>> &&
ScalableNumber<T, std::common_type_t<wrapped_type_t<T>, std::intmax_t>>);
template<typename T>
concept WeaklyRegular = std::copyable<T> && std::equality_comparable<T>;
} // namespace detail
MP_UNITS_EXPORT template<typename T>
concept Representation = (is_scalar<T> || is_vector<T> || is_tensor<T>)&&std::regular<T> && detail::Scalable<T>;
concept Representation =
(is_scalar<T> || is_vector<T> || is_tensor<T>)&&detail::WeaklyRegular<T> && detail::Scalable<T>;
MP_UNITS_EXPORT template<typename T, quantity_character Ch>
concept RepresentationOf = Representation<T> && ((Ch == quantity_character::scalar && is_scalar<T>) ||

View File

@@ -64,7 +64,7 @@ template<Magnitude auto M, Unit U>
struct scaled_unit_impl;
template<typename T>
inline constexpr bool is_specialization_of_scaled_unit = false;
constexpr bool is_specialization_of_scaled_unit = false;
template<DerivedUnitExpr... Expr>
struct derived_unit_impl;
@@ -241,7 +241,7 @@ struct scaled_unit final : detail::scaled_unit_impl<M, U> {};
namespace detail {
template<auto M, Unit U>
inline constexpr bool is_specialization_of_scaled_unit<scaled_unit<M, U>> = true;
constexpr bool is_specialization_of_scaled_unit<scaled_unit<M, U>> = true;
} // namespace detail
@@ -386,7 +386,7 @@ struct named_unit<Symbol, U, QS, PO> : decltype(U)::_base_type_ {
* struct kilo_ : prefixed_unit<"k", mag_power<10, 3>, U> {};
*
* template<PrefixableUnit auto U>
* inline constexpr kilo_<U> kilo;
* constexpr kilo_<U> kilo;
*
* inline constexpr auto kilogram = si::kilo<gram>;
* @endcode
@@ -534,10 +534,10 @@ template<Unit T, typename... Expr>
}
template<typename T>
inline constexpr bool is_specialization_of_derived_unit = false;
constexpr bool is_specialization_of_derived_unit = false;
template<typename... Expr>
inline constexpr bool is_specialization_of_derived_unit<derived_unit<Expr...>> = true;
constexpr bool is_specialization_of_derived_unit<derived_unit<Expr...>> = true;
} // namespace detail
@@ -675,14 +675,14 @@ template<Unit U1, Unit U2>
* be provided.
*/
template<Unit auto U>
inline constexpr bool space_before_unit_symbol = true;
constexpr bool space_before_unit_symbol = true;
template<>
inline constexpr bool space_before_unit_symbol<one> = false;
MP_UNITS_INLINE constexpr bool space_before_unit_symbol<one> = false;
template<>
inline constexpr bool space_before_unit_symbol<percent> = false;
MP_UNITS_INLINE constexpr bool space_before_unit_symbol<percent> = false;
template<>
inline constexpr bool space_before_unit_symbol<per_mille> = false;
MP_UNITS_INLINE constexpr bool space_before_unit_symbol<per_mille> = false;
// get_unit_symbol

View File

@@ -59,7 +59,7 @@ template<symbol_text Symbol, auto... Args>
void to_base_specialization_of_named_unit(const volatile named_unit<Symbol, Args...>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_named_unit =
constexpr bool is_derived_from_specialization_of_named_unit =
requires(T* t) { to_base_specialization_of_named_unit(t); };
} // namespace detail
@@ -73,14 +73,13 @@ concept PrefixableUnit = Unit<T> && detail::is_derived_from_specialization_of_na
namespace detail {
template<typename T>
inline constexpr bool is_power_of_unit =
requires { requires is_specialization_of_power<T> && Unit<typename T::factor>; };
constexpr bool is_power_of_unit = requires { requires is_specialization_of_power<T> && Unit<typename T::factor>; };
template<typename T>
inline constexpr bool is_per_of_units = false;
constexpr bool is_per_of_units = false;
template<typename... Ts>
inline constexpr bool is_per_of_units<per<Ts...>> = (... && (Unit<Ts> || is_power_of_unit<Ts>));
constexpr bool is_per_of_units<per<Ts...>> = (... && (Unit<Ts> || is_power_of_unit<Ts>));
template<typename T>
concept DerivedUnitExpr = Unit<T> || detail::is_power_of_unit<T> || detail::is_per_of_units<T>;

View File

@@ -44,13 +44,12 @@ namespace mp_units {
*
* @tparam ToU a unit to use for a target quantity
*/
template<Unit auto ToU, typename Q>
requires Quantity<std::remove_cvref_t<Q>> && (convertible(std::remove_reference_t<Q>::reference, ToU))
[[nodiscard]] constexpr Quantity auto value_cast(Q&& q)
template<Unit auto ToU, typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>>
requires(convertible(Q::reference, ToU))
[[nodiscard]] constexpr Quantity auto value_cast(FwdQ&& q)
{
using q_type = std::remove_reference_t<Q>;
return detail::sudo_cast<quantity<detail::make_reference(q_type::quantity_spec, ToU), typename q_type::rep>>(
std::forward<Q>(q));
return detail::sudo_cast<quantity<detail::make_reference(Q::quantity_spec, ToU), typename Q::rep>>(
std::forward<FwdQ>(q));
}
/**
@@ -63,13 +62,11 @@ template<Unit auto ToU, typename Q>
*
* @tparam ToRep a representation type to use for a target quantity
*/
template<Representation ToRep, typename Q>
requires Quantity<std::remove_cvref_t<Q>> &&
RepresentationOf<ToRep, std::remove_reference_t<Q>::quantity_spec.character> &&
std::constructible_from<ToRep, typename std::remove_reference_t<Q>::rep>
[[nodiscard]] constexpr quantity<std::remove_reference_t<Q>::reference, ToRep> value_cast(Q&& q)
template<Representation ToRep, typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>>
requires RepresentationOf<ToRep, Q::quantity_spec.character> && std::constructible_from<ToRep, typename Q::rep>
[[nodiscard]] constexpr quantity<Q::reference, ToRep> value_cast(FwdQ&& q)
{
return detail::sudo_cast<quantity<std::remove_reference_t<Q>::reference, ToRep>>(std::forward<Q>(q));
return detail::sudo_cast<quantity<Q::reference, ToRep>>(std::forward<FwdQ>(q));
}
/**
@@ -83,14 +80,12 @@ template<Representation ToRep, typename Q>
* @tparam ToU a unit to use for the target quantity
* @tparam ToRep a representation type to use for the target quantity
*/
template<Unit auto ToU, Representation ToRep, typename Q>
requires Quantity<std::remove_cvref_t<Q>> && (convertible(std::remove_reference_t<Q>::reference, ToU)) &&
RepresentationOf<ToRep, std::remove_reference_t<Q>::quantity_spec.character> &&
std::constructible_from<ToRep, typename std::remove_reference_t<Q>::rep>
[[nodiscard]] constexpr Quantity auto value_cast(Q&& q)
template<Unit auto ToU, Representation ToRep, typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>>
requires(convertible(Q::reference, ToU)) && RepresentationOf<ToRep, Q::quantity_spec.character> &&
std::constructible_from<ToRep, typename Q::rep>
[[nodiscard]] constexpr Quantity auto value_cast(FwdQ&& q)
{
using q_type = std::remove_reference_t<Q>;
return detail::sudo_cast<quantity<detail::make_reference(q_type::quantity_spec, ToU), ToRep>>(std::forward<Q>(q));
return detail::sudo_cast<quantity<detail::make_reference(Q::quantity_spec, ToU), ToRep>>(std::forward<FwdQ>(q));
}
@@ -108,13 +103,12 @@ template<Unit auto ToU, Representation ToRep, typename Q>
*
* @tparam ToQ a target quantity type to which to cast the representation
*/
template<Quantity ToQ, typename Q>
requires Quantity<std::remove_cvref_t<Q>> && (convertible(std::remove_reference_t<Q>::reference, ToQ::unit)) &&
(ToQ::quantity_spec == std::remove_reference_t<Q>::quantity_spec) &&
std::constructible_from<typename ToQ::rep, typename std::remove_reference_t<Q>::rep>
[[nodiscard]] constexpr Quantity auto value_cast(Q&& q)
template<Quantity ToQ, typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>>
requires(convertible(Q::reference, ToQ::unit)) &&
(ToQ::quantity_spec == Q::quantity_spec) && std::constructible_from<typename ToQ::rep, typename Q::rep>
[[nodiscard]] constexpr Quantity auto value_cast(FwdQ&& q)
{
return detail::sudo_cast<ToQ>(std::forward<Q>(q));
return detail::sudo_cast<ToQ>(std::forward<FwdQ>(q));
}
/**
@@ -127,12 +121,12 @@ template<Quantity ToQ, typename Q>
*
* @tparam ToU a unit to use for a target quantity point
*/
template<Unit auto ToU, typename QP>
requires QuantityPoint<std::remove_cvref_t<QP>> && (convertible(std::remove_reference_t<QP>::reference, ToU))
[[nodiscard]] constexpr QuantityPoint auto value_cast(QP&& qp)
template<Unit auto ToU, typename FwdQP, QuantityPoint QP = std::remove_cvref_t<FwdQP>>
requires(convertible(QP::reference, ToU))
[[nodiscard]] constexpr QuantityPoint auto value_cast(FwdQP&& qp)
{
return quantity_point{value_cast<ToU>(std::forward<QP>(qp).quantity_from_origin_is_an_implementation_detail_),
std::remove_reference_t<QP>::point_origin};
return quantity_point{value_cast<ToU>(std::forward<FwdQP>(qp).quantity_from_origin_is_an_implementation_detail_),
QP::point_origin};
}
/**
@@ -145,16 +139,12 @@ template<Unit auto ToU, typename QP>
*
* @tparam ToRep a representation type to use for a target quantity point
*/
template<Representation ToRep, typename QP>
requires QuantityPoint<std::remove_cvref_t<QP>> &&
RepresentationOf<ToRep, std::remove_reference_t<QP>::quantity_spec.character> &&
std::constructible_from<ToRep, typename std::remove_reference_t<QP>::rep>
[[nodiscard]] constexpr quantity_point<std::remove_reference_t<QP>::reference,
std::remove_reference_t<QP>::point_origin, ToRep>
value_cast(QP&& qp)
template<Representation ToRep, typename FwdQP, QuantityPoint QP = std::remove_cvref_t<FwdQP>>
requires RepresentationOf<ToRep, QP::quantity_spec.character> && std::constructible_from<ToRep, typename QP::rep>
[[nodiscard]] constexpr quantity_point<QP::reference, QP::point_origin, ToRep> value_cast(FwdQP&& qp)
{
return {value_cast<ToRep>(std::forward<QP>(qp).quantity_from_origin_is_an_implementation_detail_),
std::remove_reference_t<QP>::point_origin};
return {value_cast<ToRep>(std::forward<FwdQP>(qp).quantity_from_origin_is_an_implementation_detail_),
QP::point_origin};
}
/**
@@ -168,14 +158,14 @@ value_cast(QP&& qp)
* @tparam ToU a unit to use for the target quantity
* @tparam ToRep a representation type to use for the target quantity
*/
template<Unit auto ToU, Representation ToRep, typename QP>
requires QuantityPoint<std::remove_cvref_t<QP>> && (convertible(std::remove_reference_t<QP>::reference, ToU)) &&
RepresentationOf<ToRep, std::remove_reference_t<QP>::quantity_spec.character> &&
std::constructible_from<ToRep, typename std::remove_reference_t<QP>::rep>
[[nodiscard]] constexpr QuantityPoint auto value_cast(QP&& qp)
template<Unit auto ToU, Representation ToRep, typename FwdQP, QuantityPoint QP = std::remove_cvref_t<FwdQP>>
requires(convertible(QP::reference, ToU)) && RepresentationOf<ToRep, QP::quantity_spec.character> &&
std::constructible_from<ToRep, typename QP::rep>
[[nodiscard]] constexpr QuantityPoint auto value_cast(FwdQP&& qp)
{
return quantity_point{value_cast<ToU, ToRep>(std::forward<QP>(qp).quantity_from_origin_is_an_implementation_detail_),
std::remove_reference_t<QP>::point_origin};
return quantity_point{
value_cast<ToU, ToRep>(std::forward<FwdQP>(qp).quantity_from_origin_is_an_implementation_detail_),
QP::point_origin};
}
/**
@@ -194,14 +184,13 @@ template<Unit auto ToU, Representation ToRep, typename QP>
*
* @tparam ToQ a target quantity type to which to cast the representation of the point
*/
template<Quantity ToQ, typename QP>
requires QuantityPoint<std::remove_cvref_t<QP>> && (convertible(std::remove_reference_t<QP>::reference, ToQ::unit)) &&
(ToQ::quantity_spec == std::remove_reference_t<QP>::quantity_spec) &&
std::constructible_from<typename ToQ::rep, typename std::remove_reference_t<QP>::rep>
[[nodiscard]] constexpr QuantityPoint auto value_cast(QP&& qp)
template<Quantity ToQ, typename FwdQP, QuantityPoint QP = std::remove_cvref_t<FwdQP>>
requires(convertible(QP::reference, ToQ::unit)) &&
(ToQ::quantity_spec == QP::quantity_spec) && std::constructible_from<typename ToQ::rep, typename QP::rep>
[[nodiscard]] constexpr QuantityPoint auto value_cast(FwdQP&& qp)
{
return quantity_point{value_cast<ToQ>(std::forward<QP>(qp).quantity_from_origin_is_an_implementation_detail_),
std::remove_reference_t<QP>::point_origin};
return quantity_point{value_cast<ToQ>(std::forward<FwdQP>(qp).quantity_from_origin_is_an_implementation_detail_),
QP::point_origin};
}
/**
@@ -232,16 +221,13 @@ template<Quantity ToQ, typename QP>
*
* @tparam ToQP a target quantity point type to which to cast the representation of the point
*/
template<QuantityPoint ToQP, typename QP>
requires QuantityPoint<std::remove_cvref_t<QP>> &&
(convertible(std::remove_reference_t<QP>::reference, ToQP::unit)) &&
(ToQP::quantity_spec == std::remove_reference_t<QP>::quantity_spec) &&
(detail::same_absolute_point_origins(ToQP::point_origin, std::remove_reference_t<QP>::point_origin)) &&
std::constructible_from<typename ToQP::rep, typename std::remove_reference_t<QP>::rep>
[[nodiscard]] constexpr QuantityPoint auto value_cast(QP&& qp)
template<QuantityPoint ToQP, typename FwdQP, QuantityPoint QP = std::remove_cvref_t<FwdQP>>
requires(convertible(QP::reference, ToQP::unit)) && (ToQP::quantity_spec == QP::quantity_spec) &&
(detail::same_absolute_point_origins(ToQP::point_origin, QP::point_origin)) &&
std::constructible_from<typename ToQP::rep, typename QP::rep>
[[nodiscard]] constexpr QuantityPoint auto value_cast(FwdQP&& qp)
{
return detail::sudo_cast<ToQP>(std::forward<QP>(qp));
return detail::sudo_cast<ToQP>(std::forward<FwdQP>(qp));
}
} // namespace mp_units

View File

@@ -62,8 +62,8 @@ inline constexpr auto deg2 = square(degree);
} // namespace angular
template<>
inline constexpr bool space_before_unit_symbol<angular::degree> = false;
MP_UNITS_INLINE constexpr bool space_before_unit_symbol<angular::degree> = false;
template<>
inline constexpr bool space_before_unit_symbol<angular::gradian> = false;
MP_UNITS_INLINE constexpr bool space_before_unit_symbol<angular::gradian> = false;
} // namespace mp_units

View File

@@ -36,7 +36,7 @@ MP_UNITS_EXPORT
namespace mp_units {
template<>
inline constexpr std::optional<std::intmax_t> known_first_factor<334'524'384'739> = 334'524'384'739;
constexpr std::optional<std::intmax_t> known_first_factor<334'524'384'739> = 334'524'384'739;
namespace hep {

View File

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

View File

@@ -93,7 +93,7 @@ struct chrono_point_origin_ final : absolute_point_origin<isq::time> {
using clock = C;
};
MP_UNITS_EXPORT template<typename C>
inline constexpr chrono_point_origin_<C> chrono_point_origin;
constexpr chrono_point_origin_<C> chrono_point_origin;
MP_UNITS_EXPORT_BEGIN

View File

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

View File

@@ -126,10 +126,10 @@ using namespace non_si;
} // namespace si
template<>
inline constexpr bool space_before_unit_symbol<non_si::degree> = false;
MP_UNITS_INLINE constexpr bool space_before_unit_symbol<non_si::degree> = false;
template<>
inline constexpr bool space_before_unit_symbol<non_si::arcminute> = false;
MP_UNITS_INLINE constexpr bool space_before_unit_symbol<non_si::arcminute> = false;
template<>
inline constexpr bool space_before_unit_symbol<non_si::arcsecond> = false;
MP_UNITS_INLINE constexpr bool space_before_unit_symbol<non_si::arcsecond> = false;
} // namespace mp_units

View File

@@ -50,7 +50,7 @@ import mp_units;
template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
constexpr bool mp_units::is_vector<T> = true;
using namespace mp_units;
using namespace mp_units::si::unit_symbols;

View File

@@ -44,10 +44,10 @@ template<typename Rep = double>
using vector = STD_LA::fixed_size_column_vector<Rep, 3>;
template<class Rep>
inline constexpr bool mp_units::treat_as_floating_point<vector<Rep>> = mp_units::treat_as_floating_point<Rep>;
constexpr bool mp_units::treat_as_floating_point<vector<Rep>> = mp_units::treat_as_floating_point<Rep>;
template<typename Rep>
inline constexpr bool mp_units::is_vector<vector<Rep>> = true;
constexpr bool mp_units::is_vector<vector<Rep>> = true;
template<typename Rep>
std::ostream& operator<<(std::ostream& os, const vector<Rep>& v)
@@ -301,7 +301,7 @@ TEST_CASE("vector quantity", "[la]")
template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
constexpr bool mp_units::is_vector<T> = true;
TEST_CASE("vector of quantities", "[la]")
{

View File

@@ -73,3 +73,9 @@ TEST_CASE("Issue 474 is fixed", "[conversion][radical]")
REQUIRE(within_4_ulps(sqrt(val_issue_474).numerical_value_in(m / s),
sqrt(val_issue_474.numerical_value_in(m * m / s / s))));
}
TEST_CASE("Volatile representation type", "[volatile]")
{
volatile std::int16_t vint = 123;
REQUIRE(quantity(vint * m).numerical_value_in(m) == 123);
}

View File

@@ -28,7 +28,7 @@
template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
constexpr bool mp_units::is_vector<T> = true;
namespace {

View File

@@ -37,7 +37,7 @@ import std;
#if MP_UNITS_HOSTED
template<typename T>
inline constexpr bool mp_units::is_scalar<std::complex<T>> = true;
constexpr bool mp_units::is_scalar<std::complex<T>> = true;
#endif
namespace {

View File

@@ -61,7 +61,7 @@ public:
} // namespace
template<typename T>
inline constexpr bool mp_units::is_scalar<min_impl<T>> = true;
constexpr bool mp_units::is_scalar<min_impl<T>> = true;
template<typename T, typename U>
struct std::common_type<min_impl<T>, min_impl<U>> : std::type_identity<min_impl<std::common_type_t<T, U>>> {};

View File

@@ -26,7 +26,7 @@
template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
constexpr bool mp_units::is_vector<T> = true;
namespace {

View File

@@ -28,7 +28,7 @@
template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
constexpr bool mp_units::is_vector<T> = true;
namespace {

View File

@@ -32,7 +32,7 @@ using namespace units;
using namespace units::detail;
template<>
inline constexpr std::optional<std::intmax_t> units::known_first_factor<9223372036854775783> = 9223372036854775783;
constexpr std::optional<std::intmax_t> units::known_first_factor<9223372036854775783> = 9223372036854775783;
namespace {

View File

@@ -24,7 +24,7 @@
template<class T>
requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;
constexpr bool mp_units::is_vector<T> = true;
namespace {

View File

@@ -111,12 +111,12 @@ static_assert(ground_level != other_ground_level);
template<auto QS>
struct absolute_po_ final : absolute_point_origin<QS> {};
template<auto QS>
inline constexpr absolute_po_<QS> absolute_po;
constexpr absolute_po_<QS> absolute_po;
template<auto QP>
struct relative_po_ final : relative_point_origin<QP> {};
template<auto QP>
inline constexpr relative_po_<QP> relative_po;
constexpr relative_po_<QP> relative_po;
static_assert(relative_po<absolute_po<isq::length> + isq::height(42 * m)>.quantity_spec == isq::height);
static_assert(relative_po<absolute_po<kind_of<isq::length>> + isq::height(42 * m)>.quantity_spec == isq::height);

View File

@@ -41,7 +41,7 @@ import std;
#endif
template<>
inline constexpr bool mp_units::is_vector<int> = true;
constexpr bool mp_units::is_vector<int> = true;
namespace {
@@ -115,6 +115,10 @@ static_assert(quantity<isq::length[m]>::unit == si::metre);
static_assert(is_same_v<quantity<isq::length[m]>::rep, double>);
static_assert(is_same_v<quantity<isq::length[m], int>::rep, int>);
[[maybe_unused]] volatile std::int16_t vint = 123;
static_assert(is_same_v<decltype(quantity(vint * m))::rep, std::int16_t>);
static_assert(is_same_v<decltype(quantity(vint, m))::rep, std::int16_t>);
////////////////////////////
// static member functions
@@ -260,6 +264,9 @@ static_assert(quantity<isq::length[km], int>(2 * km).force_in(km).numerical_valu
static_assert(quantity<isq::length[km], int>(2 * km).force_in(m).numerical_value_in(m) == 2000);
static_assert(quantity<isq::length[m], int>(2000 * m).force_in(km).numerical_value_in(km) == 2);
static_assert((15. * m).in(nm).numerical_value_in(m) == 15.);
static_assert((15'000. * nm).in(m).numerical_value_in(nm) == 15'000.);
template<template<auto, typename> typename Q>
concept invalid_unit_conversion = requires {
requires !requires { Q<isq::length[m], int>(2000 * m).in(km); }; // truncating conversion
@@ -901,7 +908,7 @@ static_assert(1 * si::si2019::speed_of_light_in_vacuum == 299'792'458 * isq::spe
// Different named dimensions
template<Reference auto R1, Reference auto R2>
inline constexpr bool invalid_comparison = !requires { 2 * R1 == 2 * R2; } && !requires { 2 * R2 == 2 * R1; };
constexpr bool invalid_comparison = !requires { 2 * R1 == 2 * R2; } && !requires { 2 * R2 == 2 * R1; };
static_assert(invalid_comparison<isq::activity[Bq], isq::frequency[Hz]>);

View File

@@ -31,7 +31,7 @@ import std;
#endif
template<auto V, typename T>
inline constexpr bool is_of_type = std::is_same_v<MP_UNITS_REMOVE_CONST(decltype(V)), T>;
constexpr bool is_of_type = std::is_same_v<MP_UNITS_REMOVE_CONST(decltype(V)), T>;
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
#if MP_UNITS_API_NO_CRTP