Proposed SG14 <system_error2> ready for feedback

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
20 messages Options
Reply | Threaded
Open this post in threaded view
|

Proposed SG14 <system_error2> ready for feedback

Boost - Dev mailing list
Back after the Outcome discussion died down, I started a thread
discussing making breaking changes to Boost.System to test the
feasibility of fixing some of the unfortunate design choices now
apparent through hindsight in `<system_error>`. I won one argument -
constexprification - the others I lost. I said at the time that I'd go
off and make a better mousetrap i.e. a proposed `<system_error2>`.

That proposed `<system_error2>` is now ready for feedback:

Single file include:
https://github.com/ned14/status-code/raw/develop/single-header/system_error2.hpp

Github: https://github.com/ned14/status-code

Docs: https://ned14.github.io/status-code/

Lots of detail about the differences between proposed `<system_error2>`
and `<system_error>` is in the front page of the docs and pasted after
this email, but essentially it fixes all the problems listed in
https://wg21.link/P0824, and a few other problems I considered important
as well. It works well in C++ 11, is believed to be freestanding C++
(https://wg21.link/P0829) friendly, and generates really lovely and
tight codegen. If SG14 smile upon it in the April meeting, it'll be
heading to Rapperswil for standardisation.

Boost members should be aware that there will be shortly a push by the
WG21 leadership to improve the current state of exception and error
handling in the C++ standard as it is becoming increasingly obvious that
the current design is no longer sufficient. You may see a paper on that
from the leadership at Jacksonville, if not then fairly definitely you
will at Rapperswil. This proposed `<system_error2>` *may* have a part to
play in the reform proposals, if it is felt that this approach is a wise
one by SG14, you guys here, and then WG21.


So, as a quick introduction given that there is no documentation,
instead of `error_code` we have a `status_code<D>` where `D` is the type
of the code's *domain*. The domain provides all of the heavy lifting so
status code itself can be trivial. Status codes can be type erased of
their domain, you can always erase to `status_code<void>` but at the
cost that it cannot be copied, moved nor deleted. If your domain's value
type is trivially copyable, it can also be type erased into any
`status_code<erased<U>>` where `U` is an integer type, and the size of
the erased status code is bigger or equal to its unerased form.

There is a special status_code called `generic_code`, which is a typedef
for `status_code<_generic_code_domain_impl>`. Its value type is `errc`,
which is a slight superset of `std::errc`. The reason it is special is
because it is the baseline error code, all other codes must be
semantically comparable to it, if that is possible. In current STL
speak, `generic_code` is what `std::error_condition` with a
`std::generic_category` is.

On all POSIX platforms, we provide a `posix_code`. Its value type is
`int`, as from `errno`. This maps the local POSIX implementation's error
coding. It is *not* necessarily equal to `generic_code`, but usually is
a superset, so all codes in `generic_code` can exist in `posix_code`,
but not the other way round.

On Windows, we have three main system error coding systems, so we have
`win32_code`, `nt_code` and `com_code`. Their value types are `DWORD`
from GetLastError(), `LONG` from `NTSTATUS`, and `HRESULT` from
Microsoft COM.

On all systems, there is a typedef `system_code` which is to the erased
status code sufficiently large that you are guaranteed that all possible
system error coding schemes can be safely erased into it. For POSIX,
this is `status_code<erased<int>>`, as `int` can hold all possible error
codings. For Windows, this is `status_code<erased<intptr_t>>`, as
`intptr_t` can hold any of a `DWORD`, a `LONG` and a `HRESULT`.

All comparisons between status codes are *semantic*. As in,
`operator==()` returns true if the two types are semantically equal e.g.
`win32_code(ERROR_SUCCESS) == generic_code(errc::success) ==
com_code(S_OK)`. Semantic comparison is the only form of comparison, if
you want literal comparison, it can be done by hand by comparing value
and domain by hand.

There is no boolean testing at all, as it is ambiguous as we learned
with `std::error_code`. One always writes: `if(sc.failure()) ...` etc.

`status_code` can represent success codes, failure codes, and empty.
Empty is there to say "no code was set". Empty is neither a success, nor
a failure. It is there for code where a virtual function call to
determine success/failure is too expensive and where the domain is known
for a fact to only ever represent failure. You should only ever use
empty as a local optimisation, and never ever expose empty status codes
to code that you don't 100% control.

And that's basically it. Idiomatic usage is for extern functions to
return type erased status codes or to take one by lvalue reference. Code
local to the translation unit uses typed status codes, and lets any
implicit conversion into the erased form occur as needed.

I've copy and pasted relevant excerpts from the Readme.txt below for
those not willing to click on a link. If you have any questions, please
do shout. My next step is to start work on preparing Outcome for entry
into Boost now a month of settling time has passed since the review.
Once the Jacksonville meeting has finished and I've done any
post-meeting correspondence, I'll be starting on my (currently five!)
papers for Rapperswil where I'll hopefully be attending my very first
WG21 meeting!

Niall


--- Readme.md (excerpt) --

Solves the problems for low latency/large code base users with
`<system_error>` as listed by [WG21 P0824](https://wg21.link/P0824).
This proposed `<system_error2>` library is EXPERIMENTAL and is subject
to change as the committee evolves the design. To fetch a drop-in
standalone single file implementation:

```
wget
https://github.com/ned14/status-code/raw/develop/single-header/system_error2.hpp
```

## Features:

- Portable to any C++ 11 compiler. These are known to work:
    - &gt;= GCC 5 (due to requiring libstdc++ 5 for sufficient C++ 11
type traits)
    - &gt;= clang 3.3 with a new enough libstdc++ (previous clangs don't
implement inheriting constructors)
    - &gt;= Visual Studio 2015 (previous MSVC's don't implement
inheriting constructors)
- Aims to cause zero code generated by the compiler most of the time.
- Never calls `malloc()`.
- Header-only library friendly.
- Type safe yet with type erasure in public interfaces so it can scale
across huge codebases.
- Minimum compile time load, making it suitable for use in the global
headers of multi-million line codebases.

## Problems with `<system_error>` solved:

1. Does not cause `#include <string>`, and thus including the entire STL
allocator and algorithm machinery, thus preventing use in freestanding
C++ as well as substantially impacting compile times which can be a
showstopper for very large C++ projects. Only includes the following
headers:

    - `<atomic>` to reference count localised strings retrieved from the OS.
    - `<cassert>` to trap when misuse occurs.
    - `<cerrno>` for the generic POSIX error codes (`errno`) which is
required to define `errc`.
    - `<cstddef>` for the definition of `size_t` and other types.
    - `<cstring>` for the system call to fetch a localised string and C
string functions.
    - `<exception>` for the basic `std::exception` type so we can
optionally throw STL exceptions.
    - `<initializer_list>` so we can permit in-place construction.
    - `<new>` so we can perform placement new.
    - `<type_traits>` as we need to do some very limited metaprogramming.
    - `<utility>` if on C++ 17 or later for `std::in_place`.

    These may look like a lot, but in fact just including `<atomic>` on
libstdc++ actually brings in most of the others in any case, and a total
of 200Kb (8,000 lines) of text is including by `system_error2.hpp` on
libstdc++ 7. Compiling a file including `status_code.hpp` takes less
than 150 ms with clang 3.3 as according to the `-ftime-report`
diagnostic (a completely empty file takes 5 ms).

2. Unlike `std::error_code` which was designed before `constexpr`, this
proposed implementation has all-`constexpr` construction and destruction
with as many operations as possible being trivial or literal, with only
those exact minimum operations which require runtime code generation
being non-trivial (note: requires C++ 14 for a complete implementation
of this).

3. This in turn means that we solve a long standing problem with
`std::error_category` in that it is not possible to define a safe custom
C++ 11 error category in a header only library where semantic
comparisons would randomly break depending on the direction of wind
blowing when the linker ran. This proposed design is 100% safe to use in
header only libraries.

4. `std::error_code`'s boolean conversion operator i.e. `if(ec) ...` has
become unfortunately ambiguous in real world C++ out there. Its correct
meaning is "if `ec` has a non-zero value". Unfortunately, much code out
in the wild uses it as if "if `ec` is errored". This is incorrect,
though safe most of the time where `ec`'s category is well known i.e.
non-zero values are always an error. For unknown categories supplied by
third party code however, it is dangerous and leads to unpleasant,
hard-to-debug, surprise.

    The `status_code` proposed here suffers from no such ambiguity. It
can be one of exactly three meanings: (i) success (ii) failure (iii)
empty (uninitialised). There is no boolean conversion operator, so users
must write out exactly what they mean e.g. `if(sc.success()) ...`,
`if(sc.failure()) ...`, `if(sc.empty()) ...`.

5. Relatedly, `status_code` can now represent successful (informational)
codes as well as failure codes. Unlike `std::error_code` where zero is
given special meaning, we impose no requirements at all on the choice of
coding. This permits safe usage of more complex C status coding such as
the NT kernel's `NTSTATUS`, which is a `LONG` whereby bits 31 and 30
determine which of four categories the status is (success,
informational, warning, error), or the very commone case where negative
numbers mean failure and positive numbers mean success-with-information.

6. The relationship between `std::error_code` and `std::error_condition`
is confusing to many users reading code based on `<system_error>`,
specifically when is a comparison between codes *semantic* or *literal*?
`status_code` makes all comparisons *semantic*, **always**. If you want
a literal comparison, you can do one by hand by comparing domains and
values directly.

7. `std::error_code` enforced its value to always be an `int`. This is
problematic for coding systems which might use a `long` and implement
coding namespaces within the extended number of bits, or for end users
wishing to combine a code with a `void *` in order to transmit payload
or additional context. As a result, `status_code` is templated to its
domain, and the domain sets its type. A type erased edition of
`status_code<D>` is available as `status_code<void>`, this is for
obvious reasons non-copyable, non-movable and non-destructible.

    A more useful type erased edition is `status_code<erased<T>>`
which is available if `D::value_type` is trivially copyable, `T` is an
integral type, and `sizeof(T) >= sizeof(D::value_type)`. This lets you
use `status_code<erased<T>>` in all your public interfaces without
restrictions. As a pointer to the original category is retained, and
trivially copyable types may be legally copied by `memcpy()`, type
erased status codes work exactly as normal, except that publicly it does
not advertise its type.

8. `std::system_category` assumes that there is only one "system" error
coding, something mostly true on POSIX, but not elsewhere. This library
defines `system_code` to a type erased status code sufficiently large
enough to carry any of the system error codings on the current platform.
This allows code to construct the precise error code for the system
failure in question, and return it type erased from the function.
Depending on the system call which failed, a function may therefore
return any one of many system code domains.

9. Too much `<system_error>` code written for POSIX uses
`std::generic_category` when they really meant `std::system_category`
because the two are interchangeable on POSIX. Further confusion stems
from `std::error_condition` also sharing the same coding and type. This
causes portability problems. This library's `generic_code` has a value
type of `errc` which is a strong enum. This prevents implicit confusion
with `posix_code`, whose value type is an `int` same as `errno` returns.
There is no distinction between codes and conditions in this library,
rather we treat `generic_code` as something special, because it
represents `errc`. The cleanup of these ambiguities in `<system_error>`
should result in users writing clearer code with fewer unintended
portability problems.


--
ned Productions Limited Consulting
http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Proposed SG14 <system_error2> ready for feedback

Boost - Dev mailing list
2018-02-28 23:10 GMT+01:00 Niall Douglas via Boost <[hidden email]>:

> Back after the Outcome discussion died down, I started a thread
> discussing making breaking changes to Boost.System to test the
> feasibility of fixing some of the unfortunate design choices now
> apparent through hindsight in `<system_error>`. I won one argument -
> constexprification - the others I lost. I said at the time that I'd go
> off and make a better mousetrap i.e. a proposed `<system_error2>`.
>
> That proposed `<system_error2>` is now ready for feedback:
>
> Single file include:
> https://github.com/ned14/status-code/raw/develop/
> single-header/system_error2.hpp
>
> Github: https://github.com/ned14/status-code
>
> Docs: https://ned14.github.io/status-code/


The github readme at https://github.com/ned14/status-code  gives a clear
description how I can make use of POSIX and Windows-standard error codes.
Could we have similar short and clear examples illustrating how one can:
1. Create a custom error_category (or equivalent thing)
2. Use parametrized statuc_code
3. Use type-erased status_code

Regards,
&rzej;

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Proposed SG14 <system_error2> ready for feedback

Boost - Dev mailing list
> The github readme at https://github.com/ned14/status-code  gives a clear
> description how I can make use of POSIX and Windows-standard error codes.

It must therefore not be self obvious? Darn. I had thought it very
simple, basically <system_error> with a few obvious tweaks. Not worth
much explaining, anyway.

> Could we have similar short and clear examples illustrating how one can:
> 1. Create a custom error_category (or equivalent thing)
> 2. Use parametrized statuc_code

There is a test custom domain at
https://github.com/ned14/status-code/blob/master/test/main.cpp#L44.

But how about a mini-tutorial on building a custom code domain whose
status code wraps any arbitrary thrown C++ exception? It looks like SG14
and WG21 is going to need one of those in any case.

> 3. Use type-erased status_code

Niall

--
ned Productions Limited Consulting
http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Proposed SG14 <system_error2> ready for feedback

Boost - Dev mailing list
2018-03-01 18:57 GMT+01:00 Niall Douglas via Boost <[hidden email]>:

> > The github readme at https://github.com/ned14/status-code  gives a clear
> > description how I can make use of POSIX and Windows-standard error codes.
>
> It must therefore not be self obvious? Darn. I had thought it very
> simple, basically <system_error> with a few obvious tweaks. Not worth
> much explaining, anyway.
>

Not obvious to me: especially the templetized codes, and two kinds of type
erasure. This part of the design is definitely more complicated than the
original <system_error>. I would expect examples and some rationale.

If you choose to describe <system_error2> as a diff from <system_error>, I
would expect all relevant differences to be mentioned in the docs. For
instance I can see you use `status_code_domain` rather than
`error_category`. This looks like an improvement in clarity. Also, how do
you handle error code names without strings? Do i need to define my own
string_ref?

Similarly, how did you fix the "duplicated globals" problem on Windows?

Also, there is this paragraph:

The relationship between std::error_code and std::error_condition is
confusing to many users reading code based on <system_error>, specifically
when is a comparison between codes *semantic* or *literal*? status_code
makes all comparisons *semantic*, *always*. If you want a literal
comparison, you can do one by hand by comparing domains and values
directly.

I fail to see what you mean by "semantic comparison" and "literal
comparison". Do they mean comparing error_code to error_category and
comparing error_code to error_code respectively?


> > Could we have similar short and clear examples illustrating how one can:
> > 1. Create a custom error_category (or equivalent thing)
> > 2. Use parametrized statuc_code
>
> There is a test custom domain at
> https://github.com/ned14/status-code/blob/master/test/main.cpp#L44.
>
> But how about a mini-tutorial on building a custom code domain whose
> status code wraps any arbitrary thrown C++ exception? It looks like SG14
> and WG21 is going to need one of those in any case.
>

Any information would be useful, but I think it is more basic things that
are insufficiently described.
For now, consider it a feedback on what a casual reader will not understand
about <system_error2> .

Regards,
&rzej;

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Proposed SG14 <system_error2> ready for feedback

Boost - Dev mailing list
> Not obvious to me: especially the templetized codes, and two kinds of type
> erasure. This part of the design is definitely more complicated than the
> original <system_error>. I would expect examples and some rationale.

Oh ok. Thanks for explaining.

> If you choose to describe <system_error2> as a diff from <system_error>, I
> would expect all relevant differences to be mentioned in the docs. For
> instance I can see you use `status_code_domain` rather than
> `error_category`. This looks like an improvement in clarity.

Previous feedback from Arthur suggested that.

> Also, how do
> you handle error code names without strings? Do i need to define my own
> string_ref?

The default string_ref implementation returns unmanaged static const
char strings. So if your strings are those, you need do nothing.

The posix_code domain implementation shows how to implement a reference
counted string retrieved dynamically from the system.

> Similarly, how did you fix the "duplicated globals" problem on Windows?

That's actually a problem on POSIX as well. All platforms are affected.
You cannot make header only libraries with custom error category which
work right on any platform.

It was fixed by making the category source a constexpr variable, and
each is given a 64 bit random unique id by its author from random.org.
If the ids match, the domains are considered equal.

Much of the time the compiler can therefore skip id comparison, as it
knows both sources are constexpr and it knows the unique id at
constexpr. But if push comes to shove, it can compare the ids, and it
doesn't matter if the singleton is not unique in the process.

> Also, there is this paragraph:
>
> The relationship between std::error_code and std::error_condition is
> confusing to many users reading code based on <system_error>, specifically
> when is a comparison between codes *semantic* or *literal*? status_code
> makes all comparisons *semantic*, *always*. If you want a literal
> comparison, you can do one by hand by comparing domains and values
> directly.
>
> I fail to see what you mean by "semantic comparison" and "literal
> comparison". Do they mean comparing error_code to error_category and
> comparing error_code to error_code respectively?

Correct. So, in error_category's case, semantic comparisons occur via
.equivalent() which is called when operator==() is done between an
error_code and error_condition. It's the same for status code domain,
there is an .equivalent() function. The difference is that there is no
such thing as error conditions, and operator==() always calls .equivalent().

We also don't provide ordering nor hashing for status code, as equality
testing is semantic. These ought to not be used in maps without a custom
comparison.

> Any information would be useful, but I think it is more basic things that
> are insufficiently described.
> For now, consider it a feedback on what a casual reader will not understand
> about <system_error2> .

I'm hesitant to write much in the way of docs until the design has been
empirically tested in AFIO. But it's basically a concrete implementation
of the possible improvements to error_code we discussed here a few weeks
back.

Niall

--
ned Productions Limited Consulting
http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Proposed SG14 <system_error2> ready for feedback

Boost - Dev mailing list
Niall Douglas wrote:

> It was fixed by making the category source a constexpr variable, and each
> is given a 64 bit random unique id by its author from random.org. If the
> ids match, the domains are considered equal.

That's something I was considering for Boost.System (literally the same
thing, a random 64 bit ID). Unfortunately, the virtual destructor makes
error_category a non-literal type, so categories can't be constexpr
variables. Hence, my earlier inquiry about the switch to a protected
nonvirtual ~error_category.

There is talk of/hope for language changes allowing constexpr virtuals, but
it's yet unclear whether we'll get that or if so, when.


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Proposed SG14 <system_error2> ready for feedback

Boost - Dev mailing list
>> It was fixed by making the category source a constexpr variable, and
>> each is given a 64 bit random unique id by its author from random.org.
>> If the ids match, the domains are considered equal.
>
> That's something I was considering for Boost.System (literally the same
> thing, a random 64 bit ID). Unfortunately, the virtual destructor makes
> error_category a non-literal type, so categories can't be constexpr
> variables. Hence, my earlier inquiry about the switch to a protected
> nonvirtual ~error_category.

