Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the standard conforming way to decide what will be returned by what() from a class inherited from std::system_error without duplicating data?

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.

like image 838
Norbert Bérci Avatar asked Jul 03 '13 21:07

Norbert Bérci


1 Answers

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.

like image 174
rici Avatar answered Sep 20 '22 23:09

rici