8

I use a class inherited from std::system_error for error handling and I'd like to control what is returned when what() is called. Reason: the standard (both C++11 and the draft C++1y CD - N3690, § references below are into the latter) does not specify how exactly the string returned by what() shall look like, it just give a note in §19.5.6.2 (14):

Note: The returned NTBS might be the contents of what_arg + ": " + code.message(). — end note

so it shall be considered implementation dependent. (By the way, shouldn't it be code().message() instead of code.message()?)

So, the question is: how can I define precisely the string that what() returns if I want to be standard conformant and not relying on the implementation (i.e. want to be portable)?

For those, who prefer the code:

class my_class : public std::system_error {
    public:
        my_class(std::error_code ec, std::string const & what_arg)
            : system_error(ec, /* this string is not required to be equal to what is returned by what() */)
        {
            // ok, try it here
            // but what is the name of the member storing the string?
        }
        const char * what() const noexcept
        {
            // ok, try it here
            // but how to access what_arg in its unaltered form?
        }
};

Ok, a trivial solution which I do not like could be the following:

class my_class : public std::system_error {
        std::string my_what;
    public:
        my_class(std::error_code ec, std::string const & what_arg)
            : system_error(ec, what_arg),
              my_what( /* construct my what string */ )
        { }
        const char * what() const noexcept
        { return my_what.c_str(); }
};

Since std::exception::what() is virtual, it will work, but is there a more elegant way without using any implementation detail? I do not like the idea of having two strings stored: one in std::system_error and the other in my_what.

The root of the problem: std::runtime_error — which happens to be std::system_error's parent class — has an exact requirement in §1.9.2.6 (3), a postcondition of the constructor:

strcmp(what(), what_arg.c_str()) == 0

Which, in the case of std::system_error becomes the following in §19.5.6.2 (2):

string(what()).find(what_arg) != string::npos

Does anybody have a clue why the standard tries so hard to include code().message() into what()? Note that code() returns the error code object, so anybody can include code().message() in the string at any time (even at the time when an exception of this class is catched).

If the requirement of std::system_error was the same as of std::runtime_error, I could just write:

class my_class : public std::system_error {
    public:
        my_class(std::error_code ec, std::string const & what_arg)
            : system_error(ec, /* build my what string here */ )
        { }
};

Is there any elegant and portable solution?

UPDATE: Lots of the comments below are stating that the error messages are implementation defined. I understand that, I just want to format the string returned by what(), I do not want to be it byte-by-byte equivalent on all systems. Just think about that I want to log it or pass it to a 3rd party, and it shall obey some fixed format (which is not what is suggested by the standard).

UPDATE2: I believe that std::system_error is not just for OS or STL errors. I can (and suppose to) derive my own classes from it and use them for error reporting. What if I am writing a low level API? By the way, why is it forbidden to use it in a high level API?

If I pass all the arguments to its constructor in the error handling part of my API, there is no implementation defined (i.e. unknown) error strings involved, but I still can not format it without duplicating data.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
Norbert Bérci
  • 424
  • 1
  • 4
  • 11
  • 2
    Just out of curiosity: Why do you need the return of `what()` to be portable (/ define it precisely)? – dyp Jul 03 '13 at 21:54
  • I'd like the **code** to be portable (which means to give the same results on all conforming platforms), i.e. I do not want to use anything just what the standard provides. (Otherwise I could look up the storage of the string (a member of system_error or one of its ancestors) that is returned by what() and I would simply modify it. The problem is that the standard does not say anything about how is it stored or how can it be accessed.) – Norbert Bérci Jul 03 '13 at 23:00
  • 1
    That's not what I meant. The error messages (`what()`) from exceptions thrown as required by the Standard are already implementation-defined and therefore not portable. Other problems such as missing internationalization features make the result of `what()` only useful as a debugging tool (a hint for developers) IMO. That's why I asked why you need to have the result of `what()` precisely defined for your own exception type; as you know it's guaranteed your message will occur in the string returned by `what()`. – dyp Jul 04 '13 at 00:23
  • I do want to use the facilities in `system_error` for my own code, not just errors from the OS or STL. If I create a `system_error` object with an `error_code` that I have created (as it is intended with `error_code`s and `error_category`s), and supplying the `what_arg` also, then there is no string that is implementation defined except the construction of the string returned by `what()`, and that is exactly the issue I want to avoid. Why do I want to control the contents? Readability (even for debug messages). – Norbert Bérci Jul 04 '13 at 01:44
  • 1
    Would you then also add an extra layer of detection/substitutions to replace and unify all standard messages? For example so that GCC's and MSVC's "out of memory" error prints exactly the same nonimplementationdefined string? How about different languages? They will be in Polish on my machine you know.. I'm sure you would not touch that, there's just too much work and to little gain. So, if you don't care that much about the **contents** of the platform's error messages, so .. why do you try to care about contents of platform's error messages? – quetzalcoatl Jul 04 '13 at 07:24
  • It is not that important whever your app will behave 100% the same between various platforms. In terms of error reporting, more important is that your app on platform X behaves consistently with that platforms' mechanisms and standards. If someone ports and compiles it on iOS, the iOS guys will read the error messages and they need to quickly know what's the problem! If their typical compiler uses different format, then, well, its theirs.. – quetzalcoatl Jul 04 '13 at 07:27
  • btw, of course, that all just "IMHO", so feel free to disagree, I will not try to convince you anyharder :) – quetzalcoatl Jul 04 '13 at 07:28
  • This question doesn't even make sense: the standard places no requirements on what your derived exception type returns from `what()` so any string is "standard conforming". Return whatever you want to. – Jonathan Wakely Jul 04 '13 at 14:32
  • Please guys, I just want to format the error string that is returned from my_class (see above), and do it elegantly. This is the question! I do NOT want the error messages from the OS or STL or whatever to be the same. std:system_error (or any exception classes in the standard) are not just for errors from the OS or STL! We shall inherit from them and use them in our code! Am I wrong? – Norbert Bérci Jul 04 '13 at 15:45
  • @JonathanWakely: Please write the code that returns just the `what_arg` from my_class when I call its `what()` function without using a separate `string` member. If you can do that, I will accept your solution, because that was the question. If you can not, your "Return whatever you want to" statement just don't stand up. – Norbert Bérci Jul 04 '13 at 16:01
  • If the question is "how do I return my own string without storing it somewhere" then please edit the question summary. Answer: you can't, you need a member to store your string. It doesn't have to be a `std::string` it could be a `std::array` or `std:vector` or `std::unique_ptr` but you have to store something. – Jonathan Wakely Jul 04 '13 at 16:06
  • @JonathanWakely: Thanks for the suggestion, I will modify the question accordingly. Sad thing, that I have to duplicate... – Norbert Bérci Jul 05 '13 at 11:32
  • @NorbertBérci: Basically, what it comes down to, is you have 0 control over the string contained in `system_error`, and whatever you pass to it's constructor, it is impossible to retrieve that string in a portable way. So you'll have to store it yourself somewhere, or write different nonportable code for each target. – Mooing Duck Feb 03 '14 at 23:16

