docs: Documentation now prefers refrences over UDLs

This commit is contained in:
Mateusz Pusz
2021-04-02 13:11:20 +02:00
parent 7cc34646b9
commit c4e9ff7f66
12 changed files with 211 additions and 175 deletions

View File

@ -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

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 = 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::

View File

@ -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
++++++++++++++++++++

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

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 / 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::

View File

@ -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});

View File

@ -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::

View File

@ -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);
// ...
}