1103. system_error constructor postcondition overly strict

Section: 22.5.7.2 [syserr.syserr.members] Status: C++11 Submitter: Howard Hinnant Opened: 2009-04-25 Last modified: 2016-02-10

Priority: Not Prioritized

View all issues with C++11 status.

Discussion:

22.5.7.2 [syserr.syserr.members] says:

system_error(error_code ec, const string& what_arg);

Effects: Constructs an object of class system_error.

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

However the intent is for:

std::system_error se(std::errc::not_a_directory, "In FooBar");
...
se.what();  // returns something along the lines of:
            //   "In FooBar: Not a directory"

The way the constructor postconditions are set up now, to achieve both conformance, and the desired intent in the what() string, the system_error constructor must store "In FooBar" in the base class, and then form the desired output each time what() is called. Or alternatively, store "In FooBar" in the base class, and store the desired what() string in the derived system_error, and override what() to return the string in the derived part.

Both of the above implementations seem suboptimal to me. In one I'm computing a new string every time what() is called. And since what() can't propagate exceptions, the client may get a different string on different calls.

The second solution requires storing two strings instead of one.

What I would like to be able to do is form the desired what() string once in the system_error constructor, and store that in the base class. Now I'm:

  1. Computing the desired what() only once.
  2. The base class what() definition is sufficient and nothrow.
  3. I'm not storing multiple strings.

This is smaller code, smaller data, and faster.

ios_base::failure has the same issue.

[ Comments about this change received favorable comments from the system_error designers. ]

[ Batavia (2009-05): ]

We agree with the proposed resolution.

Move to Tentatively Ready.

Proposed resolution:

In 22.5.7.2 [syserr.syserr.members], change the following constructor postconditions:

system_error(error_code ec, const string& what_arg);

-2- Postconditions: code() == ec and strcmp(runtime_error::what(), what_arg.c_str()) == 0 string(what()).find(what_arg) != string::npos.

system_error(error_code ec, const char* what_arg);

-4- Postconditions: code() == ec and strcmp(runtime_error::what(), what_arg) == 0 string(what()).find(what_arg) != string::npos.

system_error(error_code ec);

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

system_error(int ev, const error_category& ecat, const string& what_arg);

-8- Postconditions: code() == error_code(ev, ecat) and strcmp(runtime_error::what(), what_arg.c_str()) == 0 string(what()).find(what_arg) != string::npos.

system_error(int ev, const error_category& ecat, const char* what_arg);

-10- Postconditions: code() == error_code(ev, ecat) and strcmp(runtime_error::what(), what_arg) == 0 string(what()).find(what_arg) != string::npos.

system_error(int ev, const error_category& ecat);

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

In 22.5.7.2 [syserr.syserr.members], change the description of what():

const char *what() const throw();

-14- Returns: An NTBS incorporating runtime_error::what() and code().message() the arguments supplied in the constructor.

[Note: One possible implementation would be: The return NTBS might take the form: what_arg + ": " + code().message()


if (msg.empty()) { 
  try { 
    string tmp = runtime_error::what(); 
    if (code()) { 
      if (!tmp.empty()) 
        tmp += ": "; 
      tmp += code().message(); 
    } 
    swap(msg, tmp); 
  } catch(...) { 
    return runtime_error::what(); 
  } 
return msg.c_str();

end note]

In 30.5.3.1.1 [ios::failure], change the synopsis:

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

In 30.5.3.1.1 [ios::failure], change the description of the constructors:

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

-3- Effects: Constructs an object of class failure by constructing the base class with msg and ec.

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

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

-5- Effects: Constructs an object of class failure by constructing the base class with msg and ec.

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

In 30.5.3.1.1 [ios::failure], remove what (the base class definition need not be repeated here).

const char* what() const;

-7- Returns: The message msg with which the exception was created.