status code domain has a trivial destructor, and thus is 100% constexpr
end to end of its lifetime.

You should be aware Peter that your input during the previous
discussions was unusually valuable in deciding the design of
<system_error2>. Sufficiently so that I previously have mentioned you by
name in the private discussions as having given particularly valuable
feedback. Thank you.

Something I've probably not mentioned to you before is that you called
the `filesystem::path` design right during its peer review. Your
proposed design was the correct one. The one which ended up being chosen
and is now to be standardised is inferior to your design.

AFIO chose your design for its path_view. It's considerably the better
for it.


BTW, I'm not sure if you or Boost is aware, but WG21 is busy making all
of the std::filesystem error_code APIs throwing as well as error code
returning i.e. they might fail either by error code, or also by throwing
an exception, and to date I have not seen a clear articulation of when
which will happen other than error_code being used for "file errors" and
exception throws for "non-file errors", whatever that means.

They expect to complete this "improvement" at Jacksonville by
retroactively modifying the C++ 17 standard via the defect process, as
apparently the original noexcept semantics is a defect. You can see more
at http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#3013,
keep reading down to see more madness.

I'll state now on the record that some parts of WG21 clearly most be
smoking something real good for such an action to make any sense. I know
that it was originally intended that Filesystem ought to throw in its
error code overloads around a decade ago, but ASIO's supremacy has
changed what the C++ user base expect when they see an error_code& API.
They expect that all failures will be indicated by said error_code&.

