C++ Logo


Advanced search

Re: Reconciling P1028 SG14 status code and P2170 Feedback on implementation

From: Tjernstrom, Staffan <Staffan.Tjernstrom_at_[hidden]>
Date: Thu, 12 May 2022 11:37:52 +0000
>From a finance perspective, I'd be happy to have SG14 deep-dive into this (especially if we can save LEWG time). The next planned Finance meeting is a few months out, however.

-----Original Message-----
From: SG14 <sg14-bounces_at_[hidden]> On Behalf Of Niall Douglas via SG14
Sent: Thursday, 12 May, 2022 06:26
To: sg14_at_[hidden]; C++ Library Evolution Working Group <lib-ext_at_[hidden]>; charles.a.salvia_at_[hidden]
Cc: Niall Douglas <s_sourceforge_at_[hidden]>
Subject: [SG14] Reconciling P1028 SG14 status code and P2170 Feedback on implementation

Dear SG14,

CC: LEWG, P2170 author

I expect to start work on the next revision of P1028 SG14 status code soon. Apologies for the long break, there was covid and I was unwell.

The last time this paper was seen by LEWG it was pre-pandemic in early 2020. Since then https://wg21.link/P2170 "Feedback on designing the proposed std::error type" landed, and LEWG wishes that P2170's concerns be addressed.

Given that P1028 originated from SG14, it seems the most appropriate that SG14 do the reconciliation to save LEWG time. Should we have a telecon to do this?

My own summary of P2170's main points to save everybody time (note that I excluded P2170 points which were already discussed and decided upon by
SG14 e.g. 64 bit vs 128 bit unique ids):

1. P2170 wants proposed std::error (which is an erased status_code) to be copy constructible instead of move only, which is the current P1028 design. There is a clone() function in P1028 which **may** be able to copy the underlying erased state, but the copy constructor is disabled.

2. P2170 wants std::error to be able to encapsulate any std::error_code without loss of fidelity, and for a default constructed std::error_code to either be UB or map to a special state like valueless_by_exception().

3. P2170 feels that the semantic comparison of std::error with an underlying std::exception_ptr with the std::errc constants is problematic for the case where exception types which refine std::runtime_error compare equal to std::errc::resource_unavailable_try_again.

4. P2170 suggests that std::errc ought to be extended with generic codes which do not map into posix_code, specifically generic codes for all the standard library C++ exception types, but also a yet to be proposed set of "universal" codes for common application failures.

5. P2170 feels that the semantic comparison logic ought to be different than P1028's.

6. P2170 wants there to be free functions which can access the erased bits within a std::error.

7. P2170 wants to dispose of most of the platform specific strongly typed status codes, specifically posix_code, win32_code, com_code, nt_code, getaddrinfo_code, replacing them with fewer codes:
generic_domain, error_code_domain, dynamic_exception_domain, dynamic_exception_code_domain, system_domain.

I hope that I have been fair in the above to the author of P2170, and I would like to take this opportunity to thank him for taking the time to submit such detailed and valuable design feedback.

My answers to each of the points above:

1. I'm not keen on allowing copy constructible for erased status codes because it adds very significant runtime overhead, which is why the present design specifically has the user explicitly opt into requesting a copy via clone(). This is because an erased status code needs to call a virtual function every time a copy is done, in order to have the erased implementation do the copy. Move-only erased status codes eliminate that overhead, as erasure is not available to status codes without bit copyable payload. Also, there will be payloads which are uncopyable, and that would mean copy construction would have to be able to throw. Given how much C++ likes to choose copies over moves, I think this would add many more opportunities for unexpected control flow inversion for comparatively little gain.

2. It appears that the author did not realise that P1028R3 provides a status code domain for std::error_code, and std::error_code instances implicitly will convert into status_code (and proposed std::error). This conversion preserves and continues to use the original std::error_category. The present behaviour for handling default constructed std::error_code is to trigger the same contract violation as when one constructs a std::error from a non-errored std::status_code.

(I would like to apologise to everybody at this point including the
P2170 author for P1028R3 not containing the status code domain for exception_ptr, despite the prose clearly describing that there is one, and the reference library actually having a commented worked example of its implementation. I have no idea how this got omitted for three revisions without being noticed, sorry. This will be fixed in R4)

3 & 4. I'm pretty sympathetic to this argument, as I have seen this problem in real world production codebases. I think that the idea of extending std::errc with better fidelity codes to represent more categories of exception type is a good idea.

Re: Adding more "universal" codes for common causes of application failure, well I would really like to do so. However this is **hard**. At work our code base probably has deployed several dozen custom status code domains now, and how those are supposed to compare to std::errc codes comes up again and again and again. And then a lot of bikeshedding happens, because different engineers have different conceptualisations of what universal categories of application failure there ought to be, and worse, they vehemently disagree with each other.

Which is I suspect what happened during the original design of std::errc back in the day in 2008 or so, and it just became simpler and less hassle to choose std::errc to be an exact subset of the POSIX error codes. Because those are uncontroversial, and get you 80% of the way.
They then bolted on the facility for anybody to define custom std::error_conditions, and that led to a quagmire of incompatibility which SG14 explicitly wished to eliminate going forth.

If anybody not me would like to volunteer to marshall debate on extending std::errc with new universal codes, I would be super happy to see that occur.

5. I am sympathetic, however the current choice of logic was specifically chosen to be 100% compatible with the current logic employed by std::error_category in order that we could map one-one std::error_code into std::error. I assume that remains higher priority for SG14 and LEWG?

6. In the past two years there has been an improvement to the reference implementation in this area, and which will appear in R4 of the paper.
It turns out that in some corner cases, not only does one need the raw bits representing the payload, but one also needs its exact size in bytes, and also its alignment, and status_code_domain now can tell you this on request. To be clear, the raw bits may be unaligned in erased storage, but they may need overaligning to be usable by the underlying implementation, and we now have this facility.

7. LEWG did not get the time last meeting to consider whether more platform-specific code domains vs less platform-specific code domains ought to be standardised. Polling on this by SG14 I assume would be valuable.

My own personal opinion on this is that platform-specific code domains are extremely cheap to standardise, because they literally are "implementation defined", and there is absolutely zero chance of unpleasant surprise from say a win32_code which would only be available on Win32 platforms. I think platform-specific codes will get erased in real world code fairly promptly, and after that they will semantic compare to std::errc constants as needed, they can be thrown into C++ exceptions, their message can be printed etc. So I think the genericity part is already well covered, and I don't think adding more genericity is useful here.

Having been writing code at work using status code for well over two years now, I can testify to just how incredibly useful it is to have a platform-specific code. If for example I am writing C++ which is speaking Microsoft COM, I can just fire the HRESULT into a com_code and be absolutely sure it will be handled perfectly. I think having no platform specific code domains robs the C++ userbase of this major boon when it costs WG21 virtually no effort to aid users here, and I think less-platform-specific codes forces C++ users to have to think more instead of just blindly typing out the intuitive thing and it "just works".

This turned out a bit longer than expected. I look forward to your feedback.

SG14 mailing list


IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Received on 2022-05-12 11:37:53