Function style exception rethrow. Bug in standard/compilers? - c++

Consider this artificial example.
Two identical destructors, one catches the exception inside function style try/catch, one scope based try/catch.
#include <iostream>
struct A {
~A() noexcept try {
throw std::runtime_error("~A");
} catch (std::exception const &e) {
std::cout<<__LINE__<<" "<<e.what()<<std::endl;
}
};
struct B {
~B() noexcept {
try {
throw std::runtime_error("~B");
} catch (std::exception const &e) {
std::cout<<__LINE__<<" "<<e.what()<<std::endl;
}
}
};
int main() try {
// A a; // std::terminate is called, exception not caught at line 26. The try-catch block in main is not relevant.
B b;
return 0;
} catch (std::exception const &e) {
std::cout<<__LINE__<<" "<<e.what()<<std::endl;
}
Godbolt link: example
So class B's destructor catches the exception and prints: 16 ~B
Were in case of class A it calls terminates and prints:
terminate called after throwing an instance of 'std::runtime_error'
what(): ~A
7 ~A
May someone hint thy is this happening? (seems implicit rethrow at the end of function style try/catch, both on clang and GCC) Does standard somehow specifying this behaviour? Any quote/link may be very helpfull. Thanks in advace.

A function-level catch on a constructor or destructor automatically rethrows the current exception upon exit:
https://en.cppreference.com/w/cpp/language/function-try-block
Every catch-clause in the function-try-block for a constructor must terminate by throwing an exception. If the control reaches the end of such handler, the current exception is automatically rethrown as if by throw;. The return statement is not allowed in any catch clause of a constructor's function-try-block.
Reaching the end of a catch clause for a function-try-block on a destructor also automatically rethrows the current exception as if by throw;, but a return statement is allowed.
So, your noexcept on ~A() is lying to the compiler. If a noexcept function exits because of an uncaught exception, terminate() is called:
https://en.cppreference.com/w/cpp/language/noexcept_spec
Non-throwing functions are permitted to call potentially-throwing functions. Whenever an exception is thrown and the search for a handler encounters the outermost block of a non-throwing function, the function std::terminate is called

Related

Cannot catch in the initialize list

