This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++11 status.

### 1103. `system_error` constructor postcondition overly strict

Section: 19.5.8.2 [syserr.syserr.members] Status: C++11 Submitter: Howard Hinnant Opened: 2009-04-25 Last modified: 2016-01-28

Priority: Not Prioritized

View all other issues in [syserr.syserr.members].

View all issues with C++11 status.

Discussion:

19.5.8.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 19.5.8.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 19.5.8.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 99 [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 99 [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 99 [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.