Catch Exception by Pointer in C++

catch exception by pointer in C++

The recommended way is to throw by value and catch by reference.

Your example code throws a pointer, which is a bad idea since you would have to manage memory at the catch site.

If you really feel you should throw a pointer, use a smart pointer such as shared_ptr.

Anyway, Herb Sutter and Alexei Alexandrescu explain that really well in their C++ Coding Standards book which I paraphrased.

See C++ Coding Standards: Throw by Value, Catch by Reference.

Why should i use catch by reference when exception pointers are thrown

The difference between the two codes you posted is that the first one catches a pointer to an exception by reference, and the second one catches a pointer to an exception by value. In neither case is an exception copied, since you're dealing with pointers.

In general, exceptions should be thrown by value, and caught by reference. The C++ standard library is designed with this expectation in mind. However, older libraries, (MFC for instance) throw exceptions by pointer as you do here, and are expected to be caught by pointer.

There's no effective difference between catching a pointer by value and by reference, except that if you catch by reference that gives you the (completely useless) option of deleting the exception, allocating a new exception with that same pointer, and rethrowing the same exception-pointer.

C++: How to catch exceptions thrown from constructors?

You can abstract the object construction into a function that catches the exception:

template<typename... Args>
A make_a(Args&&... args) {
try {
return A(std::forward(args)...);
}
catch (std::exception& e) {
// print a user-friendly error message and exit
...
std::exit(EXIT_FAILURE);
}
}

// ... in the actual code:
A my_obj = make_a(arg1, arg2, arg3);

The above makes use of the fact that your program is exiting if construction fails. If the requirement were to continue running, the function could return std::optional<A> (or its boost equivalent if you don't have access to C++17.)

C++ catch blocks - catch exception by value or reference?

The standard practice for exceptions in C++ is ...

Throw by value, catch by reference

Catching by value is problematic in the face of inheritance hierarchies. Suppose for your example that there is another type MyException which inherits from CustomException and overrides items like an error code. If a MyException type was thrown your catch block would cause it to be converted to a CustomException instance which would cause the error code to change.

Why exception not the same when catching exception pointer?

You should throw exceptions by value (usually).

The problem here is you are throwing a pointer to an object. Unfortunately, by the time the pointer is caught, the object that it is pointing to has been destroyed, and thus you have an invalid pointer.

try {
bad_exception e2 = bad_exception();
cout << e2.what() << endl;
cout << &e2 << endl;

throw &e2;
} // At this point the object "e2" goes out of scope
// Thus its destructor is called.
// So the pointer you threw now points at an invlalid object
// Thus accessign the object via this pointer is UB.

catch (bad_exception* ex) {
cout << ex << endl;
cout << ex->what() << endl;
cout << (*ex).what() << endl;
}

Rewrite it like this:

try {
bad_exception e2 = bad_exception();
cout << e2.what() << endl;
cout << &e2 << endl;

throw e2; // Throw a copy.
// Throw an object that gets copied to a secure location
// so that when you catch it is still valid.
}
catch (bad_exception const& ex) { // catch and get a reference to the copy.
cout << &ex << endl;
cout << ex.what() << endl;
}

If you absolutely must throw a pointer, then use new to make sure the pointer has a dynamic life span (but remember, you will need to clean it up).



Update:

It seems the exception has the "auto delete" feature?

Not a thing.

I tried throwing a pointer to normal object (not inherited from exception).

There is nothing special about the exception object (or any of its derived classes). It is simply a normal object. Like all objects (that are a class), the destructor of the object is run when the object reaches the end of its lifetime. If A does not have a destructor then the memory used by the object may not change (but it is not usable by other objects that are not your object).

But I can still access the object and its properties in the catch block.

That is the bad side of "Undefined Behavior". I may look like it is working. It's not working, it just looks like it is. And it is just as likely to not work on some other situation.

Is catching an exception by reference dangerous?

It is indeed safe - and recommended - to catch by const reference.

"e is actually placed on the stack of some_function()"

No it's not... the object actually thrown is created in an unspecified area of memory reserved for use by the exception handling mechanism:

[except.throw] 15.1/4: The memory for the exception object is allocated in an unspecified way, except as noted in 3.7.4.1. The exception
object is destroyed after either the last remaining active handler for the exception exits by any means other than rethrowing, or the last object of type std::exception_ptr (18.8.5) that refers to the exception object is destroyed, whichever is later.

If a local variable is specified to throw, it's copied there-to if necessary (the optimiser may be able to directly create it in this other memory). That's why...

15.1/5 When the thrown object is a class object, the constructor selected for the copy-initialization and the destructor
shall be accessible, even if the copy/move operation is elided (12.8).


If that's not clicked, it might help to imagine implementation vaguely like this:

// implementation support variable...
thread__local alignas(alignof(std::max_align_t))
char __exception_object[EXCEPTION_OBJECT_BUFFER_SIZE];

void some_function() {
// throw std::exception("some error message");

// IMPLEMENTATION PSEUDO-CODE:
auto&& thrown = std::exception("some error message");
// copy-initialise __exception_object...
new (&__exception_object) decltype(thrown){ thrown };
throw __type_of(thrown);
// as stack unwinds, _type_of value in register or another
// thread_local var...
}

int main(int argc, char **argv)
{
try {
some_function();
} // IMPLEMENTATION:
// if thrown __type_of for std::exception or derived...
catch (const std::exception& e) {
// IMPLEMENTATION:
// e references *(std::exception*)(&__exception_object[0]);
...
}
}

What is the proper way to raise exception and handle certain exception type in C++

You can have multiple catch statements, like this:

try {
execute(query);
}
catch (const my_custom_exception_type& e) {
}
catch (const std::runtime_error& e) {
}
catch (const std::exception& e) {
}
catch (...) {
// fallback - exception object is not any of the above types
}

Control will flow into the first catch block whose parameter type is compatible with the exception object type. If none match and there is no ... catch-all, the exception will propagate out of that try/catch block. A more precise explanation of the exact try/catch behavior can be found here:

When an exception of type E is thrown by any statement in compound-statement, it is matched against the types of the formal parameters T of each catch-clause in handler-seq, in the order in which the catch clauses are listed. The exception is a match if any of the following is true:

  • E and T are the same type (ignoring top-level cv-qualifiers on T)
  • T is an lvalue-reference to (possibly cv-qualified) E
  • T is an unambiguous public base class of E
  • T is a reference to an unambiguous public base class of E
  • T is (possibly cv-qualified) U or const U& (since C++14), and U is a pointer or pointer to member (since C++17) type, and E is also a pointer or pointer to member (since C++17) type that is implicitly convertible to U by one or more of

    • a standard pointer conversion other than one to a private, protected, or ambiguous base class
    • a qualification conversion
    • a function pointer conversion (since C++17)
  • T is a pointer or a pointer to member or a reference to a const pointer (since C++14), while E is std::nullptr_t.

You can also use RTTI to determine the type of e but avoid that if at all possible.



Related Topics



Leave a reply



Submit