For the following code:
#include <iostream>
struct Str
{
Str() { throw 100; }
};
class Cla
{
public:
Cla()
try : m_mem() { }
catch(...)
{
std::cout << "Catch block is called"<< std::endl;
}
private:
Str m_mem;
};
int main()
{
Cla obj;
}
I tried to catch the exception in the catch block. But after the catch block is run, the system still calls std::terminate to terminate the program. I didn't re-throw the exception in the catch block, can you tell me why the system crashes? Thanks!
Here is a test on compiler explorer: https://godbolt.org/z/74sTcxrY4
You're contradicting yourself.
I tried to catch the exception in the catch block. But after the catch block is run
If the catch block is run, you succeeded catching the exception from the initializer list. You're just surprised by what happened next.
First, let's think about what happens when a constructor throws: the object is not constructed. Right? the constructor never finished setting it up, so you can't use it for anything.
int main() {
Cla obj; // member subobject constructor throw, but we caught it!
obj.print(); // but we still can't use this here, because the constructor never completed
}
So it doesn't really make sense to let you swallow the exception here. You can't handle it, because you can't go back and re-try constructing your member and base-class subobjects. If you don't have a properly-constructed object, the only way C++ has of dealing with that is to unwind the block scope in which that object would otherwise be assumed to be ... well, a real object.
Hence, per the documentation:
Every catch-clause in the function-try-block for a constructor must terminate by throwing an exception. If the control reaches the end of such handler, the current exception is automatically rethrown as if by throw;. The return statement is not allowed in any catch clause of a constructor's function-try-block.
... and equivalently in the standard (draft)
The currently handled exception is rethrown if control reaches the end of a handler of the function-try-block of a constructor or destructor.
Otherwise, flowing off the end of the compound-statement of a handler of a function-try-block is equivalent to flowing off the end of the compound-statement of that function (see [stmt.return])
There is no way to swallow an exception in a constructor's function catch block. It will necessarily throw something, the caught exception if control reaches the end of the catch block. The only way around this is to std::terminate or otherwise abort before then.
From cppreference:
Every catch-clause in the function-try-block for a constructor must terminate by throwing an exception. If the control reaches the end of such handler, the current exception is automatically rethrown as if by throw;. The return statement is not allowed in any catch clause of a constructor's function-try-block.
This is intentional and beneficial. It would not make sense to return from a constructor that did not complete, it would allow an object to be accessible despite not actually having been created.
Such a catch block can be used to log errors, release resources (if you aren't using RAII) and perform other similar cleanup. But it can't prevent an exception from propagating.

How to handle an exception thrown from a function defined as noexcept?

It is said that defining a function as noexcept will let the compiler do some optimizations to boost the program and if the function needs to throw the compiler will suppress that optimization.
Here's my simple snippet:
void func() noexcept(true){
std::vector<int> vec{2, 4, 6, 8}; // vec is a vector of 4 ints
std::cout << vec.at(5); // throws
}
int main(){
try{
func();
}
catch(std::exception const& e){
std::cout << e.what() << '\n';
}
catch(...){
//
}
}
Why I cannot catch the exception? I am sure because the noexcept exception specification. So what happens if a function is marked as noexcept but throws and how to catch its exception rather than letting the program call std::terminate() to end the program?
Why I cannot catch the exception?
Because the function is noexcept. This is exactly what it means. Declaring a function noexcept is a promise that nothing will ever be thrown out of the function. As a consequence, the compiler doesn't need to generate exception handling code. If that promise is broken, then the exception cannot be handled and only reasonable way to proceed is to terminate.
So what happens if a function is marked as noexcept but throws
If an exception is thrown out of the noexcept function, then the process is terminated.
and how to catch its exception rather than letting the program call std::terminate() to end the program?
There is no way to catch an exception thrown out of a noexcept function. Your options are:
Accept that the process will be terminated if that happens.
Don't let exceptions be thrown from noexcept functions, thus avoiding the termination. Note that throwing inside is fine, as long as nothing is thrown out.
Don't declare functions that throw as noexcept, allowing you to handle the exception.

Does function try block allows us to resolve a exception?

So I was reading about function try block in this link. And there was a line that describes the difference between normal try block and function try block like this
unlike normal catch blocks, which allow you to either resolve an exception, throw a new exception, or rethrow an existing exception, with function-level try blocks, you must throw or rethrow an exception
But then I try to write a function try block like this
#include <iostream>
int add(int a, int b) try {
throw 1;
return a + b;
}
catch (int) {
std::cout << "catch in add()";
}
int main()
{
try {
add(1, 2);
}
catch (int) {
std::cout << "catch in main()";
}
}
The output is
catch in add()
If function try block doesn't allow us to resolve an exception, then how come catch in main() doesn't got printed
And there was a line that describes the difference between normal try block and function try block like this
And that line is inaccurate. Function try blocks for regular functions, behave pretty much as if they were simply the only contents of the function. Meaning, your definition of add acts exactly the same as
int add(int a, int b) {
try {
throw 1;
return a + b;
}
catch (int) {
std::cout << "catch in add()";
}
}
The difference arises in constructors. For one, a function level try block is the only way to catch exceptions raised while initializing the members of the class.
And secondly, once an exception is thrown from a member's initialization, the constructor cannot complete, and so the object is not initialized. It is here that we are not allowed to simply swallow the exception. If initialization fails by throwing an exception, that exception must be propagated or translated to another type and rethrown.
The example on the page you linked summarizes that in code
B(int x) try : A(x) // note addition of try keyword here
{
}
catch (...) // note this is at same level of indentation as the function itself
{
// Exceptions from member initializer list or constructor body are caught here
std::cerr << "Exception caught\n";
// If an exception isn't explicitly thrown here, the current exception will be implicitly rethrown
}
Does function try block allows us to resolve a exception?
Yes.
The mandatory automatic throw only applies to a few cases such as constructors and destructors. The example function is neither a constructor nor a destructor.
P.S. The behaviour of the example is undefined because it fails to return a value from non-void function.
The tutorial you're following is partly mistaken. It says this:
Finally, unlike normal catch blocks, which allow you to either resolve an exception, throw a new exception, or rethrow an existing exception, with function-level try blocks, you must throw or rethrow an exception. If you do not explicitly throw a new exception, or rethrow the current exception (using the throw keyword by itself), the exception will be implicitly rethrown up the stack.
This is completely incorrect with regards to functions.
However, this is true with regards to constructors and destructors. The C++17 standard says this:
13 If a return statement appears in a handler of the function-try-block of a constructor, the program is ill-formed.
14 The currently handled exception is rethrown if control reaches the end of a handler of the function-try-block of a constructor or destructor. Otherwise, flowing off the end of the compound-statement of a handler of a function-try-block is equivalent to flowing off the end of the compound-statement of that function.
-- N4713 [except.handle] (emphasis mine)
The first sentence of point 14 confirms this behavior for constructors and destructors. The second sentence directly contradicts the information in the tutorial you are following, because they did not distinguish the two cases.
Note that your code causes undefined behavior because the function's catch block does not throw an exception nor does it return a value, and the function doesn't return void. You must return a value from the catch block to avoid UB.

Why is my exception still being thrown after being caught?

I have the following code where a variable is being initialized with the result of a function call. This function throws so I set up a try-catch to catch the exception. For some reason the exception is still showing up on the screen even after the catch clause runs.
#include <iostream>
#include <stdexcept>
int f() { throw std::invalid_argument("threw"); return 50; }
struct S
{
S()
try : r(f())
{
std::cout << "works";
}
catch(const std::invalid_argument&)
{
std::cout << "fails";
}
int r;
};
int main()
{
S s;
}
This code prints "fails" after showing the exception:
terminate called after throwing an instance of 'std::invalid_argument'
what(): threw
Why is the exception still thrown? I have the same code set up in main and it works without fail:
int main()
{
try { throw std::invalid_argument("blah"); }
catch(const std::invalid_argument&) { }
}
So why does it fail when being used in an initializer list?
Constructors with function try blocks (like what you have for S) automatically rethrow any exceptions caught by the catch block. Consequently, after the catch catches the exception, it rethrows it. This behavior is different from normal catch handlers, which don't do this. I think the rationale is that if construction of a data member or base class fails, the object has failed to construct. The purpose of the catch handler is just to do any extra cleanup before the exception propagates outward.
Hope this helps!
From the C++11 Standard, 15.3/15:
The currently handled exception is rethrown if control reaches the end of a handler of the function-try-block
of a constructor or destructor.
The reasons are well explained in the GOTW Jerry links under your question, but summarily: imagine if it hadn't rethrown, then the next lines after S s; will presumably attempt to use s despite it never having completed construction, and when s leaves scope the constructor will have arranged a call to the destructor for s - potentially freeing never-initialised pointers, releasing never-taken locks etc..
By way of contrast, if you let the data member be default initialised then assign to it from a try/catch block in the constructor body, the state of the object including bases and data members can potentially be kept in some coherent state: it's up to you as the programmer to decide whether that state's ok - i.e. whether you'll use the try/catch block inside the constructor body, and have later member functions handle a potentially default-constructed data member.

An exception gets thrown twice from a constructor with a function-try-block

Why does the following exception thrown from the constructor of class A get caught twice, first by the catch within the constructor itself and second time by the catch in the main function?
Why doesn't it get caught just once by the catch within the constructor?
#include <iostream>
using namespace std;
class E {
public:
const char* error;
E(const char* arg) : error(arg) { }
};
class A {
public:
int i;
A() try : i(0) {
throw E("Exception thrown in A()");
}
catch (E& e) {
cout << e.error << endl;
}
};
int main() {
try {
A x;
}
catch(...)
{
cout << "Exception caught" << endl;
}
}
If I remove the try-catch block in the main function, the program will crash.
Here is the output:
Exception thrown in A()
terminate called after throwing an instance of 'E'
zsh: abort (core dumped) ./main
Why does it crash without the try-catch block in the main function?
Function-try-blocks in a constructor cannot prevent exceptions. Once an exception occurs in a constructor, you have no object, and the exception must propagate. The only thing the function-try-block can do is some local clean-up.
Constructors are indeed a very special animal with regards to function-try-blocks.
Cf. C++11 15.3/14:
The currently handled exception is rethrown if control reaches the end of a handler of the function-try-block of a constructor or destructor.
Tl;dr: Do not use function-try-blocks, ever.
It seems logical. Consider two following scenarios.
i. Try block is inside constructor's body:
A() : i(0) {
try
{
throw E("Exception thrown in A()");
}
catch (E& e) {
cout << e.error << endl;
}
// If code reaches here,
// it means the construction finished well
}
ii. Try block is in initializer ctor:
A() try : i(0) {
throw E("Exception thrown in A()");
}
catch (E& e) {
cout << e.error << endl;
// OK, you handled the exception,
// but wait you didn't construct the object!
}
In the first case, after an exception, you will handle it inside the constructor and then you will construct the object properly.
In the second case, after an exception you will handle it there. BUT you didn't construct the object yet and you have no object in the caller's side. The caller should handle an un-constructed object situation.
You are utilizing a feature called function-try-catch. When used in a constructor, it allows catching exceptions in the initialization list (especially useful for catching exceptions in base class constructors) as well as the constructor body. But since the exception is thrown in a constructor, the class is incomplete, so the compiler automatically rethrows any caught exception.
that is why you see it caught twice.
Read the following article for more details:
Constructors and Exception in C++