mp-units
mp-units copied to clipboard
Should `math.h` be implemented as hidden friends?
Should we implement the functions from the mp_units/math.h header as non-member hidden friend functions in quantity and quantity_point?
Maybe!
The main benefit is that it would let us automatically support types that implicitly convert to, but not from, a quantity or quantity_point. (That said, mp-units has traditionally aligned itself against using this sort of type: zero was rejected, and mp-units constants do not convert to quantities.)
Another benefit is reducing the size of overload sets, which can make for shorter compiler error messages and, I think, faster compile times in a few cases.
The downside is that it's an intrusive change: we need to open up the guts of quantity and quantity_point to add it. Also, it locks us in to forcing a <cmath> dependency on all users who include these files. (Maybe this is already the case; I didn't check.)
Au has done this for min, max, clamp, and %, without the <cmath> dependency. We get a lot more benefit out of it than mp-units would, because of our "shapeshifter types" (i.e., Zero and Constant<U>).
Later on, I tried using this for remainder (which wraps std::remainder), passing a Constant as one of the arguments, and it failed. I realized that in order to get this to work, we'd need to take this hidden friend approach for remainder. It felt like opening up a bit of a Pandora's box, so I didn't add it at the time, and still haven't added it. I'm not sure yet how to decide which ones should or shouldn't get this treatment.
To be concrete: which specific functions were you considering?
I thought about most if not all, functions from the mp_units/math.h header. Additionally, things like lerp and midpoint should be provided for quantity_point.
and mp-units constants do not convert to quantities
Are constants in Au modeled as units that convert to quantities?
and mp-units constants do not convert to quantities
Are constants in Au modeled as units that convert to quantities?
No, they are constants that convert to quantities. 🙂 Constant<U> is a template that is templated on the unit U, and has a perfect conversion policy (no heuristic overflow safety surface needed) with to any Quantity<OtherU, R>: every lossy conversion fails, and every lossless conversion succeeds.
But I assume that such a constant is a part of the unit of a quantity and simplifies at compile-time when needed?
But I assume that such a constant is a part of the unit of a quantity and simplifies at compile-time when needed?
I don't understand what you're asking, but I understand what Au does. 🙂 Every Constant<U> is templated only on the unit U; therefore, we need to have (or create) a unit for each constant. Constant<U> is a monovalue type, so this unit basically is its value: its value is "1 U", so to speak. If you try to assign this constant to a Quantity<U1, R1>, this assignment will succeed if 1 U can be exactly represented in Quantity<U1, R1>, and fail otherwise. And if it succeeds, the value in U1 and R1 will be computed at compile time.
The parts I didn't understand or found confusing were:
- "such a constant is a part of the unit of a quantity"
- "simplifies at compile-time when needed"
I hope my above paragraph answered the question.
I mean something like this:
constexpr auto c = speed_of_light;
quantity q1 = 0.4 * c;
quantity q2 = q1 / c;
- What is the unit of
q1(is itcor some other arbitrary unit of speed together with a proper compile-time magnitude)? - I assume that
q1stores just0.4, and no calculations are done at this point. - I assume that
q2is a dimensionless quantity with exactly a value0.4,and there was no runtime cost of the division in the initializer.
Thanks for giving a concrete example. I think the answers will be just as you expect (with the caveat that this syntax wouldn't work for Au because we don't have deduction guides for C++17's CTAD):
- The units of
q1are the units ofc. - Yep, we store just
0.4under the hood, with no calculations. - Yep,
q2would be just a dimensionless quantity with exactly0.4, and no runtime cost.
I'm a little uncertain for (3) as to whether we would produce a quantity or just a raw double, but if it's the latter then it's what we now consider a design defect and we plan to fix it in the next "breaking change" release (probably 0.5.0).
Thanks. So, it seems the only drawback of this solution to what we have in mp-units is that a possibly long magnitude of a constant is visible in a quantity type. When two constants are involved, magnitude no longer has a "familiar" value. But you gained implicit constructions from a constant.
I also think that Au uses implicit conversions for operators. It means that things like c - 0.5 * c and -0.5 * c + c will probably work? In our case, where we do not have an implicit conversion and arguments are deduced function template parameters, we would need to add dedicated overloads for constants.
I also think that Au uses implicit conversions for operators. It means that things like
c - 0.5 * cand-0.5 * c + cwill probably work?