Doc. no.   N2422=07-0282
Date:        2007-10-05
Project:     Programming Language C++
Reply to:   Beman Dawes <bdawes@acm.org>
                Christopher Kohlhoff <chris@kohlhoff.com>               

Diagnostics Issues (Rev. 2)

Introduction
Development history
Revision history
Issues
Issue 1: Missing error_code::clear() specification
Issue 2: <system_error> should not force an implementation to expose <cerrno> macros
Issue 3: <functional> hash specialization for error_code
Issue 4: Class error_code constructor should be explicit
Issue 5: Class error_code constructors should be constexpr
Issue 6: Class error_category should be non-copyable
Issue 7: Problems with posix_errno overloads
  Rationale: Unintended consequences
  Rationale: Motivation and use for error categories, codes, and conditions
Issue 8: [Withdrawn]
Issue 9: EOTHER hijacks POSIX macro space, constant name too general
Issue 10: Header dependencies are non-trivial
Issue 11: Overly complex error_code::value_type with circular dependencies
Issue 12: Missing throw specifications
Issue 13: Missing system_error constructor
Issue 14: error_category equality via address comparison
Issue 15: Extending error_code::value_types constants and user-defined error_category objects
Issue 16: POSIX versus native error_category
Issue 17: Broken error_category::message member functions
Issue 18: system_error::what and constructors
Issue 19: Should classes error_code and error_category be less-than comparable?
Issue 20: Which error_category applies to POSIX-like operating system errors unclear
Issue 21: Error enums add lots of names to namespace std
Issue 22: Out of memory should throw bad_alloc, not system_error
Issue 23: error_category::name() should return const char *
Issue 24: system_error what-less constructors needed
Issue 25: system_category order-of-initialization problems
Acknowledgements

Introduction

The resolution of many of the issues are interrelated or modify overlapping sections of text in the standard. The proposed resolutions of these issues are combined in the proposed resolution wording for issue 7.

The proposed resolutions for many of these issues were drafted by Beman Dawes and Chris Kohlhoff. The issue submitters do not necessarily agree with these PR's.

Development history

N2241, Diagnostics Enhancements for C++0x (Rev. 1), provides introduction, design rationale, and history for the diagnostic enhancements as added to the C++ Standard Library at the Oxford C++ committee meeting in the spring of 2007. In adopting the proposal, the committee's Library Working Group made a major change by replacing constants of class error_code with enumerated values. This change broke a number of critical use cases, and so has forced a redesign of many details. The overall picture remains the same, and most end-user existing code is preserved. The detailed scrutiny that followed uncovered various issues resulting in a number of improvements and clarifications.

All of the proposed resolutions contained in this paper have been implemented in the Boost library, and are being used by early adopters.

Revision history

This paper is a minor revision of N2415, Diagnostics Issues (Rev. 1). Issues 25 and 26 were added. Missing wording for a portion of issue 7 was supplied. Minor typos and errors were corrected. Additional rationale was added. Additional Development history was added.

N2415 was a major revision of N2296, Diagnostics Enhancements; Resolution of Small Issues. The name was changed to reflect the wider scope of several proposals.

Issues

Issue 1: Missing error_code::clear() specification

The clear() function is shown in the synopsis but is otherwise unspecified.

Reported separately by Pete Becker, PJ Plauger, and Benjamin Kosnik.

Proposed resolution

Specify the semantics. See proposed wording for issue 7.

Issue 2: <system_error> should not force an implementation to expose <cerrno> macros

In N2241, enum posix_errno did not specify values for each enumeration constant. Instead, a table of equivalence was provided showing the <cerrno> E* macro the constant was to be equivalent to. During editing, that table was folded into the enum for brevity. That was a useful change, but had the unintended side effect of making it look like <system_error> was required to expose the <cerrno> macros, which was not the intent.

Submitted by PJ Plaguer

Proposed resolution

Change the form of enum posix_errno in 19.4 System error support [syserr] as indicated:

