Benjamin Kosnik
Document number: N2303=07-0163
2007-06-12

Revised system_error

Revision 2

This paper details possible corrections to the error handling and diagnostics machinery detailed in N2241, which was voted into the working draft for C++0x at the Oxford meeting. In particular, it advocates replacing all of section 19.4 in the working draft with the text below.

Header <system_error>

namespace std
{
  struct error_catalog;
  struct system_error;
}

class error_catalog

The class error_catalog is a std::locale aware map between a positive integer numeric value and a null-terminated character sequence providing an elaborated description of the specific error. It is designed for exception-safe use, with the only possible exception path being the non-default constructor for named locales. In particular, all member function operations after construction are exception-free.

It is assumed that all the system-level error conditions needed to build C++0x library facilities will be represented in this class as compile-time data members. This provides a safer nested scope for compile-time querying, and less compressed identifiers than the equivalent C or POSIX macros.

namespace std
{
  struct error_catalog
  {
    typedef int value_type;

    static const value_type address_family_not_supported = 	EAFNOSUPPORT;
    static const value_type address_in_use = 			EADDRINUSE;
    static const value_type address_not_available = 	   	EADDRNOTAVAIL;
    static const value_type already_connected = 	   	EISCONN;
    static const value_type argument_list_too_long = 	   	E2BIG;
    static const value_type argument_out_of_domain = 	   	EDOM;
    static const value_type bad_address = 		   	EFAULT;
    static const value_type bad_file_descriptor = 	   	EBADF;
    static const value_type bad_message = 		   	EBADMSG;
    static const value_type broken_pipe = 		   	EPIPE;
    static const value_type connection_aborted = 	   	ECONNABORTED;
    static const value_type connection_refused = 	   	ECONNREFUSED;
    static const value_type connection_reset = 		   	ECONNRESET;
    static const value_type cross_device_link = 	   	EXDEV;
    static const value_type destination_address_required = 	EDESTADDRREQ;
    static const value_type device_or_resource_busy = 	   	EBUSY;
    static const value_type directory_not_empty = 	   	ENOTEMPTY;
    static const value_type executable_format_error = 	   	ENOEXEC;
    static const value_type file_exists = 	       	   	EEXIST;
    static const value_type file_too_large = 		   	EFBIG;
    static const value_type filename_too_long = 	   	ENAMETOOLONG;
    static const value_type function_not_supported = 	   	ENOSYS;
    static const value_type host_unreachable = 		   	EHOSTUNREACH;
    static const value_type identifier_removed = 	   	EIDRM;
    static const value_type illegal_byte_sequence = 	   	EILSEQ;
    static const value_type inappropriate_io_control_operation =ENOTTY;
    static const value_type interrupted = 		   	EINTR;
    static const value_type invalid_argument = 		   	EINVAL;
    static const value_type invalid_seek = 		   	ESPIPE;
    static const value_type io_error = 			   	EIO;
    static const value_type is_a_directory = 		   	EISDIR;
    static const value_type message_too_long = 		   	EMSGSIZE; 
    static const value_type network_down = 		   	ENETDOWN;
    static const value_type network_reset = 		   	ENETRESET;
    static const value_type network_unreachable = 	   	ENETUNREACH;
    static const value_type no_buffer_space = 		   	ENOBUFS;
    static const value_type no_child_process = 		   	ECHILD;
    static const value_type no_link = 			   	ENOLINK;
    static const value_type no_lock_available = 	   	ENOLCK;
    static const value_type no_message_available = 	   	ENODATA;
    static const value_type no_message = 		   	ENOMSG;
    static const value_type no_space_on_device = 	   	ENOSPC;
    static const value_type no_stream_resources = 	   	ENOSR;
    static const value_type no_such_device_or_address =    	ENXIO;
    static const value_type no_such_device = 		   	ENODEV;
    static const value_type no_such_file_or_directory =    	ENOENT;
    static const value_type no_such_process = 		   	ESRCH;
    static const value_type not_a_directory = 		   	ENOTDIR;
    static const value_type not_a_socket = 		   	ENOTSOCK;
    static const value_type not_a_stream = 		   	ENOSTR;
    static const value_type not_connected = 		   	ENOTCONN;
    static const value_type not_enough_memory = 	   	ENOMEM;
    static const value_type not_supported = 		   	ENOTSUP;
    static const value_type operation_already_in_progress =  	EALREADY; 
    static const value_type operation_canceled = 	   	ECANCELED;
    static const value_type operation_in_progress = 	   	EINPROGRESS;
    static const value_type operation_not_permitted = 	   	EPERM;
    static const value_type operation_not_supported = 	   	EOPNOTSUPP;
    static const value_type owner_dead = 		   	EOWNERDEAD;
    static const value_type permission_denied = 	   	EACCES;
    static const value_type protocol_error = 		   	EPROTO;
    static const value_type protocol_not_available = 	   	ENOPROTOOPT; 
    static const value_type protocol_not_supported = 	   	EPROTONOSUPPORT;
    static const value_type read_only_file_system = 	   	EROFS;
    static const value_type resource_deadlock_would_occur = 	EDEADLK;
    static const value_type result_out_of_range = 	   	ERANGE;
    static const value_type state_not_recoverable = 	   	ENOTRECOVERABLE;
    static const value_type stream_timeout = 		   	ETIME;
    static const value_type text_file_busy = 		   	ETXTBSY;
    static const value_type timed_out = 		   	ETIMEDOUT;
    static const value_type too_many_files_open_in_system = 	ENFILE;
    static const value_type too_many_files_open = 	   	EMFILE;
    static const value_type too_many_links = 		   	EMLINK;
    static const value_type too_many_synbolic_link_levels = 	ELOOP;
    static const value_type try_again = 			EAGAIN; 
    static const value_type value_too_large = 		   	EOVERFLOW;
    static const value_type wrong_protocol_type; = 	   	EPROTOTYPE;

