C++ Logo

std-proposals

Advanced search

Re: [std-proposals] On the standardization of mp-units P3045R1

From: Charles R Hogg <charles.r.hogg_at_[hidden]>
Date: Thu, 27 Jun 2024 17:10:15 -0400
On Thu, Jun 27, 2024 at 4:57 PM Lorand Szollosi via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> Hi,
>
> On Thu, Jun 27, 2024 at 10:03 PM Charles R Hogg via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
>> The way to achieve this with a units library is to make sure that every
>> interface that *brings values into* a units library type, or *extracts
>> values from* a units library type, forces the user to *name the unit* explicitly,
>> *at the callsite*. Au was designed with this approach in mind from the
>> ground up, and I was very heartened to see mp-units adopt it as well, to
>> the point that mp-units is now every bit the equal of Au in unit safety.
>> (Really, the whole 2.0 redesign for mp-units was a stunning leap forward in
>> a lot of ways --- I was thrilled to see it.)
>>
>
> What you call unit safety, I'd argue, is a coding convention. You might
> enforce it by power; you can't enforce it solely by c++ language features, *and
> that's a good thing*. It's because it goes against composability. Think
> about this code:
>
> auto l = [](double d) constexpr { return d * km; };
> static_assert(l(12) == 12 * km);
>
> Full example: https://godbolt.org/z/PWocWYo6c
>
> I'd argue that l(12) is *not* unit-safe: simply reading that line, you
> cannot tell whether that static assert should succeed (i.e., I wrote d * km
> in the lambda) or fail (i.e., if I were to write d * m, or even 2 * d *
> km). I also didn't write return type in the lambda, as it's auto. Thus, no
> matter what you ensure in the library, if a user would like a lambda
> constructing a unit from a double, *they have it today*. Therefore, the
> argument is, whether it's a *coding standard* we promote.
>

Unit safety, as defined here, is a property that any individual line of
code does or does not have. You're absolutely right that no API could ever
achieve it universally. It's always possible for a sufficiently
adversarial user to defeat it. The goal, though, is to design APIs that
make unit safety easy to achieve, by strong default.

In this specific example, I fully agree that l(12) is not unit-safe. In
fact, the *root* of this problem is that "auto l = ..." is not unit-safe.
This *individual line* takes a raw number, and returns a quantity, but the
name `l` *does not name* the unit. Therefore, *this individual line* should
fail code review because it makes an *interface* that is not unit-safe, and
that will cause downstream users to write non-unit-safe code.


>
>
>> Now to your question: if you have a typename that contains the unit name,
>> isn't that enough? Say we had a *type* called `degrees_celsius`: why
>> wouldn't it be OK to have a constructor from `double`, so you could simply
>> write `degrees_celsius{42.0}`? (We would make it `explicit`, of course!)
>>
>
> There are units that you cannot construct as a + b * x, where an and b are
> constants. A common example is date (based on some absolute time) of
> countries: there are examples of days missing from calendars. Thus, in your
> serialisation / deserialisation, you'll need a way to express
> quantity_point constructions anyways. Users of the library could create
> such a thing in non-standard ways; or, we can provide a standard function
> to create it.
>

I believe dates generally are out of scope for P3045. That's more the
domain of the chrono library. (Of course, we'll want robust interop
between chrono *durations* and *durations* in the standard units library.
Thankfully, the existing open-source libraries on which the proposal is
based already have this property.)

Cheers,
Chip


>
> Units are still checked while compiling, so zero type safety is sacrificed.
>
> Thanks,
> -lorro
>
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>

Received on 2024-06-27 21:10:29