mirror of
https://github.com/mpusz/mp-units.git
synced 2025-07-31 02:47:16 +02:00
docs: Documentation now prefers refrences over UDLs
This commit is contained in:
@ -49,7 +49,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(10_q_km / 5_q_km), std::int64_t>);
|
||||
static_assert(std::is_same_v<decltype(10 * km / (5 * 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
|
||||
|
@ -21,9 +21,9 @@ No Conversions
|
||||
No conversions (either implicit or explicit) are available across quantities of
|
||||
different dimensions::
|
||||
|
||||
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
|
||||
si::length<si::metre> d1 = 1 * s; // Compile-time error
|
||||
si::length<si::metre> d2(1 * s); // Compile-time error
|
||||
auto d3 = quantity_cast<si::metre>(1 * 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 = 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
|
||||
si::length<si::metre, int> d1 = 1 * km + 1 * m; // OK
|
||||
si::length<si::millimetre, int> d2 = 1 * km + 1 * m; // OK
|
||||
si::length<si::kilometre, int> d3 = 1 * km + 1 * m; // Compile-time error
|
||||
si::length<si::kilometre, int> d4(1 * km + 1 * m); // Compile-time error
|
||||
si::length<si::metre, int> d5 = 1 * m + 1 * ft; // Compile-time error
|
||||
si::length<si::metre, int> d6(1 * m + 1 * 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 = 1_q_km + 1_q_m; // OK
|
||||
si::length<si::metre, double> d8 = 1_q_m + 1_q_ft; // OK
|
||||
si::length<si::kilometre, double> d7 = 1 * km + 1 * m; // OK
|
||||
si::length<si::metre, double> d8 = 1 * m + 1 * ft; // OK
|
||||
|
||||
- when both sides use a floating-point representation::
|
||||
|
||||
si::length<si::metre, int> d9 = 1.23_q_m; // Compile-time error
|
||||
si::length<si::metre, double> d10 = 1.23_q_m; // OK
|
||||
si::length<si::metre, int> d9 = 1.23 * m; // Compile-time error
|
||||
si::length<si::metre, double> d10 = 1.23 * m; // OK
|
||||
|
||||
|
||||
Explicit
|
||||
@ -60,8 +60,8 @@ Explicit conversions are available with the `quantity_cast`, `quantity_point_cas
|
||||
They are especially useful to force a truncating conversion across quantities of the same
|
||||
dimension for integral representation types and ratios that may cause precision loss::
|
||||
|
||||
si::length<si::kilometre, int> d1 = quantity_cast<kilometre>(1km + 1m); // OK
|
||||
si::length<si::kilometre, int> d2 = quantity_cast<kilometre>(1s); // Error
|
||||
si::length<si::kilometre, int> d1 = quantity_cast<kilometre>(1 * km + 1 * m); // OK
|
||||
si::length<si::kilometre, int> d2 = quantity_cast<kilometre>(1 * s); // Error
|
||||
|
||||
.. seealso::
|
||||
|
||||
@ -97,14 +97,13 @@ 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{0_q_m})>(d) << '\n';
|
||||
std::cout << "Point: " << quantity_point_cast<decltype(quantity_point{0 * m})>(d) << '\n';
|
||||
|
||||
For a single explicit template argument,
|
||||
`quantity_point_kind_cast` is a superset of `quantity_kind_cast`,
|
||||
which is also a superset of `quantity_cast`.
|
||||
For a single explicit template argument, `quantity_point_kind_cast` is a superset of
|
||||
`quantity_kind_cast`, which is also a superset of `quantity_cast`.
|
||||
For the (dimension, unit) pair of explicit arguments case of `quantity_cast`,
|
||||
`quantity_point_kind_cast` and `quantity_kind_cast`
|
||||
accept a `PointKind` and `Kind` instead of a `Dimension`, respectively.
|
||||
`quantity_point_kind_cast` and `quantity_kind_cast` accept a `PointKind` and `Kind`
|
||||
instead of a `Dimension`, respectively.
|
||||
|
||||
.. seealso::
|
||||
|
||||
@ -121,8 +120,8 @@ As noted in the :ref:`framework/quantities:Dimensionless Quantities` chapter,
|
||||
for quantities. However, as they represent numbers it would be highly uncomfortable to every
|
||||
time type::
|
||||
|
||||
const auto d1 = 10_q_km;
|
||||
const auto d2 = 3_q_km;
|
||||
const auto d1 = 10 * km;
|
||||
const auto d2 = 3 * km;
|
||||
if(d1 / d2 > dimensionless<one>(2)) {
|
||||
// ...
|
||||
}
|
||||
@ -136,8 +135,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 = 10_q_km;
|
||||
const auto d2 = 3_q_km;
|
||||
const auto d1 = 10 * km;
|
||||
const auto d2 = 3 * km;
|
||||
if(d1 / d2 > 2) {
|
||||
// ...
|
||||
}
|
||||
@ -155,8 +154,8 @@ could be ambiguous. For example::
|
||||
return d1 / d2 + 1;
|
||||
}
|
||||
|
||||
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
|
||||
As long as we can reason about what such code means for ``foo(10 * km, 2 * km)`` it is not that obvious
|
||||
at all in the case of ``foo(10 * cm, 2 * 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)
|
||||
@ -175,15 +174,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(10_q_m / 5_q_m);
|
||||
auto v = std::exp(10 * m / (5 * 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>(10_q_m / 5_q_m).number());
|
||||
auto v = std::exp(quantity_cast<one>(10 * m / (5 * m)).number());
|
||||
|
||||
or use a mathematical wrapper function from `units` namespace::
|
||||
|
||||
auto v = units::exp(10_q_m / 5_q_m);
|
||||
auto v = units::exp(10 * m / (5 * m));
|
||||
|
||||
.. important::
|
||||
|
||||
|
@ -20,8 +20,8 @@ each other and the result will always be a quantity of the same dimension:
|
||||
.. code-block::
|
||||
:emphasize-lines: 3-4
|
||||
|
||||
Length auto dist1 = 2_q_m;
|
||||
Length auto dist2 = 1_q_m;
|
||||
Length auto dist1 = 2 * m;
|
||||
Length auto dist2 = 1 * m;
|
||||
Length auto res1 = dist1 + dist2;
|
||||
Length auto res2 = dist1 - dist2;
|
||||
|
||||
@ -32,7 +32,7 @@ not change:
|
||||
.. code-block::
|
||||
:emphasize-lines: 2-4
|
||||
|
||||
Length auto dist = 2_q_m;
|
||||
Length auto dist = 2 * m;
|
||||
Length auto res1 = dist * 2; // 4 m
|
||||
Length auto res2 = 3 * res1; // 12 m
|
||||
Length auto res3 = res2 / 2; // 6 m
|
||||
@ -44,9 +44,9 @@ probably will always end up in a quantity of a yet another dimension:
|
||||
.. code-block::
|
||||
:emphasize-lines: 4-6
|
||||
|
||||
Length auto dist1 = 2_q_m;
|
||||
Length auto dist2 = 3_q_m;
|
||||
Time auto dur1 = 2_q_s;
|
||||
Length auto dist1 = 2 * m;
|
||||
Length auto dist2 = 3 * m;
|
||||
Time auto dur1 = 2 * s;
|
||||
Area auto res1 = dist1 * dist2; // 6 m²
|
||||
Speed auto res2 = dist1 / dur1; // 1 m/s
|
||||
Frequency auto res3 = 10 / dur1; // 5 Hz
|
||||
@ -58,9 +58,9 @@ dimension, than we will end up with just a dimensionless quantity:
|
||||
.. code-block::
|
||||
:emphasize-lines: 4-5
|
||||
|
||||
Time auto dur1 = 10_q_s;
|
||||
Time auto dur2 = 2_q_s;
|
||||
Frequency auto fr1 = 5_q_Hz;
|
||||
Time auto dur1 = 10 * s;
|
||||
Time auto dur2 = 2 * s;
|
||||
Frequency auto fr1 = 5 * Hz;
|
||||
Dimensionless auto v1 = dur1 / dur2; // quantity(5)
|
||||
Dimensionless auto v2 = dur1 * fr1; // quantity(50)
|
||||
|
||||
@ -112,8 +112,8 @@ The result will always be a quantity point of the same dimension:
|
||||
.. code-block::
|
||||
:emphasize-lines: 3-5
|
||||
|
||||
Length auto dist1 = 2_q_m;
|
||||
Length auto dist2 = 1_q_m;
|
||||
Length auto dist1 = 2 * m;
|
||||
Length auto dist2 = 1 * m;
|
||||
QuantityPoint auto res1 = quantity_point{dist1} + dist2;
|
||||
QuantityPoint auto res2 = dist1 + quantity_point{dist2};
|
||||
QuantityPoint auto res3 = quantity_point{dist1} - dist2;
|
||||
@ -124,8 +124,8 @@ The result is a relative quantity of the same dimension:
|
||||
.. code-block::
|
||||
:emphasize-lines: 3
|
||||
|
||||
Length auto dist1 = 2_q_m;
|
||||
Length auto dist2 = 1_q_m;
|
||||
Length auto dist1 = 2 * m;
|
||||
Length auto dist2 = 1 * m;
|
||||
Length auto res1 = quantity_point{dist1} - quantity_point{dist2};
|
||||
|
||||
.. note::
|
||||
@ -139,11 +139,11 @@ The result is a relative quantity of the same dimension:
|
||||
.. code-block::
|
||||
:emphasize-lines: 3-5
|
||||
|
||||
Length auto dist1 = 2_q_m;
|
||||
Length auto dist2 = 1_q_m;
|
||||
Length auto dist1 = 2 * m;
|
||||
Length auto dist2 = 1 * m;
|
||||
auto res1 = quantity_point{dist1} + quantity_point{dist2}; // ERROR
|
||||
auto res2 = dist1 - quantity_point{dist2}; // ERROR
|
||||
auto res3 = quantity_point{dist1} / 2_q_s; // ERROR
|
||||
auto res3 = quantity_point{dist1} / (2 * s); // ERROR
|
||||
|
||||
Quantity Point Kinds
|
||||
++++++++++++++++++++
|
||||
|
@ -50,12 +50,44 @@ Thanks to that, the above example can be rewritten as follows::
|
||||
si::length<si::kilometre> d(123);
|
||||
si::speed<si::kilometre_per_hour, int> v(70);
|
||||
|
||||
Quantity References
|
||||
+++++++++++++++++++
|
||||
|
||||
Quantity References provide an alternative and simplified way to create quantities.
|
||||
They are defined using the `reference` class template::
|
||||
|
||||
namespace references {
|
||||
|
||||
inline constexpr auto km = reference<dim_length, kilometre>{};
|
||||
inline constexpr auto h = reference<dim_time, hour>{};
|
||||
|
||||
}
|
||||
|
||||
With the above our code can look as follows::
|
||||
|
||||
using namespace units::isq::si::references;
|
||||
auto d = 123. * km; // si::length<si::kilometre, double>
|
||||
auto v = 70 * (km / h); // si::speed<si::kilometre_per_hour, int>
|
||||
|
||||
.. important::
|
||||
|
||||
The following syntaxes are not allowed:
|
||||
``2 / s``, ``km * 3``, ``s / 4``, ``70 * km / h``.
|
||||
|
||||
It is also possible to easily define custom quantity references from existing ones::
|
||||
|
||||
inline constexpr auto Nm = N * m;
|
||||
inline constexpr auto km_per_h = km / h;
|
||||
inline constexpr auto mph = mi / h;
|
||||
|
||||
|
||||
User Defined Literals
|
||||
+++++++++++++++++++++
|
||||
|
||||
Alternatively, to construct quantities with compile-time known values the library provides
|
||||
:abbr:`UDL (User Defined Literal)` s for each :term:`unit` of every :term:`dimension`::
|
||||
|
||||
To further simplify construction of quantities with compile-time known
|
||||
values the library provides :abbr:`UDL (User Defined Literal)` s for each
|
||||
:term:`unit` of every :term:`dimension`::
|
||||
#if UNITS_UDLS
|
||||
|
||||
inline namespace literals {
|
||||
|
||||
@ -67,8 +99,12 @@ values the library provides :abbr:`UDL (User Defined Literal)` s for each
|
||||
|
||||
}
|
||||
|
||||
#endif // UNITS_UDLS
|
||||
|
||||
Thanks to them the same code can be as simple as::
|
||||
|
||||
#define UNITS_UDLS 1
|
||||
|
||||
using namespace units::isq::si::literals;
|
||||
auto d = 123._q_km; // si::length<si::kilometre, long double>
|
||||
auto v = 70_q_km_per_h; // si::speed<si::kilometre_per_hour, std::int64_t>
|
||||
@ -82,37 +118,13 @@ Thanks to them the same code can be as simple as::
|
||||
``d`` (day), ``l`` or ``L`` (litre), ``erg``, ``ergps``). This is why the
|
||||
``_q_`` prefix was consistently applied to all the UDLs.
|
||||
|
||||
|
||||
Quantity References
|
||||
+++++++++++++++++++
|
||||
|
||||
Quantity References provide an alternative way to simplify quantities creation.
|
||||
They are defined using the `reference` class template::
|
||||
|
||||
namespace references {
|
||||
|
||||
inline constexpr auto km = reference<dim_length, kilometre>{};
|
||||
inline constexpr auto h = reference<dim_time, hour>{};
|
||||
|
||||
}
|
||||
|
||||
With the above our code can look as follows::
|
||||
|
||||
using namespace units::isq::si::references;
|
||||
auto d = 123. * km; // si::length<si::kilometre, double>
|
||||
auto v = 70 * (km / h); // si::speed<si::kilometre_per_hour, int>
|
||||
|
||||
.. important::
|
||||
|
||||
The following syntaxes are not allowed:
|
||||
``2 / s``, ``km * 3``, ``s / 4``, ``70 * km / h``.
|
||||
|
||||
It is also allowed to easily define custom quantity references from existing ones::
|
||||
|
||||
inline constexpr auto Nm = N * m;
|
||||
inline constexpr auto km_per_h = km / h;
|
||||
inline constexpr auto mph = mi / h;
|
||||
As one can read in the next section UDLs, are considered to be inferior to `Quantity References`_
|
||||
and their definition affects compile-time performance a lot. This is why they are an opt-in feature
|
||||
of the library and in order to use them one has to provide a `UNITS_UDL` preprocessor definition.
|
||||
|
||||
|
||||
UDLs vs Quantity References
|
||||
+++++++++++++++++++++++++++
|
||||
|
||||
@ -161,7 +173,10 @@ UDLs are helpful but they also have some disadvantages compared to Quantity Refe
|
||||
auto d1 = 123._q_km; // si::length<si::kilometre, long double>
|
||||
auto d2 = 123_q_km; // si::length<si::kilometre, std::int64_t>
|
||||
|
||||
No possibility to obtain any other representation type.
|
||||
No possibility to obtain any other representation type. Additionally this gets contagious
|
||||
as the result of every arithmetic expression on quantities is always expanded to the common
|
||||
type of its arguments. For example `si::length<si::metre, int>(1) + 1_q_m` results in a
|
||||
`si::length<si::metre, int64_t>` type.
|
||||
|
||||
- Quantity References::
|
||||
|
||||
@ -183,7 +198,8 @@ UDLs are helpful but they also have some disadvantages compared to Quantity Refe
|
||||
- Quantity References:
|
||||
|
||||
- one reference per unit
|
||||
- unnamed derived units constructed from base references (i.e. ``km / h``)
|
||||
- unnamed derived units are constructed from base references so no explicit
|
||||
definition is required (i.e. ``km / h``)
|
||||
|
||||
5. Typical UDL definition for quantities when compiled with a ``-Wsign-conversion``
|
||||
flag results in a compilation warning. This warning could be silenced with a
|
||||
@ -192,6 +208,24 @@ UDLs are helpful but they also have some disadvantages compared to Quantity Refe
|
||||
Quantity References, on the opposite, always use the exact representation type provided
|
||||
by the user so there is no chance for a truncating conversion on a quantity construction.
|
||||
|
||||
6. UDLs take long to compile
|
||||
|
||||
- UDLs:
|
||||
|
||||
Every unit requires two UDLs to be defined which in turns requires two instantiations
|
||||
of "heavy" `quantity` class template. Those are then not often used by non-UDL construction
|
||||
as most users instantiate `quantity` class template with `int` or `double` which
|
||||
again have to be separately instantiated. This has a significant impact on the compile-time
|
||||
performance.
|
||||
|
||||
- Quantity References:
|
||||
|
||||
`reference` class template is "cheaper" to instantiate. Additionally, every unit requires
|
||||
only one instantiation of a `reference` class template. Such pre-defined reference instance
|
||||
is then shared among all the instantiations of `quantity` class template for this specific
|
||||
unit (no matter of its representation type). With this approach we end up with much less class
|
||||
template instantiations in the application.
|
||||
|
||||
|
||||
Dimension-specific Concepts
|
||||
---------------------------
|
||||
@ -200,8 +234,8 @@ In case the user does not care about the specific unit and representation but
|
||||
requires quantity of a concrete dimension than dimension-specific concepts can
|
||||
be used::
|
||||
|
||||
using namespace units::isq::si::literals;
|
||||
constexpr Length auto d = 123_q_km; // si::length<si::kilometre, std::int64_t>
|
||||
using namespace units::isq::si::references;
|
||||
constexpr Length auto d = 123 * km; // si::length<si::kilometre, int>
|
||||
|
||||
.. note::
|
||||
|
||||
@ -215,10 +249,10 @@ assume that the user wants to implement an ``avg_speed`` function that will
|
||||
be calculating the average speed based on provided distance and duration
|
||||
quantities. The usage of such a function can look as follows::
|
||||
|
||||
using namespace units::isq::si::literals;
|
||||
using namespace units::isq::si::international::literals;
|
||||
constexpr Speed auto v1 = avg_speed(220_q_km, 2_q_h);
|
||||
constexpr Speed auto v2 = avg_speed(140_q_mi, 2_q_h);
|
||||
using namespace units::isq::si::references;
|
||||
using namespace units::isq::si::international::references;
|
||||
constexpr Speed auto v1 = avg_speed(220 * km, 2 * h);
|
||||
constexpr Speed auto v2 = avg_speed(140 * mi, 2 * h);
|
||||
|
||||
In this and all other physical units libraries such a function can be
|
||||
implemented as::
|
||||
@ -276,7 +310,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(220_q_km, 2_q_h);
|
||||
constexpr Speed auto v = avg_speed(220 * km, 2 * h);
|
||||
using quantity_type = decltype(v);
|
||||
using dimension_type = quantity_type::dimension;
|
||||
using unit_type = quantity_type::unit;
|
||||
@ -284,7 +318,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, 2_q_h);
|
||||
constexpr Speed auto v1 = avg_speed(220. * km, 2 * h);
|
||||
constexpr si::speed<si::metre_per_second> v2 = v1;
|
||||
constexpr Speed auto v3 = quantity_cast<si::speed<si::metre_per_second>(v1);
|
||||
|
||||
@ -300,8 +334,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(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>>);
|
||||
static_assert(10 * km / (5 * km) == 2);
|
||||
static_assert(std::is_same_v<decltype(10 * km / (5 * km)), quantity<dim_one, one, int>>);
|
||||
|
||||
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
|
||||
@ -330,7 +364,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. * m / (100. * m)) << '\n';
|
||||
|
||||
will print ``50 %`` to the console output.
|
||||
|
||||
|
@ -14,7 +14,7 @@ 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(123_q_km);
|
||||
quantity_point<si::dim_length, si::kilometre, double> d(123 * 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 = 123_q_km; // ERROR
|
||||
quantity_point<si::dim_length, si::kilometre, double> d = 123 * km; // ERROR
|
||||
|
||||
|
||||
Differences To Quantity
|
||||
|
@ -21,10 +21,10 @@ Output Streams
|
||||
The easiest way to print a quantity is to provide its object to the output
|
||||
stream::
|
||||
|
||||
using namespace units::isq::si::literals;
|
||||
using namespace units::isq::si::international::literals;
|
||||
constexpr Speed auto v1 = avg_speed(220._q_km, 2_q_h);
|
||||
constexpr Speed auto v2 = avg_speed(140._q_mi, 2_q_h);
|
||||
using namespace units::isq::si::references;
|
||||
using namespace units::isq::si::international::references;
|
||||
constexpr Speed auto v1 = avg_speed(220. * km, 2 * h);
|
||||
constexpr Speed auto v2 = avg_speed(140. * mi, 2 * h);
|
||||
std::cout << v1 << '\n'; // 110 km/h
|
||||
std::cout << v2 << '\n'; // 70 mi/h
|
||||
|
||||
@ -46,9 +46,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) << 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|
|
||||
os << "|" << std::setw(10) << 123 * m << "|"; // | 123 m|
|
||||
os << "|" << std::setw(10) << std::left << 123 * m << "|"; // |123 m |
|
||||
os << "|" << std::setw(10) << std::setfill('*') << 123 * m << "|"; // |*****123 m|
|
||||
|
||||
|
||||
fmt::format
|
||||
@ -100,9 +100,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: " << 123_q_km << "\n";
|
||||
fmt::print("Distance: {}\n", 123_q_km);
|
||||
fmt::print("Distance: {:%Q %q}\n", 123_q_km);
|
||||
std::cout << "Distance: " << 123 * km << "\n";
|
||||
fmt::print("Distance: {}\n", 123 * km);
|
||||
fmt::print("Distance: {:%Q %q}\n", 123 * km);
|
||||
|
||||
|
||||
Quantity Value, Symbol, or Both?
|
||||
@ -111,9 +111,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}", 123_q_km); // 123
|
||||
fmt::print("{:%q}", 123_q_km); // km
|
||||
fmt::print("{:%Q%q}", 123_q_km); // 123km
|
||||
fmt::print("{:%Q}", 123 * km); // 123
|
||||
fmt::print("{:%q}", 123 * km); // km
|
||||
fmt::print("{:%Q%q}", 123 * km); // 123km
|
||||
|
||||
|
||||
Controlling Width, Fill, and Alignment
|
||||
@ -123,14 +123,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}|", 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***|
|
||||
fmt::print("|{:0}|", 123 * m); // |123 m|
|
||||
fmt::print("|{:10}|", 123 * m); // | 123 m|
|
||||
fmt::print("|{:<10}|", 123 * m); // |123 m |
|
||||
fmt::print("|{:>10}|", 123 * m); // | 123 m|
|
||||
fmt::print("|{:^10}|", 123 * m); // | 123 m |
|
||||
fmt::print("|{:*<10}|", 123 * m); // |123 m*****|
|
||||
fmt::print("|{:*>10}|", 123 * m); // |*****123 m|
|
||||
fmt::print("|{:*^10}|", 123 * m); // |**123 m***|
|
||||
|
||||
|
||||
ASCII-only Quantity Symbols
|
||||
@ -142,12 +142,13 @@ 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("{}", 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
|
||||
fmt::print("{}", 10 * R); // 10 Ω
|
||||
fmt::print("{:%Q %Aq}", 10 * R); // 10 ohm
|
||||
fmt::print("{}", 125 * us); // 125 µs
|
||||
fmt::print("{:%Q %Aq}", 125 * us); // 125 us
|
||||
inline constexpr auto s2 = s * s;
|
||||
fmt::print("{}", 9.8 * (m / s2)); // 9.8 m/s²
|
||||
fmt::print("{:%Q %Aq}", 9.8 * (m / s2)); // 9.8 m/s^2
|
||||
|
||||
|
||||
Controlling on How the Quantity Value Is Being Printed
|
||||
@ -155,8 +156,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}", 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
|
||||
fmt::print("{0:%Q %q},{0:%+Q %q},{0:%-Q %q},{0:% Q %q}", 1 * m); // 1 m,+1 m,1 m, 1 m
|
||||
fmt::print("{0:%Q %q},{0:%+Q %q},{0:%-Q %q},{0:% Q %q}", -1 * m); // -1 m,-1 m,-1 m,-1 m
|
||||
|
||||
where:
|
||||
|
||||
@ -168,47 +169,47 @@ where:
|
||||
|
||||
``precision`` token is allowed only for floating-point representation types::
|
||||
|
||||
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
|
||||
fmt::print("{:%.0Q %q}", 1.2345 * m); // 1 m
|
||||
fmt::print("{:%.1Q %q}", 1.2345 * m); // 1.2 m
|
||||
fmt::print("{:%.2Q %q}", 1.2345 * 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}", 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
|
||||
fmt::print("{:%bQ %q}", 42 * m); // 101010 m
|
||||
fmt::print("{:%BQ %q}", 42 * m); // 101010 m
|
||||
fmt::print("{:%dQ %q}", 42 * m); // 42 m
|
||||
fmt::print("{:%oQ %q}", 42 * m); // 52 m
|
||||
fmt::print("{:%xQ %q}", 42 * m); // 2a m
|
||||
fmt::print("{:%XQ %q}", 42 * m); // 2A m
|
||||
|
||||
The above can be printed in an alternate version thanks to the ``#`` token::
|
||||
|
||||
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
|
||||
fmt::print("{:%#bQ %q}", 42 * m); // 0b101010 m
|
||||
fmt::print("{:%#BQ %q}", 42 * m); // 0B101010 m
|
||||
fmt::print("{:%#oQ %q}", 42 * m); // 052 m
|
||||
fmt::print("{:%#xQ %q}", 42 * m); // 0x2a m
|
||||
fmt::print("{:%#XQ %q}", 42 * m); // 0X2A m
|
||||
|
||||
For floating-point values the :token:`units-rep-type` token works as follows::
|
||||
|
||||
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
|
||||
fmt::print("{:%aQ %q}", 1.2345678 * m); // 0x9.e065152d8eae841p-3 m
|
||||
fmt::print("{:%.3aQ %q}", 1.2345678 * m); // 0x9.e06p-3 m
|
||||
fmt::print("{:%AQ %q}", 1.2345678 * m); // 0X9.E065152D8EAE841P-3 m
|
||||
fmt::print("{:%.3AQ %q}", 1.2345678 * m); // 0X9.E06P-3 m
|
||||
fmt::print("{:%eQ %q}", 1.2345678 * m); // 1.234568e+00 m
|
||||
fmt::print("{:%.3eQ %q}", 1.2345678 * m); // 1.235e+00 m
|
||||
fmt::print("{:%EQ %q}", 1.2345678 * m); // 1.234568E+00 m
|
||||
fmt::print("{:%.3EQ %q}", 1.2345678 * m); // 1.235E+00 m
|
||||
fmt::print("{:%gQ %q}", 1.2345678 * m); // 1.23457 m
|
||||
fmt::print("{:%gQ %q}", 1.2345678e8 * m); // 1.23457e+08 m
|
||||
fmt::print("{:%.3gQ %q}", 1.2345678 * m); // 1.23 m
|
||||
fmt::print("{:%.3gQ %q}", 1.2345678e8 * m); // 1.23e+08 m
|
||||
fmt::print("{:%GQ %q}", 1.2345678 * m); // 1.23457 m
|
||||
fmt::print("{:%GQ %q}", 1.2345678e8 * m); // 1.23457E+08 m
|
||||
fmt::print("{:%.3GQ %q}", 1.2345678 * m); // 1.23 m
|
||||
fmt::print("{:%.3GQ %q}", 1.2345678e8 * m); // 1.23E+08 m
|
||||
|
||||
|
||||
Special Signs
|
||||
@ -217,7 +218,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}", 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
|
||||
fmt::print("{:%Q_%q}", 123 * km); // 123_km
|
||||
fmt::print("{:%Q%t%q}", 123 * km); // 123\tkm <tab>
|
||||
fmt::print("{:%Q%n%q}", 123 * km); // 123\nkm <new line>
|
||||
fmt::print("{:%Q%% %q}", 123 * km); // 123% km
|
||||
|
@ -388,9 +388,9 @@ and user should not instantiate it by him/her-self. However the user can sometim
|
||||
observe this type in case an unit/dimension conversion expression will end up with an
|
||||
unknown/undefined unit type like in the below example::
|
||||
|
||||
using namespace units::isq::si::literals;
|
||||
using namespace units::isq::si::references;
|
||||
|
||||
Length auto l = 100_q_km_per_h * 10_q_s;
|
||||
Length auto l = 100 * (km / h) * (10 * s);
|
||||
|
||||
The type of ``l`` above will be
|
||||
``si::length<scaled_unit<ratio(1, 36, 1), si::metre>, long double>``. This is caused
|
||||
|
@ -92,7 +92,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 = 123_q_m;
|
||||
Length auto d = 123 * 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
|
||||
|
@ -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 / 20_q_min);
|
||||
DeskRate auto rate = quantity_cast<desk_per_hour>(3._d / (20 * 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::
|
||||
|
@ -38,9 +38,9 @@ such an explicit conversion::
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static_assert(quantity{1s} + 1_q_s == 2_q_s);
|
||||
static_assert(quantity{1s} + 1_q_min == 61_q_s);
|
||||
static_assert(10_q_m / quantity{2s} == 5_q_m_per_s);
|
||||
static_assert(quantity{1s} + 1 * s == 2 * s);
|
||||
static_assert(quantity{1s} + 1 * min == 61 * s);
|
||||
static_assert(10 * m / quantity{2s} == 5 * (m / s));
|
||||
|
||||
.. note::
|
||||
|
||||
@ -63,4 +63,4 @@ provide a deduction guide from `QuantityPointLike`::
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static_assert(quantity_point{std::chrono::sys_seconds{1s}} + 1_q_s == quantity_point{2s});
|
||||
static_assert(quantity_point{std::chrono::sys_seconds{1s}} + 1 * s == quantity_point{2s});
|
||||
|
@ -27,9 +27,9 @@ 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 = { 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 };
|
||||
fs_vector<si::length<si::metre>, 3> v = { 1 * m, 2 * m, 3 * m };
|
||||
fs_vector<si::length<si::metre>, 3> u = { 3 * m, 2 * m, 1 * m };
|
||||
fs_vector<si::length<si::kilometre>, 3> t = { 3 * km, 2 * km, 1 * km };
|
||||
|
||||
Having such definitions we can perform full dimensional analysis operations for the operations
|
||||
allowed by the Linear Algebra rules. For example::
|
||||
@ -38,7 +38,7 @@ allowed by the Linear Algebra rules. For example::
|
||||
std::cout << "v + t = " << v + t << "\n";
|
||||
std::cout << "t[m] = " << fs_vector<si::length<si::metre>, 3>(t) << "\n";
|
||||
std::cout << "v * u = " << v * u << "\n";
|
||||
std::cout << "2_q_m * v = " << 2_q_m * v << "\n";
|
||||
std::cout << "2 * m * v = " << 2 * m * v << "\n";
|
||||
|
||||
The above code works as expected and produces the following output:
|
||||
|
||||
@ -48,7 +48,7 @@ The above code works as expected and produces the following output:
|
||||
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² |
|
||||
2 * m * v = | 2 m² 4 m² 6 m² |
|
||||
|
||||
|
||||
Quantities of Linear Algebra Types
|
||||
@ -79,7 +79,7 @@ output:
|
||||
v + t = | 3001 2002 1003 | m
|
||||
t[m] = | 3000 2000 1000 | m
|
||||
v * u = 10 m²
|
||||
2_q_m * v = | 2 4 6 | m²
|
||||
2 * m * v = | 2 4 6 | m²
|
||||
|
||||
|
||||
.. seealso::
|
||||
|
@ -25,8 +25,9 @@ The same applies to the resulting unit. For example:
|
||||
#include <units/isq/si/si.h>
|
||||
|
||||
using namespace units::isq::si;
|
||||
using namespace units::isq::si::references;
|
||||
|
||||
constexpr auto result = 144_q_km / 2_q_h;
|
||||
constexpr auto result = 144 * km / (2 * h);
|
||||
static_assert(std::is_same_v<decltype(result)::dimension,
|
||||
dim_speed>);
|
||||
static_assert(std::is_same_v<decltype(result)::unit,
|
||||
@ -45,8 +46,9 @@ dimensions used in the division operation:
|
||||
#include <units/isq/si/time.h>
|
||||
|
||||
using namespace units::isq::si;
|
||||
using namespace units::isq::si::references;
|
||||
|
||||
constexpr auto result = 144_q_km / 2_q_h;
|
||||
constexpr auto result = 144 * km / (2 * h);
|
||||
static_assert(std::is_same_v<decltype(result)::dimension,
|
||||
unknown_dimension<exponent<dim_length, 1>, exponent<dim_time, -1>>>);
|
||||
static_assert(std::is_same_v<decltype(result)::unit,
|
||||
@ -103,10 +105,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 * 200_q_km; // intermediate unknown dimension
|
||||
auto temp1 = s1 * (200 * km); // intermediate unknown dimension
|
||||
|
||||
Speed auto s2 = temp1 / 50_q_km; // back to known dimensions again
|
||||
Length auto d2 = s2 * 4_q_h;
|
||||
Speed auto s2 = temp1 / (50 * km); // back to known dimensions again
|
||||
Length auto d2 = s2 * (4 * h);
|
||||
|
||||
// ...
|
||||
}
|
||||
|
Reference in New Issue
Block a user