<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">

<style type="text/css">

body { color: #000000; background-color: #FFFFFF; }
del { text-decoration: line-through; color: #8B0040; }
ins { text-decoration: underline; color: #005100; }
li { margin: 0 0 1ex 0; }

p.example { margin-left: 2em; }
pre.example { margin-left: 2em; }
div.example { margin-left: 2em; }

code.extract { background-color: #F5F6A2; }
pre.extract { margin-left: 2em; background-color: #F5F6A2;
  border: 1px solid #E1E28E; }

p.function { }
.attribute { margin-left: 2em; }
.attribute dt { float: left; font-style: italic;
  padding-right: 1ex; }
.attribute dd { margin-left: 0em; }

blockquote.std { color: #000000; background-color: #F1F1F1;
  border: 1px solid #D1D1D1;
  padding-left: 0.5em; padding-right: 0.5em; }
blockquote.stddel { text-decoration: line-through;
  color: #000000; background-color: #FFEBFF;
  border: 1px solid #ECD7EC;
  padding-left: 0.5em; padding-right: 0.5em; }

blockquote.stdins { text-decoration: underline;
  color: #000000; background-color: #C8FFC8;
  border: 1px solid #B3EBB3; padding: 0.5em; }

table { border: 1px solid black; border-spacing: 0px;
  margin-left: auto; margin-right: auto; }
th { text-align: left; vertical-align: top;
  padding-left: 0.8em; border: none; }
td { text-align: left; vertical-align: top;
  padding-left: 0.8em; border: none; }

</style>

<title>Summary of SG14 discussion on &lt;system_error&gt;</title>
</head>
<body>

<b>Document number:</b> P0824R1 <br>
<b>Date:</b> 2018-02-05 <br>
<b>Project:</b> ISO JTC1/SC22/WG21, Programming Language C++ <br>
<b>Audience:</b> Library Evolution Working Group <br>
<b>Reply to:</b> Arthur O'Dwyer &lt;arthur.j.odwyer@gmail.com&gt;, Charley Bay &lt;charleyb123@gmail.com&gt;,
Odin Holmes &lt;holmes@auto-intern.de&gt;, Michael Wong &lt;fraggamuffin@gmail.com&gt;,
Niall Douglas &lt;s_sourceforge@nedprod.com&gt;<br>

<h1>Summary of SG14 discussion on <code>&lt;system_error&gt;</code></h1>

<p>
<a href="#Introduction">1. Introduction</a><br>
<a href="#Description">2. Description of C++11's <code>&lt;system_error&gt;</code> facilities</a><br>
<a href="#Best">3. Proposed best practices for using C++11's <code>&lt;system_error&gt;</code> facilities</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#best-arthur">3.1. Proposed by Arthur O'Dwyer</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#best-charley">3.2. Proposed by Charley Bay</a><br>
<a href="#Issues">4. Issues with the <code>&lt;system_error&gt;</code> facilities</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#issue-string">4.1. Use of <code>std::string</code></a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#issue-outparams">4.2. Proliferation of "two-API" libraries</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#issue-bool">4.3. No wording sets aside the <code>0</code> enumerator</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#issue-singletons">4.4. Reliance on singletons</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#issue-constexpr">4.5. No <code>error_category</code> subclass can be a literal type</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#issue-metadata">4.6. No guidance on attaching extra information to <code>error_code</code></a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#issue-operator">4.7. Reliance on a surprising overload of <code>operator==</code></a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#issue-naming">4.8. <code>error_category</code> should properly be named <code>error_domain</code></a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#issue-anyway">4.9. Standard <code>error_code</code>-yielding functions can throw exceptions anyway</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#issue-idb">4.10. Underspecified <code>error_code</code> comparison semantics</a><br>
</p>


<h2><a name="Introduction">1. Introduction</a></h2>

<p>
This paper summarizes a discussion that took place during the SG14 telecon of 2017-10-11.
The discussion concerned the facilities of <code>&lt;system_error&gt;</code> (introduced in C++11),
which include <code>errc</code>, <code>error_code</code>, <code>error_condition</code>,
<code>error_category</code>, and <code>system_error</code>.
</p>

<p>
The discussion naturally also concerns the current idioms for exceptionless "disappointment handling"
(<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0157r0.html">P157R0</a>, Lawrence Crowl), as seen
in <code>&lt;filesystem&gt;</code> (C++17) and <code>std::from_chars</code>/<code>to_chars</code> (also C++17);
and it concerns <i>future</i> idioms for exceptionless disappointment handling, as proposed
in <code>status_value&lt;E,T&gt;</code>
(<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0262r0.html">P0262R0</a>, Lawrence Crowl),
in <code>expected&lt;T,E&gt;</code>
(<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0323r2.pdf">P0323R2</a>, Vicente J. Botet Escribá),
and in <code>result&lt;T,EC&gt;</code> (<a href="https://ned14.github.io/outcome/">Outcome</a>, Niall Douglas).
</p>

<p>
In all of those future idioms involving <code>expected&lt;T,E&gt;</code>-style result types, SG14 expects that
the <code>E</code> type parameter will default to <code>std::error_code</code> — which is right and good. This means
that the coming years will see much increased use of <code>std::error_code</code> in both new and existing codebases.
This means that SG14 is very interested in correcting deficiencies in <code>std::error_code</code> sooner, rather
than later.
</p>

<p>
On SG14's 2017-10-11 teleconference call, various people contributed to a miscellaneous "laundry list" of
perceived deficiencies with <code>&lt;system_error&gt;</code>. Several "best practices" were also proposed for
how we expect <code>&lt;system_error&gt;</code> facilities to be used in the best codebases. (Unfortunately,
the Standard Library does not follow <i>anyone's</i> proposed "best practice"!) This paper is a summary of that
discussion.
</p>

<h2><a name="Description">2. Description of C++11's <code>&lt;system_error&gt;</code> facilities</a></h2>

<p>
<code>&lt;system_error&gt;</code> provides the following patterns for dealing with error codes:
</p>

<p>
    A <code>std::error_code</code> object wraps an integer "error enumerator" and a pointer to an "error category"
    (that is, a handle to the specific error domain associated with this integer enumerator). The addition of the
    error domain handle is what allows us to distinguish, say, <code>error=5</code> "no leader for Kafka partition"
    (in the rdkafka domain) from <code>error=5</code> "I/O error" (in the POSIX domain). Two <code>error_code</code>
    instances compare equal if and only if they represent the same error enumerator <i>in the same domain</i>.
</p>
<p>
    Notice that <code>std::error_condition</code> is not the same type as <code>std::error_code</code>!
</p>
<pre>
    namespace std {
    class error_code {
        int value_;
        std::error_category *cat_;
    public:
        error_code() noexcept : error_code(0, std::system_category()) {}
        error_code(int e, const std::error_category&amp; c) noexcept : value_(e), cat_(&amp;c) {}
        template&lt;class E&gt; error_code(E e) noexcept requires(std::is_error_code_enum_v&lt;E&gt;) { *this = make_error_code(e); }  // intentional ADL
        template&lt;class E&gt; error_code&amp; operator=(E e) noexcept requires(std::is_error_code_enum_v&lt;E&gt;) { *this = make_error_code(e); }  // intentional ADL
        void assign(int e, const std::error_category&amp; c) noexcept { value_ = e; cat_ = &amp;c; }
        void clear() noexcept { *this = std::error_code(); }

        explicit operator bool() const noexcept { return value_ != 0; }
        int value() const noexcept { return value_; }
        const std::error_category&amp; category() const noexcept { return cat_; }

        std::string message() const { return cat_->message(value_); }
        std::error_condition default_error_condition() const noexcept { return cat_->default_error_condition(value_); }
    };
    bool operator==(const std::error_code&amp; a, const std::error_code&amp; b) noexcept { return a.value() == b.value() &amp;&amp; &amp;a.category() == &amp;b.category(); }
    } // namespace std
</pre>

<p>
    Each "error domain" is represented in code by an object of type <code>std::error_category</code>. The standard effectively
    implies that these must be singletons, because <code>error_code</code> equality-comparison is implemented in terms of pointer comparison
    (see above). So two <code>error_category</code> objects located at different memory addresses represent different
    error domains, <i>by definition</i>, as far as the currently standardized scheme is concerned.
</p>
<pre>
    namespace std {
    class error_category {
    public:
        constexpr error_category() noexcept = default;
        error_category(const error_category&amp;) = delete;
        virtual ~error_category() {}

        virtual const char *name() const noexcept = 0;

        virtual std::error_condition default_error_condition(int e) const noexcept { return std::error_condition(e, *this); }
        virtual std::string message(int e) const = 0;

        virtual bool equivalent(int e, const std::error_condition&amp; condition) const noexcept { return this->default_error_condition(e) == condition; }
        virtual bool equivalent(const std::error_code&amp; code, int e) const noexcept { return code == std::error_code(e, *this); }
    };
    bool operator==(const std::error_category&amp; a, const std::error_category&amp; b) noexcept { return &amp;a == &amp;b; }
    } // namespace std
</pre>

<p>
    It is intended that the programmer should <i>inherit publicly from</i> <code>std::error_category</code> and override its pure virtual methods
    (and optionally override its non-pure virtual methods) to create new library-specific error domains. An error domain encompasses a set of
    error enumerators with their associated human meanings (for example, <code>error=5</code> meaning "no leader for Kafka partition").
    So for example we can expect that <code>rdkafka_category().message(5)</code> would return
    <code>std::string("no leader for partition")</code>.
</p>

<p>
    The standard also provides a class <code>std::error_condition</code> which is almost identical in implementation
    to <code>std::error_code</code>.
</p>
<pre>
    namespace std {
    class error_condition {
        int value_;
        std::error_category *cat_;
    public:
        error_condition() noexcept : error_code(0, std::generic_category()) {}
        error_condition(int e, const std::error_category&amp; c) noexcept : value_(e), cat_(&amp;c) {}
        template&lt;class E&gt; error_condition(E e) noexcept requires(std::is_error_condition_enum_v&lt;E&gt;) { *this = make_error_condition(e); }  // intentional ADL
        template&lt;class E&gt; error_condition&amp; operator=(E e) noexcept requires(std::is_error_condition_enum_v&lt;E&gt;) { *this = make_error_condition(e); }  // intentional ADL
        void assign(int e, const std::error_category&amp; c) noexcept { value_ = e; cat_ = &amp;c; }
        void clear() noexcept { *this = std::error_condition(); }

        explicit operator bool() const noexcept { return value_ != 0; }
        int value() const noexcept { return value_; }
        const std::error_category&amp; category() const noexcept { return cat_; }

        std::string message() const { return cat_->message(value_); }
    };
    bool operator==(const std::error_condition&amp; a, const std::error_condition&amp; b) noexcept { return a.value() == b.value() &amp;&amp; &amp;a.category() == &amp;b.category(); }
    } // namespace std
</pre>
<p>
    The best practice for using <code>std::error_condition</code> is the subject of some debate in SG14; see the rest of this paper.
    However, clearly the vague intent of <code>std::error_condition</code> is to represent in some way a high-level "condition" which
    a low-level "error code" (thrown up from the bowels of the system) might or might not "match" in some high-level semantic sense.
    Notice that the error domain of a default-constructed <code>error_code</code> is <code>system_category()</code>, whereas the
    error domain of a default-constructed <code>error_condition</code> is <code>generic_category()</code>.
</p>

<p>
    The standard provides a highly customizable codepath for comparing an <code>error_code</code> against an <code>error_condition</code>
    with <code>operator==</code>.
    (Both <code>error_code</code> and <code>error_condition</code> are value types &mdash; in fact they are trivial types &mdash; which means
    that their own <code>operator==(A,A)</code> are just bitwise comparisons. We are now speaking of <code>operator==(A,B)</code>.)
</p>
<pre>
    namespace std {
    bool operator==(const std::error_code&amp; a, const std::error_condition&amp; b) noexcept {
        return a.category().equivalent(a.value(), b) || b.category().equivalent(a, b.value());
    }
    bool operator==(const std::error_condition&amp; a, const std::error_code&amp; b) noexcept {
        return a.category().equivalent(a.value(), b) || b.category().equivalent(a, b.value());
    }
    } // namespace std
</pre>
<p>
    Recall that the base-class implementation of <code>error_category::equivalent()</code> is just to compare for strict equality;
    but the programmer's own <code>error_category</code>-derived classes can override <code>equivalent()</code> to have different
    behavior.
</p>

<p>
    Lastly, the <code>&lt;system_error&gt;</code> header provides an exception class that wraps an <code>error_code</code>
    (but not an <code>error_condition</code>):
</p>
<pre>
    namespace std {
    class system_error : public std::runtime_error {
        std::error_code code_;
        std::string what_;
    public:
        system_error(std::error_code ec) : code_(ec), what_(ec.message()) {}
        system_error(std::error_code ec, const std::string&amp; w) : code_(ec), what_(ec.message() + ": " + w) {}
        system_error(std::error_code ec, const char *w) : system_error(ec, std::string(w)) {}
        system_error(int e, const std::error_category&amp; cat) : system_error(std::error_code(e, cat)) {}
        system_error(int e, const std::error_category&amp; cat, const std::string&amp; w) : system_error(std::error_code(e, cat), w) {}
        system_error(int e, const std::error_category&amp; cat, const char *w) : system_error(std::error_code(e, cat), w) {}
        const std::error_code&amp; code() const noexcept { return code_; }
        const char *what() const noexcept override { return what_.c_str(); }
    };
    } // namespace std
</pre>
<p>
    Note that <code>std::filesystem::filesystem_error</code> inherits from <code>std::system_error</code>.
</p>

<h2><a name="Best">3. Proposed best practices for using C++11's <code>&lt;system_error&gt;</code> facilities</a></h2>

<h3><a name="best-arthur">3.1. Proposed by Arthur O'Dwyer</a></h3>

<p>
    In the example that follows, we have an application <code>appA</code>, which calls into library <code>libB</code>,
    which calls into library <code>libC</code>, which calls into library <code>libD</code>.
</p>

<p>
    Arthur O'Dwyer proposes the following general rules:
    <ol>
        <li>No enumeration type <code>E</code> should ever satisfy <i>both</i> <code>is_error_code_enum_v&lt;E&gt;</code>
        and <code>is_error_condition_enum_v&lt;E&gt;</code> simultaneously. (To do so would be to represent a low-level error code value
        and a high-level abstract condition simultaneously, which is impossible.)</li>

        <li>For any enumeration type <code>E</code>, the ADL function <code>make_error_code(E)</code> should exist if-and-only-if
        <code>is_error_code_enum_v&lt;E&gt;</code>; and the ADL function <code>make_error_condition(E)</code> should exist if-and-only-if
        <code>is_error_condition_enum_v&lt;E&gt;</code>.

        <li>In any enumeration type <code>E</code> satisfying either <code>is_error_code_enum_v&lt;E&gt;</code> or
        <code>is_error_condition_enum_v&lt;E&gt;</code>, the enumerator value <code>0</code> must be set aside as a "success"
        value, and never allotted to any mode of failure. In fact, the enumeration <code>E</code> should have an enumerator
        <code>success = 0</code> or <code>none = 0</code> to ensure that this invariant is never broken accidentally by
        a maintainer.</li>

        <li>Your library should have exactly as many <code>error_category</code> subclasses as it has enumeration types satisfying
        either <code>is_error_code_enum_v&lt;E&gt;</code> or <code>is_error_condition_enum_v&lt;E&gt;</code>, in a one-to-one correspondence.
        No <code>error_category</code> subclass should be "shared" between two enumeration types; and no <code>error_category</code>
        subclass should exist that is not associated with a specific enumeration type.</li>

        <li>Each <code>error_category</code> subclass should be a singleton; that is, it should have a single instance
        across the entire program.</li>

        <li>When your library detects a failure, it should construct an <code>std::error_code</code> representing the failure. This can
        be done using ADL <code>make_error_code(LibD::ErrCode::failure_mode)</code> or simply using <code>LibD::ErrCode::failure_mode</code>
        (which works because of <code>std::error_code</code>'s implicit constructor from error-code-enum types).</li>

        <li>This <code>std::error_code</code> is passed up the stack using out-parameters (as <code>&lt;filesystem&gt;</code>) or
        using <code>Expected&lt;T, std::error_code&gt;</code>.</li>

        <li>When <code>libB</code> receives a <code>std::error_code code</code> that must be checked for <i>failure versus success</i>,
        it should use <code>if (code)</code> or <code>if (!code)</code>.</li>

        <li>When <code>libB</code> receives a <code>std::error_code code</code> that must be checked for
        <i>a particular source-specific error</i> (such as "rdkafka partition lacks a leader"), it should use
        <code>if (code == LibD::ErrCode::failure_mode)</code>. This performs exact equality,
        and is useful if you know the exact source of the error you're looking for (such as "rdkafka").</li>

        <li>When <code>libB</code> receives a <code>std::error_code code</code> that must be checked for
        <i>a high-level condition</i> (such as "file not found"), which might correspond to any of several source-specific errors
        across different domains, it may use <code>if (code == LibC::ErrCondition::failure_mode)</code>, where <code>LibC::ErrCondition</code>
        is an error-condition-enum type provided by the topmost library (the one whose API we're calling &mdash; not any lower-level
        library). This will perform semantic classification.</li>

        <li>Your library might perhaps <i>provide</i> semantic classification by providing an error-condition-enum type <code>LibB::ErrCondition</code>
        (and its associated <code>error_category</code> subclass <code>LibB::ErrConditionCategory</code>), which encodes knowledge about
        the kinds of error values reported by <code>LibC</code>. Ideally, <code>LibB::ErrConditionCategory::equivalent()</code> should
        defer to <code>LibC::ErrConditionCategory::equivalent()</code> in any case where <code>LibB</code> is unsure of the meaning of
        a particular error code (for example, if it comes from an unrecognized error domain).</li>

        <li>Most likely, <code>std::error_condition</code> and error-condition-enum types should simply <i>not be used</i>.
        <code>libB</code> should not expect its own callers to write <code>if (code == LibB::ErrCondition::oom_failure)</code>;
        instead <code>libB</code> should expect its callers to write <code>if (LibB::is_oom_failure(code))</code>, where
        <code>bool LibB::is_oom_failure(std::error_code)</code> is a free function provided by <code>libB</code>. This successfully
        accomplishes semantic classification, and does it without any operator overloading, and therefore does it without the
        need for the <code>std::error_condition</code> type.</li>
    </ol>
</p>


<h3><a name="best-charley">3.2. Proposed by Charley Bay</a></h3>

<p>
    In the example that follows, we have an application <code>appA</code>, which calls into library <code>libB</code>,
    which calls into library <code>libC</code>, which calls into library <code>libD</code>.
</p>

<p>
    Charley Bay proposes (something like) the following general rules (paraphrased here by Arthur O'Dwyer):
    <ol>
        <li>No enumeration type <code>E</code> should ever satisfy <i>both</i> <code>is_error_code_enum_v&lt;E&gt;</code>
        and <code>is_error_condition_enum_v&lt;E&gt;</code> simultaneously. (To do so would be to represent a low-level error code value
        and a high-level abstract condition simultaneously, which is impossible.)</li>

        <li>For any enumeration type <code>E</code>, the ADL function <code>make_error_code(E)</code> should exist if-and-only-if
        <code>is_error_code_enum_v&lt;E&gt;</code>; and the ADL function <code>make_error_condition(E)</code> should exist if-and-only-if
        <code>is_error_condition_enum_v&lt;E&gt;</code>.

        <li>In any enumeration type <code>E</code> satisfying either <code>is_error_code_enum_v&lt;E&gt;</code> or
        <code>is_error_condition_enum_v&lt;E&gt;</code>, the enumerator value <code>0</code> must be set aside as a "success"
        value, and never allotted to any mode of failure. In fact, the enumeration <code>E</code> should have an enumerator
        <code>success = 0</code> or <code>none = 0</code> to ensure that this invariant is never broken accidentally by
        a maintainer.</li>

        <li><code>error_category</code> subclasses are not necessarily singletons. It is conceivable that multiple instances
        of the same <code>error_category</code> subclass type could exist within the same program.</li>

        <li>When your library detects a failure, it should construct an <code>std::error_code</code> representing the failure.
        This should be done using <code>LibD::ErrCode::failure_mode</code>
        (which works because of <code>std::error_code</code>'s implicit constructor from error-code-enum types).</li>

        <li>This <code>std::error_code</code> is passed up the stack using out-parameters (as <code>&lt;filesystem&gt;</code>) or
        using <code>Expected&lt;T, std::error_code&gt;</code>.</li>

        <li>When <code>libB</code> receives a <code>std::error_code code</code>, it must <i>never</i> be checked for
        <i>a particular source-specific error</i> (such as "rdkafka partition lacks a leader"). Every test should be done
        on the basis of semantic classification &mdash; whether at the coarse granularity of "failure versus success" or at the
        fine granularity of "partition lacks a leader." It is always conceivable that <code>libC</code> might change out its
        implementation so that it no longer uses <code>libD</code>; therefore, all testing of error codes returned by a
        <code>libC</code> API must be expressed in terms of the specific set of abstract failure modes exposed by that
        same <code>libC</code> API.</li>

        <li>When <code>libB</code> receives a <code>std::error_code code</code>, it should <i>not</i> be checked for
        <i>failure versus success</i>
        via <code>if (code)</code> or <code>if (!code)</code>. Instead, "failure" should be a semantic classification
        as described in the preceding point.</li>

        <li>As explained in the preceding point, exact-equality comparisons should never be used.
        But with the standard syntax, there is a significant risk that the programmer will accidentally write
        <code>if (code == LibB::ErrCode::oom_failure)</code> (exact-equality comparison) instead of the intended
        <code>if (code == make_error_condition(LibB::ErrCode::oom_failure))</code> (semantic classification).
        Therefore, under the current standard library design, <code>std::error_condition</code> and error-condition-enum types
        should <i>not be used</i>. <code>libB</code>
        should expect its callers to write <code>if (LibB::is_oom_failure(code))</code>, where
        <code>bool LibB::is_oom_failure(std::error_code)</code> is a free function provided by <code>libB</code>. This successfully
        accomplishes semantic classification, and does it without any operator overloading, and therefore does it without the
        need for the <code>std::error_condition</code> type.</li>
    </ol>
</p>


<h2><a name="Issues">4. Issues with the <code>&lt;system_error&gt;</code> facilities</a></h2>

<p>
    On the 2017-10-11 teleconference, the following issues were discussed.
</p>


<h3><a name="issue-string">4.1. Use of <code>std::string</code></a></h3>

<p>
    <code>std::error_category</code>'s method <code>virtual std::string message(int) const</code> converts an error-code-enumerator
    into a human-readable message. This functionality is apparently useful only for presentation to humans; i.e., we do not expect
    that anyone should be treating the result of <code>message()</code> as a unique key, nor scanning into its elements with
    <code>strstr</code>. However, as a pure virtual method, this method <i>must</i> be implemented by each <code>error_category</code>
    subclass.
</p>
<p>
    Its return type is <code>std::string</code>, i.e., it introduces a hard-coded dependency on <code>std::allocator&lt;char&gt;</code>.
    This seems to imply that if you are on a computer system without <code>new</code> and <code>delete</code>,
    without <code>std::allocator</code>, without <code>std::string</code>, then you cannot implement your own <code>error_category</code>
    subclasses, which effectively means that you cannot use <code>std::error_code</code> to deal with disappointment.
    SG14 sees any hard-coded dependency on <code>std::allocator</code> as unfortunate.
    For a supposedly "fundamental" library like <code>&lt;system_error&gt;</code> it is <i>extremely</i> unfortunate.
    (<a href="https://cplusplus.github.io/LWG/issue2955">LWG issue 2955</a> is related: <code>std::from_chars</code>
    used to depend on <code>std::error_code</code> and thence on
    <code>std::string</code>. It was resolved by the adoption of <a href="https://wg21.link/P0682R1">P0682R1</a>,
    i.e., <code>std::from_chars</code> simply stopped trying to use <code>std::error_code</code> at all.)
</p>
<p>
    During the SG14 telecon, Charley Bay commented that returning ownership of a dynamically allocated string allows the
    <code>error_category</code> subclass to return a message that differs based on <i>the current locale</i>.
    However, nobody on the call claimed that this functionality was important to them. Furthermore, if locale-awareness
    were desirable, then branching on the <i>current (global) locale</i> would be the wrong way to go about it, because
    that mechanism would not be usable by multi-threaded programs. The right way to introduce locale-awareness into
    <code>error_category</code> would be to provide a virtual method <code>std::string message(int, const std::locale&amp;)</code>.
</p>
<p>
    SG14 seems to agree that eliminating the dependency on <code>std::allocator</code> would be nice.
</p>
<p>
    SG14 seems to agree that dynamically allocated message strings are not an important feature.
</p>
<p>
    Two ways of removing <code>std::string</code> were proposed: return <code>const char*</code>, or return <code>std::string_view</code>.
    Arthur O'Dwyer commented that he strongly prefers <code>const char*</code> for simplicity (no new library dependencies) and for consistency
    with <code>std::error_category::name()</code> and <code>std::exception::what()</code>.
    Niall Douglas commented that he prefers <code>std::string_view</code> over raw null-terminated <code>const char*</code> whenever possible.
</p>
<p>
    Both ways of removing <code>std::string</code> alter the return type of a pure virtual method
    and thus inevitably break every subclass of <code>std::error_category</code> ever. SG14 has no way out of this dilemma
    other than to suggest "wait for <code>std2</code> and do it correctly there."
</p>


<h3><a name="issue-outparams">4.2. Proliferation of "two-API" libraries</a></h3>

<p>
    <code>&lt;filesystem&gt;</code> is the poster child for this issue. Every function and method in <code>&lt;filesystem&gt;</code>
    comes in two flavors: throwing and non-throwing. The throwing version gets the "natural" signature (as is right and expected
    in C++); and the non-throwing version gets a signature with an extra out-parameter of type <code>std::error_code&amp;</code>.
    The expectation is apparently that <code>&lt;system_error&gt;</code> users will be willing to write "C-style" code:
    <pre>
        namespace fs = std::filesystem;
        void truncate_if_large(const fs::path&amp; p) noexcept
        {
            std::error_code ec;  // declare an uninitialized variable
            uintmax_t oldsize = fs::file_size(p, ec);
            if (ec) { report_error(ec); return; }
            if (oldsize > 1000) {
                fs::resize_file(p, 1000, ec);
                if (ec) { report_error(ec); return; }
            }
        }
    </pre>
</p>

<p>
    It would be nicer if the non-throwing API had exactly the same signatures as the throwing API, except that it should
    return <code>expected&lt;T&gt;</code> or <code>result&lt;T&gt;</code> instead of <code>T</code>. On the telecon,
    Arthur O'Dwyer commented that this can't easily be done because you cannot have two functions with the same name
    and the same signature, differing only in return type.
</p>
<p>
    It is possible to segregate the <i>free functions</i> into a separate namespace, say <code>namespace std::filesystem::nothrow</code>,
    so that the above code could be written as
    <pre>
        namespace fs = std::filesystem::nothrow;  // hypothetical
        void truncate_if_large(const fs::path&amp; p) noexcept
        {
            auto oldsize = fs::file_size(p);
            if (!oldsize.has_value()) { report_error(oldsize.error()); return; }
            if (oldsize.value() > 1000) {
                auto failure = fs::resize_file(p, 1000);
                if (failure) { report_error(failure.error()); return; }
            }
        }
    </pre>
    However, this doesn't help with member functions, such as <code>directory_entry::is_symlink()</code>.
</p>

<p>
    Having two APIs (throwing and non-throwing) side by side in the same namespace has another disadvantage.
    There is a significant risk that the programmer might accidentally leave off the out-parameter that signifies
    "non-throwing-ness", resulting in a call to the throwing version when a call to the non-throwing version was
    intended.
    <pre>
        namespace fs = std::filesystem;
        void truncate_if_large(const fs::path&amp; p) noexcept
        {
            std::error_code ec;  // declare an uninitialized variable
            uintmax_t oldsize = fs::file_size(p, ec);
            if (ec) { report_error(ec); return; }
            if (oldsize > 1000) {
                fs::resize_file(p, 1000);  // Oops! Bug goes undetected by all major vendors.
                if (ec) { report_error(ec); return; }
            }
        }
    </pre>
    The LLVM project has already observed this failure mode happening in the wild
    (<a href="https://reviews.llvm.org/D41830#inline-366674">bug identified</a>,
    <a href="http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/filesystem/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp?r1=322351&r2=322350&pathrev=322351">bugfix commit</a>).
    It is desirable to clearly segregate throwing from non-throwing functions. But we don't know how to make
    segregation work for member functions. Therefore perhaps the <i>best</i> outcome would be to stick with
    a single (non-throwing) API for each library.
</p>
<p>
    If we had a single (non-throwing) API that returned something like <code>Expected&lt;T&gt;</code>, and if
    <code>Expected&lt;T&gt;</code> had a member function <code>T or_throw()</code> that returned the
    <code>ex.value()</code> if possible or else threw a <code>system_error</code> initialized from <code>ex.error()</code>,
    then we could write exception-throwing code fluently as follows:
    <pre>
        namespace fs = std::filesystem::nothrow;
        void truncate_if_large(const fs::path&amp; p) noexcept
        {
            uintmax_t oldsize = fs::file_size(p).or_throw();
            if (oldsize > 1000) {
                fs::resize_file(p, 1000).or_throw();
            }
        }
    </pre>
    Here we assume that the template class <code>Expected&lt;void&gt;</code> is marked with the standard
    <code>[[nodiscard]]</code> attribute, so that if the programmer accidentally leaves off the final <code>or_throw()</code>
    the compiler will emit a warning.
</p>

<p>
    SG14 seems not to have a great answer for how to avoid "two-API" libraries such as <code>&lt;filesystem&gt;</code>
    going forward; but we believe that "two-API" libraries should be avoided. The Networking TS seems to be shaping up
    to be another "two-API" library. We believe this is unfortunate.
</p>


<h3><a name="issue-bool">4.3. No wording sets aside the <code>0</code> enumerator</a></h3>

<p>
    The current Standard strongly implies the best-practice mentioned above: that every error-code-enumerator and
    every error-condition-enumerator should set aside <code>success = 0</code> as a special case.
    <pre>
        enum class TroublesomeCode { out_of_memory, out_of_files };
        struct TroublesomeCategory : public std::error_category {
            const char *name() const noexcept override { return ""; }
            std::string message(int e) const override {
                switch (e) {
                    case TroublesomeCode::out_of_memory: return "out of memory";
                    case TroublesomeCode::out_of_files: return "out of files";
                    default: __builtin_unreachable();
                }
            }
        };
        const std::error_category&amp; troublesome_category() {
            static const TroublesomeCategory instance;
            return instance;
        }

        template<> struct std::is_error_code_enum&lt;TroublesomeCode&gt; : std::true_type {};
        std::error_code make_error_code(TroublesomeCode e) {
            return std::error_code((int)e, troublesome_category());
        }

        int main() {
            std::error_code ec = TroublesomeCode::out_of_memory;
            if (ec) {
                puts("This line will not be printed.");
            }
        }
    </pre>
    If the current specification of <code>std::error_code</code> is to remain untouched, then SG14 would like to see some explicit
    acknowledgment in the Standard that error-code enumerators with value <code>0</code>
    are "special," i.e., they will not be treated as "errors" by any of the machinery in the Standard. Error codes with
    value <code>0</code> are effectively reserved for the "success" case, and programmers should not attempt to use them
    for any other purpose.
</p>
<p>
    Vice versa, programmers should be aware that using a non-zero integer value to represent "success" will not work as expected.
    Consider an HTTP library that naively attempts to use <code>ok = 200</code> as its "success" code, and then provides an
    ADL <code>make_error_code</code> like this:
    <pre>
        enum class HTTPStatusCode { ok = 200, created = 201, /* ... */ };

        template<> struct std::is_error_code_enum&lt;HTTPStatusCode&gt; : std::true_type {};
        std::error_code make_error_code(HTTPStatusCode e) {
            return std::error_code((e == ok) ? 0 : (int)e, http_status_category());
        }

        std::string HTTPStatusCategory::message(int e) const {
            switch (e) {
                case 0: return "200 OK";
                case 201: return "201 Created";
                // ...
            }
        }
    </pre>
    The programmer may head far down this "garden path" under the assumption that his goal of a non-zero "ok" code is attainable;
    but we on the Committee know that it is <i>not</i> attainable. We should save the programmer some time and some headaches,
    by explicitly reserving error-code <code>0</code> in the standard.
</p>
<p>
    However, there is an alternative and perhaps better solution, which is to replace certain constructors and member functions
    of <code>std::error_code</code> and <code>std::error_condition</code> as follows: Rather than
<pre>
        error_code() noexcept : error_code(0, std::system_category()) {}
        error_condition() noexcept : error_code(0, std::generic_category()) {}
        explicit operator bool() const noexcept { return value_ != 0; }
</pre>
    we could propose
<pre>
        error_code() noexcept : error_code(0, std::null_category()) {}
        error_condition() noexcept : error_code(0, std::null_category()) {}
        explicit operator bool() const noexcept { return cat_ != &amp;std::null_category(); }
</pre>
    This would eliminate the current requirement that error-code <code>0</code> always be reserved. Providing
    a default "null category" could also help to clarify the expected semantics of a default-constructed
    <code>error_condition</code>, which at present is implementation-defined and varies by vendor.
</p>


<h3><a name="issue-singletons">4.4. Reliance on singletons</a></h3>

<p>
    <code>std::error_category</code> implicitly relies on singletons. Even if the programmer can somehow get away with using
    non-singletons for his own categories, the standard library's own categories (e.g. <code>std::generic_category</code>)
    are singletons: their <code>operator==</code> is explicitly defined to compare instances for address-equality.
    <code>std::error_category</code> is not the only standard C++ feature to rely on singletons: we have prior art in the
    form of the <code>std::type_info</code> singletons which are used by <code>dynamic_cast</code> and also by <code>catch</code>.
</p>
<p>
    Prior to the SG14 telecon, Niall Douglas raised the point that singletons do not play well with DLLs (a.k.a. shared objects,
    a.k.a. dylibs). On some platforms, there are common programming idioms which can cause a C++ library's "singletons" to become
    duplicated. These include at least:
    <ul>
        <li>
            Library A is statically linked with Boost v1.5; library B is statically linked with Boost v1.6; the application
            is statically linked with libraries A and B. All of Boost's singletons are duplicated. (In some cases this is
            actually the desired behavior; in other cases we'd actually want some of them merged together if we had the choice.)
        </li>
        <li>
            Library A is statically linked with Boost; library B is also statically linked with Boost; the application
            dynamically loads library A (with <code>RTLD_LOCAL</code> or the equivalent) and then dynamically loads library B.
            Library B cannot "see" library A's exported symbols, so it brings in duplicate copies of all the Boost singletons.
        </li>
    </ul>
    Arthur O'Dwyer's (admittedly uninformed) opinion is that these sound like antipatterns that could reasonably be avoided.
    Niall Douglas's opinion is that these are patterns in use in big consumers, e.g. Python loads its C/C++ modules with
    <code>RTLD_LOCAL</code>, and indeed has no choice but to do so.
    Charley Bay has also seen these failure modes in practice.
</p>
<p>
    The practical difficulty of using singletons in DLLs seems to be a continuing pain point for certain programmers. The use
    of singletons by RTTI seems to be a continuing reason that some programmers avoid <code>dynamic_cast</code> and
    exception handling. The use of singletons by <code>std::error_category</code> will cause the same kinds of problems in practice
    as the use of singletons by RTTI in exception-handling. If we cannot figure out how to eliminate these practical problems,
    then <code>std::error_code</code> cannot possibly be a suitable replacement for exception-handling because it will
    continue to have the same problems (namely, a reliance on singletons compared for address-equality).
</p>
<p>
    In practice, some platforms (notably MSVC, and libc++ if built with a compile-time flag) work around the above problems
    for <code>type_info</code> singletons by implementing <code>type_info::operator==</code> as a string-equality (i.e.
    <code>!strcmp(this->name(), rhs.name())</code>) instead of an address-equality (i.e. <code>this == &amp;rhs</code>).
    This workaround is blessed by the Standard; <code>type_info::operator==</code> is specified to return
    "true if the two values describe the same type" with no constraints on <i>how</i> this "sameness" is determined.
    In contrast, <code>error_category::operator==</code> is specified to return exactly "<code>this == &amp;rhs</code>"
    with no wiggle room at all.
</p>
<p>
    SG14 suggests that perhaps <code>error_category::operator==</code> is overspecified, and that it could be relaxed to
    allow for string-equality comparison.
</p>
<p>
    SG14 identifies "singletons in DLLs" as a problem area. It is currently unclear how to handle "singletons in DLLs" in C++.
    Whoever knows the best practice in this area should speak up.
</p>

<h3><a name="issue-constexpr">4.5. No <code>error_category</code> subclass can be a literal type</a></h3>

<p>
    During the SG14 telecon, Odin Holmes and Charley Bay raised the issue that <code>std::error_code</code> is not usable in
    constexpr contexts; for example, <code>bool operator==(const error_code&amp; lhs, const error_code&amp; rhs) noexcept</code>
    is not <code>constexpr</code>. Even constructing a <code>std::error_code</code> instance cannot be done constexprly,
    because <code>error_code(int val, const error_category&amp; cat) noexcept</code> is not <code>constexpr</code>.
    Even if it <i>were</i> constexpr, we <i>still</i> wouldn't be able to construct an <code>error_code</code> constexprly,
    because we couldn't get the appropriate <code>const error_category&amp;</code>, because for example
    <code>const error_category&amp; generic_category() noexcept</code> is not constexpr!
</p>
<p>
    It is impossible to manipulate any <code>error_category</code> instance constexprly, because <code>error_category</code>
    is not a literal type: it has a virtual destructor, whereas literal types require trivial destructors. This is especially
    unfortunate because <code>error_category</code> does not need polymorphic destruction.
</p>
<p>
    During the SG14 telecon, Odin Holmes explained that in his programs he often uses <code>constexpr</code> on functions that he does
    <i>not</i> expect to be evaluated at compile-time. The reason he uses <code>constexpr</code> is to demonstrate and enforce
    that the functions are <i>pure</i>; and their compile-time-evaluability is just an occasional bonus. This usage of
    <code>constexpr</code> would have been infeasible in C++11, where constexpr functions were constrained to single statements;
    but it is feasible in C++14 and later. In C++11, the programmer must learn two different programming styles: a convenient
    and fluid style for "run-time" functions, and a highly convoluted and obfuscated style for "compile-time" constexpr functions.
    In C++14, the programmer can generally use a single, convenient, fluid style for both "run-time" functions and
    "compile-time" constexpr functions; the difference between a non-constexpr function and a constexpr function in C++14 is
    usually just the addition or subtraction of the keyword <code>constexpr</code>.
</p>
<p>
    However, if a function <i>A</i>'s body unconditionally uses some non-constexpr function or constructs some non-literal type <i>B</i>, then
    adding the keyword <code>constexpr</code> to <i>A</i> will trigger a compiler error. Examples of non-constexpr functions and types
    include <code>std::vector</code> and <code>std::regex</code>... but also <code>std::error_code</code>!
</p>
<pre>
    constexpr int f(int i) {
        std::error_code ec;
        return i;
    }

    error: variable of non-literal type 'std::error_code' cannot be defined in a constexpr function
        std::error_code ec;
                        ^
</pre>
<p>
    Even if we forget about the potentially tricky code to raise and handle <code>error_code</code>s, and just try to propagate
    an error code up from the lower level to the higher level, we find that we cannot do it in a constexpr way. (This error message
    is from GCC with libstdc++. libc++ unilaterally adds <code>constexpr</code> to <code>error_code::operator bool()</code>,
    which is a conforming extension.)
</p>
<pre>
    constexpr int constexpr_4throot(int i, std::error_code&amp; ec) {
        int j = constexpr_sqrt(i, ec);
        if (ec) return 0;
        return constexpr_sqrt(j, ec);
    }

    error: call to non-constexpr function 'std::error_code::operator bool() const'
    if (ec) return 0;
          ^
</pre>
<p>
    SG14 would like to develop some good idioms for error handing with <code>std::error_code</code>; but at present,
    these idioms (such as the <code>if (ec)</code> in the above code) cannot be used in constexpr functions. This sends C++
    programmers back to the dark ages of C++11, where we need to learn two different styles of programming: a convenient, fluid
    style (using <code>error_code</code>) for "run-time" functions and a constrained, convoluted style (eschewing <code>error_code</code>)
    for functions we want to mark <code>constexpr</code>.
</p>
<p>
    SG14 suggests that <code>std::error_category</code>'s destructor should originally have been non-virtual. It is too late to
    change <code>std::error_category</code> at this point, though, because removing the <code>virtual</code> specifier would
    noisily break idiomatic C++11 code such as the following:
    <pre>
        struct my_category : public std::error_category {
           // ...
            ~my_category() override;  // OK iff ~error_category is virtual
        }
    </pre>
    So it seems that <code>std::error_category</code> cannot be made constexpr-friendly.
    We have not investigated whether <code>std::error_code</code> itself can be made constexpr-friendly, but we would like to see
    some work in this area.
</p>


<h3><a name="issue-metadata">4.6. No guidance on attaching extra information to <code>error_code</code></a></h3>

<p>
    Niall Douglas has attempted to subclass <code>std::error_code</code> in order to create a
    kind of <code>extended_error_code</code> that contains not only a category pointer and an integer but also some kind of "payload",
    such as a string or variant holding the arguments of the operation that failed. This is very similar in intent to the C++17 standard
    library's <code>std::filesystem::filesystem_error</code>, which holds a <code>std::error_code</code> and two instances of
    <code>std::filesystem::path</code> holding the arguments of the operation that failed. However,
    <code>std::filesystem::filesystem_error</code> is a subclass of <code>std::runtime_error</code>, whereas Niall and Charley are
    trying to make something non-polymorphic to be used in the absence of exception-handling.
</p>
<p>
    Niall's further experiments with "<code>error_code</code> plus payload" have resulted in a design he calls "<code>status_code</code>",
    described in <a href="https://groups.google.com/a/isocpp.org/d/msg/sg14/erKowFV9jWk/t7WPfvaWBAAJ">a thread on the SG14 reflector</a>.
</p>
<p>
    Charley Bay has attempted a similar goal via different means. Charley created a subclass of <code>std::error_category</code>
    which maintained a lookup table of the "payload" for each <code>error_code</code> currently in flight. (This lookup table needs
    some mechanism for "garbage-collection," since <code>error_code</code> objects are trivially destructible and have no built-in
    hook by which they could be reference-counted.)
</p>
<p>
    The standard library does not currently provide an <code>extended_error_code</code> type. For people who need
    (or think they need) something along these lines, it's hard to tell whether they should be inheriting from <code>error_code</code>
    (will that lead to slicing pitfalls? overload resolution gaffes?), or aggregating á là <code>filesystem_error</code>, or simply
    passing the extra data around manually via a second out-parameter or something like <code>pair&lt;error_code, string&gt;</code>.
    Anyway, C++ does not provide a standard wheel, so different programmers may end up inventing slightly
    different wheels here.
</p>


<h3><a name="issue-operator">4.7. Reliance on a surprising overload of <code>operator==</code></a></h3>

<p>
    The library design relies on the ability to perform a "semantic match" operation between a <code>std::error_code</code> (a low-level
    error indicator) and a <code>std::error_condition</code> (a high-level condition). The "semantic match" operation is expressed
    in C++ source code as <code>(ec == econd)</code>. During the SG14 telecon, Charley Bay remarked that this use of
    <code>operator==</code> is surprising because it represents an operation that is fundamentally <i>unlike</i> equality.
</p>

<p>
    For example, "equality" is transitive, but "semantic match" is not necessarily transitive, in that we can have
    <code>ec1 == econd1 &amp;&amp; econd1 == ec2 &amp;&amp; ec1 != ec2</code> or
    <code>econd1 == ec1 &amp;&amp; ec1 == econd2 &amp;&amp; econd1 != econd2</code>.
</p>

<p>
    Also, "semantic match" is not necessarily (notionally) symmetric or reflexive, in that we can construct examples where
    <code>make_error_code(e1) == make_error_condition(e2) &amp;&amp; make_error_code(e2) != make_error_condition(e1)</code> or even
    <code>make_error_code(e1) != make_error_condition(e1)</code>. However, these examples require that the enumeration type
    of <code>e1</code> define both <code>make_error_code</code> and <code>make_error_condition</code>, which contradicts
    Arthur's suggested best practices.
</p>

<p>
    It might have been better for the library's API to use a meaningful identifier such as <code>ec.matches(econd)</code>,
    rather than hijacking the <code>==</code> operator for this distinct semantic-match operation.
</p>


<h3><a name="issue-naming">4.8. <code>error_category</code> should properly have been named <code>error_domain</code></a></h3>

<p>
    SG14 generally concluded that the best way to explain the intention of <code>error_category</code> is to say that each
    singleton derived from <code>error_category</code> represents a particular <i>domain</i> of errors. Each category object
    understands and manages error codes in a particular <i>domain</i> — for example, "POSIX errors" or "filesystem errors"
    or "rdkafka errors." It was generally concluded that the English word "domain" expresses this notion more clearly and
    appropriately than the English word "category."
</p>

<p>
    It is obviously too late to change the name of <code>std::error_category</code> to <code>std::error_domain</code>; that
    ship has sailed. In the SG14 telecon it was remarked that maybe it's fortunate that the name <code>std::error_domain</code>
    is still available. If someone designs a "better <code>error_category</code>" (one without singletons,
    or without <code>std::string</code>, or with constexpr support), then the new thing can be called <code>std::error_domain</code>
    and we can stop teaching the "awkwardly named" <code>std::error_category</code>.
</p>


<h3><a name="issue-anyway">4.9. Standard <code>error_code</code>-yielding functions can throw exceptions anyway</a></h3>

<p>
    In LWG discussion of <a href="https://cplusplus.github.io/LWG/issue3014">LWG issue 3014</a>,
    it was pointed out that <a href="http://eel.is/c++draft/fs.err.report#2.2">[fs.err.report]</a>
    makes several guarantees about the error-reporting behavior of functions in the <code>std::filesystem</code> namespace.
</p>
<blockquote>
    Functions <b>not</b> having an argument of type <code>error_code&amp;</code> handle errors as follows,
    unless otherwise specified:
    <ul>
        <li>
            (2.1) When a call by the implementation to an operating system or other underlying API results in an
            error that prevents the function from meeting its specifications, an exception of type <code>filesystem_error</code>
            shall be thrown. For functions with a single path argument, that argument shall be passed to the
            <code>filesystem_error</code> constructor with a single path argument. For functions with two path arguments,
            the first of these arguments shall be passed to the <code>filesystem_error</code> constructor as the
            <code>path1</code> argument, and the second shall be passed as the <code>path2</code> argument.
            The <code>filesystem_error</code> constructor's <code>error_code</code> argument is set as appropriate
            for the specific operating system dependent error.
        </li>
        <li>
            (2.2) <b>Failure to allocate storage is reported by throwing an exception</b> as described in [res.on.exception.handling].
        </li>
        <li>
            (2.3) Destructors throw nothing.
        </li>
    </ul>
    Functions <b>having</b> an argument of type <code>error_code&amp;</code> handle errors as follows, unless otherwise specified:
    <ul>
        <li>
            (3.1) If a call by the implementation to an operating system or other underlying API results in an error that
            prevents the function from meeting its specifications, the <code>error_code&amp;</code> argument is set as
            appropriate for the specific operating system dependent error. Otherwise, <code>clear()</code> is called on
            the <code>error_code&amp;</code> argument.
        </li>
    </ul>
</blockquote>
<p>
    It is unclear whether functions using the "<code>error_code</code>" API (e.g. <code>std::filesystem::copy_file</code>)
    are meant to report "out of memory" errors according to (3.1), or whether the lack of any explicit instruction for
    "out of memory" errors means that vendors are free to report them by throwing an exception.
    In practice, it appears that vendors <i>do</i> feel free to report "out of memory" conditions via exception-handling.
</p>
<p>
    This arguably sets a precedent that makes the "two-API solution" even harder to teach and use. A programmer might
    reasonably expect that if an API provides an <code>error_code&amp;</code> out-parameter, then <i>errors will be reported
    via that out-parameter</i>. If, as in the case of <code>std::filesystem</code>, the presence of an out-parameter
    cannot be used to detect the error-reporting mechanism — if standard APIs are free to mix several different
    error-reporting mechanisms within the same function — writing reliable code becomes more challenging.
</p>


<h3><a name="issue-idb">4.10. Underspecified <code>error_code</code> comparison semantics</a></h3>

<p>
    Arthur observes that the following program's behavior differs between libc++ and libstdc++:
</p>
<pre>
    #include &lt;system_error&gt;

    int main() {
        std::error_code ec;          // that is, {0, system_category}
        std::error_condition econd;  // that is, {0, generic_category}
        return ec == econd;          // libc++ true, libstdc++ false
    }
</pre>
<p>
    Since <code>error_condition</code> objects are meant only for "catching" (testing) <code>error_code</code> values,
    perhaps it is incorrect for the programmer to even consider default-constructing an <code>error_condition</code>
    variable. However, it might still be worthwhile for the Standard to provide guidance in this area.
</p>
<p>
    <a href="https://accu.org/index.php/journals/2422"><i>Overload</i> journal 141</a> (October 2017) includes an
    article by Ralph McArdell titled "C++11 (and beyond) Exception Handling," which presents a set of proposed
    "best practices" for using <code>&lt;system_error&gt;</code>. These best practices basically align with Arthur
    O'Dwyer's best practices in Section 3 above. A notable difference is that McArdell recommends constructing
    the "no error" <code>error_code</code> value differently:
</p>
<pre>
    class ErrCategory : std::error_category { ... };
    enum ErrCode { success = 0, failure1 = 1, failure2 = 2 };
    template<> struct std::is_error_code_enum<ErrCode> : std::true_type {};
    inline std::error_code make_error_code(ErrCode e) {
        static const ErrCategory c;
        return std::error_code((int)e, c);
    }

    std::error_code McArdellNoError() {
        return ErrCode{};  // that is, {0, ErrCategory}
    }

    std::error_code ODwyerNoError() {
        return std::error_code{};  // that is, {0, system_category}
    }
</pre>
<p>
    A programmer working with one of these libraries, who merely wants to know whether
    <code>std::error_code ec</code> represents an error or not, must have a deep knowledge
    of all the libraries in his program in order to know whether <code>!ec</code> is
    trustworthy (see point 4.3); and furthermore, he cannot necessarily trust
    <code>ec == std::error_condition{}</code> either (point 4.10); and <i>furthermore</i>,
    if one of his libraries might use McArdell's best practices, he cannot
    necessarily trust <code>ec == std::error_code{}</code> either!
</p>
</body>
</html>
