How to return a string from what() properly?

Hello everyone!

I'm dealing with a very weird issue. I have created a program that uses exceptions heavily to report errors to the user. Most exceptions come with a long error message, but to my surprise, very long messages are not returned properly by the "what()" method.

After some trial and error with smaller programs where the issue happens with way shorter strings too, I came to the conclusion that there might be an issue with local variables in the what method. Currently, the simplest method I used looks like this:

1
2
3
4
5
const char* Error::what() const noexcept {
  std::stringstream s;
  s << "Error: " << err::ErrType::msgs[errorcode] << "\n" << message;
  return s.str().c_str();
}


I'm pretty sure, the problem is that the variables are no longer valid once the method returns.
Is there any proper way to fix this issue, other than storing everything directly in a variable in the exception object because that would be very annoying for the more complicated objects.
Last edited on
Just return the std::string

1
2
3
4
5
std::string Error::what() const noexcept {
  std::stringstream s;
  s << "Error: " << err::ErrType::msgs[errorcode] << "\n" << message;
  return s.str();
}
But as far as I'm concerned, std::exception (which is the superclass) requires what to return a C string, or am I wrong?
Yes, but it wasn't clear that this inherited from std::exception.

Regardless, I wasn't paying attention. If s.str() throws, your program crashes - so you need to handle that exception if it arises.

You may consider using e.g., a static message buffer or a shared std::string to build the contents of the message. Note that copying a std::exception must not throw: make sure any data contained in the class is safely copyable. This has the side effect of forcing you to build the message earlier, so the exception object can carry data with reference semantics instead.

Does std::runtime_error suit your needs? Try something roughly like the following.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# include <stdexcept>
# include <iostream>

static std::string const msgs[] { "ENOENT: no such file or directory" /* ... */ }; 

struct Error: public std::runtime_error { 
  Error(int error_code, std::string const& msg) // may throw std::bad_alloc
    : std::runtime_error(std::string{"Error: "} + msgs[error_code] + ": " + msg) 
    , error_code{error_code}
  {}
  
  int const error_code;  
}; 

int foo() noexcept(false) { 
    std::cout << "called foo" << std::endl;
    throw Error{0, "whatever.txt"};
    std::cout << "didn't make it :(\n";
}

int main() {
    try {
        foo();
    } catch (Error const e) {
        std::cerr << e.what() << '\n'; 
    }
}


http://coliru.stacked-crooked.com/a/d3d0fc115f9c23a0
Last edited on
This looks really good, I think I'll go with that. Thanks a lot!
Topic archived. No new replies allowed.