mp-units
mp-units copied to clipboard
How to constrain a derived unit for a specific quantity kind?
I just played with a simple example:
inline constexpr struct fuel_consumption final : quantity_spec<isq::volume / isq::length> {} fuel_consumption;
inline constexpr auto l_per_100km = si::litre / (mag<100> * si::kilo<si::metre>);
quantity q1 = 5.8 * l_per_100km;
quantity q2 = fuel_consumption(6.7 * l_per_100km);
static_assert(q1.dimension == isq::area.dimension);
static_assert(q2.dimension == isq::area.dimension);
static_assert(implicitly_convertible(q1.quantity_spec, isq::area));
static_assert(!castable(q2.quantity_spec, isq::area));
it was really nice until now, but then I noticed that I can do the following 😱:
quantity q3 = q1.in(m2);
quantity q4 = q2.in(m2);
quantity q5 = isq::area(42 * l_per_100km);
quantity<isq::area[m2]> q6 = 42 * l_per_100km;
Do we have any ideas on how to improve here?
The same could be used to constrain N m to be used only with moment of force or V A to be used with apparent power.
There are several problems here:
- Derived units are not predefined in the code (they are dynamically composed as a result of unit equations), so there is no class template defined by the user where we could provide such a constraint.
- What about the scaled versions of those? Even if we invent some type trait to externally opt-in
m2as a unit of area, what aboutcm2and others? - Such a type trait would be inconsistent with what we have for named units already. For consistency, we could be forced to move the support of all the units to a type trait. This is cumbersome, requires more than one line for a unit definition, and would break our users.
The same could be used to constrain
N mto be used only with moment of force orV Ato be used with apparent power.
This is backwards.
N * m should be unconstrained.
Moment of force can require N * m.
Well, it has at least two problems as well:
- moment of force is a quantity being a part of the system of quantity, and those should be units agnostic.
- moment of force can also at least use
kg m^2 s^-2so it can't require only one unit.
You're right, quantities are unit-agnostic in the ISQ.
A number and a reference together form a quantity value. A unit is just that, but defined and adopted by convention.
In mp_units' code, however, they're different.
So you might want to replicate for units what makes quantity q5 = isq::area(q2); fail for quantity.
Or you can make l_per_100km a quantity of fuel_consumption, and it (mostly?) works: https://godbolt.org/z/jEaffYbxx.
Interesting approach 😉 It has the "chicken egg problem", though. How can we define the first quantity in terms of a unit that is a quantity? Also, I wouldn't like to embed a representation type and its value into unit definitions.
So you might want to replicate for units what makes
quantity q5 = isq::area(q2);fail forquantity.
By this, I think making what a quantity kind does for quantity, but for units.
If
inline constexpr struct fuel_consumption final : quantity_spec<isq::volume / isq::length> {} fuel_consumption;
makes
static_assert(!castable(q2.quantity_spec, isq::area));
work, then perhaps
inline constexpr struct l_per_100km final : unit_spec<si::litre / (mag<100> * si::kilo<si::metre>)> {} l_per_100km;
could make
quantity q4 = q2.in(m2);
quantity q5 = isq::area(42 * l_per_100km);
quantity<isq::area[m2]> q6 = 42 * l_per_100km;
not work.
I am afraid that even though it could improve the case here, it would break plenty of other use cases. For example, https://mpusz.github.io/mp-units/latest/users_guide/framework_basics/systems_of_units/#many-shades-of-the-same-unit.
Units have very different rules than quantities, and we probably should not try them to work the same.
Just leave those units and examples untouched. An "unit kind"s would be, specifically, for use cases like those in this issue.
Side question: for something like volume / length, are we doing the Anthony Williams thing of not simplifying numerator and denominator? Or is it just already basically equivalent to area?
We never simplified volume / length. This was always mp-units thing ;-) We simplify only the same type identifiers.