enum posix_errno {
  address_family_not_supported = EAFNOSUPPORT, // EAFNOSUPPORT
  address_in_use = EADDRINUSE, // EADDRINUSE

 
... Apply same pattern of changes to all remaining constants for the enum.

At the end of 19.4 System error support [syserr] add:

The value of each enum posix_errno constant shall be the same as the value of the <cerrno> macro shown in the above synopsis.

Whether or not the <system_error> implementation actually exposes the <cerrno> macros is unspecified.

Issue 3: <functional> hash specialization for error_code

The member function hash_value should be replaced or supplemented by an explicit specialization of template class hash (in <functional>) so people can easily make an unordered container keyed on errors.

Submitted by PJ Plauger.

Proposed resolution

In 19.4.2.1 Class error_code overview [syserr.errcode.overview], remove:

size_t hash_value(const error_code& ec);

In 19.4.2.6 Class error_code non-member functions [syserr.errcode.nonmembers], remove:

size_t hash_value(const error_code& ec);

Returns: A hash value representing ec.

In 20.5 Function objects [function.objects], paragraph 2, Header <functional> synopsis, add:

template <> struct hash<std::error_code>;

Change 20.5.15 Class template hash [unord.hash] as indicated:

The unordered associative containers defined in clause 23.4 use specializations of hash as the default hash function. This class template is only required to be instantiable for integer types (3.9.1), floating point types (3.9.1), pointer types (8.3.1), and std::string, std::u16string, std::u32string, and std::wstring, and std::error_code.

Issue 4: Class error_code constructor should be explicit

Submitted by Alisdair Meredith.

Proposed resolution

See proposed wording for issue 7.

Rationale

The conversion constructor is important design element; without it error enum ease-of-use suffers badly. Removing the conversion constructor would force yet another redesign.

The specific concern was that the constructor may cause implicit conversion from an integer to an error_code. The proposed resolution for issue 7 addresses this issue.

Issue 5: Class error_code constructors should be constexpr

Objects of class error_code will commonly be constructed in very low-level systems code where efficiency is a critical consideration. Thus static initialization is quite desirable, and would remove the runtime and code size cost implicit in the change to enumeration style error values.

A possible fix is to apply constexpr.

Submitted by Chris Kohlhoff and Beman Dawes.

Proposed resolution

Take no action at this time.

Rationale

It doesn't appear at this time that constexpr works for the error_code constructors, because error_category isn't a literal type. Once an implementation of constexpr become available, it may be possible to develop a workaround, but until then no action should be taken.

Issue 6: Class error_category should be non-copyable

Class error_category equality is stated in terms of address comparison, taking advantage of the C++ language rule that no two objects can have the same address, and thus avoiding the possibility that independently created user-defined error categories could inadvertently compare equal.

Thus it makes no sense to allow error_category objects to be copyable.

Submitted by Pete Becker.

Proposed resolution

Make error_category non-copyable. See proposed wording for issue 7.

Issue 7: Problems with posix_errno overloads

The original Diagnostics proposal, N2174, provided error_code constants for POSIX errors. The LWG requested that these be changed to an enum, and enum posix_errno was accordingly provided  in the final proposal voted into the working paper. Since the enums are replacing objects of type error_code, rather than replacing simple integer constants, such a change has turned out to have considerable fallout.

The wording in the WP included several overloads on posix_errno introduced to make the enums workable. However, the introduced overloads have several problems:

A solution to these problems must ensure:

Proposed resolution

Change 19.4 System error support [syserr] as indicated. Note that is_error_code_enum and is_error_condition_enum will be eliminated when the library is conceptized.

namespace std {

class system_error;
class error_code;
class error_condition;
class error_category;

template< class T >
struct is_error_code_enum : public false_type {};

template< class T >
struct is_error_condition_enum : public false_type {};

namespace posix_error {
  enum posix_errno {
    address_family_not_supported = EAFNOSUPPORT,
    address_in_use = EADDRINUSE,
    ...
    value_too_large = EOVERFLOW,
    wrong_protocol_type = EPROTOTYPE
    };
}
template<> struct is_error_condition_enum<posix_error::posix_errno>
  : public true_type {};

namespace posix_error {
  error_code make_error_code(posix_errno e);
  error_condition make_error_condition(posix_errno e);
}

bool operator==(const error_code& lhs, const error_code& rhs);
bool operator==(const error_code& ec code, posix_errno en const error_condition& condition);
bool operator==(posix_errno en const error_condition& condition, const error_code& ec code);
bool operator==(const error_condition& lhs, const error_condition& rhs);
bool operator!=(const error_code& lhs, const error_code& rhs);
bool operator!=(const error_code& ec code, posix_errno en const error_condition& condition);
bool operator!=(posix_errno en const error_condition& condition, const error_code& ec code);
bool operator!=(const error_condition& lhs, const error_condition& rhs);
bool operator<(const error_code& lhs, const error_code& rhs);
bool operator<(const error_condition& lhs, const error_condition& rhs);

} // namespace std

Users may specialize is_error_code_enum, and is_error_condition_enum templates to indicate that a type is eligible for class error_code and error_condition automatic conversions respectively.

Change 19.4.1.1 Class error_category overview [syserr.errcat.overview] as indicated:

namespace std {

  class error_category {
  public:
    virtual ~error_category();
    error_category(const error_category&) = delete;
    error_category& operator=(const error_category&) = delete;
    virtual const string& char* name() const = 0;
    virtual posix_errno posix(error_code::value_type ev) const = 0;
    virtual error_condition default_error_condition(int ev) const;
    virtual bool equivalent(int code, const error_condition & condition) const;
    virtual bool equivalent(const error_code & code, int condition) const;
    virtual string message(error_code::value_type ev) const = 0;
    virtual wstring wmessage(error_code::value_type ev) const = 0;

    bool operator==(const error_category& rhs) const;
    bool operator!=(const error_category& rhs) const;
    bool operator<( const error_category & rhs ) const;
  };

  const error_category& get_posix_category();
  const error_category& get_system_category();

  extern static const error_category& posix_category = get_posix_category();
  extern static const error_category& native_category system_category = get_system_category();

} // namespace std

Change 19.4.1.2 Class error_category virtual members [syserr.errcat.virtuals] as indicated:

virtual const string& char* name() const = 0;

Returns: A string naming the error category.

Throws: Nothing.

virtual posix_errno posix(error_code::value_type ev) const = 0;
virtual error_condition default_error_condition(int ev) const;

Returns: A value of type posix_errno that corresponds to ev if such a corresponding POSIX error number exists, otherwise other error_condition(ev, *this) [ Note: Since the possible values of ev are not bounded, the intent is that implementations translate commonly encountered values to the equivalent POSIX error number and translate the rest to other. --end note ]

Throws: Nothing.

virtual bool equivalent(int code, const error_condition & condition) const;

Returns: default_error_condition( code ) == condition.

Throws: Nothing.

virtual bool equivalent(const error_code & code, int condition) const;

Returns: *this == code.category() && code.value() == condition.

Throws: Nothing.

Add to 19.4.1.3 Class error_category non-virtual members [syserr.errcat.nonvirtuals]:

bool operator<(const error_category & rhs) const;

Returns: less<const error_category*>()( this, &rhs ).

[Note: less([comparisons]) provides a total ordering for pointers. --end note]

Throws: Nothing.

Add a new section before 19.4.1.4 [syserr.errcat.objects], titled "Program defined classes derived from error_category":

    virtual const char* name() const = 0;

Returns: A string naming the error category.

Throws: Nothing.

    virtual error_condition default_error_condition(int ev) const;

Returns: An object of type error_condition that corresponds to ev.

Throws: Nothing.

    virtual bool equivalent(int code, const error_condition & condition) const;

Returns: true, if for the category of error represented by *this, code is considered equivalent to condition. Otherwise, false.

Throws: Nothing.

