tags:

views:

69

answers:

3

I have an API which internally has some exceptions for error reporting. The basic structure is that it has a root exception object which inherits from std::exception, then it will throw some subclass of that.

Since catching an exception thrown in one library or thread and catching it in another can lead to undefined behavior (at least Qt complains about it and disallows it in many contexts). I would like to wrap the library calls in functions which will return a status code, and if an exception occurred, a copy of the exception object.

What is the best way to store an exception (with it's polymorphic behavior) for later use? I believe that the c++0x futures API makes use of something like this. So what is the best approach?

The best I can think of is to have a clone() method in each exception class which will return a pointer to an exception of the same type. But that's not very generic and doesn't deal with standard exceptions at all.

Any thoughts?

EDIT: It seems that c++0x will have a mechanism for this (http://www2.research.att.com/~bs/C++0xFAQ.html#rethrow). It is described as "library magic". Does that mean that is doesn't require any of the language features of c++0x? if not, are there any implementations which are compatible with c++03?

EDIT: Looks like boost has an implementation of exception copying :-). http://www.boost.org/doc/libs/1_43_0/libs/exception/doc/current_exception.html. I'll keep the question option for any non boost::copy_exception answers.

EDIT: To address j_random_hacker's concerns about the root cause of the exception being an out of memory error. For this particular library and set of exceptions, this is not the case. All exceptions derived from the root exception object represent different types of parsing errors caused by invalid user input. Memory related exceptions will simply cause a std::bad_alloc to be thrown which is addressed separately.

+2  A: 

You have what would be what I think is your best, only answer. You can't keep a reference to the original exception because it's going to leave scope. You simply have to make a copy of it and the only generic way to do that is with a prototype function like clone().

Sorry.

Noah Roberts
But beware that the root cause of the exception may be an out-of-memory condition, in which case `clone()` will fail if it tries to call `new`. This can be gotten around, e.g. by overloading `operator new()` for each exception type to allocate from a static buffer, but it's not necessarily pretty either.
j_random_hacker
@j_random_hacker: Well, since we are getting pedantic, what you call out-of-memory is better called bad-alloc and it doesn't have to be actual out of memory. Another reason for bad-alloc is requesting too much memory, and in that case subsequent small allocations will succeed
sbk
@sbk: Is it really pedantic to consider the possibility of running out of memory?
j_random_hacker
The easy answer here is to point out that with bad_alloc you're a) screwed, and b) getting a particular exception that you're not going to be able to clone since it won't be your own.
Noah Roberts
+1  A: 

You're allowed to throw anything, including pointers. You could always do something like this:

throw new MyException(args);

And then in the exception handler store the caught pointer, which will be fully polymorphic (below assuming that MyException derives from std::exception):

try {

   doSomething(); // Might throw MyException*

} catch (std::exception* pEx) {

   // store pEx pointer
}

You just have to be careful about memory leaks when you do it this way, which is why throw-by-value and catch-by-reference is normally used.

More about catch-by-pointer: http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.8

Tyler McHenry
The problem with this solution as I see it is that you might be throwing an exception due to something that has as its root cause an out-of-memory condition, in which case `new` will fail.
j_random_hacker
That's mentioned in the link, but you could in theory work around that by having a pre-allocated or static out-of-memory exception that you throw in that case. But then you would need to make sure that you don't delete this as you would your other exceptions. It's do-able but you have to be careful of a lot of pitfalls like this.
Tyler McHenry
Couldn't we check to see if the exception indicates an out of memory condition and simply set an int/boolean to indicate this?
Vulcan Eager
+1: For my particular use can this isn't a bad idea. similar to the clone approach but doing at the point of the throw.
Evan Teran
A: 

The reason why catching an exception thrown in one library and catching it in another can lead to undefined behavior is that these libraries could be linked with different Runtime libraries. If you will return exception from a function instead of throwing it you will not avoid that problem.

Kirill V. Lyadvinsky
I believe you are correct, I think the issue Qt has with it is more about passing between threads (I've added that subtlety to my question as well).
Evan Teran