Date: Thu, 12 May 2022 10:25:55 +0000
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.
Niall
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.
Niall
Received on 2022-05-12 10:25:57