So should Filesystem, as indeed the C++ 17 standard said it would.
Because that's the widely understood convention now. Undoing that now,
when you could just set ec to
std::make_error_code(std::errc::not_enough_memory) or whatever is
backwards and retrograde in my opinion.


And besides: some stats from AFIO for ext4 on Linux on a 3.4Ghz Ivy
Bridge machine with a budget Crucial MX100 SSD:

    create file:  6453 ns
 enumerate file:   192 ns
 open file read:  1133 ns
open file write:  1195 ns
    delete file: 12338 ns

Those are *nano*seconds. A throw-catch cycle is at least 1000 ns.

What the f*ck are WG21 smoking on intentionally adding more exception
throws in here on Filesystem?

Niall

--
ned Productions Limited Consulting
http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Proposed SG14 <system_error2> ready for feedback

Boost - Dev mailing list
In reply to this post by Boost - Dev mailing list
On 02/28/18 23:10, Niall Douglas via Boost wrote:

> 7. `std::error_code` enforced its value to always be an `int`. This is
> problematic for coding systems which might use a `long` and implement
> coding namespaces within the extended number of bits, or for end users
> wishing to combine a code with a `void *` in order to transmit payload
> or additional context. As a result, `status_code` is templated to its
> domain, and the domain sets its type. A type erased edition of
> `status_code<D>` is available as `status_code<void>`, this is for
> obvious reasons non-copyable, non-movable and non-destructible.
>
>      A more useful type erased edition is `status_code<erased<T>>`
> which is available if `D::value_type` is trivially copyable, `T` is an
> integral type, and `sizeof(T) >= sizeof(D::value_type)`. This lets you
> use `status_code<erased<T>>` in all your public interfaces without
> restrictions. As a pointer to the original category is retained, and
> trivially copyable types may be legally copied by `memcpy()`, type
> erased status codes work exactly as normal, except that publicly it does
> not advertise its type.

