| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  | # Generic Interfaces
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-26 11:07:21 +01:00
										 |  |  | Using a concrete unit in the interface often makes a lot of sense. It is especially useful if we | 
					
						
							| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  | store the data internally in the object. In such a case, we have to select a specific unit anyway. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | For example, let's consider a simple storage tank: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```cpp | 
					
						
							|  |  |  | class StorageTank { | 
					
						
							|  |  |  |   quantity<horizontal_area[m2]> base_; | 
					
						
							|  |  |  |   quantity<isq::height[m]> height_; | 
					
						
							|  |  |  |   quantity<isq::mass_density[kg / m3]> density_ = air_density; | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  |   constexpr StorageTank(const quantity<horizontal_area[m2]>& base, const quantity<isq::height[m]>& height) : | 
					
						
							|  |  |  |       base_(base), height_(height) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // ... | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | As the quantities provided in the function's interface are then stored in the class, there is probably | 
					
						
							|  |  |  | no sense in using generic interfaces here. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## The issues with unit-specific interfaces
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | However, in many cases, using a specific unit in the interface is counterproductive. Let's consider | 
					
						
							|  |  |  | the following function: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```cpp | 
					
						
							| 
									
										
										
										
											2023-12-26 11:07:21 +01:00
										 |  |  | quantity<km / h> avg_speed(quantity<km> distance, quantity<h> duration) | 
					
						
							| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  | { | 
					
						
							|  |  |  |   return distance / duration; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Everything seems fine for now. It also works great if we call it with: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```cpp | 
					
						
							| 
									
										
										
										
											2023-12-26 11:07:21 +01:00
										 |  |  | quantity<km / h> s1 = avg_speed(220 * km, 2 * h); | 
					
						
							| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | However, if the user starts doing the following: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```cpp | 
					
						
							| 
									
										
										
										
											2023-12-26 11:07:21 +01:00
										 |  |  | quantity<mi / h> s2 = avg_speed(140 * mi, 2 * h); | 
					
						
							|  |  |  | quantity<m / s> s3 = avg_speed(20 * m, 2 * s); | 
					
						
							| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | some issues start to be clearly visible: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 1. The arguments must be converted to units mandated by the function's parameters at each call. | 
					
						
							|  |  |  |    This involves potentially expensive multiplication/division operations at runtime. | 
					
						
							| 
									
										
										
										
											2023-12-26 11:07:21 +01:00
										 |  |  | 2. After the function returns the _speed_ in a unit of `km/h`, another potentially expensive | 
					
						
							|  |  |  |    multiplication/division operations must be performed to convert the resulting quantity into | 
					
						
							| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  |    a unit being the derived unit of the initial function's arguments. | 
					
						
							| 
									
										
										
										
											2023-12-26 11:07:21 +01:00
										 |  |  | 3. Besides the obvious runtime cost, some unit conversions may result in a value truncation, which | 
					
						
							| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  |    means that the result will not be exactly equal to a direct division of the function's arguments. | 
					
						
							|  |  |  | 4. We have to use a floating-point representation type (the `quantity` class template by default uses | 
					
						
							|  |  |  |    `double` as a representation type) which is considered | 
					
						
							| 
									
										
										
										
											2023-12-26 11:07:21 +01:00
										 |  |  |    [value-preserving](value_conversions.md#value-preserving-conversions). | 
					
						
							| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  |    Trying to use an integral type in this scenario will work only for `s1`, while `s2` and `s3` | 
					
						
							|  |  |  |    will fail to compile. Failing to compile is a good thing here as the library tries to prevent | 
					
						
							|  |  |  |    the user from doing a clearly wrong thing. To make the code compile, the user needs to use | 
					
						
							| 
									
										
										
										
											2023-09-13 10:44:50 +02:00
										 |  |  |    dedicated [`value_cast` or `force_in`](value_conversions.md#value-truncating-conversions) like this: | 
					
						
							| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     ```cpp | 
					
						
							|  |  |  |     quantity<isq::speed[mi / h]> s2 = avg_speed(value_cast<km>(140 * mi), 2 * h); | 
					
						
							| 
									
										
										
										
											2023-09-13 10:44:50 +02:00
										 |  |  |     quantity<isq::speed[m / s]> s3 = avg_speed((20 * m).force_in(km), (2 * s).force_in(h)); | 
					
						
							| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  |     ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-26 11:07:21 +01:00
										 |  |  |     but the above will obviously provide an incorrect behavior (e.g., division by `0` in the evaluation | 
					
						
							| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  |     of `s3`). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## A naive solution
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | A naive solution here would be to implement the function as an unconstrained function template: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```cpp | 
					
						
							|  |  |  | auto avg_speed(auto distance, auto duration) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return distance / duration; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-26 11:07:21 +01:00
										 |  |  | Beware, this is not a good solution. The above code is too generic. Such a function template | 
					
						
							| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  | accepts everything: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - quantities of other types | 
					
						
							| 
									
										
										
										
											2023-12-26 11:07:21 +01:00
										 |  |  |     - the compiler will not prevent accidental reordering of the function's arguments, | 
					
						
							|  |  |  |     - quantities of different types can be passed as well, | 
					
						
							|  |  |  | - plain `double` arguments, | 
					
						
							| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  | - `std::vector` and `std::lock_guard` will be accepted as well (of course, this will fail in the | 
					
						
							| 
									
										
										
										
											2023-12-26 11:07:21 +01:00
										 |  |  |   instantiation of a function's body later in the compilation process). | 
					
						
							| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | !!! note | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     The usage of `auto` instead of a function parameter type is a C++20 feature. It makes such | 
					
						
							|  |  |  |     a code a function template where the type of such a parameter will be deduced during | 
					
						
							|  |  |  |     the template instantiation process from the argument type passed by the user. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Constraining function template arguments with concepts
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-31 09:45:42 +01:00
										 |  |  | Much better generic code can be implemented using [basic concepts](concepts.md) | 
					
						
							| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  | provided with the library: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-26 11:07:21 +01:00
										 |  |  | === "Original template notation" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ```cpp | 
					
						
							|  |  |  |     template<typename Distance, typename Duration> | 
					
						
							|  |  |  |       requires QuantityOf<Distance, isq::length> && QuantityOf<Duration, isq::time> | 
					
						
							|  |  |  |     auto avg_speed(Distance distance, Duration duration) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       return isq::speed(distance / duration); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | === "The shorthand notation" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ```cpp | 
					
						
							|  |  |  |     template<QuantityOf<isq::length> Distance, QuantityOf<isq::time> Duration> | 
					
						
							|  |  |  |     auto avg_speed(Distance distance, Duration duration) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       return isq::speed(distance / duration); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | === "Terse notation" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ```cpp | 
					
						
							|  |  |  |     auto avg_speed(QuantityOf<isq::length> auto distance, | 
					
						
							|  |  |  |                    QuantityOf<isq::time> auto duration) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       return isq::speed(distance / duration); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     ``` | 
					
						
							| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | This explicitly states that the arguments passed by the user must not only satisfy | 
					
						
							| 
									
										
										
										
											2023-12-26 11:07:21 +01:00
										 |  |  | a [`Quantity`](concepts.md#Quantity) concept, but also their quantity specification must | 
					
						
							| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  | be implicitly convertible to `isq::length` and `isq::time` accordingly. This no longer leaves | 
					
						
							|  |  |  | room for error while still allowing the compiler to generate the most efficient code. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | !!! tip | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Please note that now it is safe just to use integral types all the way which again | 
					
						
							|  |  |  |     improves the runtime performance as the multiplication/division operations are often | 
					
						
							| 
									
										
										
										
											2023-12-26 11:07:21 +01:00
										 |  |  |     faster on the integral rather than floating-point types. | 
					
						
							| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Constraining function template return type
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The above function template resolves all of the [issues described before](#the-issues-with-unit-specific-interfaces). | 
					
						
							|  |  |  | However, we can do even better here by additionally constraining the return type: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```cpp | 
					
						
							|  |  |  | QuantityOf<isq::speed> auto avg_speed(QuantityOf<isq::length> auto distance, | 
					
						
							|  |  |  |                                       QuantityOf<isq::time> auto duration) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return isq::speed(distance / duration); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Doing so has two important benefits: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 1. It informs the users of our interface about what to expect to be the result of a function | 
					
						
							|  |  |  |    invocation. It is superior to just returning `auto`, which does not provide any hint about | 
					
						
							|  |  |  |    the thing being returned there. | 
					
						
							|  |  |  | 2. Such a concept constrains the type returned from the function. This means that it works as | 
					
						
							|  |  |  |    a unit test to verify if our function actually performs what it is supposed to do. If there is | 
					
						
							| 
									
										
										
										
											2023-08-03 21:23:34 +02:00
										 |  |  |    an error in [quantity equations](../../appendix/glossary.md#quantity-equation), we will learn | 
					
						
							| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  |    about it right away. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Constraining a variable on the stack
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-26 11:07:21 +01:00
										 |  |  | If we know precisely what the function does in its internals and if we know the exact argument types | 
					
						
							| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  | passed to such a function, we often know the exact type that will be returned from its invocation. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | However, if we care about performance, we should often use the generic interfaces described in this | 
					
						
							|  |  |  | chapter. A side effect is that we sometimes are unsure about the return type. Even if we know it | 
					
						
							|  |  |  | today, it might change a week from now due to some code refactoring. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | In such cases, we can again use `auto` to denote the type: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```cpp | 
					
						
							|  |  |  | auto s1 = avg_speed(220 * km, 2 * h); | 
					
						
							|  |  |  | auto s2 = avg_speed(140 * mi, 2 * h); | 
					
						
							|  |  |  | auto s3 = avg_speed(20 * m, 2 * s); | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-26 11:07:21 +01:00
										 |  |  | or benefit from CTAD: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```cpp | 
					
						
							|  |  |  | quantity s1 = avg_speed(220 * km, 2 * h); | 
					
						
							|  |  |  | quantity s2 = avg_speed(140 * mi, 2 * h); | 
					
						
							|  |  |  | quantity s3 = avg_speed(20 * m, 2 * s); | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | *[CTAD]: Class Template Argument Deduction | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | In both cases, it is probably OK to do so as the `avg_speed` function name explicitly provides | 
					
						
							| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  | the information on what to expect as a result. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | In other scenarios where the returned quantity type is not so obvious, it is again helpful to | 
					
						
							|  |  |  | constrain the type with a concept like so: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```cpp | 
					
						
							|  |  |  | QuantityOf<isq::speed> auto s1 = avg_speed(220 * km, 2 * h); | 
					
						
							|  |  |  | QuantityOf<isq::speed> auto s2 = avg_speed(140 * mi, 2 * h); | 
					
						
							|  |  |  | QuantityOf<isq::speed> auto s3 = avg_speed(20 * m, 2 * s); | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-26 11:07:21 +01:00
										 |  |  | The above explicitly provides additional information about the quantity we are dealing with in | 
					
						
							| 
									
										
										
										
											2023-07-09 13:28:07 +02:00
										 |  |  | the code, and it serves as a unit test checking if the "thing" returned from a function is actually | 
					
						
							|  |  |  | what we expected here. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | !!! note | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     The `QuantityOf` and `QuantityPointOf` concepts are probably the most useful, but there | 
					
						
							|  |  |  |     are a few more to play with. A list of all the concepts can be found in | 
					
						
							| 
									
										
										
										
											2023-12-26 11:07:21 +01:00
										 |  |  |     the [Basic Concepts](concepts.md) chapter. |