Skip to content

Comments

Use ImplicitConversion in Quantity constructor#633

Merged
chiphogg merged 3 commits intomainfrom
chiphogg/use-implicit-conversion-in-qty-ctor#528
Jan 15, 2026
Merged

Use ImplicitConversion in Quantity constructor#633
chiphogg merged 3 commits intomainfrom
chiphogg/use-implicit-conversion-in-qty-ctor#528

Conversation

@chiphogg
Copy link
Member

@chiphogg chiphogg commented Jan 15, 2026

This gives us a reasonably clear and satisfactory policy. Any time
someone uses the Quantity constructor, we should treat it as an
implicit conversion. (Recall that the Quantity-from-Quantity
constructor is always implicit, and the explicit one is explicitly
deleted.) Any time someone uses .in or .as, we continue to use
static_cast. This makes sense because it invokes a unit conversion,
at least conceptually, and therefore it isn't really an instance of the
kind of "pure wrapping" use case where we strongly want to preserve
compiler errors.

Or, in other words: the goal is that any time double to float would
produce an error, so would the same QuantityD to QuantityF. The
reason .in and .as should still use explicit static_cast is that
this is not "the same" situation, as there is no unit conversion
possible with raw double and float.

On the implementation side, the cleanest approach I found was to add an
explicit casting strategy template argument to in_impl, because every
Quantity conversion is built on top of it. The implicit constructor
gets ImplicitConversion, and everything else still uses StaticCast.
This forces us to add friendship across different template
instantiations of Quantity (because now we're initializing value_
directly), but that presents no problem from a software quality point of
view.

For QuantityPoint, we needed to add an additional rep_cast for
in_impl (because of promotion), but otherwise we can simply delegate
to the Quantity machinery, which is nice. We also uncovered and fixed
an API error: subtracting two QuantityPoint instances should return
auto, not Diff, because if promotion happens, then we want to return
the promoted rep. We want our types to behave just like the underlying
C++ types, in all their weird and dubious glory.

Finally, we had to tweak one test in the parent PR, because of our
-Wsign-conversion job. We now need to use 40u instead of 40 when
assigning to QuantityU32, because this assignment uses the implicit
conversion.

Helps #528. This is really the core of the solution on the code side.
We will need to follow up with clear docs to explain the importance of
avoiding -isystem when including Au, and the philosophy behind it.

chiphogg and others added 2 commits January 15, 2026 07:18
This gives us a reasonably clear and satisfactory policy.  Any time
someone uses the `Quantity` constructor, we should treat it as an
implicit conversion.  (Recall that the `Quantity`-from-`Quantity`
constructor is _always_ implicit, and the `explicit` one is explicitly
deleted.)  Any time someone uses `.in` or `.as`, we continue to use
`static_cast`.  This makes sense because it invokes a unit conversion,
at least conceptually, and therefore it isn't really an instance of the
kind of "pure wrapping" use case where we strongly want to preserve
compiler errors.

Or, in other words: the goal is that any time `double` to `float` would
produce an error, so would the same `QuantityD` to `QuantityF`.  The
reason `.in` and `.as` should still use explicit `static_cast` is that
this is not "the same" situation, as there is no unit conversion
possible with raw `double` and `float`.

On the implementation side, the cleanest approach I found was to add an
explicit casting strategy template argument to `in_impl`, because every
`Quantity` conversion is built on top of it.  The implicit constructor
gets `ImplicitConversion`, and everything else still uses `StaticCast`.
This forces us to add friendship across different template
instantiations of `Quantity` (because now we're initializing `value_`
directly), but that presents no problem from a software quality point of
view.

For `QuantityPoint`, we needed to add an additional `rep_cast` for
`in_impl` (because of promotion), but otherwise we can simply delegate
to the `Quantity` machinery, which is nice.  We also uncovered and fixed
an API error: subtracting two `QuantityPoint` instances should return
`auto`, not `Diff`, because if promotion happens, then we want to return
the promoted rep.  We want our types to behave just like the underlying
C++ types, in all their weird and dubious glory.

Helps #528.  This is really the core of the solution on the code side.
We will need to follow up with clear docs to explain the importance of
avoiding `-isystem` when including Au, and the philosophy behind it.
@chiphogg
Copy link
Member Author

PR Branch Title
➡️ chiphogg/use-implicit-conversion-in-qty-ctor#528 Use ImplicitConversion in Quantity constructor


TEST(Conversions, SupportIntMHzToU32Hz) {
constexpr QuantityU32<Hertz> freq = mega(hertz)(40);
constexpr QuantityU32<Hertz> freq = mega(hertz)(40u);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this wouldn't give us an error:

    constexpr unsigned freq = 40;

But of course, we creating a quantity first, then doing the assignment. I suppose this is reasonable. I'm guessing a Constant would avoid this?

    constexpr QuantityU32<Hertz> freq = make_constant(mega(hertz) * mag<40>());

So let's just say this is another use case for a more ergonomic way to create ad-hoc constants. In fact, if mega(herts)(40) created a Constant instead of a Quantity...

On the embedded side of things, we'll sometimes be more explicit of the rep we want when creating a Quantity with:

    constexpr QuantityU32<Hertz> freq = mega(hertz)(uint32_t{40});

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As much as I wish it were otherwise, mega(hertz)(40) could never be finagled to produce a Constant in the C++ language. We can't return different types based on the value of a (normal, non-template) function parameter, even if that parameter's value is known at compile time.

But yes, creation ergonomics aside, I do expect a Constant would handle this very nicely!

@chiphogg chiphogg merged commit 371ba27 into main Jan 15, 2026
27 checks passed
@chiphogg chiphogg deleted the chiphogg/use-implicit-conversion-in-qty-ctor#528 branch January 15, 2026 19:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants