# Generic Interfaces Using a concrete unit in the interface often makes a lot of sense. It is especially useful if we 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 base_; quantity height_; quantity density_ = air_density; public: constexpr StorageTank(const quantity& base, const quantity& 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 quantity avg_speed(quantity distance, quantity duration) { return distance / duration; } ``` Everything seems fine for now. It also works great if we call it with: ```cpp quantity s1 = avg_speed(220 * km, 2 * h); ``` However, if the user starts doing the following: ```cpp quantity s2 = avg_speed(140 * mi, 2 * h); quantity s3 = avg_speed(20 * m, 2 * s); ``` 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. 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 a unit being the derived unit of the initial function's arguments. 3. Besides the obvious runtime cost, some unit conversions may result in a value truncation, which 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 [value-preserving](value_conversions.md#value-preserving-conversions). 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 dedicated [`value_cast` or `force_in`](value_conversions.md#value-truncating-conversions) like this: ```cpp quantity s2 = avg_speed(value_cast(140 * mi), 2 * h); quantity s3 = avg_speed((20 * m).force_in(km), (2 * s).force_in(h)); ``` but the above will obviously provide an incorrect behavior (e.g., division by `0` in the evaluation 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; } ``` Beware, this is not a good solution. The above code is too generic. Such a function template accepts everything: - quantities of other types - the compiler will not prevent accidental reordering of the function's arguments, - quantities of different types can be passed as well, - plain `double` arguments, - `std::vector` and `std::lock_guard` will be accepted as well (of course, this will fail in the instantiation of a function's body later in the compilation process). !!! 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 Much better generic code can be implemented using [basic concepts](concepts.md) provided with the library: === "Original template notation" ```cpp template requires QuantityOf && QuantityOf auto avg_speed(Distance distance, Duration duration) { return isq::speed(distance / duration); } ``` === "The shorthand notation" ```cpp template Distance, QuantityOf Duration> auto avg_speed(Distance distance, Duration duration) { return isq::speed(distance / duration); } ``` === "Terse notation" ```cpp auto avg_speed(QuantityOf auto distance, QuantityOf auto duration) { return isq::speed(distance / duration); } ``` This explicitly states that the arguments passed by the user must not only satisfy a [`Quantity`](concepts.md#Quantity) concept, but also their quantity specification must 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 faster on the integral rather than floating-point types. ## 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 auto avg_speed(QuantityOf auto distance, QuantityOf 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 an error in [quantity equations](../../appendix/glossary.md#quantity-equation), we will learn about it right away. ## Constraining a variable on the stack If we know precisely what the function does in its internals and if we know the exact argument types 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); ``` 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 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 auto s1 = avg_speed(220 * km, 2 * h); QuantityOf auto s2 = avg_speed(140 * mi, 2 * h); QuantityOf auto s3 = avg_speed(20 * m, 2 * s); ``` The above explicitly provides additional information about the quantity we are dealing with in 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 the [Basic Concepts](concepts.md) chapter.