Doc. no.   WG21/N2066=06-0136
Date:        2006-09-07
Project:     Programming Language C++
Reply to:   Beman Dawes <bdawes@acm.org>

TR2 Diagnostics Enhancements

Introduction

N1975 Filesystem Library Proposal for TR2, accepted for Library Technical Report 2 (TR2) at the Berlin meeting, included additional components to supplement the Standard Library's Diagnostics clause. Since then, these error reporting components have received wider public scrutiny and enhancements have been made to the design. The enhanced version has been used by N2054, Networking Library Proposal for TR2, demonstrating that these error reporting components are in fact useful beyond of the original Filesystem Library.

The enhancements fall into two areas:

The original proposal viewed error categories as a binary choice between errno (i.e. POSIX-style) and the native operating system's error codes. The enhancements proposed here allow as many additional error categories as are needed by either implementations or by users. The need to support additional error categories, for example, occurs in some networking library implementations because they are built on top of the POSIX getaddrinfo API that uses error codes not based on errno.

Acknowledgements

Christopher Kohlhoff and Peter Dimov made important contributions to the design. Comments and suggestions were also received from Pavel Vozenilek, Gennaro Prota, Dave Abrahams, Jeff Garland, Iain Hanson, Oliver Kowalke, and Oleg Abrosimov. Christopher Kohlhoff suggested several improvements in the current paper.

Proposed text

Replace the text of the TR2 Diagnostics library with:


Chapter (tbs) - Diagnostics library


This clause describes components that C++ programs may use to detect and report error conditions that have an associated error code.

Header <error_code>

namespace std
{
  namespace tr2
  {
    namespace sys
    {
      class error_category;
      class error_code;
      class system_error;

      typedef int     (*errno_decoder)(const error_code &);
      typedef string  (*message_decoder)(const error_code &);
      typedef wstring (*wmessage_decoder)(const error_code &);
      const error_category  errno_ecat(unspecified-value);
      const error_category  native_ecat(unspecified-value); // possibly same value as errno_ecat

      size_t hash_value(const error_code & ec);

    } // namespace sys
  } // namespace tr2
} // namespace std

Non-member functions

size_t hash_value( const error_code & ec );

Returns:  A hash value representing ec.

Class error_category

The class error_category defines the type of objects used to identify the source and encoding of a particular category of error codes.

namespace std
{
  namespace tr2
  {
    namespace sys
    {
      class error_category
      {
      public:
        typedef uint_least32_t value_type;

        error_category();
        explicit error_category( value_type v );

        const value_type value() const;
        void assign( value_type v );

        bool operator==( const error_category & rhs ) const;
        bool operator!=( const error_category & rhs ) const;
        bool operator< ( const error_category & rhs ) const;
        bool operator<=( const error_category & rhs ) const;
        bool operator> ( const error_category & rhs ) const;
        bool operator>=( const error_category & rhs ) const;
      };
    } // namespace sys
  } // namespace tr2
} // namespace std

Class error_category members

error_category();

Effects: Constructs an object of type error_category.

Postconditions: value() == errno_ecat.

explicit error_category(value_type v);

Effects: Constructs an object of type error_category.

Postconditions: value() == v.

const value_type value() const;

Returns: The value specified by postconditions of the most recent assign, if any, or of the constructor.

void assign(value_type v);

Postconditions: value() == v.

bool operator==(const error_category & rhs) const;

Returns: value() == rhs.value().

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

Returns: value() != rhs.value().

bool operator< (const error_category & rhs) const;

Returns: value() < rhs.value().

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

Returns: value() <= rhs.value().

bool operator> (const error_category & rhs) const;

Returns: value() > rhs.value().

bool operator>=(const error_category & rhs) const;

Returns: value() >= rhs.value().

Class error_code

The class error_code defines the type of objects used to identify errors that have an associated error code value, such as those originating from the operating system.

namespace std
{
  namespace tr2
  {
    namespace sys
    {
      class error_code
      {
      public:
        typedef int_least32_t value_type;

        // constructors and assignment:
        error_code();
        error_code(value_type val, error_category cat);
        void assign(value_type val, error_category cat);

        // observers:
        value_type      value() const;
        error_category  category() const;
        int             to_errno() const;
        string          message() const;
        wstring         wmessage() const;

        operator unspecified-bool-type() const;
        bool operator!() const;

        // relationals:
        bool operator==(const error_code & rhs) const;
        bool operator!=(const error_code & rhs) const;
        bool operator< (const error_code & rhs) const;
        bool operator<=(const error_code & rhs) const;
        bool operator> (const error_code & rhs) const;
        bool operator>=(const error_code & rhs) const;

        // statics:
        static error_category new_category(errno_decoder ed=0, message_decoder md=0, wmessage_decoder wmd=0);
        static bool get_decoders(error_category cat, errno_decoder & ed, message_decoder & md, wmessage_decoder & wmd);
      };
    } // namespace sys
  } // namespace tr2
} // namespace std

Class error_code members

error_code();

Effects: Constructs an object of type error_code.

Postconditions: value() == 0 && category() == errno_ecat.

error_code(value_type val, error_category cat);

Effects: Constructs an object of type error_code.

Postconditions: value() == val && category() == cat.

void assign(value_type val, error_category cat);

Postconditions: value() == val && category() == cat.

value_type value() const;

Returns: value() as specified by postconditions of the most recent assign, if any, or of the constructor.

error_category category() const;

Returns: category() as specified by postconditions of the most recent assign, if any, or of the constructor.

int to_errno() const;

Effects:  errno_decoder ed;
       message_decoder md;
       wmessage_decoder wmd;
       bool ok( get_decoders( category(), ed, md, wmd ) );

Returns:  If ok && ed, ed(*this), otherwise EOTHER.

[Note: The intent is to return the ISO/IEC 9945:2003, Portable Operating System Interface (POSIX) error number that the implementation determines most closely corresponds to value(). --end note.]

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

Effects:  errno_decoder ed;
       message_decoder md;
       wmessage_decoder wmd;
       bool ok( get_decoders( category(), ed, md, wmd ) );

Returns:
    message():
  If ok && md, md(*this), otherwise string().
    wmessage():  If ok && wmd, wmd(*this), otherwise wstring().

Remarks: If category() == errno_ec, the string is as returned by strerror(). Otherwise, the method used by the implementation to determine the string is unspecified.

[Note: The intent is to return a locale sensitive string that describes the error corresponding to value()--end note.]

operator unspecified-bool-type() const;

Returns: if value() != value_type(), returns a value that will evaluate true in a boolean context; otherwise, returns a value that will evaluate false in a boolean context. The value type returned 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 ]

bool operator!() const;

Returns: value() == value_type().

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

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

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

Returns: !(*this == rhs).

bool operator< (const error_code & rhs) const;

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

bool operator<=(const error_code & rhs) const;

Returns: !(*this > rhs).

bool operator> (const error_code & rhs) const;

Returns: category()>rhs.category() || (category()==rhs.category() && value()>rhs.value()).

bool operator>=(const error_code & rhs) const;

Returns: !(*this < rhs).

static error_category new_category(errno_decoder ed=0, message_decoder md=0, wmessage_decoder wmd=0 );

Effects: Constructs a new error_category object. Creates an association between the object's value() and the edmd, and wmd decoder pointers.

Returns: The constructed object.

static bool get_decoders(error_category cat, errno_decoder & ed, message_decoder & md, wmessage_decoder & wmd);

Effects: If cat.value() has an association created by new_category(), sets edmd, and wmd to the association's respective decoder pointers. Otherwise, no effects.

Returns: true if the association existed, otherwise false.

Class system_error

namespace std
{
  namespace tr2
  {
    namespace sys
    {
      enum message_action { append_message, no_message };

      class system_error : public std::runtime_error
      {
      public:
        explicit system_error(error_code ec);
        system_error(error_code ec, const std::string & what_arg, message_action ma=append_message);
        system_error(error_code::value_type ev, error_category ecat);
        system_error(error_code::value_type ev, error_category ecat,
                     const std::string & what_arg, message_action ma=append_message);
        virtual ~system_error() throw();

        const error_code &   code() const throw();
        bool message_action  append_message() const throw();
        const char *         what() const throw();

      private:
        string what_msg; // for exposition only
      };
    } // namespace sys
  } // namespace tr2
} // namespace std

The class system_error defines the type of objects thrown as exceptions to report errors originating from the operating system.

system_error(error_code ec);

Effects: Constructs an object of class system_error, calls std::runtime_error(std::string()).

Postcondition: code() == ec && append_message() == true.

system_error(error_code ec, const std::string & what_arg, message_action ma=append_message );

Effects: Constructs an object of class system_error, calls std::runtime_error(what_arg).

Postcondition: code() == ec && append_message() == ma.

system_error(error_code::value_type ev, error_category ecat);

Effects: Constructs an object of class system_error, calls std::runtime_error(std::string()).

Postcondition: code() == error_code(ev, ecat) && append_message() == true.

system_error(error_code::value_type ev, error_category ecat,
                     const std::string & what_arg, message_action ma=append_message);

Effects: Constructs an object of class system_error, calls std::runtime_error(what_arg).

Postcondition: code() == error_code(ev, ecat) && append_message() == ma.

error_code code() const;

Returns: ec or error_code(ev, ecat), from the constructor, as indicated.

bool message_action  append_message() const throw();

Returns: ma or true, from the constructor, as indicated.

const char * what() const;

Returns: If !code() || !append_message(), return this->runtime_error::what(). Otherwise, as if:

what_msg = this->runtime_error::what();
if ( !what_msg.empty() ) what_msg += ": ";
what_msg += code().message();
return what_msg.c_str();