While I do see the need for providing extensions, I am not sure if
status_code<void> is useful given all its "non-" characteristics.
Can you elaborate on its use-cases?

I propose that the customizable status_code<D> is renamed to
basic_status_code<D>.

In keeping with the pre-established practices of std::error_code using
int, I furthermore suggest that status_code becomes a non-templated
alias for basic_status_code<int>.

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Proposed SG14 <system_error2> ready for feedback

Boost - Dev mailing list
In reply to this post by Boost - Dev mailing list
>> But how about a mini-tutorial on building a custom code domain whose
>> status code wraps any arbitrary thrown C++ exception? It looks like SG14
>> and WG21 is going to need one of those in any case.

That worked example is written and can be viewed at
https://github.com/ned14/status-code/blob/master/doc/custom_domain_worked_example.md.
It should explain a lot more of how <system_error2> works.

Wandbox playpen of custom error code with payload of a thrown C++
exception: https://wandbox.org/permlink/cRDS3kWqcAmYHBD6

Niall

--
ned Productions Limited Consulting
http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Proposed SG14 <system_error2> ready for feedback

Boost - Dev mailing list
In reply to this post by Boost - Dev mailing list
>>      A more useful type erased edition is `status_code<erased<T>>`
>> which is available if `D::value_type` is trivially copyable, `T` is an
>> integral type, and `sizeof(T) >= sizeof(D::value_type)`. This lets you
>> use `status_code<erased<T>>` in all your public interfaces without
>> restrictions. As a pointer to the original category is retained, and
>> trivially copyable types may be legally copied by `memcpy()`, type
>> erased status codes work exactly as normal, except that publicly it does
>> not advertise its type.
>
> While I do see the need for providing extensions, I am not sure if
> status_code<void> is useful given all its "non-" characteristics.
> Can you elaborate on its use-cases?

`const status_code<void> &`

> I propose that the customizable status_code<D> is renamed to
> basic_status_code<D>.

Maybe. I think customisable is the wrong adjective though. It's simply
the type-aware edition, often in practice the "type restored" edition.

> In keeping with the pre-established practices of std::error_code using
> int, I furthermore suggest that status_code becomes a non-templated
> alias for basic_status_code<int>.

I see active harm in doing that.

status_code is type safe in a way error_code never was. So, generic_code
has a value type of enum class errc for example. You cannot construct
a generic_code with anything other than a errc. That's a big gain.
Similarly, a com_code has a value type of HRESULT. It too cannot be
constructed from anything other than a HRESULT.

Niall

--
ned Productions Limited Consulting
http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Proposed SG14 <system_error2> ready for feedback

Boost - Dev mailing list
I don't claim to understand all the implementation details, so sorry if this is  dumb question:

What happened to the idea to encode failure or success in a single bit in the status_code_domain pointer, thus making the check if an operation succeeded trivial even in the type-erased case?

Best
Mike
 


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Proposed SG14 <system_error2> ready for feedback

Boost - Dev mailing list
On 03/03/2018 21:11, mike via Boost wrote:
> I don't claim to understand all the implementation details, so sorry if this is  dumb question:
>
> What happened to the idea to encode failure or success in a single bit in the status_code_domain pointer, thus making the check if an operation succeeded trivial even in the type-erased case?

That was a potential workaround for Boost.System to not break existing code.

A complete rebuild like <system_error2> doesn't need such hacks. It has
a formal empty state for those users who won't risk the cost of a
virtual function call. For everybody else, it costs one virtual function
call at worst, usually not even that as the compiler will have
speculatively inlined the implementation and/or the linker will have
implemented the 'final' modifier.

Niall

--
ned Productions Limited Consulting
http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Proposed SG14 <system_error2> ready for feedback

Boost - Dev mailing list
In reply to this post by Boost - Dev mailing list
On Fri, Mar 2, 2018 at 2:26 PM, Niall Douglas via Boost
<[hidden email]> wrote:
> ASIO's supremacy has
> changed what the C++ user base expect when they see an error_code& API.
> They expect that all failures will be indicated by said error_code&.

Asio functions which have `error_code&` in their signature can still
throw exceptions. Not sure why you think otherwise.

Regards

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Proposed SG14 <system_error2> ready for feedback

Boost - Dev mailing list
In reply to this post by Boost - Dev mailing list
> -----Original Message-----
> From: On Behalf Of Niall Douglas via Boost
> Sent: Saturday, March 3, 2018 11:27 PM
>
> On 03/03/2018 21:11, mike via Boost wrote:
> > I don't claim to understand all the implementation details, so sorry if this
> is  dumb question:
> >
> > What happened to the idea to encode failure or success in a single bit in
> the status_code_domain pointer, thus making the check if an operation
> succeeded trivial even in the type-erased case?
>
> That was a potential workaround for Boost.System to not break existing
> code.
>
> A complete rebuild like <system_error2> doesn't need such hacks. It has
> a formal empty state for those users who won't risk the cost of a
> virtual function call. For everybody else, it costs one virtual function
> call at worst, usually not even that as the compiler will have
> speculatively inlined the implementation and/or the linker will have
> implemented the 'final' modifier.
>

