docs: Quantity Construction Helpers chapter updated

This commit is contained in:
Mateusz Pusz
2021-04-16 19:03:30 +02:00
parent e88cdadd5f
commit 58f21e7e1c

View File

@@ -28,8 +28,22 @@ the value to the `quantity` class template explicit constructor::
quantity<si::dim_length, si::kilometre, double> d = 123; // ERROR quantity<si::dim_length, si::kilometre, double> d = 123; // ERROR
Dimension-Specific Aliases
++++++++++++++++++++++++++ Quantity Construction Helpers
-----------------------------
.. important::
Currently the library provides multiple experimental helpers to instantiate
quantities of different dimensions and units. Users are encourages to try them
out, vote, and share feedback in this
`discussion on GitHub <https://github.com/mpusz/units/discussions/274>`_.
Most probably only one of the options will be included in the final product so
please do not hesitate to vote on the one that suits you the best.
Dimension-Specific Aliases (Experimental)
+++++++++++++++++++++++++++++++++++++++++
To simplify `quantity` objects creation the library provides helper aliases for To simplify `quantity` objects creation the library provides helper aliases for
quantities of each :term:`dimension` which additionally set the representation quantities of each :term:`dimension` which additionally set the representation
@@ -50,8 +64,54 @@ Thanks to that, the above example can be rewritten as follows::
si::length<si::kilometre> d(123); si::length<si::kilometre> d(123);
si::speed<si::kilometre_per_hour, int> v(70); si::speed<si::kilometre_per_hour, int> v(70);
Quantity References Unit-Specific Aliases (Experimental)
+++++++++++++++++++ ++++++++++++++++++++++++++++++++++++
Additionally to the dimension-specific aliases there are also ones provided for
each and every :term:`unit` in the library::
#ifdef UNITS_ALIASES
namespace units::aliases::isq::si::inline length {
template<Representation Rep = double> using m = units::isq::si::length<units::isq::si::metre, Rep>;
template<Representation Rep = double> using km = units::isq::si::length<units::isq::si::kilometre, Rep>;
}
namespace units::aliases::isq::si::inline speed {
template<Representation Rep = double> using m_per_s = units::isq::si::speed<units::isq::si::metre_per_second, Rep>;
template<Representation Rep = double> using km_per_h = units::isq::si::speed<units::isq::si::kilometre_per_hour, Rep>;
}
#endif // UNITS_ALIASES
Using the above our code can look as follows::
using namespace units::aliases::isq;
si::length::km<> d(123);
si::speed::km_per_h<int> v(70);
Please note that with the C++20 :abbr:`CTAD (Class Template Argument Deduction)` support
for alias templates the above can be rewritten as::
using namespace units::aliases::isq;
si::length::km d(123.);
si::speed::km_per_h v(70);
which will deduce the representation type automatically from the initializer provided
by the user.
Also, this feature allows to be more terse if desired::
using namespace units::aliases::isq::si;
auto d = km(123.);
auto v = km_per_h(70);
Quantity References (Experimental)
++++++++++++++++++++++++++++++++++
Quantity References provide an alternative and simplified way to create quantities. Quantity References provide an alternative and simplified way to create quantities.
They are defined using the `reference` class template:: They are defined using the `reference` class template::
@@ -91,8 +151,8 @@ It is also possible to easily define custom quantity references from existing on
inline constexpr auto mph = mi / h; inline constexpr auto mph = mi / h;
User Defined Literals User Defined Literals (Experimental)
+++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++
Alternatively, to construct quantities with compile-time known values the library provides 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`:: :abbr:`UDL (User Defined Literal)` s for each :term:`unit` of every :term:`dimension`::
@@ -235,6 +295,184 @@ UDLs are helpful but they also have some disadvantages compared to Quantity Refe
template instantiations in the application. template instantiations in the application.
Quantity References vs Unit-specific Aliases
++++++++++++++++++++++++++++++++++++++++++++
1. Shadowing issues
- Quantity References
References occupy a pool of many short identifiers which sometimes shadow the variables,
function arguments, or even template parameters provided by the user or other libraries. This
results in warnings being generated by some compilers. The most restrictive here is MSVC which
for example emits a warning of shadowing ``N`` template parameter for an array size provided
in a header file with Newton unit included via namespace declaration in the ``main()`` program
function (see `experimental_angle <https://github.com/mpusz/units/blob/master/example/references/experimental_angle.cpp>`_).
In other cases user is forced to rename its local identifiers to not collide with predefined
references (see `capacitor_time_curve <https://github.com/mpusz/units/blob/master/example/references/capacitor_time_curve.cpp>`_).
- Unit-specific Aliases
As aliases are defined in terms of types rather variables no major shadowing issues were found
so far. In case of identifiers abiguity it was always possible to disambiguate with more
namespaces prefixed in front of the alias.
2. Adjustable verbosity
- Quantity References
References allow creating custom helpers for complex units. Instead of typing::
static_assert(2 * km / (2 * (km / h)) == 1 * h);
one can do the following::
inline constexpr auto kmph = km / h;
static_assert(2 * km / (2 * kmph) == 1 * h);
- Unit-specific Aliases
There is no need to create custom helpers for complex units as most of them are predefined in
a library already. However, this feature also allows controlling verbosity of the code. For
example in the below example ``d1``, ``d2``, and ``d3`` will end up being of the same type
and having the same value::
auto d1 = m(123.45);
double a = 123.45;
auto d2 = m(a);
auto d3 = length::m(a);
3. Readability
- Quantity References
As long as references are easy to understand in the following code::
auto d = 123 * m;
it is not that nice when a variable is used instead of a compile time number::
constexpr Speed auto avg_speed(double d, double t)
{
using namespace units::isq::si::length_references;
using namespace units::isq::si::time_references;
return d * m / (t * s);
}
Notice that if ``using namespace units::isq::si::references;`` was used instead above it could
cause a clash of ``t`` function parameter with ``si::tonne`` unit symbol if ``si/mass.h`` was
included.
- Unit-specific Aliases
The same using aliases can look as follows::
constexpr Speed auto avg_speed(double d, double t)
{
using namespace units::aliases::isq::si;
return m(d) / s(t);
}
or::
constexpr Speed auto avg_speed(double d, double t)
{
using namespace units::aliases::isq::si;
return length::m(d) / time::s(t);
}
4. Operators Precedence
- Quantity References
The syntax for references uses ``*`` operator which has some predefined precedence. This operator
always takes a magnitude or a reference as ``lhs`` and a reference as ``rhs``. All other comibnations
are not allowed. It means that in order to satisfy the operators precedence sometimes quite a lot
of parenthesis have to be sprinkled in the code in order for the code to compile::
static_assert(2 * km / (2 * (km / h)) == 1 * h);
- Unit-specific Aliases
Aliases do not use operator syntax thus they are not affected by the precedence issue.
5. Composition for unnamed derived units
- Quantity References
References have only to be defined for named units. Also for the user's conveniance references are
predefined for units raised to a specific power (e.g. ``m2``, ``km3``, etc). All other derived units
can be constructed using the provided ones already even if they do not correspond to any predefined
dimension::
inline constexpr auto kmph = km / h;
inline constexpr auto kmph3 = kmph * kmph * kmph;
- Unit-specific Aliases
Such a feature is not possible with aliases. In order to create a derived unit a full alias template
has to be explicitly provided::
template<Representation Rep = double> using km_per_h = units::isq::si::speed<units::isq::si::kilometre_per_hour, Rep>;
6. Explicit control over the representation type
Both options here allow to preserve user provide representation type but only aliases allow
to override it if needed.
7. Simplified quantity casting
Aliases can easily replace ``quantity_cast<Unit>()`` which is not possible with references::
constexpr auto meter = 1 * m;
std::cout << " = " << quantity_cast<si::international::foot>(meter) << '\n';
The above code for references may look as follows::
constexpr auto meter = m(1);
std::cout << " = " << international::ft(meter) << '\n';
std::cout << " = " << ft(meter) << '\n';
The above will preserve the representation type of the source type.
8. Compilation performance
For our experiments it seems that aliases are 2-5x faster to compile than references (declaring an
alias template is much faster than instantiating a class template).
Summary
+++++++
+-----------------------------------------------+-------------+------------+---------------+
| Feature | Aliases | References | UDLs |
+===============================================+=============+============+===============+
| Literals and variables support | **Yes** | **Yes** | Literals only |
+-----------------------------------------------+-------------+------------+---------------+
| Preserves user provided representation type | **Yes** | **Yes** | No |
+-----------------------------------------------+-------------+------------+---------------+
| Explicit control over the representation type | **Yes** | No | No |
+-----------------------------------------------+-------------+------------+---------------+
| Possibility to resolve ambiguity | **Yes** | **Yes** | No |
+-----------------------------------------------+-------------+------------+---------------+
| Readability | **Good** | Medium | **Good** |
+-----------------------------------------------+-------------+------------+---------------+
| Hard to resolve shadowing issues | **No** | Yes | **No** |
+-----------------------------------------------+-------------+------------+---------------+
| Operators precedence issue | **No** | Yes | **No** |
+-----------------------------------------------+-------------+------------+---------------+
| Controlled verbosity | **Yes** | No | No |
+-----------------------------------------------+-------------+------------+---------------+
| Easy composition for derived units | No | **Yes** | No |
+-----------------------------------------------+-------------+------------+---------------+
| Simplified quantity casting | **Yes** | No | No |
+-----------------------------------------------+-------------+------------+---------------+
| Implementation and standardization effort | Medium | **Lowest** | Highest |
+-----------------------------------------------+-------------+------------+---------------+
| Compile-time performance | **Fastest** | Medium | Slowest |
+-----------------------------------------------+-------------+------------+---------------+
Dimension-specific Concepts Dimension-specific Concepts
--------------------------- ---------------------------