From 58f21e7e1c825e285c5e06083c4bf88faa24b37b Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 16 Apr 2021 19:03:30 +0200 Subject: [PATCH] docs: Quantity Construction Helpers chapter updated --- docs/framework/quantities.rst | 250 +++++++++++++++++++++++++++++++++- 1 file changed, 244 insertions(+), 6 deletions(-) diff --git a/docs/framework/quantities.rst b/docs/framework/quantities.rst index 2bad4158..23786c50 100644 --- a/docs/framework/quantities.rst +++ b/docs/framework/quantities.rst @@ -28,8 +28,22 @@ the value to the `quantity` class template explicit constructor:: quantity 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 `_. + + 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 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 d(123); si::speed 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 using m = units::isq::si::length; + template using km = units::isq::si::length; + + } + + namespace units::aliases::isq::si::inline speed { + + template using m_per_s = units::isq::si::speed; + template using km_per_h = units::isq::si::speed; + + } + + #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 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. 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; -User Defined Literals -+++++++++++++++++++++ +User Defined Literals (Experimental) +++++++++++++++++++++++++++++++++++++ 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`:: @@ -235,6 +295,184 @@ UDLs are helpful but they also have some disadvantages compared to Quantity Refe 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 `_). + In other cases user is forced to rename its local identifiers to not collide with predefined + references (see `capacitor_time_curve `_). + + - 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 using km_per_h = units::isq::si::speed; + +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()`` which is not possible with references:: + + constexpr auto meter = 1 * m; + std::cout << " = " << quantity_cast(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 ---------------------------