Doc. no.   N2296=07-0156
Date:        2007-06-24
Project:     Programming Language C++
Reply to:   Beman Dawes <bdawes@acm.org>
                Benjamin Kosnik <bkoz@redhat.com>

Diagnostics Enhancements; Resolution of Small Issues

Note well: The Proposed resolutions for many of these issues were drafted by Beman Dawes. The issue submitters do not necessarily agree with these PR's.

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

To 19.4.2.3 Class error_code modifiers [syserr.errcode.modifiers], add:

void clear();

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

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> must 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] from:

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

To:

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

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 equivalent <cerrno> macro shown as a comment in the above synopsis.

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], removed:

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

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

explicit error_code(posix_errno val);

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

explicit error_code(posix_errno val);

Issue 5: Class error_code constructor(s?) 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 Library Working Group's switch to enumeration style error values.

A possible fix is to apply constexpr. Note, however, that since constexpr is not yet a part of the working paper, and there is no compiler available for testing, it isn't possible to tell if this change will actually work as intended.

Submitted by Chris Kohlhoff and Beman Dawes.

Proposed resolution

If N2235, Generalized Constant Expressions—Revision 5, or successor has been accepted for inclusion in the Working Paper, in 19.4.2.1 Class error_code overview [syserr.errcode.overview]  and 19.4.2.2 Class error_code constructors [syserr.errcode.constructors], add constexpr to the constructors.

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

To 19.4.1.1 Class error_category overview [syserr.errcat.overview] synopsis add:

error_category(const error_category&) = delete;
error_category& operator=(const error_category&) = delete;

This also resolves issue 14.

Issue 7: error_code/posix_errno operator== semantics wrong

The semantics of the error_code/posix_errno operator== are error prone, inherently non-portable, and do not support the most common intended use case for writing portable code.

For example, say the user writes:

error_code ec = some_operation(...);
if ( ec )
{
  if ( ec == io_error )
  {
    // do something...
  }
  else
  {
    // do something else...
  }
}

Submitted by Beman Dawes, in response to a question raised by Chris Kohlhoff

Proposed resolution:

Editorial note: The error_code/posix_errno non-member functions are currently described in the 19.4.2.5 Class error_code relational operators [syserr.errcode.relational] section. They should be described in the section that follows that, 19.4.2.6 Class error_code non-member functions [syserr.errcode.nonmembers].

Change 19.4.2.5 Class error_code relational operators [syserr.errcode.relational] paragraph 5 as indicated:

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

Returns: ec.value() == static_cast<error_code::value_type>(en) && ec.category() == posix_category ec.posix() == en.

Throws: Nothing.

Issue 8: posix_errno enum minor issues

Submitted by Beman Dawes

Proposed resolution

In 19.4 System error support [syserr] paragraph 2, 19.4.1.2, Class error_category virtual members [syserr.errcat.virtuals] paragraph 3 (2 places), and the resolution to issue 9 (2 places), change other to no_posix_equivalent.

To 19.4 System error support [syserr], paragraph 2, change as indicated:

enum posix_errno {
  no_error = 0,
  address_family_not_supported = EAFNOSUPPORT,
  ...

Issue 9: EOTHER hijacks POSIX macro space.

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 macros 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:

Note: The resolution of Issue 8 changes other to no_posix_equivalent.

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 unspecified-value,

Following 19.4 System error support [syserr], Header <system_error> synopsis, add:

The value of posix_errno::other shall be non-zero and not equal to any other posix_errno value.

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.

Submitted by Benjamin Kosnik.

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.

Proposed resolution:

To be supplied.

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.

Beman Dawes comments: The typedef is a historical artifact. An int is sufficient for all operating systems I am aware of. I support the change.

Proposed resolution:

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);

    // modifiers:
    void assign(value_type int val, const error_category& cat);
    error_code& operator=(posix_errno val);
    void clear();

    // observers:
    value_type int value() const;
    cont error_category& category() const;
    posix_errno posix() 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(value_type int val, const error_category& cat);

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

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

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

value_type int value() const;

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.

Submitted by Benjamin Kosnik.

Proposed Fix: Add in missing throw() specifications.

Proposed resolution:

No action necessary.

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.

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.

The design of the constructors, including order of arguments, was a considered decision after 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:

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.

Yes, but that seems unavoidable fallout from the use of an enum as mandated by the LWG. Also, it is possible, if tedious, for another enum to refer to the posix_errno values one-by-one.

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.

The specification of posix_category and native_category is:

extern const error_category& posix_category;
extern const error_category& native_category;

That doesn't seem vague. The critical member functions of error_category are public virtuals; that seems sufficient access to functionality without violating encapsulation.

Submitted by Benjamin Kosnik.

Proposed resolution:

No action necessary. See above comments for rationale.

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, no, because no real operating system exactly implements the POSIX specification. Real operating systems at the minimum add some of their own error codes.

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 member functions. This mechanism unravels if error_category::posix 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 divided the world along standard library / native lines. The design evolved into a POSIX / native / 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. That isn't to say another design wouldn't work, but this design is known to work and represents successful existing practice.

Submitted by Benjamin Kosnik.

Proposed resolution:

No action necessary. See above comments for rationale.

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 wstring wmessage(error_code::value_type ev) const = 0;

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

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

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

wstring wmessage() const;

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

wstring wmessage() const;

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

Throws: Nothing.

For the locale issue, no action necessary. This underspecification of "locale-specific" was deliberate; there doesn't appear to be a solution ready for standardization, and in the absence of a standard solution, library suppliers know best how to meet their user's needs.

Rationale for removing wmessage: It is difficult or impossible to implement well on some operating systems. None of the current standard exception classes worry about wide messages, so a fix in one particular place only probably isn't the right approach.

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.

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.

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.

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 string modification is extremely valuable to users, and any inefficiency is minor compared to the general overhead of an exception being thrown. No additional action necessary.

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.

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.

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

See issue 17.

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. See above comments for rationale.

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; perhaps it was a simple oversight. It seems to me they should be provided as a courtesy if nothing else. On the other hand, users can provide their own compare functions if needed.

Submitted by Beman Dawes.

Proposed resolution:

To be supplied.