It might not be necessary, but wouldn't it be a worthwhile optimization regardless? Even in the generic case it would work without any cross-module analysis and should generally make the compiler's task easier. It would also make the checks for failure or success constexpr (not sure if that is typically relevant).
Or would it imply problematic tradeoffs?

> Niall

Mike


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Proposed SG14 <system_error2> ready for feedback

Boost - Dev mailing list
>> A complete rebuild like <system_error2> doesn't need such hacks. It has
>> a formal empty state for those users who won't risk the cost of a
>> virtual function call.
>
> It might not be necessary, but wouldn't it be a worthwhile optimization regardless?

As I mentioned, it has a formal empty state. No further optimisation is
needed. It is as efficient as is possible.

> Even in the generic case it would work without any cross-module analysis and should generally make the compiler's task easier. It would also make the checks for failure or success constexpr (not sure if that is typically relevant).
> Or would it imply problematic tradeoffs?

Unlike with <system_error>, with <system_error2> the implementation of
the domain may be exposed to all translation units using it (i.e. if you
use the typed status_code<DomainType>, then by definition DomainType
must be visible to all translation units).

The compiler generally does the right thing when it can see a 'final'
implementation. So if you want maximum optimisation, never use the type
erased forms of status_code.

Niall

--
ned Productions Limited Consulting
http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Proposed SG14 <system_error2> ready for feedback

Boost - Dev mailing list
In reply to this post by Boost - Dev mailing list
>> ASIO's supremacy has
>> changed what the C++ user base expect when they see an error_code& API.
>> They expect that all failures will be indicated by said error_code&.
>
> Asio functions which have `error_code&` in their signature can still
> throw exceptions. Not sure why you think otherwise.

ASIO itself never does I believe. That's what I was referring to.

Niall

--
ned Productions Limited Consulting
http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Proposed SG14 <system_error2> ready for feedback

Boost - Dev mailing list
In reply to this post by Boost - Dev mailing list
On 1/03/2018 11:10, Niall Douglas wrote:
> On all systems, there is a typedef `system_code` which is to the erased
> status code sufficiently large that you are guaranteed that all possible
> system error coding schemes can be safely erased into it. For POSIX,
> this is `status_code<erased<int>>`, as `int` can hold all possible error
> codings. For Windows, this is `status_code<erased<intptr_t>>`, as
> `intptr_t` can hold any of a `DWORD`, a `LONG` and a `HRESULT`.

DWORD (aka uint32_t) can hold any of DWORD, LONG, and HRESULT.

Or use LONG (aka int32_t) if you want to retain signedness of LONGs,
though I don't think any Win32 APIs require this.

In any case intptr_t is either larger than needed to preserve the value
bits (in 64-bit applications) or smaller than needed to preserve the
value and sign (in 32-bit applications), so this seems like an
inappropriate choice.

(HRESULT is not a pointer type, despite most other H* types being
derived from HANDLE.  Yay for consistency.)


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Proposed SG14 <system_error2> ready for feedback

