mirror of
https://github.com/mpusz/mp-units.git
synced 2025-08-01 03:14:29 +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",
|
||||
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",
|
||||
os: macos-13,
|
||||
@@ -196,8 +210,8 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt install -y g++-${{ matrix.config.compiler.version }}
|
||||
- name: Install Clang
|
||||
if: matrix.config.compiler.type == 'CLANG'
|
||||
- name: Install Clang with apt
|
||||
if: matrix.config.compiler.type == 'CLANG' && matrix.config.os != 'macos-14'
|
||||
shell: bash
|
||||
working-directory: ${{ env.HOME }}
|
||||
run: |
|
||||
@@ -205,8 +219,13 @@ jobs:
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh ${{ 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++
|
||||
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
|
||||
run: |
|
||||
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",
|
||||
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",
|
||||
os: macos-14,
|
||||
@@ -188,8 +202,8 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt install -y g++-${{ matrix.config.compiler.version }}
|
||||
- name: Install Clang
|
||||
if: matrix.config.compiler.type == 'CLANG'
|
||||
- name: Install Clang with apt
|
||||
if: matrix.config.compiler.type == 'CLANG' && matrix.config.os != 'macos-14'
|
||||
shell: bash
|
||||
working-directory: ${{ env.HOME }}
|
||||
run: |
|
||||
@@ -197,8 +211,13 @@ jobs:
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh ${{ 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++
|
||||
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
|
||||
run: |
|
||||
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);
|
||||
```
|
||||
|
||||
_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
|
||||
them the user gets a powerful but still easy to use interfaces and all unit conversions
|
||||
|
@@ -13,14 +13,29 @@
|
||||
The table below provides the minimum compiler version required to compile the code using a specific
|
||||
C++ feature:
|
||||
|
||||
| C++ Feature | C++ version | gcc | clang | apple-clang | MSVC |
|
||||
|-----------------------------------------------------------|:-----------:|:----:|:-----:|:-----------:|:----:|
|
||||
| **Minimum support** | 20 | 12 | 16 | 15 | None |
|
||||
| **`std::format`** | 20 | 13 | 17 | None | None |
|
||||
| **C++ modules** | 20 | None | 17 | None | None |
|
||||
| **`import std;`** | 23 | None | 18 | None | None |
|
||||
| **Static `constexpr` variables in `constexpr` functions** | 23 | 13 | 17 | None | None |
|
||||
| **Explicit `this` parameter** | 23 | 14 | 18 | None | None |
|
||||
| C++ Feature | C++ version | gcc | clang | apple-clang | MSVC |
|
||||
|-----------------------------------------------------------|:-----------:|:----:|:-----:|:-----------:|:-----------------------------------------:|
|
||||
| **Minimum support** | 20 | 12 | 16 | 15 | 194 :bug:{ title="BEWARE of MSVC Bugs!" } |
|
||||
| **`std::format`** | 20 | 13 | 17 | None | 194 |
|
||||
| **C++ modules** | 20 | None | 17 | None | None |
|
||||
| **`import std;`** | 23 | None | 18 | None | None |
|
||||
| **Static `constexpr` variables in `constexpr` functions** | 23 | 13 | 17 | 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
|
||||
|
||||
|
@@ -56,7 +56,7 @@ Here is a small example of operations possible on scalar quantities:
|
||||
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),
|
||||
|
@@ -2,12 +2,11 @@
|
||||
tags:
|
||||
- CGS System
|
||||
- International System
|
||||
- Text Formatting
|
||||
---
|
||||
|
||||
# `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
|
||||
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:
|
||||
|
||||
```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
|
||||
and integral and floating-point representation types, respectively, and a third function
|
||||
that we introduced in the [previous example](hello_units.md):
|
||||
|
||||
```cpp title="avg_speed.cpp" linenums="16"
|
||||
--8<-- "example/avg_speed.cpp:44:58"
|
||||
```cpp title="avg_speed.cpp" linenums="20"
|
||||
--8<-- "example/avg_speed.cpp:48:62"
|
||||
```
|
||||
|
||||
We also added a simple utility to print our results:
|
||||
|
||||
```cpp title="avg_speed.cpp" linenums="31"
|
||||
--8<-- "example/avg_speed.cpp:60:66"
|
||||
```cpp title="avg_speed.cpp" linenums="35"
|
||||
--8<-- "example/avg_speed.cpp:64:70"
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```cpp title="avg_speed.cpp" linenums="38"
|
||||
--8<-- "example/avg_speed.cpp:68:82"
|
||||
```cpp title="avg_speed.cpp" linenums="42"
|
||||
--8<-- "example/avg_speed.cpp:72:86"
|
||||
```
|
||||
|
||||
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
|
||||
have floating-point representation types:
|
||||
|
||||
```cpp title="avg_speed.cpp" linenums="53"
|
||||
--8<-- "example/avg_speed.cpp:84:95"
|
||||
```cpp title="avg_speed.cpp" linenums="57"
|
||||
--8<-- "example/avg_speed.cpp:88:99"
|
||||
```
|
||||
|
||||
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
|
||||
using international mile:
|
||||
|
||||
```cpp title="avg_speed.cpp" linenums="65"
|
||||
--8<-- "example/avg_speed.cpp:97:129"
|
||||
```cpp title="avg_speed.cpp" linenums="69"
|
||||
--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
|
||||
@@ -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:
|
||||
|
||||
```cpp title="avg_speed.cpp" linenums="97"
|
||||
--8<-- "example/avg_speed.cpp:131:161"
|
||||
```cpp title="avg_speed.cpp" linenums="101"
|
||||
--8<-- "example/avg_speed.cpp:134:165"
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```cpp title="avg_speed.cpp" linenums="128"
|
||||
--8<-- "example/avg_speed.cpp:163:"
|
||||
```cpp title="avg_speed.cpp" linenums="133"
|
||||
--8<-- "example/avg_speed.cpp:167:"
|
||||
```
|
||||
|
@@ -6,32 +6,32 @@ tags:
|
||||
|
||||
# `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.
|
||||
|
||||
First, we either import the `mp_units` module or include the headers for:
|
||||
|
||||
- an International System of Quantities (ISQ)
|
||||
- an International System of units (SI)
|
||||
- units derived from the International Yard and Pound
|
||||
- text formatting and stream output support
|
||||
- an International System of Quantities (ISQ),
|
||||
- an International System of units (SI),
|
||||
- units derived from the International Yard and Pound,
|
||||
- text formatting and stream output support.
|
||||
|
||||
```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.
|
||||
|
||||
```cpp title="hello_units.cpp" linenums="14"
|
||||
--8<-- "example/hello_units.cpp:42:43"
|
||||
```cpp title="hello_units.cpp" linenums="18"
|
||||
--8<-- "example/hello_units.cpp:46:47"
|
||||
```
|
||||
|
||||
Next, we define a simple function that calculates the average speed based on the provided
|
||||
arguments of length and time:
|
||||
|
||||
```cpp title="hello_units.cpp" linenums="15"
|
||||
--8<-- "example/hello_units.cpp:44:47"
|
||||
```cpp title="hello_units.cpp" linenums="19"
|
||||
--8<-- "example/hello_units.cpp:48:51"
|
||||
```
|
||||
|
||||
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
|
||||
of what to expect from a function than just using `auto`.
|
||||
|
||||
```cpp title="hello_units.cpp" linenums="19"
|
||||
--8<-- "example/hello_units.cpp:49:52"
|
||||
```cpp title="hello_units.cpp" linenums="23"
|
||||
--8<-- "example/hello_units.cpp:53:56"
|
||||
```
|
||||
|
||||
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
|
||||
implicitly while including a header file.
|
||||
|
||||
```cpp title="hello_units.cpp" linenums="23"
|
||||
--8<-- "example/hello_units.cpp:54:60"
|
||||
```cpp title="hello_units.cpp" linenums="27"
|
||||
--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
|
||||
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.
|
||||
- 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
|
||||
[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)
|
||||
(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)
|
||||
(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`.
|
||||
|
||||
```cpp title="hello_units.cpp" linenums="30"
|
||||
--8<-- "example/hello_units.cpp:62"
|
||||
```cpp title="hello_units.cpp" linenums="34"
|
||||
--8<-- "example/hello_units.cpp:66"
|
||||
```
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
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`
|
||||
|
||||
!!! 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
|
||||
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.
|
||||
|
||||
```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
|
||||
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.
|
||||
The main part of the example prints all of the SI-defining constants:
|
||||
|
||||
```cpp title="si_constants.cpp" linenums="17"
|
||||
--8<-- "example/si_constants.cpp:45:"
|
||||
```
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
instance of either `quantity` or `quantity_point`:
|
||||
|
||||
| Forcing | Representation | Unit | Member function | Conversion function |
|
||||
|:-------:|:--------------:|:----:|--------------------|-----------------------|
|
||||
| No | Same | `u` | `x.in(u)` | |
|
||||
| No | `T` | Same | `x.in<T>()` | |
|
||||
| No | `T` | `u` | `x.in<T>(u)` | |
|
||||
| Yes | Same | `u` | `x.force_in(u)` | `value_cast<u>(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)` |
|
||||
| Forcing | Representation | Unit | Member function | Non-member function |
|
||||
|:-------:|:--------------:|:----:|--------------------|------------------------------------------------|
|
||||
| No | Same | `u` | `x.in(u)` | |
|
||||
| No | `T` | Same | `x.in<T>()` | |
|
||||
| No | `T` | `u` | `x.in<T>(u)` | |
|
||||
| Yes | Same | `u` | `x.force_in(u)` | `value_cast<u>(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)` or `value_cast<T, u>(x)` |
|
||||
|
@@ -57,6 +57,7 @@ add_example(currency)
|
||||
add_example(foot_pound_second)
|
||||
add_example(glide_computer glide_computer_lib)
|
||||
add_example(hello_units)
|
||||
add_example(hw_voltage)
|
||||
add_example(measurement)
|
||||
add_example(si_constants)
|
||||
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
|
||||
- avg_speed: users_guide/examples/avg_speed.md
|
||||
- si_constants: users_guide/examples/si_constants.md
|
||||
- hw_voltage: users_guide/examples/hw_voltage.md
|
||||
- Appendix:
|
||||
- Glossary: appendix/glossary.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
|
||||
// calling this at runtime, so we want maximum accuracy.
|
||||
long double xld = static_cast<long double>(x);
|
||||
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.
|
||||
//
|
||||
@@ -398,7 +399,7 @@ template<typename T>
|
||||
}
|
||||
|
||||
// Early return if we get lucky with an exact answer.
|
||||
if (result.value() == x) {
|
||||
if (result.value() == xld) {
|
||||
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)`.
|
||||
if (result.value() < x) {
|
||||
if (result.value() < xld) {
|
||||
lo = mid;
|
||||
} else {
|
||||
hi = mid;
|
||||
@@ -416,8 +417,8 @@ template<typename T>
|
||||
}
|
||||
|
||||
// Pick whichever one gets closer to the target.
|
||||
const auto lo_diff = x - checked_int_pow(lo, n).value();
|
||||
const auto hi_diff = checked_int_pow(hi, n).value() - x;
|
||||
const auto lo_diff = xld - checked_int_pow(lo, n).value();
|
||||
const auto hi_diff = checked_int_pow(hi, n).value() - xld;
|
||||
return static_cast<T>(lo_diff < hi_diff ? lo : hi);
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// 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));
|
||||
}
|
||||
|
||||
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
|
||||
@@ -168,6 +176,14 @@ template<Unit auto ToU, Representation ToRep, typename FwdQP, QuantityPoint QP =
|
||||
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
|
||||
*
|
||||
|
@@ -35,7 +35,7 @@ namespace mp_units {
|
||||
|
||||
template<Quantity T>
|
||||
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>
|
||||
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);
|
||||
if constexpr (treat_as_floating_point<rep>) {
|
||||
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 {
|
||||
if (x >= 0) {
|
||||
return x - 1 <= y && y - 1 <= x;
|
||||
@@ -71,12 +71,13 @@ struct AlmostEqualsMatcher : Catch::Matchers::MatcherGenericBase {
|
||||
|
||||
private:
|
||||
const T& target_;
|
||||
int n_eps_;
|
||||
};
|
||||
|
||||
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(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));
|
||||
}
|
||||
|
||||
@@ -475,7 +475,7 @@ TEST_CASE("Angle trigonometric functions", "[trig][angle]")
|
||||
REQUIRE_THAT(tan(0 * angle[grad]), AlmostEquals(0. * one));
|
||||
REQUIRE_THAT(tan(50 * 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<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<float, m>(lvalue_qp).quantity_from_zero().numerical_value_in(m) == 2000.f);
|
||||
} // namespace lvalue_tests
|
||||
|
||||
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>
|
||||
#if MP_UNITS_HOSTED
|
||||
#include <chrono>
|
||||
#include <complex>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if MP_UNITS_HOSTED
|
||||
template<typename T>
|
||||
constexpr bool mp_units::is_scalar<std::complex<T>> = true;
|
||||
#endif
|
||||
|
||||
template<>
|
||||
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'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>
|
||||
concept invalid_unit_conversion = requires {
|
||||
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<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((2000 * m).force_in(km).numerical_value_in(km) == 2);
|
||||
|
Reference in New Issue
Block a user