    virtual bool equivalent(const error_code & code, int condition) const;

Returns: true, if for the category of error represented by *this, condition is considered equivalent to code. Otherwise, false..

Throws: Nothing.

Replace 19.4.1.4 Error category objects [syserr.errcat.objects] with:

const error_category& get_posix_category();

Returns: A reference to an object of a type derived from class error_category.

Remarks: The object's default_error_condition and equivalent virtual functions shall behave as specified for class error_category. The object's name virtual function shall return a pointer to the string "POSIX".

const error_category& get_system_category();

Returns: A reference to an object of a type derived from class error_category.

Remarks:  The object's equivalent virtual functions shall behave as specified for class error_category. The object's name virtual function shall return a pointer to the string "system". The object's default_error_condition virtual function shall behave as follows.

If the argument ev corresponds to a POSIX errno value posv, return error_condition(posv, posix_category). Otherwise, return error_condition(ev, system_category). What constitutes correspondence for any given operating system is unspecified. [Note: The number of potential system error codes is large and unbounded, and some may not correspond to any POSIX errno value. Thus implementations are given latitude in determining correspondence. --end note]

Change 19.4.2.1 Class error_code overview [syserr.errcode.overview] as indicated:

namespace std {

  class error_code {
  public:
    typedef int_least32_t value_type;

    // constructors:
    error_code();
    error_code(value_type int val, const error_category& cat);
    error_code(posix_errno val);
    template <class ErrorCodeEnum>
      error_code(ErrorCodeEnum e,
        typename enable_if<is_error_code_enum<ErrorCodeEnum> >::type* = 0);

    // modifiers:
    void assign(value_type int val, const error_category& cat);
    error_code& operator=(posix_errno val);
    template<typename ErrorCodeEnum>
      typename enable_if<is_error_code_enum<ErrorCodeEnum>, error_code>::type &
        operator=( ErrorCodeEnum e );;
    void clear();

    // observers:
    value_type int value() const;
    cont error_category& category() const;
    posix_errno posix() const;
    error_condition default_error_condition() const;
    string message() const;
    wstring wmessage() const;
    operator unspecified-bool-type const;

    // relational operators:
    bool operator==(const error_code& rhs) const;
    bool operator!=(const error_code& rhs) const;

    private:
    value_type int val_; // exposition only
    const error_category& cat_; // exposition only
  };

  template <class charT, class traits>
    basic_ostream<charT,traits>&
      operator<<(basic_ostream<charT,traits>& os, const error_code& ec);

  size_t hash_value(const error_code& ec);

} // namespace std

Change 19.4.2.2 Class error_code constructors [syserr.errcode.constructors] as indicated:

error_code();

Effects: Constructs an object of type error_code.

Postconditions:  val_ == 0 and cat_ == posix_category &system_category.

Throws: Nothing.

error_code(value_type int val, const error_category& cat);

Effects: Constructs an object of type error_code.

Postconditions:  val_ == 0 and cat_ == cat.

Throws: Nothing.

error_code(posix_errno val);
template <class ErrorCodeEnum>
  error_code(ErrorCodeEnum e,
    typename enable_if<is_error_code_enum<ErrorCodeEnum> >::type* = 0);

Effects: Constructs an object of type error_code.

Postconditions: val_ == static_cast<value_type>(val) and cat_ == posix_category *this == make_error_code(e).

Throws: Nothing.

Change 19.4.2.3 Class error_code modifiers [syserr.errcode.modifiers] as indicated:

error_code(value_type int val, const error_category& cat);

Postconditions:  val_ == val and cat_ == cat.

Throws: Nothing.

error_code& operator=(posix_errno val);
template<typename ErrorCodeEnum>
  typename enable_if<is_error_code_enum<ErrorCodeEnum>, error_code>::type &
    operator=( ErrorCodeEnum e );

Postconditions: val_ == static_cast<value_type>(val) and cat_ == posix_category *this == make_error_code(e).

Returns: *this.

Throws: Nothing.

Change 19.4.2.4 Class error_code observers [syserr.errcode.observers] as indicated:

value_type int value() const;

Returns: val_.

Throws: Nothing.

...

posix_errno posix() const;
error_condition default_error_condition() const;

Returns: category().posix(value) category().default_error_condition(value()).

Throws: Nothing.

Change 19.4.2.5 Class error_code relational operators [syserr.errcode.relational] as indicated, and change the sub-section title to "Non-member functions" or similar:

error_code make_error_code(posix_errno e);

Returns: error_code(e, posix_category).

error_condition make_error_condition(posix_errno e);

Returns: error_condition(e, posix_category).

bool operator==(const error_code& lhs, const error_code& rhs);

Returns: lhs.category() == rhs.category() && lhs.value() == rhs.value().

Throws: Nothing.

bool operator==(const error_code& ec code, posix_errno en const error_condition& condition);
bool operator==(posix_errno en const error_condition& condition, const error_code& ec code);

Returns: ec.value() == static_cast<error_code::value_type>(en) && ec.category() == posix_-
category
code.category().equivalent(code.value(), condition)
|| condition.category().equivalent(code, condition.value())
.

Throws: Nothing.

bool operator==(const error_condition& lhs, const error_condition& rhs);

Returns: lhs.category() == rhs.category() && lhs.value() == rhs.value().

Throws: Nothing.

bool operator!=(const error_code& lhs, const error_code& rhs);

Returns: !(lhs == rhs).

Throws: Nothing.

bool operator!=(const error_code& ec code, posix_errno en const error_condition& condition);
bool operator!=(posix_errno en const error_condition& condition, const error_code& ec code);
Returns: !(ec code == en condition).

Throws: Nothing.

bool operator!=(const error_condition& lhs, const error_condition& rhs);

Returns: !(lhs == rhs).

Throws: Nothing.

bool operator<(const error_code& lhs, const error_code& rhs);

Returns: lhs.category() < rhs.category()
  || (lhs.category() == rhs.category() && lhs.value() < rhs.value()).

Throws: Nothing.

bool operator<(const error_condition& lhs, const error_condition& rhs);

Returns: lhs.category() < rhs.category()
  || (lhs.category() == rhs.category() && lhs.value() < rhs.value()).

Throws: Nothing.

Insert before 19.4.3 Class system_error [syserr.syserr], a new section:

19.4.n Class error_condition [syserr.errcondition]

19.4.n.1 Class error_condition overview [syserr.errcondition.overview]

The class error_condition describes an object used to hold values identifying error conditions. [Note: error_condition values are portable abstractions, while error_code values ([syserr.errcode]) are implementation specific. --end note]

namespace std {