Boost - Dev mailing list
>> On all systems, there is a typedef `system_code` which is to the erased
>> status code sufficiently large that you are guaranteed that all possible
>> system error coding schemes can be safely erased into it. For POSIX,
>> this is `status_code<erased<int>>`, as `int` can hold all possible error
>> codings. For Windows, this is `status_code<erased<intptr_t>>`, as
>> `intptr_t` can hold any of a `DWORD`, a `LONG` and a `HRESULT`.
>
> DWORD (aka uint32_t) can hold any of DWORD, LONG, and HRESULT.
>
> Or use LONG (aka int32_t) if you want to retain signedness of LONGs,
> though I don't think any Win32 APIs require this.
>
> In any case intptr_t is either larger than needed to preserve the value
> bits (in 64-bit applications) or smaller than needed to preserve the
> value and sign (in 32-bit applications), so this seems like an
> inappropriate choice.

Structure alignment would align any int to the pointer, so same space in
any case. Two CPU registers.

> (HRESULT is not a pointer type, despite most other H* types being
> derived from HANDLE.  Yay for consistency.)

I did actually look that up before choosing it, and at the time found
one of the platform configs was defining it to HANDLE. But I can't find
that again now, so I must have been tired and confused.

In private discussions with some on WG21, it is felt that status code
needs to always be two CPU registers exactly. So I'll be bumping that to
intptr_t on all platforms.

It's also been requested that a new `error` type be created which
refines status_code. `error` always represents a failed status_code, and
nothing else. It cannot be empty, it cannot be success. Outcome will be
using this new type `error` as its E type in its experimental form.

You may be seeing a ton load more about this proposed `error` type
during the coming year. It is very exciting.

Niall

--
ned Productions Limited Consulting
http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Proposed SG14 <system_error2> ready for feedback

Boost - Dev mailing list
On 5/03/2018 22:13, Niall Douglas wrote:
>> In any case intptr_t is either larger than needed to preserve the value
>> bits (in 64-bit applications) or smaller than needed to preserve the
>> value and sign (in 32-bit applications), so this seems like an
>> inappropriate choice.
>
> Structure alignment would align any int to the pointer, so same space in
> any case. Two CPU registers.

Perhaps, but the difference between value bits and padding bits is
significant, and it seems pointless (and prone to compiler warnings
about losing significant bits if you then try to assign the value back
to its "real" type) to misrepresent its size.

> In private discussions with some on WG21, it is felt that status code
> needs to always be two CPU registers exactly. So I'll be bumping that to
> intptr_t on all platforms.

Why?  What benefit does this provide?


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Proposed SG14 <system_error2> ready for feedback

Boost - Dev mailing list
>> Structure alignment would align any int to the pointer, so same space in
>> any case. Two CPU registers.
>
> Perhaps, but the difference between value bits and padding bits is
> significant, and it seems pointless (and prone to compiler warnings
> about losing significant bits if you then try to assign the value back
> to its "real" type) to misrepresent its size.

Ah, I have a cunning piece of code to handle that. The problem was if
the original source was say a char padded by the compiler to an
intptr_t, and you erase that into an intptr_t. Seven bytes are therefore
being accessed as UB, and random data could appear in there. Both of
which are unhelpful.

So I have a bit of trampoline code which constexpr initialises a union
of the source type and the intptr_t which is initialised to all bits
zero. We then set the source type, and read back the intptr_t. This
ensures that the padding bits are all bits zero in the type erased form.

>> In private discussions with some on WG21, it is felt that status code
>> needs to always be two CPU registers exactly. So I'll be bumping that to
>> intptr_t on all platforms.
>
> Why?  What benefit does this provide?

So the compiler could be given a hard guarantee that an `error` can
always be trivially returned. On x64 Itanium ABI, RAX+RDX is used to
return a 16 byte value. The MSVC ABI currently does not use that
mechanism on x64, it can only return up to 8 bytes trivially.

A function potentially returning `error` would be mangled to say that it
does so. The compiler therefore knows that one of the CPU flags (e.g. DF
or SF) means whether to interpret RAX+RDX as an `error` or as an
ordinary non-failure return value.

That's why it's important that `error` fit into two CPU registers.

`error` is now ready incidentally.
https://ned14.github.io/status-code/doc_error.html. It's looking very
promising. Be aware that https://wg21.link/P0709 currently results in a 404.

Niall

--
ned Productions Limited Consulting
http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost