deriving from std::exception
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
// could be any exception, doesn't matter.
throw Exception(...);
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_alloc
is thrown), there are two possibilities, depending on how you write your throw
statement:
std::string
is created before thethrow
statement,std::bad_alloc
replaces the exception you thought you would raise, problem is sort-of badly reported.std::string
is 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_alloc
will be raised unless the original exception was std::bad_alloc
in the first place. Hence, it depends on your level of paranoia.
I won't go in to the myriad problems there are with your code because it's a huge can of worms. But here is how you call a base class method:
std::exception::what()
The answer to this question is, unless you've explicitly set the value that what() should return from std::exception then you don't want to call it. Fact of the matter is that it's going to behave in ways you might not expect and dissimilarly on different implementations.
Note that the standard provides no functionality in std::exception to provide the string value to begin with. Those implementations that actually provide useful information from calling std::exception::what() add additional, non-standard functionality to the class. For example, MSVC has an exception(char const* const&)
constructor. This isn't in the standard and you probably don't want to depend upon it.
Your better bet is to never call std::exception::what from a derived class. Sure, do upcalls in your custom versions that subclass things UNDER std::exception, but don't do it in direct derivatives.
If you do insist on calling this function directly then you'd damn well better check for NULL because that's what you're probably going to get.
for the second part. I have several hundred kloc that runs on >100 platforms that has a std::exception derived class with std::string members. Never had any problem with it
class myex: public std::exception
{
public:
std::string m_msg;
std::string m_className;
int m_rc;
...
精彩评论