    virtual const value_type
    last_value() const throw();

    virtual bool
    is_valid_value(value_type) const throw();

    virtual const char*
    str(value_type) const throw();

    const locale&
    getloc() const throw();

    error_catalog(const locale& __loc = locale::classic()) throw();

    error_catalog(const char* __name);

    virtual ~error_catalog() throw();

    bool 
    operator==(const error_catalog&) const throw();

    bool 
    operator!=(const error_catalog&) const throw();

  private:
    const locale	_M_loc;
  };
}

Defined values for the const static data members of this class are all non-zero, positive integer values.

Changes from N2241 are: change resource_unavailable_try_again to try_again, no_protocol_option to protocol_not_available, connection_already_in_progress to operation_already_in_progress, message_size to message_too_long. In addition, remove operation_would_block and other.

Definitions for the member functions of this class are as follows. Please note, the requirements for virtual member functions are the same for the base class and any derived classes.

 virtual const value_type last_value() const throw();

Returns: A const value_type representing the largest value n such that is_valid_value(n) is true.

 virtual bool is_valid_value(value_type __n) const throw();

Returns: If the argument __n is equal to any of the static constant data members of error_catalog, then true, else false.

 virtual const char* str(value_type __n) const throw();

Returns: If is_valid_value(__n), returns a null-terminated character sequence corresponding to __n in the std::locale that would be returned by getloc, else an empty string (ie, "").

