forked from mpusz/mp-units
		
	
		
			
	
	
		
			246 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			246 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
|   | # Interface Introduction
 | ||
|  | 
 | ||
|  | ## New style of definitions
 | ||
|  | 
 | ||
|  | The **mp-units** library decided to use a rather unusual pattern to define entities. | ||
|  | Here is how we define `metre` and `second` [SI](../../../appendix/glossary/#si) base units: | ||
|  | 
 | ||
|  | ```cpp | ||
|  | inline constexpr struct metre : named_unit<"m", kind_of<isq::length>> {} metre; | ||
|  | inline constexpr struct second : named_unit<"s", kind_of<isq::time>> {} second; | ||
|  | ``` | ||
|  | 
 | ||
|  | Please note that the above reuses the same identifier for a type and its object. The rationale | ||
|  | behind this is that: | ||
|  | 
 | ||
|  | - Users always work with objects and never have to spell such a type name. | ||
|  | - The types appear in the compilation errors and during debugging. | ||
|  | 
 | ||
|  | !!! info | ||
|  | 
 | ||
|  |     To improve compiler errors' readability and make it easier to correlate them with | ||
|  |     a user's written code, a new idiom in the library is to use the same identifier for | ||
|  |     a type and its instance. | ||
|  | 
 | ||
|  | 
 | ||
|  | ## Entities composability
 | ||
|  | 
 | ||
|  | Many physical units libraries (in C++ or any other programming language) assign strong types | ||
|  | to library entities (i.e. derived units). While `metre_per_second` as a type may not look too | ||
|  | scary, consider, for example, units of angular momentum. If we followed this path, its | ||
|  | [coherent unit](../../../appendix/glossary/#coherent-derived-unit) would look like | ||
|  | `kilogram_metre_sq_per_second`. Now, consider how many scaled versions of this unit would you | ||
|  | predefine in the library to ensure that all users are happy with your choice? | ||
|  | How expensive would it be from the implementation point of view? | ||
|  | What about potential future standardization efforts? | ||
|  | 
 | ||
|  | This is why in **mp-units**, we put a strong requirement to make everything as composable as | ||
|  | possible. For example, to create a quantity with a unit of speed, one may write: | ||
|  | 
 | ||
|  | ```cpp | ||
|  | quantity<si::metre / si::second> q; | ||
|  | ``` | ||
|  | 
 | ||
|  | In case you use such an unit often and would prefer to have a handy helper for it, you can | ||
|  | always do something like this: | ||
|  | 
 | ||
|  | ```cpp | ||
|  | constexpr auto metre_per_second = si::metre / si::second; | ||
|  | quantity<metre_per_second> q; | ||
|  | ``` | ||
|  | 
 | ||
|  | or choose any shorter identifier of your choice. | ||
|  | 
 | ||
|  | Coming back to the angular momentum case, thanks to the composability of units, a user can | ||
|  | create such a quantity in the following way: | ||
|  | 
 | ||
|  | ```cpp | ||
|  | using namespace mp_units::si::unit_symbols; | ||
|  | auto q = la_vector{1, 2, 3} * isq::angular_momentum[kg * m2 / s]; | ||
|  | ``` | ||
|  | 
 | ||
|  | It is a much better solution. It is terse and easy to understand. Please also notice how | ||
|  | easy it is to obtain any scaled version of such a unit (i.e. `mg * square(mm) / min`) | ||
|  | without having to introduce hundreds of types to predefine them. | ||
|  | 
 | ||
|  | 
 | ||
|  | ## Expression templates
 | ||
|  | 
 | ||
|  | The previous chapter provided a rationale for not having predefined types for derived entities. | ||
|  | In many libraries, such an approach results in long and unreadable compilation errors, as | ||
|  | framework-generated types are typically far from being easy to read and understand. | ||
|  | 
 | ||
|  | The **mp-units** library greatly improves the user experience by extensively using | ||
|  | expression templates. Such expressions are used consistently throughout the entire library | ||
|  | to describe the results of: | ||
|  | 
 | ||
|  | - [dimension equation](../../../appendix/glossary/#dimension-equation) - the result is put into | ||
|  |   the `derived_dimension<>` class template | ||
|  | - [quantity equation](../../../appendix/glossary/#quantity-equation) - the result is put into | ||
|  |   the `derived_quantity_spec<>` class template | ||
|  | - [unit equation](../../../appendix/glossary/#unit-equation) - the result is put into | ||
|  |   the `derived_unit<>` class template | ||
|  | 
 | ||
|  | For example, if we take the above-defined base units and put the results of their division into | ||
|  | the quantity class template like this: | ||
|  | 
 | ||
|  | ```cpp | ||
|  | quantity<metre / second> q; | ||
|  | ``` | ||
|  | 
 | ||
|  | we will observe the following type in the debugger | ||
|  | 
 | ||
|  | ``` | ||
|  | (gdb) ptype q | ||
|  | type = class mp_units::quantity<mp_units::derived_unit<metre, mp_units::per<second>>(), double> [with Rep = double] { | ||
|  | ``` | ||
|  | 
 | ||
|  | The same type identifier will be visible in the compilation error (in case it happens). | ||
|  | 
 | ||
|  | !!! info | ||
|  | 
 | ||
|  |     Expressions templates are extensively used throughout the library to improve the readability | ||
|  |     of the resulting types. | ||
|  | 
 | ||
|  | 
 | ||
|  | ### Identities
 | ||
|  | 
 | ||
|  | As mentioned above, equations can be done on dimensions, quantities, and units. Each such domain must | ||
|  | introduce an identity object that can be used in the resulting expressions. Here is the list of | ||
|  | identities used in the library: | ||
|  | 
 | ||
|  | 
 | ||
|  | | Domain Concept |    Identity     | | ||
|  | |----------------|:---------------:| | ||
|  | | `Dimension`    | `dimension_one` | | ||
|  | | `QuantitySpec` | `dimensionless` | | ||
|  | | `Unit`         |      `one`      | | ||
|  | 
 | ||
|  | In the equations, a user can refer to an identity object either explicitly: | ||
|  | 
 | ||
|  | ```cpp | ||
|  | constexpr auto my_unit = one / second; | ||
|  | ``` | ||
|  | 
 | ||
|  | or implicitly: | ||
|  | 
 | ||
|  | ```cpp | ||
|  | constexpr auto my_unit = 1 / second; | ||
|  | ``` | ||
|  | 
 | ||
|  | Both cases with result in the same expression template being generated and put into the wrapper | ||
|  | class template. | ||
|  | 
 | ||
|  | 
 | ||
|  | ### Supported operations and their results
 | ||
|  | 
 | ||
|  | There are only a few operations that one can do on such entities and the result of each of them has | ||
|  | its unique representation in the library: | ||
|  | 
 | ||
|  | |                   Operation                   | Resulting template expression arguments | | ||
|  | |:---------------------------------------------:|:---------------------------------------:| | ||
|  | |                    `A * B`                    |                 `A, B`                  | | ||
|  | |                    `B * A`                    |                 `A, B`                  | | ||
|  | |                    `A * A`                    |              `power<A, 2>`              | | ||
|  | |               `{identity} * A`                |                   `A`                   | | ||
|  | |               `A * {identity}`                |                   `A`                   | | ||
|  | |                    `A / B`                    |               `A, per<B>`               | | ||
|  | |                    `A / A`                    |              `{identity}`               | | ||
|  | |               `A / {identity}`                |                   `A`                   | | ||
|  | |               `{identity} / A`                |          `{identity}, per<A>`           | | ||
|  | |                  `pow<2>(A)`                  |              `power<A, 2>`              | | ||
|  | |             `pow<2>({identity})`              |              `{identity}`               | | ||
|  | |          `sqrt(A)` or `pow<1, 2>(A)`          |            `power<A, 1, 2>`             | | ||
|  | | `sqrt({identity})` or `pow<1, 2>({identity})` |              `{identity}`               | | ||
|  | 
 | ||
|  | 
 | ||
|  | ### Simplifying the resulting expression templates
 | ||
|  | 
 | ||
|  | To limit the length and improve the readability of generated types, there are many rules to simplify | ||
|  | the resulting expression template. | ||
|  | 
 | ||
|  | 1. **Ordering** | ||
|  | 
 | ||
|  |     The resulting comma-separated arguments of multiplication are always sorted according to | ||
|  |     a specific predicate. This is why: | ||
|  | 
 | ||
|  |     ```cpp | ||
|  |     static_assert(A * B == B * A); | ||
|  |     static_assert(std::is_same_v<decltype(A * B), decltype(B * A)>); | ||
|  |     ``` | ||
|  | 
 | ||
|  |     This is probably the most important of all steps, as it allows comparing types and enables the rest of | ||
|  |     simplification rules. | ||
|  | 
 | ||
|  | 2. **Aggregation** | ||
|  | 
 | ||
|  |     In case two of the same identifiers are found next to each other on the argument list they | ||
|  |     will be aggregated in one entry: | ||
|  | 
 | ||
|  |     |              Before              |      After       | | ||
|  |     |:--------------------------------:|:----------------:| | ||
|  |     |              `A, A`              |  `power<A, 2>`   | | ||
|  |     |         `A, power<A, 2>`         |  `power<A, 3>`   | | ||
|  |     |  `power<A, 1, 2>, power<A, 2>`   | `power<A, 5, 2>` | | ||
|  |     | `power<A, 1, 2>, power<A, 1, 2>` |       `A`        | | ||
|  | 
 | ||
|  | 3. **Simplification** | ||
|  | 
 | ||
|  |     In case two of the same identifiers are found in the numerator and denominator argument lists; | ||
|  |     they are being simplified into one entry: | ||
|  | 
 | ||
|  |     |        Before         |        After         | | ||
|  |     |:---------------------:|:--------------------:| | ||
|  |     |      `A, per<A>`      |     `{identity}`     | | ||
|  |     | `power<A, 2>, per<A>` |         `A`          | | ||
|  |     | `power<A, 3>, per<A>` |    `power<A, 2>`     | | ||
|  |     | `A, per<power<A, 2>>` | `{identity}, per<A>` | | ||
|  | 
 | ||
|  | 4. **Repacking** | ||
|  | 
 | ||
|  |     In case an expression uses two results of other operations, the components of its arguments are repacked | ||
|  |     into one resulting type and simplified there. | ||
|  | 
 | ||
|  |     For example, assuming: | ||
|  | 
 | ||
|  |     ```cpp | ||
|  |     constexpr auto X = A / B; | ||
|  |     ``` | ||
|  | 
 | ||
|  |     then: | ||
|  | 
 | ||
|  |     | Operation | Resulting template expression arguments | | ||
|  |     |:---------:|:---------------------------------------:| | ||
|  |     |  `X * B`  |                   `A`                   | | ||
|  |     |  `X * A`  |          `power<A, 2>, per<B>`          | | ||
|  |     |  `X * X`  |     `power<A, 2>, per<power<B, 2>>`     | | ||
|  |     |  `X / X`  |              `{identity}`               | | ||
|  |     |  `X / A`  |          `{identity}, per<B>`           | | ||
|  |     |  `X / B`  |          `A, per<power<B, 2>>`          | | ||
|  | 
 | ||
|  | 
 | ||
|  | ## Example
 | ||
|  | 
 | ||
|  | Thanks to all of the features described above, a user may write the code like this one: | ||
|  | 
 | ||
|  | ```cpp | ||
|  | using namespace mp_units::si::unit_symbols; | ||
|  | auto speed = 60. * isq::speed[km / h]; | ||
|  | auto duration = 8 * s; | ||
|  | auto acceleration = speed / duration; | ||
|  | std::cout << "acceleration: " << acceleration << " (" << acceleration[m / s2] << ")\n"; | ||
|  | ``` | ||
|  | 
 | ||
|  | The `acceleration`, being the result of the above code, has the following type | ||
|  | (after stripping the `mp_units` namespace for brevity): | ||
|  | 
 | ||
|  | ```text | ||
|  | quantity<reference<derived_quantity_spec<isq::speed, per<isq::time>>{}, derived_unit<si::kilo_<si::metre{}>, per<non_si::hour, si::second>>{}>{}, int> | ||
|  | ``` | ||
|  | 
 | ||
|  | and the text output presents: | ||
|  | 
 | ||
|  | ```text | ||
|  | acceleration: 7.5 km h⁻¹ s⁻¹ (2.08333 m/s²) | ||
|  | ``` |