i want to derive from std::exception to add specific information to my log files, but i cant figure how to access the .what() from the std::exception.
furthermore, i know that it is unsafe to create a string in my exception handler, but i’m not an expert on this topic, so what are some safer alternatives?
struct Exception : public std::exception, private boost::noncopyable
{
public:
Exception(std::string msg)
: message(msg)
{}
~Exception()
{}
virtual const char* what() const throw
{
std::string what = message + // and now what? base.what()
LOG(what); // write to log file
return what.c_str();
}
private:
std::string message;
};
EDIT:
i really have asked my question the wrong way. i’m rather interested in safety, i just thought it’d be nice to have more data for logging. i was wrong.
now, i’m not being so paranoid about bad_alloc being thrown by the message string in case there was a bad_alloc before, i’d rather have a neat message. that being said i rewrote some stuff:
struct Exception : public std::exception
{
public:
Exception(std::string msg)
: message(msg)
{}
~Exception()
{}
virtual const char* what() const throw
{
LOG(what); // write to log file
return what.c_str();
}
private:
std::string message;
};
are there still any big concerns about that code now? the LOG() throws std::exception i case something goes wrong, because i didn’t want an infinite loop of log calling by derived exception class, and that class again calling log which would cause the same exeception again.
Will this work like i want it to, or will a logging exception in my derived class call terminate() or cause core dump?
EDIT: Since writing this answer, I’ve stumbled upon the Error and Exception Handling section in the Boost document. I would recommend that document over this answer.
First off, making your exception not-copyable is a bad idea. When you write something such as
The runtime creates a copy of that object into a special location. Some compilers might optimize this and create the original object in that location, but the The C++ Programming Language says it’s a copy, and I also believe that’s what the standard says, though I’m not sure. You might get away with this in your current environment, but that might not always be the case.
Then, everything else depends on how paranoid you are with corner cases.
The memory allocation part is mostly flaky in the exception clause (i.e. the constructor). If this memory allocation happens to fail (i.e.
std::bad_allocis thrown), there are two possibilities, depending on how you write yourthrowstatement:std::stringis created before thethrowstatement,std::bad_allocreplaces the exception you thought you would raise, problem is sort-of badly reported.std::stringis created inline in the constructor call. If this is considered “during exception handling” by the standard,std::unexpected()/std::terminate()will be invoked and you basically get a core dump.In any case, it seems like you won’t get the desired effect of reporting your error.
I always recommend to create some sort of temporary state that doesn’t allocate memory in the constructor and wait for the call to
std::what()to create the string that reports the error, but that might still lead to case #1. You could resort to some compile-time determined buffer size to make sure that doesn’t happen.Many people will tell you they’ve never had problems with allocating strings in constructors because it’s unlikely
std::bad_allocwill be raised unless the original exception wasstd::bad_allocin the first place. Hence, it depends on your level of paranoia.