2 Answers2

6

I don't know of an "elegant and portable" solution, but I don't think there is anything wrong with the solution you propose in the question, which is to make your own copy of what_arg.

The exception object will not last very long: typically, only long enough to unwind the stack. Furthermore, the string in what_arg is not likely to be very long, given that a megabyte of what is not going to be very readable. And finally, the exception object will only be created in exceptional circumstances, so the "unnecessary" duplication of a small string is not going to have any noticeable impact on your program's performance.

Basically, the class designers have chosen to make the class's state opaque, and -- to put it bluntly -- you don't trust them to produce a usable result (a readable message) from that state. In that case, you will have to duplicate the state. This is a not uncommon consequence of state encapsulation, and the general solution is almost always to duplicate the state. In the case, the cost is minor, so if controlling the value of what() is important to you, you should just accept the cost and move on.

rici
  • 234,347
  • 28
  • 237
  • 341
  • You can't even count on a copy of `what_arg` to exist in the base class. Once you realize that, it's obvious you need your own copy. – MSalters Jul 04 '13 at 09:42
  • @MSalters: Would you please explain? The string from `what_arg` shall be stored somewhere, but yes, I agree it is not known where and how. I do not like data duplication, but the more replies I get the more confident I am that there is no elegant and portable solution :( – Norbert Bérci Jul 04 '13 at 16:03
  • @rici: If nobody comes up with a better solution, I will accept your answer, as data duplication seems to be necessary... :( – Norbert Bérci Jul 04 '13 at 16:05
  • @NorbertBérci You can avoid data duplication, as `what()` is a virtual function. This also provides a way to specify the return of `what()` precisely for types derived from `system_error`. – dyp Jul 04 '13 at 16:27
  • @DyP: I do not think what you have written is true. Please fill in my_class::what() and show with real code how can it return (for example) the `what_arg` only that was passed to its constructor, without duplicating data! – Norbert Bérci Jul 05 '13 at 12:04
  • @dyp: Data duplciation _is_ required, the derived class needs its own copy of `what_arg` – Mooing Duck Feb 03 '14 at 23:17
2

I want to make a longer comment, so I'll post this as community wiki answer.

The Standard (C++14 draft N3690) describes system_error in [syserr.syserr.overview]/1:

The class system_error describes an exception object used to report error conditions that have an associated error code. Such error conditions typically originate from the operating system or other low-level application program interfaces.

Errors and error messages originating from the operating system or other low-level APIs are necessarily non-portable.

Furthermore, if you derive from system_error, someone who catches the exception by ref might expect the error codes from the OS / low level APIs as specified by the C++ implementation; at least this user knows the result of what() isn't guaranteed by the Standard. If this user catches an exception of your derived type, you might as well provide another what2() function that precisely returns your string.

The solution I'd propose is not to derive from system_error. As far as I can see, your analysis of the guarantees (and the typo code().message()) is correct, i.e. you cannot precisely specify the return of what() for system_error. If you want to have an error code in your exception, you could use the error_code facilities; yet, as with system_error, the Standard specifies in [syserr]/1

This subclause describes components that the standard library and C ++ programs may use to report error conditions originating from the operating system or other low-level application program interfaces.

Therefore, it might be better to use another error code type.

As far as I can see, deriving from system_error is useful if you want to enhance the error message from system_errors originating from the OS / low-level APIs. In that case, you might want to add some description to the message (what()), and this is what system_error guarantees for the message passed to the ctor.


Another "comment" to answer how you could derive from system_error and precisely specify the return of what():

class my_class : public std::system_error
{
    std::string my_what;
public:
    my_class(std::error_code ec, std::string const& what_arg)
        : system_error(ec)
        //            ^^^^ note: NOT passing the string
          , my_what( /* construct my what string */ )
    { }
    const char* what() const noexcept
    { return my_what.c_str(); }
};

Yes, there's data duplication in the sense that there are (probably) two string objects, but there's no data duplication of the contents of the string. If you don't need to pass a string to the ctor of the exception (e.g. the type of the exception and the error code are descriptive enough), then you can even leave out the string object; something like

class my_class : public std::system_error
{
public:
    my_class(std::error_code ec)
        : system_error(ec)
        //            ^^^^ note: NOT passing the string
    { }
    const char* what() const noexcept
    { return "description of error"; }
};

Although it's probably better to include the error code in the description returned by what() (requires some sort of storage in my_class).

It might be worthwhile to mention that construction the string on demand (i.e. when what() is called) might be favourable.

dyp
  • 38,334
  • 13
  • 112
  • 177
  • Note that I want to use my_class (above) in my own API. In this case, there is no error message that is implementation defined (I pass it to the constructor in my API, and it will be caught in a code that uses my API), but even in this case, I can not exactly decide what will be the string returned by `my_class::what()` without duplicating data. For example, I can not return the `what_arg` that was passed to the constructor. – Norbert Bérci Jul 05 '13 at 12:12
  • Regarding to the second part of your argument: I do not think that std::system_error is just for OS / low-level APIs. By the way: what makes an API low level? What if I am writing a low-level API? I think the exception hierarchy in the standard is there for using it or deriving from it. Don't you think that? – Norbert Bérci Jul 05 '13 at 12:16
  • Yes, `system_error` is not just for OS / low-level APIs, but it's explicitly "to report error conditions originating" from those. And yes, I also think that the exceptions are made to be derived from; yet I think `system_error` should not be used but to enhance the error message of an error condition originating from OS/... If you're writing a low-level API, I don't understand why you would want to precisely define the error message if the errors themselves are platform-dependent. – dyp Jul 05 '13 at 17:37
  • Thank you, I now see how it is possible to do it without data duplication in the narrow sense, but a new member still required, and it feels like a workaround than a real solution. There is a facility in `std::system_error` to store the error string, but we can not use it (without restrictions) due to the way it was defined in the standard, and have to include a new member for that purpose in the inherited class. – Norbert Bérci Jul 07 '13 at 22:52