refactor: 💥 q_* UDL renamed to _q_*

We had some fun exploring the STD UDLs for potential collisions,
we have learnt our lesson and know how to proceed.
Now is high time to start behaving and obeying C++ rules.
This commit is contained in:
Mateusz Pusz
2020-09-09 19:20:35 +02:00
parent 2b408f19c0
commit 9527b39005
130 changed files with 2221 additions and 2235 deletions
+1
View File
@@ -19,6 +19,7 @@
- Added support for `exp()` mathematical support
- Dimensionless quantities redesigned to be of `quantity` type
- `Scalar` concept renamed to `ScalableNumber`
- `q_*` UDL renamed to `_q_*`
- **0.5.0 May 17, 2020**
- Major refactoring and rewrite of the library
+9 -20
View File
@@ -36,7 +36,7 @@ Why a dimensionless quantity is not just an fundamental arithmetic type?
In the initial design of this library the resulting type of the division of
two quantities was their common representation type::
static_assert(std::is_same_v<decltype(10q_km / 5q_km), std::int64_t>);
static_assert(std::is_same_v<decltype(10_q_km / 5_q_km), std::int64_t>);
The reasoning behind it was to not provide a false impression of a strong `quantity` type
for something that looks and feels like a regular number. Also all of the mathematic
@@ -67,8 +67,13 @@ Well, this is how [ISO-80000]_ defines it (British English spelling by default).
User Defined Literals (UDLs)
----------------------------
Why all UDLs are prefixed with ``q_`` instead of just using unit symbol?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Why all UDLs are prefixed with ``_q_`` instead of just using unit symbol?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
..note ..
Every ``_q_*`` UDL will be replaced by the ``q_*`` literal when/if **mp-units**
will become a part of the C++ Standard Library.
Usage of only unit symbols in UDLs would be a preferred approach (less to type,
easier to understand and maintain). However, while increasing the coverage for
@@ -77,26 +82,10 @@ built-in types or numeric extensions. A few of those are: ``F`` (farad),
``J`` (joule), ``W`` (watt), ``K`` (kelvin), ``d`` (day),
``l`` or ``L`` (litre), ``erg``, ``ergps``. For a while for those we used ``_``
prefix to make the library work at all, but at some point we had to unify the
naming and we came up with ``q_`` prefix which results in a creation of
naming and we came up with ``_q_`` prefix which results in a creation of
quantity of a provided unit.
Why do we use UDLs reserved for the C++ standard (no ``_`` prefix)?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This library is meant to become a part of the C++ Standard Library at some point
in the future. We decided to work with the target interface to get implementation
and user experience. Only thanks to this we were able to detect multiple collisions
with literals already existing in the language in our initial approach
(`Why all UDLs are prefixed with q_ instead of just using unit symbol?
<#why-unicode-quantity-symbols-are-used-by-default-instead-of-ascii-only-characters>`_).
This approach has some side effects though. We had to disable some compiler warnings
to make it work:
- ``/wd4455`` on MSVC,
- ``-Wno-literal-suffix`` on other compilers.
Text formatting
---------------
+23 -23
View File
@@ -21,9 +21,9 @@ No Conversions
No conversions (either implicit or explicit) are available across quantities of
different dimensions::
si::length<si::metre> d1 = 1q_s; // Compile-time error
si::length<si::metre> d2(1q_s); // Compile-time error
auto d3 = quantity_cast<si::metre>(1q_s); // Compile-time error
si::length<si::metre> d1 = 1_q_s; // Compile-time error
si::length<si::metre> d2(1_q_s); // Compile-time error
auto d3 = quantity_cast<si::metre>(1_q_s); // Compile-time error
Implicit
@@ -33,23 +33,23 @@ Implicit conversions are allowed only across quantities of the same dimension:
- for integral types with ratios that guarantee no precision loss::
si::length<si::metre, int> d1 = 1q_km + 1q_m; // OK
si::length<si::millimetre, int> d2 = 1q_km + 1q_m; // OK
si::length<si::kilometre, int> d3 = 1q_km + 1q_m; // Compile-time error
si::length<si::kilometre, int> d4(1q_km + 1q_m); // Compile-time error
si::length<si::metre, int> d5 = 1q_m + 1q_ft; // Compile-time error
si::length<si::metre, int> d6(1q_m + 1q_ft); // Compile-time error
si::length<si::metre, int> d1 = 1_q_km + 1_q_m; // OK
si::length<si::millimetre, int> d2 = 1_q_km + 1_q_m; // OK
si::length<si::kilometre, int> d3 = 1_q_km + 1_q_m; // Compile-time error
si::length<si::kilometre, int> d4(1_q_km + 1_q_m); // Compile-time error
si::length<si::metre, int> d5 = 1_q_m + 1_q_ft; // Compile-time error
si::length<si::metre, int> d6(1_q_m + 1_q_ft); // Compile-time error
- from an integral to a floating-point representation even in case of a truncating
ratio::
si::length<si::kilometre, double> d7 = 1q_km + 1q_m; // OK
si::length<si::metre, double> d8 = 1q_m + 1q_ft; // OK
si::length<si::kilometre, double> d7 = 1_q_km + 1_q_m; // OK
si::length<si::metre, double> d8 = 1_q_m + 1_q_ft; // OK
- when both sides use a floating-point representation::
si::length<si::metre, int> d9 = 1.23q_m; // Compile-time error
si::length<si::metre, double> d10 = 1.23q_m; // OK
si::length<si::metre, int> d9 = 1.23_q_m; // Compile-time error
si::length<si::metre, double> d10 = 1.23_q_m; // OK
Explicit
@@ -92,7 +92,7 @@ once and leave the rest intact:
`quantity_point_cast` takes anything that works for `quantity_point`
or a specific target `quantity_point`::
std::cout << "Point: " << quantity_point_cast<decltype(quantity_point{0q_m})>(d) << '\n';
std::cout << "Point: " << quantity_point_cast<decltype(quantity_point{0_q_m})>(d) << '\n';
.. seealso::
@@ -108,8 +108,8 @@ As noted in the :ref:`Dimensionless Quantities` chapter, :term:`quantity of dime
is somehow special but still obey most of the rules defined for quantities. However, as they
represent numbers it would be highly uncomfortable to every time type::
const auto d1 = 10q_km;
const auto d2 = 3q_km;
const auto d1 = 10_q_km;
const auto d2 = 3_q_km;
if(d1 / d2 > dimensionless<one, 2>) {
// ...
}
@@ -123,8 +123,8 @@ This is why it was decided to allow the ``dimensionless<one>`` quantity of any
representation type to be implicitly constructible from this representation type.
With that the above examples can be rewritten as follows::
const auto d1 = 10q_km;
const auto d2 = 3q_km;
const auto d1 = 10_q_km;
const auto d2 = 3_q_km;
if(d1 / d2 > 2) {
// ...
}
@@ -142,8 +142,8 @@ could be ambiguous. For example::
return d1 / d2 + 1;
}
As long as we can reason about what such code means for ``foo(10q_km, 2q_km)`` it is not that obvious
at all in the case of ``foo(10q_cm, 2q_ft)``. To make such code to compile for every case we have to
As long as we can reason about what such code means for ``foo(10_q_km, 2_q_km)`` it is not that obvious
at all in the case of ``foo(10_q_cm, 2_q_ft)``. To make such code to compile for every case we have to
either change the type of the resulting unit to the one having ``ratio(1)`` (:term:`coherent derived unit`)::
Dimensionless auto foo(Length auto d1, Length auto d2)
@@ -162,15 +162,15 @@ There is one more important point to note here. As the the dimensionless quantit
a number, it is never implicitly converted back to the representation type. This means that the following
code will not compile::
auto v = std::exp(10q_m / 5q_m);
auto v = std::exp(10_q_m / 5_q_m);
To make it compile fine we have to either explicitly get the value stored in the quantity::
auto v = std::exp(quantity_cast<one>(10q_m / 5q_m).count());
auto v = std::exp(quantity_cast<one>(10_q_m / 5_q_m).count());
or use a mathematical wrapper function from `units` namespace::
auto v = units::exp(10q_m / 5q_m);
auto v = units::exp(10_q_m / 5_q_m);
.. important::
+15 -15
View File
@@ -17,8 +17,8 @@ each other and the result will always be a quantity of the same dimension:
.. code-block::
:emphasize-lines: 3-4
Length auto dist1 = 2q_m;
Length auto dist2 = 1q_m;
Length auto dist1 = 2_q_m;
Length auto dist2 = 1_q_m;
Length auto res1 = dist1 + dist2;
Length auto res2 = dist1 - dist2;
@@ -29,7 +29,7 @@ not change:
.. code-block::
:emphasize-lines: 2-4
Length auto dist = 2q_m;
Length auto dist = 2_q_m;
Length auto res1 = dist * 2; // 4 m
Length auto res2 = 3 * res1; // 12 m
Length auto res3 = res2 / 2; // 6 m
@@ -41,9 +41,9 @@ probably will always end up in a quantity of a yet another dimension:
.. code-block::
:emphasize-lines: 4-6
Length auto dist1 = 2q_m;
Length auto dist2 = 3q_m;
Time auto dur1 = 2q_s;
Length auto dist1 = 2_q_m;
Length auto dist2 = 3_q_m;
Time auto dur1 = 2_q_s;
Area auto res1 = dist1 * dist2; // 6 m²
Speed auto res2 = dist1 / dur1; // 1 m/s
Frequency auto res3 = 10 / dur1; // 5 Hz
@@ -55,9 +55,9 @@ dimension, than we will end up with just a scalable number type:
.. code-block::
:emphasize-lines: 4-5
Time auto dur1 = 10q_s;
Time auto dur2 = 2q_s;
Frequency auto fr1 = 5q_Hz;
Time auto dur1 = 10_q_s;
Time auto dur2 = 2_q_s;
Frequency auto fr1 = 5_q_Hz;
ScalableNumber auto v1 = dur1 / dur2; // 5
ScalableNumber auto v2 = dur1 * fr1; // 50
@@ -69,8 +69,8 @@ The result will always be a quantity point of the same dimension:
.. code-block::
:emphasize-lines: 3-5
Length auto dist1 = 2q_m;
Length auto dist2 = 1q_m;
Length auto dist1 = 2_q_m;
Length auto dist2 = 1_q_m;
QuantityPoint auto res1 = quantity_point{dist1} + dist2;
QuantityPoint auto res2 = dist1 + quantity_point{dist2};
QuantityPoint auto res3 = quantity_point{dist1} - dist2;
@@ -82,8 +82,8 @@ The result will always be a quantity point of the same dimension:
.. code-block::
:emphasize-lines: 3
Length auto dist1 = 2q_m;
Length auto dist2 = 1q_m;
Length auto dist1 = 2_q_m;
Length auto dist2 = 1_q_m;
auto res1 = dist1 - quantity_point{dist2}; // ERROR
We can also subtract two quantity points.
@@ -92,8 +92,8 @@ The result is a relative quantity of the same dimension:
.. code-block::
:emphasize-lines: 3
Length auto dist1 = 2q_m;
Length auto dist2 = 1q_m;
Length auto dist1 = 2_q_m;
Length auto dist2 = 1_q_m;
Length auto res1 = quantity_point{dist1} - quantity_point{dist2};
That's it! You can't multiply nor divide quantity points with anything else.
+13 -13
View File
@@ -48,15 +48,15 @@ values the library provides :abbr:`UDL (User Defined Literal)` s for each
be as simple as::
using namespace units::physical::si::literals;
constexpr auto d1 = 123q_km; // si::length<si::kilometre, std::int64_t>
constexpr auto d2 = 123.q_km; // si::length<si::kilometre, long double>
constexpr auto d1 = 123_q_km; // si::length<si::kilometre, std::int64_t>
constexpr auto d2 = 123._q_km; // si::length<si::kilometre, long double>
``123q_km`` should be read as a quantity of length in kilometers. Initially the
library did not use the ``q_`` prefix for UDLs but it turned out that there are
``123_q_km`` should be read as a quantity of length in kilometers. Initially the
library did not use the ``_q_`` prefix for UDLs but it turned out that there are
a few unit symbols that collide with literals already existing in C and C++
language (i.e. ``F`` (farad), ``J`` (joule), ``W`` (watt), ``K`` (kelvin),
``d`` (day), ``l`` or ``L`` (litre), ``erg``, ``ergps``). This is why the
``q_`` prefix was consistently applied to all the UDLs.
``_q_`` prefix was consistently applied to all the UDLs.
Dimension-specific Concepts
@@ -67,7 +67,7 @@ requires quantity of a concrete dimension than dimension-specific concepts can
be used::
using namespace units::physical::si::literals;
constexpr Length auto d = 123q_km; // si::length<si::kilometre, std::int64_t>
constexpr Length auto d = 123_q_km; // si::length<si::kilometre, std::int64_t>
.. note::
@@ -83,8 +83,8 @@ quantities. The usage of such a function can look as follows::
using namespace units::physical::si::literals;
using namespace units::physical::international::literals;
constexpr Speed auto v1 = avg_speed(220q_km, 2q_h);
constexpr Speed auto v2 = avg_speed(140q_mi, 2q_h);
constexpr Speed auto v1 = avg_speed(220_q_km, 2_q_h);
constexpr Speed auto v2 = avg_speed(140_q_mi, 2_q_h);
In this and all other physical units libraries such a function can be
implemented as::
@@ -142,7 +142,7 @@ but often we would like to know a specific type too. We have two options here:
- query the actual dimension, unit, and representation types::
constexpr Speed auto v = avg_speed(220q_km, 2q_h);
constexpr Speed auto v = avg_speed(220_q_km, 2_q_h);
using quantity_type = decltype(v);
using dimension_type = quantity_type::dimension;
using unit_type = quantity_type::unit;
@@ -150,7 +150,7 @@ but often we would like to know a specific type too. We have two options here:
- convert or cast to a desired quantity type::
constexpr Speed auto v1 = avg_speed(220.q_km, 2q_h);
constexpr Speed auto v1 = avg_speed(220._q_km, 2_q_h);
constexpr si::speed<si::metre_per_second> v2 = v1;
constexpr Speed auto v3 = quantity_cast<si::speed<si::metre_per_second>(v1);
@@ -166,8 +166,8 @@ Dimensionless Quantities
Whenever we divide two quantities of the same dimension we end up with a
:term:`dimensionless quantity` otherwise known as :term:`quantity of dimension one`::
static_assert(10q_km / 5q_km == 2);
static_assert(std::is_same_v<decltype(10q_km / 5q_km), quantity<dim_one, one, std::int64_t>>);
static_assert(10_q_km / 5_q_km == 2);
static_assert(std::is_same_v<decltype(10_q_km / 5_q_km), quantity<dim_one, one, std::int64_t>>);
According to the official ISO definition `dim_one` is a dimension "for which all the
exponents of the factors corresponding to the base quantities in its quantity dimension
@@ -196,7 +196,7 @@ There are two special units provided for usage with such a quantity:
For example the following code::
std::cout << quantity_cast<percent>(50.q_m / 100.q_m) << '\n';
std::cout << quantity_cast<percent>(50._q_m / 100._q_m) << '\n';
will print ``50 %`` to the console output.
+2 -2
View File
@@ -14,7 +14,7 @@ Quantity Point Construction
To create the quantity point object from a `quantity` we just have to pass
the value to the `quantity_point` class template explicit constructor::
quantity_point<si::dim_length, si::kilometre, double> d(123q_km);
quantity_point<si::dim_length, si::kilometre, double> d(123_q_km);
.. note::
@@ -25,7 +25,7 @@ the value to the `quantity_point` class template explicit constructor::
`copy initialization <https://en.cppreference.com/w/cpp/language/copy_initialization>`_
**does not compile**::
quantity_point<si::dim_length, si::kilometre, double> d = 123q_km; // ERROR
quantity_point<si::dim_length, si::kilometre, double> d = 123_q_km; // ERROR
Differences to quantity
+61 -61
View File
@@ -18,8 +18,8 @@ stream::
using namespace units::physical::si::literals;
using namespace units::physical::international::literals;
constexpr Speed auto v1 = avg_speed(220.q_km, 2q_h);
constexpr Speed auto v2 = avg_speed(140.q_mi, 2q_h);
constexpr Speed auto v1 = avg_speed(220._q_km, 2_q_h);
constexpr Speed auto v2 = avg_speed(140._q_mi, 2_q_h);
std::cout << v1 << '\n'; // 110 km/h
std::cout << v2 << '\n'; // 70 mi/h
@@ -41,9 +41,9 @@ Stream Output Formatting
Only a basic formatting can be applied for output streams. It includes control
over width, fill, and alignment::
os << "|" << std::setw(10) << 123q_m << "|"; // | 123 m|
os << "|" << std::setw(10) << std::left << 123q_m << "|"; // |123 m |
os << "|" << std::setw(10) << std::setfill('*') << 123q_m << "|"; // |*****123 m|
os << "|" << std::setw(10) << 123_q_m << "|"; // | 123 m|
os << "|" << std::setw(10) << std::left << 123_q_m << "|"; // |123 m |
os << "|" << std::setw(10) << std::setfill('*') << 123_q_m << "|"; // |*****123 m|
fmt::format
@@ -91,9 +91,9 @@ In case it is left empty the default formatting of ``{:%Q %q}`` is applied. The
default formatting is also applied to the output streams. This is why the following
code lines produce the same output::
std::cout << "Distance: " << 123q_km << "\n";
fmt::print("Distance: {}\n", 123q_km);
fmt::print("Distance: {:%Q %q}\n", 123q_km);
std::cout << "Distance: " << 123_q_km << "\n";
fmt::print("Distance: {}\n", 123_q_km);
fmt::print("Distance: {:%Q %q}\n", 123_q_km);
Quantity Value, Symbol, or Both?
@@ -102,9 +102,9 @@ Quantity Value, Symbol, or Both?
The user can easily decide to either print a whole quantity (value and symbol) or
only its parts. Also a different quantity formatting might be applied::
fmt::print("{:%Q}", 123q_km); // 123
fmt::print("{:%q}", 123q_km); // km
fmt::print("{:%Q%q}", 123q_km); // 123km
fmt::print("{:%Q}", 123_q_km); // 123
fmt::print("{:%q}", 123_q_km); // km
fmt::print("{:%Q%q}", 123_q_km); // 123km
Controlling Width, Fill, and Alignment
@@ -114,14 +114,14 @@ To control width, fill, and alignment the C++ standard grammar tokens ``fill-and
and ``width`` are being used and they treat a quantity value and symbol as a contiguous
text::
fmt::print("|{:0}|", 123q_m); // |123 m|
fmt::print("|{:10}|", 123q_m); // | 123 m|
fmt::print("|{:<10}|", 123q_m); // |123 m |
fmt::print("|{:>10}|", 123q_m); // | 123 m|
fmt::print("|{:^10}|", 123q_m); // | 123 m |
fmt::print("|{:*<10}|", 123q_m); // |123 m*****|
fmt::print("|{:*>10}|", 123q_m); // |*****123 m|
fmt::print("|{:*^10}|", 123q_m); // |**123 m***|
fmt::print("|{:0}|", 123_q_m); // |123 m|
fmt::print("|{:10}|", 123_q_m); // | 123 m|
fmt::print("|{:<10}|", 123_q_m); // |123 m |
fmt::print("|{:>10}|", 123_q_m); // | 123 m|
fmt::print("|{:^10}|", 123_q_m); // | 123 m |
fmt::print("|{:*<10}|", 123_q_m); // |123 m*****|
fmt::print("|{:*>10}|", 123_q_m); // |*****123 m|
fmt::print("|{:*^10}|", 123_q_m); // |**123 m***|
ASCII-only Quantity Symbols
@@ -133,12 +133,12 @@ this by default. From the engineering point of view sometimes Unicode text migh
not be a solution as terminals of many (especially embedded) devices are ASCII-only.
In such a case the unit symbol can be forced to be printed using ASCII-only characters::
fmt::print("{}", 10q_R); // 10 Ω
fmt::print("{:%Q %Aq}", 10q_R); // 10 ohm
fmt::print("{}", 125q_us); // 125 µs
fmt::print("{:%Q %Aq}", 125q_us); // 125 us
fmt::print("{}", 9.8q_m_per_s2); // 9.8 m/s²
fmt::print("{:%Q %Aq}", 9.8q_m_per_s2); // 9.8 m/s^2
fmt::print("{}", 10_q_R); // 10 Ω
fmt::print("{:%Q %Aq}", 10_q_R); // 10 ohm
fmt::print("{}", 125_q_us); // 125 µs
fmt::print("{:%Q %Aq}", 125_q_us); // 125 us
fmt::print("{}", 9.8_q_m_per_s2); // 9.8 m/s²
fmt::print("{:%Q %Aq}", 9.8_q_m_per_s2); // 9.8 m/s^2
Controlling on How the Quantity Value Is Being Printed
@@ -146,8 +146,8 @@ Controlling on How the Quantity Value Is Being Printed
``sign`` token allows us to specify on how the value's sign is being printed::
fmt::print("{0:%Q %q},{0:%+Q %q},{0:%-Q %q},{0:% Q %q}", 1q_m); // 1 m,+1 m,1 m, 1 m
fmt::print("{0:%Q %q},{0:%+Q %q},{0:%-Q %q},{0:% Q %q}", -1q_m); // -1 m,-1 m,-1 m,-1 m
fmt::print("{0:%Q %q},{0:%+Q %q},{0:%-Q %q},{0:% Q %q}", 1_q_m); // 1 m,+1 m,1 m, 1 m
fmt::print("{0:%Q %q},{0:%+Q %q},{0:%-Q %q},{0:% Q %q}", -1_q_m); // -1 m,-1 m,-1 m,-1 m
where:
@@ -159,47 +159,47 @@ where:
``precision`` token is allowed only for floating-point representation types::
fmt::print("{:%.0Q %q}", 1.2345q_m); // 1 m
fmt::print("{:%.1Q %q}", 1.2345q_m); // 1.2 m
fmt::print("{:%.2Q %q}", 1.2345q_m); // 1.23 m
fmt::print("{:%.0Q %q}", 1.2345_q_m); // 1 m
fmt::print("{:%.1Q %q}", 1.2345_q_m); // 1.2 m
fmt::print("{:%.2Q %q}", 1.2345_q_m); // 1.23 m
:token:`units-rep-type` specifies how a value of the representation type is being
printed. For integral types::
fmt::print("{:%bQ %q}", 42q_m); // 101010 m
fmt::print("{:%BQ %q}", 42q_m); // 101010 m
fmt::print("{:%dQ %q}", 42q_m); // 42 m
fmt::print("{:%oQ %q}", 42q_m); // 52 m
fmt::print("{:%xQ %q}", 42q_m); // 2a m
fmt::print("{:%XQ %q}", 42q_m); // 2A m
fmt::print("{:%bQ %q}", 42_q_m); // 101010 m
fmt::print("{:%BQ %q}", 42_q_m); // 101010 m
fmt::print("{:%dQ %q}", 42_q_m); // 42 m
fmt::print("{:%oQ %q}", 42_q_m); // 52 m
fmt::print("{:%xQ %q}", 42_q_m); // 2a m
fmt::print("{:%XQ %q}", 42_q_m); // 2A m
The above can be printed in an alternate version thanks to the ``#`` token::
fmt::print("{:%#bQ %q}", 42q_m); // 0b101010 m
fmt::print("{:%#BQ %q}", 42q_m); // 0B101010 m
fmt::print("{:%#oQ %q}", 42q_m); // 052 m
fmt::print("{:%#xQ %q}", 42q_m); // 0x2a m
fmt::print("{:%#XQ %q}", 42q_m); // 0X2A m
fmt::print("{:%#bQ %q}", 42_q_m); // 0b101010 m
fmt::print("{:%#BQ %q}", 42_q_m); // 0B101010 m
fmt::print("{:%#oQ %q}", 42_q_m); // 052 m
fmt::print("{:%#xQ %q}", 42_q_m); // 0x2a m
fmt::print("{:%#XQ %q}", 42_q_m); // 0X2A m
For floating-point values the :token:`units-rep-type` token works as follows::
fmt::print("{:%aQ %q}", 1.2345678q_m); // 0x9.e065152d8eae841p-3 m
fmt::print("{:%.3aQ %q}", 1.2345678q_m); // 0x9.e06p-3 m
fmt::print("{:%AQ %q}", 1.2345678q_m); // 0X9.E065152D8EAE841P-3 m
fmt::print("{:%.3AQ %q}", 1.2345678q_m); // 0X9.E06P-3 m
fmt::print("{:%eQ %q}", 1.2345678q_m); // 1.234568e+00 m
fmt::print("{:%.3eQ %q}", 1.2345678q_m); // 1.235e+00 m
fmt::print("{:%EQ %q}", 1.2345678q_m); // 1.234568E+00 m
fmt::print("{:%.3EQ %q}", 1.2345678q_m); // 1.235E+00 m
fmt::print("{:%gQ %q}", 1.2345678q_m); // 1.23457 m
fmt::print("{:%gQ %q}", 1.2345678e8q_m); // 1.23457e+08 m
fmt::print("{:%.3gQ %q}", 1.2345678q_m); // 1.23 m
fmt::print("{:%.3gQ %q}", 1.2345678e8q_m); // 1.23e+08 m
fmt::print("{:%GQ %q}", 1.2345678q_m); // 1.23457 m
fmt::print("{:%GQ %q}", 1.2345678e8q_m); // 1.23457E+08 m
fmt::print("{:%.3GQ %q}", 1.2345678q_m); // 1.23 m
fmt::print("{:%.3GQ %q}", 1.2345678e8q_m); // 1.23E+08 m
fmt::print("{:%aQ %q}", 1.2345678_q_m); // 0x9.e065152d8eae841p-3 m
fmt::print("{:%.3aQ %q}", 1.2345678_q_m); // 0x9.e06p-3 m
fmt::print("{:%AQ %q}", 1.2345678_q_m); // 0X9.E065152D8EAE841P-3 m
fmt::print("{:%.3AQ %q}", 1.2345678_q_m); // 0X9.E06P-3 m
fmt::print("{:%eQ %q}", 1.2345678_q_m); // 1.234568e+00 m
fmt::print("{:%.3eQ %q}", 1.2345678_q_m); // 1.235e+00 m
fmt::print("{:%EQ %q}", 1.2345678_q_m); // 1.234568E+00 m
fmt::print("{:%.3EQ %q}", 1.2345678_q_m); // 1.235E+00 m
fmt::print("{:%gQ %q}", 1.2345678_q_m); // 1.23457 m
fmt::print("{:%gQ %q}", 1.2345678e8_q_m); // 1.23457e+08 m
fmt::print("{:%.3gQ %q}", 1.2345678_q_m); // 1.23 m
fmt::print("{:%.3gQ %q}", 1.2345678e8_q_m); // 1.23e+08 m
fmt::print("{:%GQ %q}", 1.2345678_q_m); // 1.23457 m
fmt::print("{:%GQ %q}", 1.2345678e8_q_m); // 1.23457E+08 m
fmt::print("{:%.3GQ %q}", 1.2345678_q_m); // 1.23 m
fmt::print("{:%.3GQ %q}", 1.2345678e8_q_m); // 1.23E+08 m
Special Signs
@@ -208,7 +208,7 @@ Special Signs
Beside adding any list of regular characters as a separator between the value and the
symbol, it is possible to type a few special signs there too::
fmt::print("{:%Q_%q}", 123q_km); // 123_km
fmt::print("{:%Q%t%q}", 123q_km); // 123\tkm <tab>
fmt::print("{:%Q%n%q}", 123q_km); // 123\nkm <new line>
fmt::print("{:%Q%% %q}", 123q_km); // 123% km
fmt::print("{:%Q_%q}", 123_q_km); // 123_km
fmt::print("{:%Q%t%q}", 123_q_km); // 123\tkm <tab>
fmt::print("{:%Q%n%q}", 123_q_km); // 123\nkm <new line>
fmt::print("{:%Q%% %q}", 123_q_km); // 123% km
+1 -1
View File
@@ -381,7 +381,7 @@ unknown/undefined unit type like in the below example::
using namespace units::physical::si::literals;
Length auto l = 100q_km_per_h * 10q_s;
Length auto l = 100_q_km_per_h * 10_q_s;
The type of ``l`` above will be
:expr:`si::length<scaled_unit<ratio(1, 36, 1), si::metre>, long double>`. This is caused
+10 -10
View File
@@ -4,22 +4,22 @@ Quick Start
Here is a small example of possible operations::
// simple numeric operations
static_assert(10q_km / 2 == 5q_km);
static_assert(10_q_km / 2 == 5_q_km);
// unit conversions
static_assert(1q_h == 3600q_s);
static_assert(1q_km + 1q_m == 1001q_m);
static_assert(1_q_h == 3600_q_s);
static_assert(1_q_km + 1_q_m == 1001_q_m);
// dimension conversions
static_assert(1q_km / 1q_s == 1000q_m_per_s);
static_assert(2q_km_per_h * 2q_h == 4q_km);
static_assert(2q_km / 2q_km_per_h == 1q_h);
static_assert(1_q_km / 1_q_s == 1000_q_m_per_s);
static_assert(2_q_km_per_h * 2_q_h == 4_q_km);
static_assert(2_q_km / 2_q_km_per_h == 1_q_h);
static_assert(2q_m * 3q_m == 6q_m2);
static_assert(2_q_m * 3_q_m == 6_q_m2);
static_assert(10q_km / 5q_km == 2);
static_assert(10_q_km / 5_q_km == 2);
static_assert(1000 / 1q_s == 1q_kHz);
static_assert(1000 / 1_q_s == 1_q_kHz);
.. admonition:: Try it on Compiler Explorer
@@ -46,7 +46,7 @@ of basic library features::
int main()
{
using namespace units::physical::si::literals;
Speed auto v1 = avg_speed(220q_km, 2q_h);
Speed auto v1 = avg_speed(220_q_km, 2_q_h);
Speed auto v2 = avg_speed(si::length<international::mile>(140), si::time<si::hour>(2));
Speed auto v3 = quantity_cast<si::metre_per_second>(v2);
Speed auto v4 = quantity_cast<int>(v3);
@@ -90,7 +90,7 @@ representation types we have to obey similar rules::
This also applies when we want to create a quantity with a custom representation type
from a regular quantity value::
Length auto d = 123q_m;
Length auto d = 123_q_m;
si::length<si::metre, impl> d1(d); // OK
si::length<si::metre, expl> d2(d); // Compile-time error
si::length<si::metre, expl> d3(quantity_cast<expl>(d)); // OK
+1 -1
View File
@@ -130,7 +130,7 @@ coherent unit::
With the above we can now check what is the production rate::
DeskRate auto rate = quantity_cast<desk_per_hour>(3._d / 20q_min);
DeskRate auto rate = quantity_cast<desk_per_hour>(3._d / 20_q_min);
std::cout << "Desk rate: " << rate << '\n'; // prints 'Desk rate: 9 desk/h'
and how much wood is being consumed over a unit of time::
+19 -19
View File
@@ -27,28 +27,28 @@ The official :term:`quantity` definition states:
So the most common use case would be to create a vector or matrix of quantities::
fs_vector<si::length<si::metre>, 3> v = { 1q_m, 2q_m, 3q_m };
fs_vector<si::length<si::metre>, 3> u = { 3q_m, 2q_m, 1q_m };
fs_vector<si::length<si::kilometre>, 3> t = { 3q_km, 2q_km, 1q_km };
fs_vector<si::length<si::metre>, 3> v = { 1_q_m, 2_q_m, 3_q_m };
fs_vector<si::length<si::metre>, 3> u = { 3_q_m, 2_q_m, 1_q_m };
fs_vector<si::length<si::kilometre>, 3> t = { 3_q_km, 2_q_km, 1_q_km };
Having such definitions we can perform full dimensional analysis operations for the operations
allowed by the Linear Algebra rules. For example::
std::cout << "v + u = " << v + u << "\n";
std::cout << "v + t = " << v + t << "\n";
std::cout << "t[m] = " << vector<si::length<si::metre>>(t) << "\n";
std::cout << "v * u = " << v * u << "\n";
std::cout << "2q_m * v = " << 2q_m * v << "\n";
std::cout << "v + u = " << v + u << "\n";
std::cout << "v + t = " << v + t << "\n";
std::cout << "t[m] = " << vector<si::length<si::metre>>(t) << "\n";
std::cout << "v * u = " << v * u << "\n";
std::cout << "2_q_m * v = " << 2_q_m * v << "\n";
The above code works as expected and produces the following output:
.. code-block:: text
v + u = | 4 m 4 m 4 m |
v + t = | 3001 m 2002 m 1003 m |
t[m] = | 3000 m 2000 m 1000 m |
v * u = 10 m²
2q_m * v = | 2 m² 4 m² 6 m² |
v + u = | 4 m 4 m 4 m |
v + t = | 3001 m 2002 m 1003 m |
t[m] = | 3000 m 2000 m 1000 m |
v * u = 10 m²
2_q_m * v = | 2 m² 4 m² 6 m² |
Quantities of Linear Algebra Types
@@ -74,14 +74,14 @@ output:
.. code-block:: text
v + u = | 4 4 4 | m
v + t = | 3001 2002 1003 | m
t[m] = | 3000 2000 1000 | m
v * u = 10 m²
2q_m * v = | 2 4 6 | m²
v + u = | 4 4 4 | m
v + t = | 3001 2002 1003 | m
t[m] = | 3000 2000 1000 | m
v * u = 10 m²
2_q_m * v = | 2 4 6 | m²
.. seealso::
For more examples of Linear Algebra definition and operations please refer to
the :ref:`linear_algebra` example.
the :ref:`linear_algebra` example.
+5 -5
View File
@@ -26,7 +26,7 @@ will determine its type. The same applies to the resulting unit. For example:
using namespace units::physical::si;
constexpr auto result = 144q_km / 2q_h;
constexpr auto result = 144_q_km / 2_q_h;
static_assert(is_same_v<decltype(result)::dimension, dim_speed>);
static_assert(is_same_v<decltype(result)::unit, kilometre_per_hour>);
@@ -44,7 +44,7 @@ we forget to include a header file with the resulting dimension definition:
using namespace units::physical::si;
constexpr auto result = 144q_km / 2q_h;
constexpr auto result = 144_q_km / 2_q_h;
static_assert(is_same_v<decltype(result)::dimension,
unknown_dimension<exponent<dim_length, 1>, exponent<dim_time, -1>>>);
static_assert(is_same_v<decltype(result)::unit,
@@ -96,10 +96,10 @@ in your program. A typical example here are temporary results of a long calculat
{
Speed auto s1 = avg_speed(d, t);
auto temp1 = s1 * 200q_km; // intermediate unknown dimension
auto temp1 = s1 * 200_q_km; // intermediate unknown dimension
Speed auto s2 = temp1 / 50q_km; // back to known dimensions again
Length auto d2 = s2 * 4q_h;
Speed auto s2 = temp1 / 50_q_km; // back to known dimensions again
Length auto d2 = s2 * 4_q_h;
// ...
}