C++ Logo

std-proposals

Advanced search

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

From: Tiago Freire <tmiguelf_at_[hidden]>
Date: Thu, 27 Jun 2024 22:59:19 +0000
> 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.

I don’t get it!
It looks very selective what you consider an “adversarial” example.
I have my type explicitly written out, I know what I want, its “40 of this thing”, but you force me to use 3 different types and 2 or more different objects multiplying together to create an object of an explicit type.
I never heard std::chrono::duration to be described as unsafe… in order to declare it you need to write a type or pass it too a thing that has a type, am I the only one that looks at the type of things?

I’m not of the opinion that forcing developers to write extra appendages makes it safer as opposed to making developers more frustrated.

Even recently a quite significant change was forced upon it because of how blatantly unsafe it was when dealing with temperatures.
It was not the first time it was brought up, but it was simply ignored until I had shown a quite common example, publicly, and someone else other than myself started to complain “this is not safe at all”.

And the “solution” was to make the design even more complicated, forcing the user to write even more appendages, requiring more to learn to even use.
And the problem isn’t fixed by the way, sure it forces you to read that extra text while instantiating the object directly, but there are still many other ways to do it indirectly, and I have shown an example on how one could do it in code that could be reasonably written without thinking “adversarially” (perhaps you might as well just rename the whole thing as quantity_delta).
Yet refuse to take what I would argue is the easiest decision that would immediately improve safety… there is no such thing as delta temperatures in degrees Celsius...
You are not using the type system to stop users from doing the wrong thing, you are using the type system to force people to write documentation and hopefully they will read it and figure out that they are doing the wrong thing.


I’m really not trying to beat up a dead horse here… but It doesn’t need to be like this.
You are struggling because the system is over complicated, and you don’t understand where the complexity comes from.



From: Std-Proposals <std-proposals-bounces_at_[hidden]> On Behalf Of Charles R Hogg via Std-Proposals
Sent: Thursday, June 27, 2024 23:10
To: std-proposals_at_[hidden]
Cc: Charles R Hogg <charles.r.hogg_at_[hidden]>
Subject: Re: [std-proposals] On the standardization of mp-units P3045R1



On Thu, Jun 27, 2024 at 4:57 PM Lorand Szollosi via Std-Proposals <std-proposals_at_[hidden]<mailto: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]<mailto: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]<mailto:Std-Proposals_at_[hidden]>
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2024-06-27 22:59:25