Document number:       N2769 = 08-0279
Date:                            2008-09-17
Project:                        Programming Language C++, Library Working Group
Reply-to:                      Beman Dawes <bdawes at acm.org>                                     

Detailed Reporting for Input/Output Library Errors (Revision 2)

Introduction
Revision History
Motivation
Approach
C++0x or TR2?
Implementation experience
Acknowledgements
Proposed wording

Introduction

This paper proposes adding detailed error reporting, based on C++ standard library System error support (19.4), to the C++ standard library's Input/output library (27).

Revision History

This proposal is a major revision of N2655. At the request of the LWG, the proposed change is now limited to changing the base class of ios_base::failure to system_error, and supplying the boilerplate to support that change.

The specification of  std::system_error as a base class of std::basic_ios::failure was originally proposed in N2503, Indicating iostream failures with system_error.

Motivation

Lack of detailed error reporting has long been a concern about the C++ I/O Streams library. For example, The C++ Standard Library by Nicolai Josuttis, complained in 1999:

Unfortunately, the standard does not require that the exception object includes any information about the erroneous stream or the kind of error.

The problem is particularly acute for failures to open a file. Such failures are extremely common and there are numerous possible reasons for failure, such as the file not being found, the permissions being wrong, and so forth. Diagnosing these failures without knowing the exact reason for failure is difficult, irritating, and time wasting.

Impact on existing code

The only known case where the proposal might break existing code is code that depends on ios_base::failure being derived directly from exception. Library clause 17.4.4.7 has always granted implementers permission to derive classes from classes other than the specified base, so such code is already suspect.

The proposal does break ABI compatibility in that the exception class derivation changes.

C++0x or TR2?

Because of the ABI breakage, the LWG prefers to see this change in C++0x.

Implementation experience

A C++03 proof-of-concept prototype of the proposal has been implemented by modifying the Apache C++ Standard Library. No difficulties were encountered and coding was straightforward.  Virtually all changes were on paths through the code that are only executed when a failure has already been detected.

Acknowledgements

Alisdair Meredith initiated the original N2503 proposal and helped with the preparation of the current proposal.

Proposed wording

To 27.4 Iostreams base classes [iostreams.base], Header <ios> synopsis, add:

enum class ioerrc { 
  stream=1
};

concept_map ErrorCodeEnum<ioerrc> {};

error_code make_error_code(ioerrc e);
error_condition make_error_condition(ioerrc e);

storage-class-specifier const error_category& iostream_category;

These additions are the boilerplate required to create a new error category. See 19.4 of the WP.

In a new sub-section, Error category object, add:

storage-class-specifier const error_category& iostream_category;

The implementation shall initialize iostream_category. Its storage-class-specifier is permitted to be static or extern. It is unspecified if initialization is static or dynamic (3.6.2 [basic.start.init]). If initialization is dynamic, it shall occur before completion of the dynamic initialization of the first translation unit dynamically initialized that includes header <system_error>.

The exact form and wording for describing error_category initialization is the topic of issue 890. The above paragraph needs to be kept in sync with the final disposition of 890.

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

At a location to be determined, add:

error_code make_error_code(ioerrc e);

Returns: error_code(static_cast<int>(e), io_category).

error_condition make_error_condition(ioerrc e);

Returns: error_condition(static_cast<int>(e), io_category).

Change 27.4.2.1.1 Class ios_base::failure [ios::failure] as indicated:

namespace std {
  class ios_base::failure : public exceptionsystem_error {
  public:
    explicit failure(const string& msg, const error_code& ec = ioerrc::stream);
    explicit failure(const char* msg, const error_code& ec = ioerrc::stream);
    virtual const char* what() const throw();
  };
}

The class failure defines the base class for the types of all objects thrown as exceptions, by functions in the iostreams library, to report errors detected during stream buffer operations.

When throwing ios_base::failure exceptions, implementations are encouraged to provide values of ec that identify the specific reason for the failure.  [Note - Errors arising from the operating system would typically be reported as system_category errors with an error value of the error number reported by the operating system. Errors arising from within the stream library would typically be reported as  error_code(ioerrc::stream, iostream_category) -- end note].

The above wording provides only normative encouragement for implementers to do the right thing, and thus relies on market forces and the good intensions of implementers to produce results useful to users. Anything stronger (such as changing "are encouraged to" to "shall") would require much additional specification and is beyond the scope of what the LWG can realistically tackle for C++0x.

explicit failure(const string& msg, const error_code& ec = ioerrc::stream);

Effects: Constructs an object of class failure.

Postcondition: code() == ec and strcmp(what(), msg.c_str()) == 0

explicit failure(const char* msg, const error_code& ec = ioerrc::stream);

Effects: Constructs an object of class failure.

Postcondition: code() == ec and strcmp(what(), msg ) == 0