mirror of
https://github.com/mpusz/mp-units.git
synced 2025-08-02 20:04:27 +02:00
Merge branch 'master' of github.com:mpusz/units
This commit is contained in:
25
.github/workflows/ci-conan.yml
vendored
25
.github/workflows/ci-conan.yml
vendored
@@ -143,6 +143,20 @@ jobs:
|
|||||||
std_format_support: "True",
|
std_format_support: "True",
|
||||||
conan-config: "",
|
conan-config: "",
|
||||||
}
|
}
|
||||||
|
- {
|
||||||
|
name: "Clang-18 on Apple M1 (arm64)",
|
||||||
|
os: macos-14,
|
||||||
|
compiler:
|
||||||
|
{
|
||||||
|
type: CLANG,
|
||||||
|
version: 18,
|
||||||
|
cc: "/opt/homebrew/opt/llvm@18/bin/clang-18",
|
||||||
|
cxx: "/opt/homebrew/opt/llvm@18/bin/clang++",
|
||||||
|
},
|
||||||
|
lib: "libc++",
|
||||||
|
cxx_modules: "False",
|
||||||
|
std_format_support: "True"
|
||||||
|
}
|
||||||
- {
|
- {
|
||||||
name: "Apple Clang 15",
|
name: "Apple Clang 15",
|
||||||
os: macos-13,
|
os: macos-13,
|
||||||
@@ -196,8 +210,8 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
sudo apt install -y g++-${{ matrix.config.compiler.version }}
|
sudo apt install -y g++-${{ matrix.config.compiler.version }}
|
||||||
- name: Install Clang
|
- name: Install Clang with apt
|
||||||
if: matrix.config.compiler.type == 'CLANG'
|
if: matrix.config.compiler.type == 'CLANG' && matrix.config.os != 'macos-14'
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: ${{ env.HOME }}
|
working-directory: ${{ env.HOME }}
|
||||||
run: |
|
run: |
|
||||||
@@ -205,8 +219,13 @@ jobs:
|
|||||||
chmod +x llvm.sh
|
chmod +x llvm.sh
|
||||||
sudo ./llvm.sh ${{ matrix.config.compiler.version }}
|
sudo ./llvm.sh ${{ matrix.config.compiler.version }}
|
||||||
sudo apt install -y clang-tools-${{ matrix.config.compiler.version }}
|
sudo apt install -y clang-tools-${{ matrix.config.compiler.version }}
|
||||||
|
- name: Install Clang using homebrew
|
||||||
|
if: matrix.config.compiler.type == 'CLANG' && matrix.config.os == 'macos-14'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
brew install llvm@18
|
||||||
- name: Install Libc++
|
- name: Install Libc++
|
||||||
if: matrix.config.compiler.type == 'CLANG' && matrix.config.lib == 'libc++'
|
if: matrix.config.compiler.type == 'CLANG' && matrix.config.lib == 'libc++' && matrix.config.os != 'macos-14'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
sudo apt install -y libc++-${{ matrix.config.compiler.version }}-dev libc++abi-${{ matrix.config.compiler.version }}-dev libunwind-${{ matrix.config.compiler.version }}-dev
|
sudo apt install -y libc++-${{ matrix.config.compiler.version }}-dev libc++abi-${{ matrix.config.compiler.version }}-dev libunwind-${{ matrix.config.compiler.version }}-dev
|
||||||
|
25
.github/workflows/ci-test-package-cmake.yml
vendored
25
.github/workflows/ci-test-package-cmake.yml
vendored
@@ -136,6 +136,20 @@ jobs:
|
|||||||
cxx_modules: "False",
|
cxx_modules: "False",
|
||||||
std_format_support: "True"
|
std_format_support: "True"
|
||||||
}
|
}
|
||||||
|
- {
|
||||||
|
name: "Clang-18 on Apple M1 (arm64)",
|
||||||
|
os: macos-14,
|
||||||
|
compiler:
|
||||||
|
{
|
||||||
|
type: CLANG,
|
||||||
|
version: 18,
|
||||||
|
cc: "/opt/homebrew/opt/llvm@18/bin/clang-18",
|
||||||
|
cxx: "/opt/homebrew/opt/llvm@18/bin/clang++",
|
||||||
|
},
|
||||||
|
lib: "libc++",
|
||||||
|
cxx_modules: "False",
|
||||||
|
std_format_support: "True"
|
||||||
|
}
|
||||||
- {
|
- {
|
||||||
name: "Apple Clang 15",
|
name: "Apple Clang 15",
|
||||||
os: macos-14,
|
os: macos-14,
|
||||||
@@ -188,8 +202,8 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
sudo apt install -y g++-${{ matrix.config.compiler.version }}
|
sudo apt install -y g++-${{ matrix.config.compiler.version }}
|
||||||
- name: Install Clang
|
- name: Install Clang with apt
|
||||||
if: matrix.config.compiler.type == 'CLANG'
|
if: matrix.config.compiler.type == 'CLANG' && matrix.config.os != 'macos-14'
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: ${{ env.HOME }}
|
working-directory: ${{ env.HOME }}
|
||||||
run: |
|
run: |
|
||||||
@@ -197,8 +211,13 @@ jobs:
|
|||||||
chmod +x llvm.sh
|
chmod +x llvm.sh
|
||||||
sudo ./llvm.sh ${{ matrix.config.compiler.version }}
|
sudo ./llvm.sh ${{ matrix.config.compiler.version }}
|
||||||
sudo apt install -y clang-tools-${{ matrix.config.compiler.version }}
|
sudo apt install -y clang-tools-${{ matrix.config.compiler.version }}
|
||||||
|
- name: Install Clang using homebrew
|
||||||
|
if: matrix.config.compiler.type == 'CLANG' && matrix.config.os == 'macos-14'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
brew install llvm@18
|
||||||
- name: Install Libc++
|
- name: Install Libc++
|
||||||
if: matrix.config.compiler.type == 'CLANG' && matrix.config.lib == 'libc++'
|
if: matrix.config.compiler.type == 'CLANG' && matrix.config.lib == 'libc++' && matrix.config.os != 'macos-14'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
sudo apt install -y libc++-${{ matrix.config.compiler.version }}-dev libc++abi-${{ matrix.config.compiler.version }}-dev libunwind-${{ matrix.config.compiler.version }}-dev
|
sudo apt install -y libc++-${{ matrix.config.compiler.version }}-dev libc++abi-${{ matrix.config.compiler.version }}-dev libunwind-${{ matrix.config.compiler.version }}-dev
|
||||||
|
@@ -83,7 +83,7 @@ static_assert(10 * km / (5 * km) == 2 * one);
|
|||||||
static_assert(1000 / (1 * s) == 1 * kHz);
|
static_assert(1000 / (1 * s) == 1 * kHz);
|
||||||
```
|
```
|
||||||
|
|
||||||
_Try it on the [Compiler Explorer](https://godbolt.org/z/8acPeq743)._
|
_Try it on the [Compiler Explorer](https://godbolt.org/z/fT1r4sohs)._
|
||||||
|
|
||||||
This library heavily uses C++20 features (concepts, classes as NTTPs, ...). Thanks to
|
This library heavily uses C++20 features (concepts, classes as NTTPs, ...). Thanks to
|
||||||
them the user gets a powerful but still easy to use interfaces and all unit conversions
|
them the user gets a powerful but still easy to use interfaces and all unit conversions
|
||||||
|
@@ -14,14 +14,29 @@ The table below provides the minimum compiler version required to compile the co
|
|||||||
C++ feature:
|
C++ feature:
|
||||||
|
|
||||||
| C++ Feature | C++ version | gcc | clang | apple-clang | MSVC |
|
| C++ Feature | C++ version | gcc | clang | apple-clang | MSVC |
|
||||||
|-----------------------------------------------------------|:-----------:|:----:|:-----:|:-----------:|:----:|
|
|-----------------------------------------------------------|:-----------:|:----:|:-----:|:-----------:|:-----------------------------------------:|
|
||||||
| **Minimum support** | 20 | 12 | 16 | 15 | None |
|
| **Minimum support** | 20 | 12 | 16 | 15 | 194 :bug:{ title="BEWARE of MSVC Bugs!" } |
|
||||||
| **`std::format`** | 20 | 13 | 17 | None | None |
|
| **`std::format`** | 20 | 13 | 17 | None | 194 |
|
||||||
| **C++ modules** | 20 | None | 17 | None | None |
|
| **C++ modules** | 20 | None | 17 | None | None |
|
||||||
| **`import std;`** | 23 | None | 18 | None | None |
|
| **`import std;`** | 23 | None | 18 | None | None |
|
||||||
| **Static `constexpr` variables in `constexpr` functions** | 23 | 13 | 17 | None | None |
|
| **Static `constexpr` variables in `constexpr` functions** | 23 | 13 | 17 | None | None |
|
||||||
| **Explicit `this` parameter** | 23 | 14 | 18 | None | None |
|
| **Explicit `this` parameter** | 23 | 14 | 18 | None | None |
|
||||||
|
|
||||||
|
??? note "MSVC bugs"
|
||||||
|
|
||||||
|
MSVC still has a poor C++20 conformance. We had to make many workarounds to our codebase to
|
||||||
|
make it compile on this compiler. Usage of such nasty preprocessor macros degrade the
|
||||||
|
readability and maintainability of our code. This is why we've applied those patches to
|
||||||
|
the main library code but not to unit tests and examples. Those still do not compile on MSVC.
|
||||||
|
|
||||||
|
Here is a list of the most important MSVC bugs:
|
||||||
|
|
||||||
|
- [Discrepancy in Behavior of operator*= and operator* for Multiplying int and double at compile time](https://developercommunity.visualstudio.com/t/Discrepancy-in-Behavior-of-operator-an/10732445)
|
||||||
|
- [Syntax error when using non-type template parameters in templated class member function](https://developercommunity.visualstudio.com/t/Syntax-error-when-using-non-type-templat/10729428)
|
||||||
|
- [Type always preferred over value when using qualified identifiers](https://developercommunity.visualstudio.com/t/Type-always-prefered-over-value-when-usi/10729382)
|
||||||
|
|
||||||
|
Please upvote them so they get a higher fixing priority at Microsoft.
|
||||||
|
|
||||||
!!! important
|
!!! important
|
||||||
|
|
||||||
Enabling/disabling features listed above may influence the API of the library and the ABI of
|
Enabling/disabling features listed above may influence the API of the library and the ABI of
|
||||||
|
@@ -56,7 +56,7 @@ Here is a small example of operations possible on scalar quantities:
|
|||||||
static_assert(1000 / (1 * s) == 1 * kHz);
|
static_assert(1000 / (1 * s) == 1 * kHz);
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/8acPeq743)"
|
!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/fT1r4sohs)"
|
||||||
|
|
||||||
|
|
||||||
This library requires some C++20 features ([concepts and constraints](https://en.cppreference.com/w/cpp/language/constraints),
|
This library requires some C++20 features ([concepts and constraints](https://en.cppreference.com/w/cpp/language/constraints),
|
||||||
|
@@ -2,12 +2,11 @@
|
|||||||
tags:
|
tags:
|
||||||
- CGS System
|
- CGS System
|
||||||
- International System
|
- International System
|
||||||
- Text Formatting
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# `avg_speed`
|
# `avg_speed`
|
||||||
|
|
||||||
!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/TnqGa4sdn)"
|
!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/v9c5T6bc4)"
|
||||||
|
|
||||||
Let's continue the previous example. This time, our purpose will not be to showcase as many
|
Let's continue the previous example. This time, our purpose will not be to showcase as many
|
||||||
library features as possible, but we will scope on different interfaces one can provide
|
library features as possible, but we will scope on different interfaces one can provide
|
||||||
@@ -18,28 +17,28 @@ First, we either import a module or include all the necessary header files and i
|
|||||||
the identifiers from the `mp_units` namespace:
|
the identifiers from the `mp_units` namespace:
|
||||||
|
|
||||||
```cpp title="avg_speed.cpp" linenums="1"
|
```cpp title="avg_speed.cpp" linenums="1"
|
||||||
--8<-- "example/avg_speed.cpp:28:42"
|
--8<-- "example/avg_speed.cpp:28:46"
|
||||||
```
|
```
|
||||||
|
|
||||||
Next, we define two functions calculating average speed based on quantities of fixed units
|
Next, we define two functions calculating average speed based on quantities of fixed units
|
||||||
and integral and floating-point representation types, respectively, and a third function
|
and integral and floating-point representation types, respectively, and a third function
|
||||||
that we introduced in the [previous example](hello_units.md):
|
that we introduced in the [previous example](hello_units.md):
|
||||||
|
|
||||||
```cpp title="avg_speed.cpp" linenums="16"
|
```cpp title="avg_speed.cpp" linenums="20"
|
||||||
--8<-- "example/avg_speed.cpp:44:58"
|
--8<-- "example/avg_speed.cpp:48:62"
|
||||||
```
|
```
|
||||||
|
|
||||||
We also added a simple utility to print our results:
|
We also added a simple utility to print our results:
|
||||||
|
|
||||||
```cpp title="avg_speed.cpp" linenums="31"
|
```cpp title="avg_speed.cpp" linenums="35"
|
||||||
--8<-- "example/avg_speed.cpp:60:66"
|
--8<-- "example/avg_speed.cpp:64:70"
|
||||||
```
|
```
|
||||||
|
|
||||||
Now, let's analyze how those three utility functions behave with different sets of arguments.
|
Now, let's analyze how those three utility functions behave with different sets of arguments.
|
||||||
First, we are going to use quantities of SI units and integral representation:
|
First, we are going to use quantities of SI units and integral representation:
|
||||||
|
|
||||||
```cpp title="avg_speed.cpp" linenums="38"
|
```cpp title="avg_speed.cpp" linenums="42"
|
||||||
--8<-- "example/avg_speed.cpp:68:82"
|
--8<-- "example/avg_speed.cpp:72:86"
|
||||||
```
|
```
|
||||||
|
|
||||||
The above provides the following output:
|
The above provides the following output:
|
||||||
@@ -61,8 +60,8 @@ representation types (the resulting speed is `108 km/h`).
|
|||||||
The second scenario is really similar to the previous one, but this time, function arguments
|
The second scenario is really similar to the previous one, but this time, function arguments
|
||||||
have floating-point representation types:
|
have floating-point representation types:
|
||||||
|
|
||||||
```cpp title="avg_speed.cpp" linenums="53"
|
```cpp title="avg_speed.cpp" linenums="57"
|
||||||
--8<-- "example/avg_speed.cpp:84:95"
|
--8<-- "example/avg_speed.cpp:88:99"
|
||||||
```
|
```
|
||||||
|
|
||||||
Conversion from floating-point to integral representation types is
|
Conversion from floating-point to integral representation types is
|
||||||
@@ -82,8 +81,8 @@ Average speed of a car that makes 220 km in 2 h is 110 km/h.
|
|||||||
Next, let's do the same for integral and floating-point representations, but this time
|
Next, let's do the same for integral and floating-point representations, but this time
|
||||||
using international mile:
|
using international mile:
|
||||||
|
|
||||||
```cpp title="avg_speed.cpp" linenums="65"
|
```cpp title="avg_speed.cpp" linenums="69"
|
||||||
--8<-- "example/avg_speed.cpp:97:129"
|
--8<-- "example/avg_speed.cpp:101:132"
|
||||||
```
|
```
|
||||||
|
|
||||||
One important difference here is the fact that as it is not possible to make a lossless conversion
|
One important difference here is the fact that as it is not possible to make a lossless conversion
|
||||||
@@ -108,8 +107,8 @@ Please note how the first and third results get truncated using integral represe
|
|||||||
|
|
||||||
In the end, we repeat the scenario for CGS units:
|
In the end, we repeat the scenario for CGS units:
|
||||||
|
|
||||||
```cpp title="avg_speed.cpp" linenums="97"
|
```cpp title="avg_speed.cpp" linenums="101"
|
||||||
--8<-- "example/avg_speed.cpp:131:161"
|
--8<-- "example/avg_speed.cpp:134:165"
|
||||||
```
|
```
|
||||||
|
|
||||||
Again, we observe `value_cast` being used in the same places and consistent truncation errors
|
Again, we observe `value_cast` being used in the same places and consistent truncation errors
|
||||||
@@ -129,6 +128,6 @@ Average speed of a car that makes 2.2e+07 cm in 7200 s is 110 km/h.
|
|||||||
|
|
||||||
The example file ends with a simple `main()` function:
|
The example file ends with a simple `main()` function:
|
||||||
|
|
||||||
```cpp title="avg_speed.cpp" linenums="128"
|
```cpp title="avg_speed.cpp" linenums="133"
|
||||||
--8<-- "example/avg_speed.cpp:163:"
|
--8<-- "example/avg_speed.cpp:167:"
|
||||||
```
|
```
|
||||||
|
@@ -6,32 +6,32 @@ tags:
|
|||||||
|
|
||||||
# `hello_units`
|
# `hello_units`
|
||||||
|
|
||||||
!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/bT4GGPbef)"
|
!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/MYn5qjPzh)"
|
||||||
|
|
||||||
This is a really simple example showcasing the features of the **mp-units** library.
|
This is a really simple example showcasing the features of the **mp-units** library.
|
||||||
|
|
||||||
First, we either import the `mp_units` module or include the headers for:
|
First, we either import the `mp_units` module or include the headers for:
|
||||||
|
|
||||||
- an International System of Quantities (ISQ)
|
- an International System of Quantities (ISQ),
|
||||||
- an International System of units (SI)
|
- an International System of units (SI),
|
||||||
- units derived from the International Yard and Pound
|
- units derived from the International Yard and Pound,
|
||||||
- text formatting and stream output support
|
- text formatting and stream output support.
|
||||||
|
|
||||||
```cpp title="hello_units.cpp" linenums="1"
|
```cpp title="hello_units.cpp" linenums="1"
|
||||||
--8<-- "example/hello_units.cpp:28:41"
|
--8<-- "example/hello_units.cpp:28:45"
|
||||||
```
|
```
|
||||||
|
|
||||||
Also, to shorten the definitions, we "import" all the symbols from the `mp_units` namespace.
|
Also, to shorten the definitions, we "import" all the symbols from the `mp_units` namespace.
|
||||||
|
|
||||||
```cpp title="hello_units.cpp" linenums="14"
|
```cpp title="hello_units.cpp" linenums="18"
|
||||||
--8<-- "example/hello_units.cpp:42:43"
|
--8<-- "example/hello_units.cpp:46:47"
|
||||||
```
|
```
|
||||||
|
|
||||||
Next, we define a simple function that calculates the average speed based on the provided
|
Next, we define a simple function that calculates the average speed based on the provided
|
||||||
arguments of length and time:
|
arguments of length and time:
|
||||||
|
|
||||||
```cpp title="hello_units.cpp" linenums="15"
|
```cpp title="hello_units.cpp" linenums="19"
|
||||||
--8<-- "example/hello_units.cpp:44:47"
|
--8<-- "example/hello_units.cpp:48:51"
|
||||||
```
|
```
|
||||||
|
|
||||||
The above function template takes any quantities implicitly convertible to `isq::length`
|
The above function template takes any quantities implicitly convertible to `isq::length`
|
||||||
@@ -45,37 +45,37 @@ that its quantity type is implicitly convertible to `isq::speed`.
|
|||||||
type is beneficial for users of such a function as it provides more information
|
type is beneficial for users of such a function as it provides more information
|
||||||
of what to expect from a function than just using `auto`.
|
of what to expect from a function than just using `auto`.
|
||||||
|
|
||||||
```cpp title="hello_units.cpp" linenums="19"
|
```cpp title="hello_units.cpp" linenums="23"
|
||||||
--8<-- "example/hello_units.cpp:49:52"
|
--8<-- "example/hello_units.cpp:53:56"
|
||||||
```
|
```
|
||||||
|
|
||||||
The above lines explicitly opt into using unit symbols from two systems of units.
|
The above lines explicitly opt into using unit symbols from two systems of units.
|
||||||
As this introduces a lot of short identifiers into the current scope, it is not done
|
As this introduces a lot of short identifiers into the current scope, it is not done
|
||||||
implicitly while including a header file.
|
implicitly while including a header file.
|
||||||
|
|
||||||
```cpp title="hello_units.cpp" linenums="23"
|
```cpp title="hello_units.cpp" linenums="27"
|
||||||
--8<-- "example/hello_units.cpp:54:60"
|
--8<-- "example/hello_units.cpp:58:64"
|
||||||
```
|
```
|
||||||
|
|
||||||
- Lines `23` & `24` create a quantity of kind `isq::length / isq::time` with the numbers
|
- Lines `27` & `28` create a quantity of kind `isq::length / isq::time` with the numbers
|
||||||
and units provided. Such quantities can be converted or assigned to any other quantity
|
and units provided. Such quantities can be converted or assigned to any other quantity
|
||||||
with a matching kind.
|
with a matching kind.
|
||||||
- Line `25` calls our function template with quantities of kind `isq::length` and
|
- Line `29` calls our function template with quantities of kind `isq::length` and
|
||||||
`isq::time` and number and units provided.
|
`isq::time` and number and units provided.
|
||||||
- Line `26` explicitly provides quantity types of the quantities passed to a function template.
|
- Line `30` explicitly provides quantity types of the quantities passed to a function template.
|
||||||
This time, those will not be quantity kinds anymore and will have
|
This time, those will not be quantity kinds anymore and will have
|
||||||
[more restrictive conversion rules](../framework_basics/simple_and_typed_quantities.md#quantity_cast-to-force-unsafe-conversions).
|
[more restrictive conversion rules](../framework_basics/simple_and_typed_quantities.md#quantity_cast-to-force-unsafe-conversions).
|
||||||
- Line `27` changes the unit of a quantity `v3` to `m / s` in a
|
- Line `31` changes the unit of a quantity `v3` to `m / s` in a
|
||||||
[value-preserving way](../framework_basics/value_conversions.md#value-preserving-conversions)
|
[value-preserving way](../framework_basics/value_conversions.md#value-preserving-conversions)
|
||||||
(floating-point representations are considered to be value-preserving).
|
(floating-point representations are considered to be value-preserving).
|
||||||
- Line `28` does a similar operation, but this time, it would also succeed for
|
- Line `32` does a similar operation, but this time, it would also succeed for
|
||||||
[value-truncating cases](../framework_basics/value_conversions.md#value-truncating-conversions)
|
[value-truncating cases](../framework_basics/value_conversions.md#value-truncating-conversions)
|
||||||
(if that was the case).
|
(if that was the case).
|
||||||
- Line `29` does a [value-truncating conversion](../framework_basics/value_conversions.md#value-truncating-conversions)
|
- Line `33` does a [value-truncating conversion](../framework_basics/value_conversions.md#value-truncating-conversions)
|
||||||
of changing the underlying representation type from `double` to `int`.
|
of changing the underlying representation type from `double` to `int`.
|
||||||
|
|
||||||
```cpp title="hello_units.cpp" linenums="30"
|
```cpp title="hello_units.cpp" linenums="34"
|
||||||
--8<-- "example/hello_units.cpp:62"
|
--8<-- "example/hello_units.cpp:66"
|
||||||
```
|
```
|
||||||
|
|
||||||
The above presents [various ways to print a quantity](../framework_basics/text_output.md).
|
The above presents [various ways to print a quantity](../framework_basics/text_output.md).
|
||||||
@@ -86,3 +86,6 @@ Both stream insertion operations and `std::format` facilities are supported.
|
|||||||
`MP_UNITS_STD_FMT` is used for compatibility reasons. If a specific compiler
|
`MP_UNITS_STD_FMT` is used for compatibility reasons. If a specific compiler
|
||||||
does not support `std::format` or a user prefers to use the `{fmt}` library, this macro
|
does not support `std::format` or a user prefers to use the `{fmt}` library, this macro
|
||||||
will resolve to `fmt` namespace. Otherwise, the `std` namespace will be used.
|
will resolve to `fmt` namespace. Otherwise, the `std` namespace will be used.
|
||||||
|
|
||||||
|
More about it can be found in the [Wide Compatibility](../use_cases/wide_compatibility.md#mp_units_std_fmt)
|
||||||
|
chapter.
|
||||||
|
72
docs/users_guide/examples/hw_voltage.md
Normal file
72
docs/users_guide/examples/hw_voltage.md
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Affine Space
|
||||||
|
- Embedded
|
||||||
|
- Text Formatting
|
||||||
|
---
|
||||||
|
|
||||||
|
# `hw_voltage`
|
||||||
|
|
||||||
|
!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/jjod7hvsd)"
|
||||||
|
|
||||||
|
As it was stated in [The Affine Space](../framework_basics/the_affine_space.md) chapter,
|
||||||
|
every measurement can (and probably should) be modelled as a `quantity_point`. This is
|
||||||
|
a perfect example of such a use case.
|
||||||
|
|
||||||
|
This example implements a simplified scenario of measuring voltage read from hardware through
|
||||||
|
a mapped 16-bits register. The actual voltage range of [-10 V, 10 V] is mapped to [-32767, 32767]
|
||||||
|
on hardware. Translation of the value requires not only scaling of the value but also applying
|
||||||
|
of an offset.
|
||||||
|
|
||||||
|
First we include all the dependencies:
|
||||||
|
|
||||||
|
```cpp title="hw_voltage.cpp" linenums="1"
|
||||||
|
--8<-- "example/hw_voltage.cpp:28:44"
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, we specify the real measurement voltage range to be in the range of [-10, 10]:
|
||||||
|
|
||||||
|
```cpp title="hw_voltage.cpp" linenums="18"
|
||||||
|
--8<-- "example/hw_voltage.cpp:46:49"
|
||||||
|
```
|
||||||
|
|
||||||
|
and provide a storage type and special values for the hardware representation:
|
||||||
|
|
||||||
|
```cpp title="hw_voltage.cpp" linenums="22"
|
||||||
|
--8<-- "example/hw_voltage.cpp:51:57"
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, we define a quantity point origin, an offset unit that scales the value and uses this
|
||||||
|
origin to offset the zero of the sale, and a dedicated quantity point alias using those:
|
||||||
|
|
||||||
|
```cpp title="hw_voltage.cpp" linenums="29"
|
||||||
|
--8<-- "example/hw_voltage.cpp:61:67"
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, when everything is ready, we can simulate mapping of our hardware register, and provide
|
||||||
|
a helper function that will read the value and construct a quantity point from the obtained copy:
|
||||||
|
|
||||||
|
```cpp title="hw_voltage.cpp" linenums="36"
|
||||||
|
--8<-- "example/hw_voltage.cpp:70:78"
|
||||||
|
```
|
||||||
|
|
||||||
|
We also provide a simple print helper for our quantity points:
|
||||||
|
|
||||||
|
```cpp title="hw_voltage.cpp" linenums="45"
|
||||||
|
--8<-- "example/hw_voltage.cpp:80:84"
|
||||||
|
```
|
||||||
|
|
||||||
|
In the main function we simulate setting of 3 values by our hardware. Each of them is read
|
||||||
|
and printed in the voltage unit used on the hardware as well as in the standard SI unit:
|
||||||
|
|
||||||
|
```cpp title="hw_voltage.cpp" linenums="50"
|
||||||
|
--8<-- "example/hw_voltage.cpp:86:"
|
||||||
|
```
|
||||||
|
|
||||||
|
The above program results with the following text output:
|
||||||
|
|
||||||
|
```text
|
||||||
|
0 hwV (-10 V)
|
||||||
|
32767 hwV ( 0 V)
|
||||||
|
65534 hwV ( 10 V)
|
||||||
|
```
|
@@ -6,7 +6,7 @@ tags:
|
|||||||
|
|
||||||
# `si_constants`
|
# `si_constants`
|
||||||
|
|
||||||
!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/MevcK8vYT)"
|
!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/eGqbW5d8K)"
|
||||||
|
|
||||||
The next example presents all the seven defining constants of the SI system. We can observe
|
The next example presents all the seven defining constants of the SI system. We can observe
|
||||||
how [Faster-than-lightspeed Constants](../framework_basics/faster_than_lightspeed_constants.md)
|
how [Faster-than-lightspeed Constants](../framework_basics/faster_than_lightspeed_constants.md)
|
||||||
@@ -22,15 +22,20 @@ the simplicity of this example, we
|
|||||||
to be able to express vector quantities with simple scalar types.
|
to be able to express vector quantities with simple scalar types.
|
||||||
|
|
||||||
```cpp title="si_constants.cpp" linenums="14"
|
```cpp title="si_constants.cpp" linenums="14"
|
||||||
--8<-- "example/si_constants.cpp:42:"
|
--8<-- "example/si_constants.cpp:42:44"
|
||||||
```
|
```
|
||||||
|
|
||||||
The main part of the example prints all of the SI-defining constants. While analyzing the output of
|
The main part of the example prints all of the SI-defining constants:
|
||||||
this program (provided below), we can easily notice that a direct printing of the quantity provides
|
|
||||||
just a value `1` with a proper constant symbol. This is the main power of the
|
```cpp title="si_constants.cpp" linenums="17"
|
||||||
[Faster-than-lightspeed Constants](../framework_basics/faster_than_lightspeed_constants.md) feature.
|
--8<-- "example/si_constants.cpp:45:"
|
||||||
Only after we explicitly convert the unit of a quantity to proper SI units we get an actual numeric
|
```
|
||||||
value of the constant.
|
|
||||||
|
While analyzing the output of this program (provided below), we can easily notice that a direct
|
||||||
|
printing of the quantity provides just a value `1` with a proper constant symbol. This is the main
|
||||||
|
power of the [Faster-than-lightspeed Constants](../framework_basics/faster_than_lightspeed_constants.md)
|
||||||
|
feature. Only after we explicitly convert the unit of a quantity to proper SI units we get an
|
||||||
|
actual numeric value of the constant.
|
||||||
|
|
||||||
```text
|
```text
|
||||||
The seven defining constants of the SI and the seven corresponding units they define:
|
The seven defining constants of the SI and the seven corresponding units they define:
|
||||||
|
@@ -190,3 +190,70 @@ inline constexpr struct mag_pi final : magnitude<std::numbers::pi_v<long double>
|
|||||||
```cpp
|
```cpp
|
||||||
inline constexpr struct degree final : named_unit<{u8"°", "deg"}, mag_pi / mag<180> * si::radian> {} degree;
|
inline constexpr struct degree final : named_unit<{u8"°", "deg"}, mag_pi / mag<180> * si::radian> {} degree;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Unit symbols
|
||||||
|
|
||||||
|
Units are available via their full names or through their short symbols.
|
||||||
|
To use a long version, it is enough to type:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
quantity q1 = 42 * si::metre / si::second;
|
||||||
|
quantity q2 = 42 * si::kilo<si::metre> / si::hour;
|
||||||
|
```
|
||||||
|
|
||||||
|
To simplify how we spell it a short, user-friendly symbols are provided in a dedicated
|
||||||
|
subnamespace in systems definitions:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace si::unit_symbols {
|
||||||
|
|
||||||
|
constexpr auto m = si::metre;
|
||||||
|
constexpr auto km = si::kilo<si::metre>;
|
||||||
|
constexpr auto s = si::second;
|
||||||
|
constexpr auto h = si::hour;
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Unit symbols introduce a lot of short identifiers into the current namespace. This is why they
|
||||||
|
are opt-in. A user has to explicitly "import" them from a dedicated `unit_symbols` namespace:
|
||||||
|
|
||||||
|
=== "using-declaration"
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
using namespace si::unit_symbols;
|
||||||
|
|
||||||
|
quantity q1 = 42 * m / s;
|
||||||
|
quantity q2 = 42 * km / h;
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "using-directive"
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
using si::unit_symbols::m;
|
||||||
|
using si::unit_symbols::km;
|
||||||
|
using si::unit_symbols::s;
|
||||||
|
using si::unit_symbols::h;
|
||||||
|
|
||||||
|
quantity q1 = 42 * m / s;
|
||||||
|
quantity q2 = 42 * km / h;
|
||||||
|
```
|
||||||
|
|
||||||
|
We also provide alternative object identifiers using Unicode characters in their names for most
|
||||||
|
unit symbols. The code using Unicode looks nicer, but it is harder to type on the keyboard.
|
||||||
|
This is why we provide both versions of identifiers for such units.
|
||||||
|
|
||||||
|
=== "ASCII only"
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
quantity resistance = 60 * kohm;
|
||||||
|
quantity capacitance = 100 * uF;
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "With Unicode glyphs"
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
quantity resistance = 60 * kΩ;
|
||||||
|
quantity capacitance = 100 * µF;
|
||||||
|
```
|
||||||
|
@@ -182,11 +182,11 @@ which may well be outside the range of one or both quantity types.
|
|||||||
The table below provides all the value conversions functions that may be run on `x` being the
|
The table below provides all the value conversions functions that may be run on `x` being the
|
||||||
instance of either `quantity` or `quantity_point`:
|
instance of either `quantity` or `quantity_point`:
|
||||||
|
|
||||||
| Forcing | Representation | Unit | Member function | Conversion function |
|
| Forcing | Representation | Unit | Member function | Non-member function |
|
||||||
|:-------:|:--------------:|:----:|--------------------|-----------------------|
|
|:-------:|:--------------:|:----:|--------------------|------------------------------------------------|
|
||||||
| No | Same | `u` | `x.in(u)` | |
|
| No | Same | `u` | `x.in(u)` | |
|
||||||
| No | `T` | Same | `x.in<T>()` | |
|
| No | `T` | Same | `x.in<T>()` | |
|
||||||
| No | `T` | `u` | `x.in<T>(u)` | |
|
| No | `T` | `u` | `x.in<T>(u)` | |
|
||||||
| Yes | Same | `u` | `x.force_in(u)` | `value_cast<u>(x)` |
|
| Yes | Same | `u` | `x.force_in(u)` | `value_cast<u>(x)` |
|
||||||
| Yes | `T` | Same | `x.force_in<T>()` | `value_cast<T>(x)` |
|
| Yes | `T` | Same | `x.force_in<T>()` | `value_cast<T>(x)` |
|
||||||
| Yes | `T` | `u` | `x.force_in<T>(u)` | `value_cast<u, T>(x)` |
|
| Yes | `T` | `u` | `x.force_in<T>(u)` | `value_cast<u, T>(x)` or `value_cast<T, u>(x)` |
|
||||||
|
@@ -57,6 +57,7 @@ add_example(currency)
|
|||||||
add_example(foot_pound_second)
|
add_example(foot_pound_second)
|
||||||
add_example(glide_computer glide_computer_lib)
|
add_example(glide_computer glide_computer_lib)
|
||||||
add_example(hello_units)
|
add_example(hello_units)
|
||||||
|
add_example(hw_voltage)
|
||||||
add_example(measurement)
|
add_example(measurement)
|
||||||
add_example(si_constants)
|
add_example(si_constants)
|
||||||
add_example(spectroscopy_units)
|
add_example(spectroscopy_units)
|
||||||
|
99
example/hw_voltage.cpp
Normal file
99
example/hw_voltage.cpp
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
// The MIT License (MIT)
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 Mateusz Pusz
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
// !!! Before you commit any changes to this file please make sure to check if it !!!
|
||||||
|
// !!! renders correctly in the documentation "Examples" section. !!!
|
||||||
|
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
|
||||||
|
#include <mp-units/compat_macros.h>
|
||||||
|
#include <mp-units/ext/format.h>
|
||||||
|
#ifdef MP_UNITS_IMPORT_STD
|
||||||
|
import std;
|
||||||
|
#else
|
||||||
|
#include <iostream>
|
||||||
|
#include <optional>
|
||||||
|
#endif
|
||||||
|
#ifdef MP_UNITS_MODULES
|
||||||
|
import mp_units;
|
||||||
|
#else
|
||||||
|
#include <mp-units/format.h>
|
||||||
|
#include <mp-units/systems/isq.h>
|
||||||
|
#include <mp-units/systems/si.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace mp_units;
|
||||||
|
|
||||||
|
// real voltage range
|
||||||
|
inline constexpr int min_voltage = -10;
|
||||||
|
inline constexpr int max_voltage = 10;
|
||||||
|
inline constexpr int voltage_range = max_voltage - min_voltage;
|
||||||
|
|
||||||
|
// hardware encoding of voltage
|
||||||
|
using voltage_hw_t = std::uint16_t;
|
||||||
|
inline constexpr voltage_hw_t voltage_hw_error = std::numeric_limits<voltage_hw_t>::max();
|
||||||
|
inline constexpr voltage_hw_t voltage_hw_min = 0;
|
||||||
|
inline constexpr voltage_hw_t voltage_hw_max = voltage_hw_error - 1;
|
||||||
|
inline constexpr voltage_hw_t voltage_hw_range = voltage_hw_max - voltage_hw_min;
|
||||||
|
inline constexpr voltage_hw_t voltage_hw_zero = voltage_hw_range / 2;
|
||||||
|
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
inline constexpr struct hw_voltage_origin final :
|
||||||
|
relative_point_origin<absolute<si::volt>(min_voltage)> {} hw_voltage_origin;
|
||||||
|
|
||||||
|
inline constexpr struct hw_voltage_unit final :
|
||||||
|
named_unit<"hwV", mag_ratio<voltage_range, voltage_hw_range> * si::volt, hw_voltage_origin> {} hw_voltage_unit;
|
||||||
|
|
||||||
|
using hw_voltage_quantity_point = quantity_point<hw_voltage_unit, hw_voltage_origin, voltage_hw_t>;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
// mapped HW register
|
||||||
|
volatile voltage_hw_t hw_voltage_value;
|
||||||
|
|
||||||
|
std::optional<hw_voltage_quantity_point> read_hw_voltage()
|
||||||
|
{
|
||||||
|
voltage_hw_t local_copy = hw_voltage_value;
|
||||||
|
if (local_copy == voltage_hw_error) return std::nullopt;
|
||||||
|
return absolute<hw_voltage_unit>(local_copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(QuantityPoint auto qp)
|
||||||
|
{
|
||||||
|
std::cout << MP_UNITS_STD_FMT::format("{:10} ({:5})", qp.quantity_from_zero(),
|
||||||
|
value_cast<double, si::volt>(qp).quantity_from_zero());
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// simulate reading of 3 values from the hardware
|
||||||
|
hw_voltage_value = voltage_hw_min;
|
||||||
|
quantity_point qp1 = read_hw_voltage().value();
|
||||||
|
hw_voltage_value = voltage_hw_zero;
|
||||||
|
quantity_point qp2 = read_hw_voltage().value();
|
||||||
|
hw_voltage_value = voltage_hw_max;
|
||||||
|
quantity_point qp3 = read_hw_voltage().value();
|
||||||
|
|
||||||
|
print(qp1);
|
||||||
|
print(qp2);
|
||||||
|
print(qp3);
|
||||||
|
}
|
@@ -175,6 +175,7 @@ nav:
|
|||||||
- hello_units: users_guide/examples/hello_units.md
|
- hello_units: users_guide/examples/hello_units.md
|
||||||
- avg_speed: users_guide/examples/avg_speed.md
|
- avg_speed: users_guide/examples/avg_speed.md
|
||||||
- si_constants: users_guide/examples/si_constants.md
|
- si_constants: users_guide/examples/si_constants.md
|
||||||
|
- hw_voltage: users_guide/examples/hw_voltage.md
|
||||||
- Appendix:
|
- Appendix:
|
||||||
- Glossary: appendix/glossary.md
|
- Glossary: appendix/glossary.md
|
||||||
- References: appendix/references.md
|
- References: appendix/references.md
|
||||||
|
@@ -380,8 +380,9 @@ template<typename T>
|
|||||||
|
|
||||||
// Always use `long double` for intermediate computations. We don't ever expect people to be
|
// Always use `long double` for intermediate computations. We don't ever expect people to be
|
||||||
// calling this at runtime, so we want maximum accuracy.
|
// calling this at runtime, so we want maximum accuracy.
|
||||||
|
long double xld = static_cast<long double>(x);
|
||||||
long double lo = 1.0;
|
long double lo = 1.0;
|
||||||
long double hi = static_cast<long double>(x);
|
long double hi = xld;
|
||||||
|
|
||||||
// Do a binary search to find the closest value such that `checked_int_pow` recovers the input.
|
// Do a binary search to find the closest value such that `checked_int_pow` recovers the input.
|
||||||
//
|
//
|
||||||
@@ -398,7 +399,7 @@ template<typename T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Early return if we get lucky with an exact answer.
|
// Early return if we get lucky with an exact answer.
|
||||||
if (result.value() == x) {
|
if (result.value() == xld) {
|
||||||
return static_cast<T>(mid);
|
return static_cast<T>(mid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -408,7 +409,7 @@ template<typename T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Preserve the invariant that `checked_int_pow(lo, n) < x < checked_int_pow(hi, n)`.
|
// Preserve the invariant that `checked_int_pow(lo, n) < x < checked_int_pow(hi, n)`.
|
||||||
if (result.value() < x) {
|
if (result.value() < xld) {
|
||||||
lo = mid;
|
lo = mid;
|
||||||
} else {
|
} else {
|
||||||
hi = mid;
|
hi = mid;
|
||||||
@@ -416,8 +417,8 @@ template<typename T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pick whichever one gets closer to the target.
|
// Pick whichever one gets closer to the target.
|
||||||
const auto lo_diff = x - checked_int_pow(lo, n).value();
|
const auto lo_diff = xld - checked_int_pow(lo, n).value();
|
||||||
const auto hi_diff = checked_int_pow(hi, n).value() - x;
|
const auto hi_diff = checked_int_pow(hi, n).value() - xld;
|
||||||
return static_cast<T>(lo_diff < hi_diff ? lo : hi);
|
return static_cast<T>(lo_diff < hi_diff ? lo : hi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) 2018 Mateusz Pusz
|
// Copyright (c) 2018 Mateusz Pusz
|
||||||
|
@@ -88,6 +88,14 @@ template<Unit auto ToU, Representation ToRep, typename FwdQ, Quantity Q = std::r
|
|||||||
return detail::sudo_cast<quantity<detail::make_reference(Q::quantity_spec, ToU), ToRep>>(std::forward<FwdQ>(q));
|
return detail::sudo_cast<quantity<detail::make_reference(Q::quantity_spec, ToU), ToRep>>(std::forward<FwdQ>(q));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<Representation ToRep, Unit auto ToU, 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)
|
||||||
|
{
|
||||||
|
return value_cast<ToU, ToRep>(std::forward<FwdQ>(q));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Explicit cast of a quantity's representation
|
* @brief Explicit cast of a quantity's representation
|
||||||
@@ -168,6 +176,14 @@ template<Unit auto ToU, Representation ToRep, typename FwdQP, QuantityPoint QP =
|
|||||||
QP::point_origin};
|
QP::point_origin};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<Representation ToRep, Unit auto ToU, 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 value_cast<ToU, ToRep>(std::forward<FwdQP>(qp));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Explicit cast of a quantity point's representation
|
* @brief Explicit cast of a quantity point's representation
|
||||||
*
|
*
|
||||||
|
@@ -35,7 +35,7 @@ namespace mp_units {
|
|||||||
|
|
||||||
template<Quantity T>
|
template<Quantity T>
|
||||||
struct AlmostEqualsMatcher : Catch::Matchers::MatcherGenericBase {
|
struct AlmostEqualsMatcher : Catch::Matchers::MatcherGenericBase {
|
||||||
explicit AlmostEqualsMatcher(const T& target) : target_{target} {}
|
explicit AlmostEqualsMatcher(const T& target, int n_eps) : target_{target}, n_eps_{n_eps} {}
|
||||||
|
|
||||||
template<std::convertible_to<T> U>
|
template<std::convertible_to<T> U>
|
||||||
requires std::same_as<typename T::rep, typename U::rep>
|
requires std::same_as<typename T::rep, typename U::rep>
|
||||||
@@ -48,7 +48,7 @@ struct AlmostEqualsMatcher : Catch::Matchers::MatcherGenericBase {
|
|||||||
const auto y = common(other).numerical_value_in(common::unit);
|
const auto y = common(other).numerical_value_in(common::unit);
|
||||||
if constexpr (treat_as_floating_point<rep>) {
|
if constexpr (treat_as_floating_point<rep>) {
|
||||||
const auto maxXYOne = std::max({rep{1}, abs(x), abs(y)});
|
const auto maxXYOne = std::max({rep{1}, abs(x), abs(y)});
|
||||||
return abs(x - y) <= std::numeric_limits<rep>::epsilon() * maxXYOne;
|
return abs(x - y) <= (n_eps_ * std::numeric_limits<rep>::epsilon()) * maxXYOne;
|
||||||
} else {
|
} else {
|
||||||
if (x >= 0) {
|
if (x >= 0) {
|
||||||
return x - 1 <= y && y - 1 <= x;
|
return x - 1 <= y && y - 1 <= x;
|
||||||
@@ -71,12 +71,13 @@ struct AlmostEqualsMatcher : Catch::Matchers::MatcherGenericBase {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
const T& target_;
|
const T& target_;
|
||||||
|
int n_eps_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<Quantity T>
|
template<Quantity T>
|
||||||
AlmostEqualsMatcher<T> AlmostEquals(const T& target)
|
AlmostEqualsMatcher<T> AlmostEquals(const T& target, int n_eps = 1)
|
||||||
{
|
{
|
||||||
return AlmostEqualsMatcher<T>{target};
|
return AlmostEqualsMatcher<T>{target, n_eps};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -448,7 +448,7 @@ TEST_CASE("Angle trigonometric functions", "[trig][angle]")
|
|||||||
|
|
||||||
REQUIRE_THAT(sin(0 * angle[grad]), AlmostEquals(0. * one));
|
REQUIRE_THAT(sin(0 * angle[grad]), AlmostEquals(0. * one));
|
||||||
REQUIRE_THAT(sin(100 * angle[grad]), AlmostEquals(1. * one));
|
REQUIRE_THAT(sin(100 * angle[grad]), AlmostEquals(1. * one));
|
||||||
REQUIRE_THAT(sin(200 * angle[grad]), AlmostEquals(0. * one));
|
REQUIRE_THAT(sin(200 * angle[grad]), AlmostEquals(0. * one, 2));
|
||||||
REQUIRE_THAT(sin(300 * angle[grad]), AlmostEquals(-1. * one));
|
REQUIRE_THAT(sin(300 * angle[grad]), AlmostEquals(-1. * one));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,7 +475,7 @@ TEST_CASE("Angle trigonometric functions", "[trig][angle]")
|
|||||||
REQUIRE_THAT(tan(0 * angle[grad]), AlmostEquals(0. * one));
|
REQUIRE_THAT(tan(0 * angle[grad]), AlmostEquals(0. * one));
|
||||||
REQUIRE_THAT(tan(50 * angle[grad]), AlmostEquals(1. * one));
|
REQUIRE_THAT(tan(50 * angle[grad]), AlmostEquals(1. * one));
|
||||||
REQUIRE_THAT(tan(150 * angle[grad]), AlmostEquals(-1. * one));
|
REQUIRE_THAT(tan(150 * angle[grad]), AlmostEquals(-1. * one));
|
||||||
REQUIRE_THAT(tan(200 * angle[grad]), AlmostEquals(0. * one));
|
REQUIRE_THAT(tan(200 * angle[grad]), AlmostEquals(0. * one, 2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1725,6 +1725,7 @@ constexpr quantity_point lvalue_qp{2 * km};
|
|||||||
static_assert(value_cast<m>(lvalue_qp).quantity_from_zero().numerical_value_in(m) == 2000);
|
static_assert(value_cast<m>(lvalue_qp).quantity_from_zero().numerical_value_in(m) == 2000);
|
||||||
static_assert(value_cast<float>(lvalue_qp).quantity_from_zero().numerical_value_in(km) == 2.f);
|
static_assert(value_cast<float>(lvalue_qp).quantity_from_zero().numerical_value_in(km) == 2.f);
|
||||||
static_assert(value_cast<m, float>(lvalue_qp).quantity_from_zero().numerical_value_in(m) == 2000.f);
|
static_assert(value_cast<m, float>(lvalue_qp).quantity_from_zero().numerical_value_in(m) == 2000.f);
|
||||||
|
static_assert(value_cast<float, m>(lvalue_qp).quantity_from_zero().numerical_value_in(m) == 2000.f);
|
||||||
} // namespace lvalue_tests
|
} // namespace lvalue_tests
|
||||||
|
|
||||||
static_assert(value_cast<quantity<km, int>>(quantity_point{2000 * m}).quantity_from_zero().numerical_value_in(km) == 2);
|
static_assert(value_cast<quantity<km, int>>(quantity_point{2000 * m}).quantity_from_zero().numerical_value_in(km) == 2);
|
||||||
|
@@ -37,9 +37,15 @@ import std;
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#if MP_UNITS_HOSTED
|
#if MP_UNITS_HOSTED
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <complex>
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if MP_UNITS_HOSTED
|
||||||
|
template<typename T>
|
||||||
|
constexpr bool mp_units::is_scalar<std::complex<T>> = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
constexpr bool mp_units::is_vector<int> = true;
|
constexpr bool mp_units::is_vector<int> = true;
|
||||||
|
|
||||||
@@ -267,6 +273,12 @@ static_assert(quantity<isq::length[m], int>(2000 * m).force_in(km).numerical_val
|
|||||||
static_assert((15. * m).in(nm).numerical_value_in(m) == 15.);
|
static_assert((15. * m).in(nm).numerical_value_in(m) == 15.);
|
||||||
static_assert((15'000. * nm).in(m).numerical_value_in(nm) == 15'000.);
|
static_assert((15'000. * nm).in(m).numerical_value_in(nm) == 15'000.);
|
||||||
|
|
||||||
|
#if MP_UNITS_HOSTED
|
||||||
|
using namespace std::complex_literals;
|
||||||
|
static_assert(((2. + 1i) * V).in(mV).numerical_value_in(mV) == 2000. + 1000i);
|
||||||
|
static_assert(((2. + 1i) * V).in(mV).numerical_value_in(V) == 2. + 1i);
|
||||||
|
#endif
|
||||||
|
|
||||||
template<template<auto, typename> typename Q>
|
template<template<auto, typename> typename Q>
|
||||||
concept invalid_unit_conversion = requires {
|
concept invalid_unit_conversion = requires {
|
||||||
requires !requires { Q<isq::length[m], int>(2000 * m).in(km); }; // truncating conversion
|
requires !requires { Q<isq::length[m], int>(2000 * m).in(km); }; // truncating conversion
|
||||||
@@ -1014,6 +1026,7 @@ static_assert(value_cast<km / h>(2000.0 * m / (3600.0 * s)).numerical_value_in(k
|
|||||||
|
|
||||||
static_assert(value_cast<int>(1.23 * m).numerical_value_in(m) == 1);
|
static_assert(value_cast<int>(1.23 * m).numerical_value_in(m) == 1);
|
||||||
static_assert(value_cast<km, int>(1.23 * m).numerical_value_in(km) == 0);
|
static_assert(value_cast<km, int>(1.23 * m).numerical_value_in(km) == 0);
|
||||||
|
static_assert(value_cast<int, km>(1.23 * m).numerical_value_in(km) == 0);
|
||||||
|
|
||||||
static_assert((2 * km).force_in(m).numerical_value_in(m) == 2000);
|
static_assert((2 * km).force_in(m).numerical_value_in(m) == 2000);
|
||||||
static_assert((2000 * m).force_in(km).numerical_value_in(km) == 2);
|
static_assert((2000 * m).force_in(km).numerical_value_in(km) == 2);
|
||||||
|
Reference in New Issue
Block a user