Document Number |
P1198R0 |
Date |
2018-09-28 |
Project |
Programming Language C++ |
Audience |
Library Evolution Working Group |
Summary |
This paper proposes that |
Rationale
At present, the idiomatic pattern
void f(std::error_code& ec)
{
perform_first_action(ec);
if(ec) return;
perform_second_action(ec);
if(ec) return;
// ...
}
tests for failure by using operator bool
, which is hardcoded to return value() != 0
. This makes it impossible to define categories for which the equivalence between
0 and success doesn’t hold. For instance, Windows HRESULT
codes represent success when nonnegative, and HTTP status codes represent success when between 200 and 299.
Categories should encapsulate the knowledge of interpreting a code as a success or as a failure, and this paper proposes a mechanism by which they can do so, by defining
a virtual member function failed
.
In addition, failed()
is added to error_code
and error_condition
, so that the idiomatic use can be
if(ec.failed()) return;
Practice shows that if(ec)
is often confusing, as it can be interpreted as both "has succeeded" and "has failed". No such ambiguity exists with if(ec.failed())
.
Implementability
The proposed changes have been implemented in Boost.System and currently reside on its develop branch. They are expected to ship in Boost 1.69.
The Boost implementation calls category().failed(value())
in the constructor of error_code
(and error_condition
) and caches the result in a bool
member. This
results in significantly improved code generation because the category is almost always known at compile time at the point the constructor of error_code
is invoked,
whereas it is typically not known at the point error_code::failed
is invoked.
On 64 bit platforms, the bool
member can fit into the unused padding between int val_
and error_category const* cat_
, so error_code
doesn’t grow in size.
Proposed Changes
-
namespace std { class error_category { public: constexpr error_category() noexcept; virtual ~error_category(); error_category& operator=(const error_category&) = delete; virtual const char* name() const noexcept = 0; virtual error_condition default_error_condition(int ev) const noexcept; virtual bool equivalent(int code, const error_condition& condition) const noexcept; virtual bool equivalent(const error_code& code, int condition) const noexcept; virtual string message(int ev) const = 0; constexpr virtual bool failed(int ev) const noexcept; bool operator==(const error_category& rhs) const noexcept; bool operator!=(const error_category& rhs) const noexcept; bool operator< (const error_category& rhs) const noexcept; }; const error_category& generic_category() noexcept; const error_category& system_category() noexcept; }
-
constexpr virtual bool failed(int ev) const noexcept;
Returns: ev != 0
.
-
virtual bool failed(int ev) const noexcept;
Returns: true
whenev
represents a failure.Remarks: All calls passing the same
ev
shall yield the same result.
-
explicit operator bool() const noexcept;
Returns: value() != 0
failed()
.constexpr bool failed() const noexcept;
Returns: category().failed(value())
.[Note: The implementation may call
category().failed(value())
once, for example at construction time, and cache the result. --end note]
-
explicit operator bool() const noexcept;
Returns: value() != 0
failed()
.constexpr bool failed() const noexcept;
Returns: category().failed(value())
.[Note: The implementation may call
category().failed(value())
once, for example at construction time, and cache the result. --end note]
ABI Implications
The proposed addition of a new virtual function to error_category
unfortunately constitutes an ABI break.
It affects new code calling the new failed
function on error_code
objects from user-defined categories
returned by old code. In addition, since the contextual conversion to bool
is proposed to now call
failed
, code using this old construct will also be affected.
Adopting the versioning mechanism proposed in P1196R0 — if deemed workable — would solve both issues. Otherwise, if affecting just code using the new construct is considered acceptable,
the bool
conversion can be sacrificed on the altar of compatibility and left unchanged.
-- end