Note: An example of the expected behavior is as follows.

  std::locale loc;
  std::string s;
  try
    { loc = std::locale("fr_FR"); }
  catch(...)
    { // Construction of named locale didn't work. }

  std::error_catalog e1(loc);
  s = e1.str(std::error_catalog::file_exists);
  bool b1 = s == "Le fichier existe.";

  std::error_catalog e2;
  s = e2.str(std::error_catalog::file_exists);
  bool b2 = s == "File exists";
  
 const locale& getloc() const throw();

Returns: A const std::locale reference to the current locale of the error_catalog instance.

 error_catalog(const char* __name);

Effects: Construct an error_catalog object localized for the std::locale named by the const char* argument.

Postcondition: getloc() == std::locale(__name).

Throws: If __name is not a valid name, construction of std::locale could fail, which may throw std::runtime_error.

Note: Valid names could be "POSIX" or "C", or any other implementation-defined-as-valid string.

 error_catalog(const locale& __loc) throw();

Effects: Construct an error_catalog object localized for the std::locale named by the __loc argument. The default argument is std::locale::classic()

Postcondition: getloc() == __loc.

 bool operator==(const error_catalog& __other) const throw();

Returns: If getloc() == __other.getloc() and typeinfo(*this) == typeinfo(__other), then return true. Else return false.

 bool operator!=(const error_catalog& __other) const throw();

Returns: !(*this == __other)

class system_error

The class system_error is an exception derived from std::runtime_error that is designed to be useful for reporting the detailed character sequences corresponding to a given error_catalog and error_catalog::value_type.

namespace std
{
  class system_error : public std::runtime_error
  {
  public:

    system_error(const string&);

    system_error(error_catalog::value_type, const error_catalog&);
  };
}

Definitions for the member functions of this class are as follows.

 system_error(const string& __s);

Postcondition: strcmp(what(), __s.c_str()) == 0.

 error_catalog(error_catalog::value_type __v, const error_catalog& __cat);

Postcondition: strcmp(what(), __cat.str(__v)) == 0.

Extending by derivation for domain-specific errors

The standard components detailed above are designed so that consumers of system_error will be able to extend namespace std components for non-standard domains.

As a example of extensibility, here's a demonstration of an non-localized set of new error conditions, perhaps for a inkjet printer, implemented as a derived class of error_catalog. As can be seen, it's quite straightforward.

namespace __gnu_test
{
  struct derived_error_catalog : virtual public std::error_catalog
  {
    typedef std::error_catalog base_type;

    static const value_type out_of_ink = 20001;
    static const value_type out_of_paper = 20002;
  
    virtual bool
    is_valid_value(value_type __v) const throw()
    {
      bool ret = false;
      if (base_type::is_valid_value(__v))
	ret = true;
      else
	{
	  if (__v == out_of_ink || __v == out_of_paper)
	    ret = true;
	}
      return ret;
    }
  
    virtual const char*
    str(value_type __v) const throw()
    {
      const char* s = NULL;
      const char* s1 = "Out of Ink";
      const char* s2 = "Out of Paper";
      if (is_valid_value(__v))
	{
	  if (base_type::is_valid_value(__v))
	    s = base_type::str(__v);
	  else
	    {
	      switch (__v)
		{
		case out_of_ink:
		  s = s1;
		  break;
		case out_of_paper:
		  s = s2;
		  break;
		default:
		  s;
		}
	    }
	}
      return s;
    }

    virtual const value_type
    last_value() const throw() { return out_of_paper; }
  };
}

The code below is but one example: it would be possible for operating system vendors to derive os-specific error catalogs. (For instance, a possible Microsoft extension could conceivably be win_error_catalog, with the requisite sixteen thousand constants mapped to locale-specific error strings.)

Another possibility for extensions to error_catalog would be a derived class for user-specified wide-character messages. In this way it would be possible for users to specify unicode (or other encoding) messages for either the standard error conditions or some extended catalog of error conditions, without bogging down the required system behavior.

Acknowledgments

Ulrich Drepper commented on draft versions of this paper, sanity checked C and POSIX usage, suggested better semantics for testing equality, and championed the strongest possible exception specifications.

References

Microsoft System Error Codes

Diagnostics Enhancements for C++0x (Rev. 1)

Becker, Pete, Working Draft, Standard for Programming Language C++, N2284=07-0144

Issues in system_error