  class error_condition {
  public:

    // constructors:
    error_condition();
    error_condition(int val, const error_category& cat);
    template <class ErrorConditionEnum>
      error_condition(ErrorConditionEnum e,
        typename enable_if<is_error_condition_enum<ErrorConditionEnum> >::type* = 0);

    // modifiers:
    void assign(int val, const error_category& cat);
    template<typename ErrorConditionEnum>
      typename enable_if<is_error_condition_enum<ErrorConditionEnum>, error_code>::type &
        operator=( ErrorConditionEnum e );
    void clear();

    // observers:
    int value() const;
    const error_category& category() const;
    string message() const;
    operator unspecified-bool-type () const;

  private:
    int val_; // exposition only
    const error_category& cat_; // exposition only
  };

} // namespace std

19.4.n.2 Class error_condition constructors [syserr.errcondition.constructors]

error_condition(); 

Effects: Constructs an object of type error_condition.

Postconditions: val_ == 0 and cat_ == posix_category.

Throws: Nothing.

error_condition(value_type val, const error_category& cat);

Effects: Constructs an object of type error_condition.

Postconditions: val_ == val and cat_ == cat.

Throws: Nothing.

template <class ErrorConditionEnum>
  error_condition(ErrorConditionEnum e,
    typename enable_if<is_error_condition_enum<ErrorConditionEnum> >::type* = 0);;

Effects: Constructs an object of type error_condition.

Postconditions: *this == make_error_condition(e).

Throws: Nothing.

19.4.n.3 Class error_condition modifiers [syserr.errcondition.modifiers]

void assign(value_type val, const error_category& cat); 

Postconditions: val_ == val and cat_ == cat.

Throws: Nothing.

template<typename ErrorConditionEnum>
  typename enable_if<is_error_condition_enum<ErrorConditionEnum>, error_code>::type &
    operator=( ErrorConditionEnum e );;

Postconditions: *this == make_error_condition(e).

Throws: Nothing.

void clear();

postcondition: value() == 0 && category() == posix_category

19.4.n.4 Class error_condition observers [syserr.errcondition.observers]

value_type value() const;

Returns: val_.

Throws: Nothing

const error_category& category() const;

Returns: cat_.

Throws: Nothing.

string message() const;

Returns: category().message(value()).

Throws: Nothing.

operator unspecified-bool-type () const;

Returns: If value() != 0, returns a value that will evaluate true in a boolean context; otherwise, returns a value that will evaluate false. The return type shall not be convertible to int.

Throws: Nothing.

 [ Note: This conversion can be used in contexts where a bool is expected (e.g., an if condition); however, implicit conversions (e.g., to int) that can occur with bool are not allowed, eliminating some sources of user error. One possible implementation choice for this type is pointer to member. --end note ]

Rationale:Unintended consequences

For example, say the user writes:

error_code ec;
some_operation(ec);
if (ec == boo_boo)
  { ... }

operator == is currently overload on enum posix_errno , so if boo_boo is a posix_errno constant, the result will be as if the code read:

if (ec.posix() == boo_boo)

But this is assuming the users is trying to write portable code. If the user instead is writing system specific code, what is really wanted is the equivalent of:

if (ec == error_code(boo_boo, boo_boo's category))

With the interface as currently specified, the user would have to write that out in full. Unfortunately, the user might well opt for this instead:

if (ec.value() == boo_boo)

Unfortunately, that is really error prone. If the implementation happens to return an error_code with the same value as boo_boo, but in a different category, so the result will be true when it should be false.

Rationale: Motivation and use for error categories, codes, and conditions

// netdb header ----------------------------------------------------------------

namespace netdb_errors
{
  enum code
  {
    host_not_found = HOST_NOT_FOUND,
    try_again = TRY_AGAIN,
    no_recovery = NO_RECOVERY,
    no_data = NO_DATA
  };

  const error_category& get_category();
  static const error_category& category = get_category();

  inline error_code make_error_code(code e)
  {
    return error_code(static_cast<int>(e), category);
  }
}

namespace std
{
  template <>
  struct is_error_code_enum<netdb_errors::code>
    : public true_type {};
}

// netdb source ----------------------------------------------------------------

namespace netdb_errors
{
  class category_impl : public error_category
  {
  public:
    const char* name() const { return "netdb"; }
  };

  const error_category& get_category()
  {
    static category_impl c;
    return c;
  }
}

// addrinfo header -------------------------------------------------------------

namespace addrinfo_errors
{
  enum code
  {
    again = EAI_AGAIN,
    bad_flags = EAI_BADFLAGS,
    fail = EAI_FAIL,
    family = EAI_FAMILY,
    memory = EAI_MEMORY,
    no_name = EAI_NONAME,
    service = EAI_SERVICE,
    socket_type = EAI_SOCKTYPE,
    overflow = EAI_OVERFLOW
  };

  const error_category& get_category();
  static const error_category& category = get_category();

  inline error_code make_error_code(code e)
  {
    return error_code(static_cast<int>(e), category);
  }
}

namespace std
{
  template <>
  struct is_error_code_enum<addrinfo_errors::code>
    : public true_type {};
}

// addrinfo source -------------------------------------------------------------

namespace addrinfo_errors
{
  class category_impl : public error_category
  {
  public:
    const char* name() const { return "addrinfo"; }
  };

  const error_category& get_category()
  {
    static category_impl c;
    return c;
  }
}

// resolve header --------------------------------------------------------------

namespace resolve
{
  enum condition
  {
    host_not_found = 1,
    try_again
  };

  const error_category& get_category();
  static const error_category& category = get_category();

  inline error_condition make_error_condition(condition e)
  {
    return error_condition(static_cast<int>(e), category);
  }

  string get_fqdn(string host);
  string get_fqdn(string host, error_code& ec);
}

namespace std
{
  template <>
  struct is_error_condition_enum<resolve::condition>
    : public true_type {};
}

// resolve source --------------------------------------------------------------

namespace resolve
{
  class category_impl : public error_category
  {
  public:
    const char* name() const { return "resolve"; }

    bool equivalent(const error_code& code, int condition)
    {
      switch (condition)
      {
      case host_not_found:
        return code == netdb_errors::host_not_found
          || code == addrinfo_errors::no_name;
      case try_again:
        return code == netdb_errors::try_again
          || code == addrinfo_errors::again;
      default:
        return false;
      }
    }
  };

  const error_category& get_category()
  {
    static category_impl c;
    return c;
  }

  string get_fqdn(string host)
  {
    error_code ec;
    string s = get_fqdn(host, ec);
    if (ec) throw system_error(ec, "get_fqdn");
    return s;
  }

#if defined(USE_NETDB)
  string get_fqdn(string host, error_code& ec)
  {
    if (hostent* h = gethostbyname(host.c_str()))
    {
      ec.clear();
      return h->h_name;
    }
    else
    {
      ec.assign(h_errno, netdb_errors::category);
      return string();
    }
  }
#else
  string get_fqdn(string host, error_code& ec)
  {
    addrinfo hints = addrinfo();
    hints.ai_flags = AI_CANONNAME;
    addrinfo* result;
    if (int err = getaddrinfo(host.c_str(), "", &hints, &result))
    {
      if (err == EAI_SYSTEM)
      ec.assign(errno, system_category);
      else
      ec.assign(err, addrinfo_errors::category);
      return string();
    }
    else
    {
      ec.clear();
      string fqdn = result->ai_canonname;
      freeaddrinfo(result);
      return fqdn;
    }
  }
#endif
}

// -----------------------------------------------------------------------------

#include <iostream>
#include <ostream>

int main(int argc, char* argv[])
{
  if (argc != 2)
  return 1;

  error_code ec;
  string fqdn = resolve::get_fqdn(argv[1], ec);
  if (ec)
  {
    if (ec == resolve::host_not_found)
      cout << "Host not found" << endl;
    else if (ec == resolve::try_again)
      cout << "Try again later" << endl;
    else
      cout << "Some other error: " << ec.value() << endl;
  }
  else
  {
    cout << fqdn << endl;
  }

  try
  {
    cout << resolve::get_fqdn(argv[1]) << endl;
  }
  catch (exception& e)
  {
    cerr << e.what() << endl;
  }
}

Issue 8: [Withdrawn]

Issue 9: EOTHER hijacks POSIX macro space, constant name too general.

The working draft specifies other = EOTHER in the specification of posix_errno in 19.4 System error support, with the specification that EOTHER is not a POSIX-defined macro in 19.3 Error numbers. Earlier papers stated that this new macro will be added to the include file cerrno.

The problem with this is that C++ is hijacking the POSIX macro space, which should be avoided as future versions of the POSIX standard may add more error macros, including EOTHER, but may have it mean something else.

Submitted by Benjamin Kosnik.

Proposed resolution

Change 19.3 Error numbers [errno], paragraph one, as indicated:

The header <cerrno> is described in (Table 29). Its contents are the same as the POSIX header <errno.h>, except that errno shall be defined as a macro, and an additional macro EOTHER shall be defined to represent errors not specified by the POSIX standard. [ Note: The intent is to remain in close alignment with the POSIX standard. --end note ]

From 19.3 Error numbers [errno], Header <cerrno> synopsis, strike EOTHER.

Change 19.4 System error support [syserr], Header <system_error> synopsis as indicated:

other = EOTHER,

For 19.4.1.2 Class error_category virtual members [syserr.errcat.virtuals] paragraph 3, see issue 7 proposed wording.

Issue 10: Header dependencies are non-trivial

In section 19.4.2.6 Class error_code non-member functions, an ostream inserter for error_code is defined for the primary ostream template. This will necessitate the inclusion of the ostream include file, which has large dependencies on other standard library features. The practical result is that the system_error include, far from being a lightweight, stand-alone error-reporting facility, will instead come with significant compile time cost.

Fix: Move the error_code inserter to the ostream include file, or relax the requirements such that only char and wchar_t specializations are required.

Submitted by Benjamin Kosnik.

Proposed resolution

Take no action at this time. The issue will be left open to see if a solution can be found.

Rationale

Discussed in Kona. LWG members sympathised with the problem, but did not wish to see a requirement that the ostream header include the system_error header either.

Issue 11: Overly complex error_code::value_type with circular dependencies

Section 19.4.2.1 specifies error_code::value_type as typedef int_least32_t value_type;. However, this type is used in error_category, which has to be defined before any of the trivial members in error_code can be made inline.

In addition, this complexity results in unsightly casting in the error_code relational operators and constructor specifications.

Possible Fix: Assume error_code::value_type is of type int, as specified by both C and POSIX.

Submitted by Benjamin Kosnik.

Proposed resolution

Change the type to int. See proposed wording for issue 7.

Rationale

The typedef is a historical artifact left over from early development of the class. An int is sufficient for all operating systems we are aware of.

Issue 12: Missing throw specifications

In 19.4.1.2 Class error_category virtual members, all member functions are defined to not throw, but do not have throw() specifications.

The same hold for error_code member functions that are modifiers and observers. i.e, sections 19.4.2.3 and 19.4.2.4.

Proposed Fix: Add in missing throw() specifications.

Submitted by Benjamin Kosnik.

Proposed resolution

No action necessary.

Rationale

The Working Paper is inconsistent in its use of  throw() specifications. Many LWG members prefer the "Throws: Nothing" specification as used in the current working paper wording for diagnostics. LWG members noted that type traits may be used to detect empty throw specifications, and so this may be revisited in a future cleanup of this inconsistency across the whole library.

Issue 13: Missing system_error constructor

All existing classes derived from std::exception provide either a default constructor or a constructor taking a single const std::string& parameter. The current specification for system_error breaks with this.

Submitted by Benjamin Kosnik.

Proposed Fix: Add a constructor to system_error that takes a const std::string& argument.

Proposed resolution

No action necessary.

Rationale

The design of the constructors, including order of arguments, was a considered decision after much discussion with Peter Dimov and others on the Boost list. A primary objective of system_error is to report an error_code. We don't want to encourage construction of system_error objects that don't have one. Thus the constructors all take error_code or equivalent arguments first in addition to the what string argument. Not supplying a default constructor avoids the nonsensical construction of system_error representing "no error".

Issue 14: error_category equality via address comparison

Section 19.4.1.3 Class error_category non-virtual members specifies address comparisons between error_category base objects to determine equality and inequality. This can be fragile or difficult in shared memory or distributed systems, and may lead to incorrect results for equivalent error_category objects.

Submitted by Benjamin Kosnik.

Fix: Equality compare on internal details of the message catalog characteristics.

Proposed resolution

No action necessary.

Rationale

Resolved by Issue 6: Class error_category should be non-copyable. No further action needed.

Issue 15: Extending error_code::value_types constants and user-defined error_category objects

Section 19.4 specifies posix_errno as an enum. As such, there is no way to extend it for native error values, which will presumably be in the form of another enum, in some user-defined space.

Section 19.4.1.4 Error category objects vaguely specifies posix_category and native_category as external objects. As such, the implementation details are likely to remain private, making simple derivation and extension tricky or difficult.

Submitted by Benjamin Kosnik.

Proposed resolution

After 17.4.4.8 Restrictions on exception handling [res.on.exception.handling], add a new sub-section (presumably 17.4.4.9):

17.4.4.9 Value of error codes

Certain functions in the C++ Standard Library report errors via a std::error_code([syserr.errcode.overview]) object. That object's category() member shall return a reference to std::system_category for errors originating from the operating system, or a reference to an implementation-defined error_category object for errors originating elsewhere. The implementation shall define the possible values of value() for each of these error categories. [Example: For a POSIX-based operating system, an implementation is encouraged to define the std::system_category values as identical to the POSIX errno values, plus additional values as defined by the operating system's documentation. An implementation on a non-POSIX operating system is encouraged to define values identical to the operating system's error values. For errors that don't originate from the operating system, the implementation may provide enums for the associated values. -- end example]

Rationale

posix_errno should not be extended by users or implementers. Only the POSIX committee can extend the set of POSIX errnos. Thus the concern about 19.4 is not a defect.

Regarding 19.4.1.4; yes, the implementation details of error_categories are private, and that is deliberate encapsulation.

The concern over derivation and extension can be addressed in several ways. For implementers, the proposed addition to clause 17 provides general guidance. An example of actual implementation could be outside the scope of the standard. For illustration, extracts from the current Boost implementation for several platforms are given below:

    //  Operating system specific interfaces  --------------------------------//

    //  The interface is divided into general and system-specific portions to
    //  meet these requirements:
    //
    //  * Code calling an operating system API can create an error_code with
    //    a single category (system_category), even for POSIX-like operating
    //    systems that return some POSIX errno values and some native errno
    //    values. This code should not have to pay the cost of distinguishing
    //    between categories, since it is not yet known if that is needed.
    //
    //  * Users wishing to write system-specific code should be given enums for
    //    at least the common error cases.
    //
    //  * System specific code should fail at compile time if moved to another
    //    operating system.

#ifdef BOOST_POSIX_API

    //  POSIX-based systems  -------------------------------------------------//

    //  To construct an error_code after a API error:
    //
    //      error_code( errno, system_category )

    //  User code should use the portable "posix" enums for POSIX errors; this
    //  allows such code to be portable to non-POSIX systems. For the non-POSIX
    //  errno values that POSIX-based systems typically provide in addition to 
    //  POSIX values, use the system specific enums below.

# ifdef __CYGWIN__

    namespace cygwin
    {
      enum cygwin_errno
      {
        no_net = ENONET,
        no_package = ENOPKG,
        no_share = ENOSHARE,
      };
    }  // namespace cygwin

    template<> struct is_error_code_enum<cygwin::cygwin_errno>
      { static const bool value = true; };

    namespace cygwin
    {
      inline error_code make_error_code(cygwin_errno e)
        { return error_code( e, system_category ); }
    }

# elif defined(linux) || defined(__linux) || defined(__linux__)

    namespace Linux  // linux lowercase name preempted by use as predefined macro
    {
      enum linux_error
      {
        advertise_error = EADV,
        bad_exchange = EBADE,
        bad_file_number = EBADFD,
        bad_font_format = EBFONT,
        bad_request_code = EBADRQC,
        ...
        unclean = EUCLEAN,
      };
    }  // namespace Linux

    template<> struct is_error_code_enum<Linux::linux_errno>
      { static const bool value = true; };

    namespace Linux
    {
      inline error_code make_error_code(linux_error e)
        { return error_code( e, system_category ); }
    }

#elif defined(BOOST_WINDOWS_API)

    //  Microsoft Windows  ---------------------------------------------------//

    //  To construct an error_code after a API error:
    //
    //      error_code( ::GetLastError(), system_category )

    namespace windows
    {
      enum windows_error
      {
        success = 0,
        // These names and values are based on Windows winerror.h
        invalid_function = ERROR_INVALID_FUNCTION,
        file_not_found = ERROR_FILE_NOT_FOUND,
        path_not_found = ERROR_PATH_NOT_FOUND,
        too_many_open_files = ERROR_TOO_MANY_OPEN_FILES,
        access_denied = ERROR_ACCESS_DENIED,
        invalid_handle = ERROR_INVALID_HANDLE,
        arena_trashed = ERROR_ARENA_TRASHED,
        not_enough_memory = ERROR_NOT_ENOUGH_MEMORY,
        ...
        already_exists = ERROR_ALREADY_EXISTS,
      };
    }  // namespace windows

    template<> struct is_error_code_enum<windows::windows_error>
      { static const bool value = true; };

    namespace windows
    {
      inline error_code make_error_code(windows_error e)
        { return error_code( e, system_category ); }
    }

#else
#  error BOOST_POSIX_API or BOOST_WINDOWS_API must be defined
#endif

Issue 16: POSIX versus native error_category

Should posix_category and native_category be able to have the same address?

In theory, yes. In practice, real POSIX-based operating systems such as Linux add additional error codes, so the error categories have to be different. That allows an implementation to use error_code(errno, native_category) to construct an error_code. If the POSIX values of errno were a different category from the non-POSIX values, an expensive lookup would have to be done to assign the category.

Should the member function error_category::posix exist for the predefined object posix_category? Isn't this a no-op, and best added to the class implementing native_category?

The error_category virtuals exist to support the equivalent error_code and error_condition member functions, allowing user-defined error categories. This mechanism unravels if error_category::posix (or its issue 7 replacements) isn't present for all error categories.

Should the division be between underlying system errors needed by the C++0x standard library and user-defined error messages, instead of between POSIX and native?

Early versions of the current design did divide the world along standard library / native lines. The design evolved into a POSIX / operating-system / user-defined breakdown because it met specific needs of users and third-party library suppliers, who must deal with a variety of real-world use cases, and implementers who sometimes must rely on API's other than those of the operating system.. That isn't to say another design wouldn't work, but this design is known to work and represents successful existing practice. Note that the dichotomy between POSIX and native is clarified greatly by the introduction of class error_condition in issue 7.

Submitted by Benjamin Kosnik.

Proposed resolution

Change the name native_category to system_category. See proposed wording for issue 7.

Rationale

The name native_category seems to be causing some confusion. It is changed to system_category to more clearly indicate this category represents errors from the operating system itself rather than some other library used by the implementation.

The answers to the questions are given in italics above.

Issue 17: Broken error_category::message member functions

Section 19.4.1.2 Class error_category virtual members defines members message and wmessage, and notes an intention to provide a locale-specific string. However, there is no indication that this is integrated into the existing standard library infrastructure for locale, ie std::locale.

There are two distinct issues: one, construction of error_category would have to take some kind of std::locale parameter, some kind of getter/setter would have to exist to provide locale data, or message would have to take a std::locale argument. This last option would require serious re-work of the system_error::what member function.

The second issue is code conversion so that wmessage could return a wide string from a message catalog. The use case for this is theoretical at the moment, as wide-character message catalogs are not known. For this, std::codecvt would be the preferred mechanism, and that depends on three template parameters. If this route is followed, it may make sense to templatize the entire class, and separate out message for char instantiations and wmessage for wchar_t instantiations.

Note that error_code has these same member functions, which forward to the error_category member functions, so this could be considered a defect in both class specifications.

Submitted by Benjamin Kosnik.

Proposed resolution

Change 19.4.1.1 Class error_category overview [syserr.errcat.overview] paragraph 1 as indicated:

    virtual string message(error_code::value_type ev) const = 0;
    virtual wstring wmessage(error_code::value_type ev) const = 0;

Change 19.4.1.2 Class error_category virtual members [syserr.errcat.virtuals] paragraph 4 and 5 as indicated:

    virtual string message(error_code::value_type ev) const = 0;
    virtual wstring wmessage(error_code::value_type ev) const = 0;

Returns: A string that describes the error condition denoted by ev. [ Note: The intent is to return a locale-specific string that describes the error corresponding to ev. --end note ]

Throws: Nothing.

Change 19.4.2.1 Class error_code overview [syserr.errcode.overview] paragraph 1 as indicated:

    string message() const;
    wstring wmessage() const;

Change 19.4.2.4 Class error_code observers [syserr.errcode.observers] beginning at paragraph 6 as indicated:

string message() const;

Returns: category().message(value()).

Throws: Nothing.

wstring wmessage() const;

Returns: category().wmessage(value()).

Throws: Nothing.

Rationale

Discussed by the LWG in Toronto. None of the current standard exception classes worry about wide messages or locales, so a fix for one particular exception probably isn't the right approach. Better to remove the functions until the problem and solution can be more fully explored.

After the Toronto meeting, an email discussion between Beman Dawes, Peter Dimov, Christopher Kohlhoff, and Benjamin Kosnik discussed possible solutions without reaching consensus. The most likely solution was something along the lines of:

string error_message(error_code ec, const locale& loc )
{
  messages<char>& f = use_facet< messages<char> >( loc );
  int cat = f.open( ec.category().name(), loc );
  string r = f.get( cat, 0, ec.value(), "" );
  f.close( cat );
  return r;
}

While this approach looked promising, it wasn't clear how it would work with user-defined error categories, and whether or not it was actually practical for the full range of operating systems.

At the Kona meeting, the LWG resolved to remove the wide character message functions and all reference to locales.

Issue 18: system_error::what and constructors

In 19.4.3.2 Class system_error members, the constructor definitions and what specification collude to force exception-unsafe behavior, and multiple std::string data members.

This in effect mandates elaborate string modification as part of system_error::what, which may cause unanticipated exceptions to occur, in addition to being inefficient.

These specifications are a break with the existing stdexcept requirements and the exception hierarchy design philosophy: by making what virtual, users can reasonably expect to have derived classes with different return strings from what. Indeed, that's the point.

In addition, with all these additional requirements, there still exists no way to pass in locale-specific strings.

See issue 17.

Submitted by Benjamin Kosnik.

Proposed resolution

Change 19.4.3.2 Class system_error members [syserr.syserr.members], paragraph 4, as indicated:

const error_code& code() const throw();

Change 19.4.3.2 Class system_error members [syserr.syserr.members], paragraph 5, as indicated:

const char *what() const throw();

No additional action necessary.

Rationale

Exception safety is dealt with by the proposed resolution. The data members are not specified; implementations are free to use a std::string or some completely different mechanism. Implementations will presumably use lazy evaluation, so much of the cost will only occur if what() is actually called. No action necessary.

system_error::what  and code do specify throw() in the synopsis to deal with the exception issue, but not in the member descriptions. See proposed resolution.

The semantics of what() are extremely valuable to users, and any inefficiency is minor compared to the general overhead of an exception being thrown. Existing code, such as main() exception monitors that catch std::exception and report the results of exception::what(), works without modification for std::system_error. and that's very desirable.  No additional action necessary.

what() is virtual in the base class, so is virtual in class system_error too. And system_error::what does normally return a different string than runtime_error::what. No action necessary.

Issue 19: Should classes error_code and error_category be less-than comparable?

The Boost version provides operator<  for both, allowing objects of class error_code to be easily used as keys for associative containers. I can't remember any rationale for removing them from the proposal; it looks like a simple oversight.

Submitted by Beman Dawes.

Proposed resolution

Add operator<. See proposed wording for issue 7.

Issue 20: Which error_category applies to POSIX-like operating system errors unclear

For POSIX based operating systems (Unix, Linux, Mac OS, etc.) it isn't clear to either implementors or users whether operating system errors will be classified as native_category or posix_category.

Submitted by Beman Dawes.

Proposed resolution

No action necessary. This is essentially a FAQ (see below) that has to be dealt with by education. The issue 15 rationale may also be helpful.

FAQ

What error_category are the errors arising from POSIX-based operating systems such as Unix, Linux, Mac OS? They are system_category errors.

Issue 21: Error enums add lots of names to namespace std

Would it be better to move them into sub-namespaces?

Submitted by Chris Kohlhoff

Proposed resolution

Resolved by moving POSIX enums into a sub-namespace. See proposed wording for issue 7.

Rationale

These names should reflect the names assigned by the originating organization, such as the POSIX committee, so are somewhat beyond the control of the C++ committee. Also, name clashes with platform specific error enums occur in the absence of sub-namespaces. Experiments with real code show introduction of a posix sub-namespace improves readability.

The LWG indicated that the namespace name "posix" should be reserved for future use, and the name "posix_error" was proposed as an alternative without any objections.

Issue 22: Out of memory should throw bad_alloc, not system_error

If an out of memory condition occurs in a call to an operating system API from a standard library implementation, should the resulting exception be bad_alloc or system_error?

Proposed resolution

To 19.4.3.1 Class system_error overview [syserr.syserr.overview] add:

[Note: if an error represents an out-of-memory condition, implementations are encouraged to throw an exception of type bad_alloc ([bad.alloc]) rather than system_error. --end note]

Rationale

The LWG discussed this in Toronto, and wishes bad_alloc be thrown.

The proposed wording is in the form of a non-normative note because the type of exception throw on out of memory is implementation defined. See 17.4.4.8 Restrictions on exception handling [res.on.exception.handling]

Issue 23: error_category::name() should return const char *

The specification of error_category::name() as returning a constant string reference makes it too easy to implement in a non-thread-safe way such as a static function-scope variable. To be safe I think the function should return either a std::string by value or const char*. My preference would be for the latter, since it better reflects that what you are defining is a string constant that corresponds to the category.

Submitted by Chris Kohlhoff.

Proposed resolution

Change the return to const char*. See proposed wording for issue 7.

Issue 24: system_error what-less constructors needed

The problem of the what arg becomes more significant once you start composing operations. Since we're providing both throwing and non-throwing overloads, to reduce code duplication I like to implement the operation once in non-throwing form and then wrap it with the throwing version. For example:

  error_code download_to_directory(
      std::string dirname, std::string url, error_code& ec)
  {
    if (mkdir(dirname, ec))
      return ec;
  
    std::string host = url2host(url);
    tcp::endpoint ep = resolve(host, ec);
    if (ec) return ec;
  
    return download(dirname, ep, url2path(url), ec);
  }
  
  void download_to_directory(
      std::string dirname, std::string url)
  {
    error_code ec;
    download_to_directory(dirname, url, ec);
    if (ec) throw system_error(ec, ?);
  }  
The functions resolve() and download() are themselves composed operations, which may in turn use other composed operations. The original "what" of any error code is well and truly lost by the time you reach the throw.

Submitted by Chris Kohlhoff.

Beman Dawes comments: It is often very useful to users to know the name of the function where a system_error originates. Thus I would write the line in question like this:

if (ec) throw system_error(ec, "download_to_directory");

But that is a QOI issue, so I support adding constructors that do not require a what string.

Proposed resolution

Change 19.4.3.1 Class system_error overview [syserr.syserr.overview] as indicated:

class system_error : public runtime_error {
public:
  system_error(error_code ec, const string& what_arg);
  system_error(error_code ec);
  system_error(error_code::value_type ev, const error_category& ecat,
      const string& what_arg);
  system_error(error_code::value_type ev, const error_category& ecat);
  const error_code& code() const throw();
  const char* what() const throw();
};

To 19.4.3.2 Class system_error members [syserr.syserr.members] add:

system_error(error_code ec);

Effects: Constructs an object of class system_error.

Postconditions: code() == ec and strcmp(runtime_error::what(), "") == 0.

system_error(int ev, const error_category& ecat);

Effects: Constructs an object of class system_error.

Postconditions: code() == error_code(ev, ecat) and strcmp(runtime_error::what(), "") == 0.

Issue 25: system_category order-of-initialization problems

Supplying system_category and posix_category predefined objects as extern const refs causes order-of-initialization problems, depending on the exact order in which dynamic initialization occurs.

There are two aspects of this problem:

Reported by David Deakins and Christopher Kohlhoff.

Proposed resolution

Add explicit dynamic initialization. See proposed wording for issue 7.

Acknowledgements

Chris Kohlhoff, Benjamin Kosnik, and Peter Dimov provided much assistance in identifying and resolving issues.