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.
Related
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.
This is a follow-up question of the post. Please see the end of this question for a definition of "function try block".
Question: If a function try block does not "handle" the exception raised in the constructor, why do we need them after all? Could you give an example taking advantage of function try block?
Considering the following code.
#include <iostream>
#include <new>
#include <exception>
using namespace std;
struct X {
int *p;
X() try : p(new int[10000000000]) {}
catch (bad_alloc &e) {
cerr << "exception caught in the constructor: " << e.what() << endl;
}
};
int main() {
try {
X x;
}
catch (exception &e){
cerr << "exception caught outside the constructor: " << e.what() << endl;
}
return 0;
}
The output is
exception caught in the constructor: std::bad_alloc
exception caught outside the constructor: std::bad_alloc
It seems to me that, no matter what I do in the function try block, the exception is always going to be thrown to the outer scope which calls the constructor, e.g. the X x; in the above code.
Definition of "function try block", excerpted from "C++ Primer 5th."
To handle an exception from a constructor initializer, we must write the constructor as a function try block. A function try block lets us associate a group of catch clauses with the initialization phase of a constructor (or the destruction phase of a destructor) as well as with the constructor’s (or destructor’s) function body.
You're right that the exception is always propagated.
The function try block enables you to catch that exception and e.g. destroy an object passed as argument (maybe this is a smart pointer class?), without introducing an artificial base class.
More generally, it gives you the ability to clean up state changes introduced by the call of the function.
For the case of a constructor:
With the exception propagation destructors are called for all successfully constructed sub-objects, including base class sub-objects (if any).
However, this not completely constructed object's own destructor is not called. The function try block is ideally not a device for doing things that would go in that destructor. The not-completely-created object's own destructor has nothing to do, for its task is to clean up state changes introduced by the constructor body and/or later member function calls, and with a common "rule of zero" design there's no such as yet.
When a constructor throws, the corresponding destructor doesn't run. That's the reason for the quote in your book : cleanup has to be done by the constructor itself.
But your example shows that the exception propagates. This is necessary since the constructor failed, and therefore no object was created. The calling code should not proceed as if the constructor created an object.
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.
I have an exception class:
class MyException : public std::exception
{
public:
MyException( char* message )
: message_( message )
{
if( !message_ ) throw std::invalid_argument("message param must not be null");
}
};
And at my throw site:
try {
throw MyException( NULL );
}
catch( std::exception const& e ) {
std::cout << e.what();
}
(code was not compiled, so please excuse any errors)
I'm wondering what will happen when I throw from a constructor while constructing due to another throw. I assume this is legal, and the catch will end up catching a std::invalid_argument, and the previous exception thrown (MyException) will be ignored or cancelled out.
My goal with this design is to enforce invariants in my exception class. message_ should never be NULL, and I don't want to have if conditions to check if it is null in my what() overload, so I check them in the constructor and throw if they are invalid.
Is this correct, or is the behavior different?
The object you intend to throw (in this case MyException) must be successfully constructed before it can be thrown. So you're not throwing it yet, since it hasn't been constructed.
So this will work, throwing the exception from within MyException's constructor. You won't trigger the "throwing an exception while handling an exception causes std::terminate" issue.
15.1 Throwing an exception n3376
Paragraph 7
If the exception handling mechanism, after completing evaluation of the expression to be thrown but before the exception is caught, calls a function that exits via an exception, std::terminate is called (15.5.1).
This means that until the constructor (of the object being thrown in this case) completes nothing special is going to happen. But after the constructor completes any other uncought exception will result in terminate() being called.
The standard goes on to provide an example:
struct C
{
C() { }
C(const C&) { throw 0; }
};
int main()
{
try
{
throw C(); // calls std::terminate()
}
catch(C) { }
}
Here terminate is called because the object is first created. But then the copy construction is called to copy the exception to the holding location (15.1.4). During this function call (copy construction) an uncaught exception is generated and thus terminate is called.
So your code as shown should work as expected.
Either: A MyException is generated with a good message and thrown
Or: A std::invalid_argument is generated and thrown
If you want to check invariants that should always be true, you should use assertions. Exceptions are meant for unusual situations that are expected to happen, like corner cases or bad user input.
If you use exceptions to report bugs in your code, you could accidentally hide them with catch(...). This is especially important if you are writing library code, because then you never know if other people might catch and ignore the exception, even if it means that the system has reached an invalid state.
Another advantage of assertions is that you can completely disable them if you want, such that they will no longer incur any performance penalties. This allows you to be really paranoid with those checks in your debug builds and still have a lightning fast release build.
Is it possible to handle exceptions in these scenarios:
thrown from constructor before entering main()
thrown from destructor after leaving main()
You can wrap up your constructor withing a try-catch inside of it.
No, you should never allow exception throwing in a destructor.
The funny less-known feature of how to embed try-catch in a constructor:
object::object( int param )
try
: optional( initialization )
{
// ...
}
catch(...)
{
// ...
}
Yes, this is valid C++. The added benefit here is the fact that the try will catch exceptions thrown by the constructors of the data members of the class, even if they're not mentioned in the ctor initializer or there is no ctor initializer:
struct Throws {
int answer;
Throws() : answer(((throw std::runtime_error("whoosh!")), 42)) {}
};
struct Contains {
Throws baseball;
Contains() try {} catch (std::exception& e) { std::cerr << e.what() << '\n'; }
};
Yes: don't use dangerous global objects!
It might be possible to set an exception handler before construction / destruction of the objects in question, that one should be able to handle those exceptions.
For constructors, there's some weird new syntax that allows exceptions to be caught within the constructor. Not sure how that works, and it's not commonly implemented in many compilers.
For destructors, you have to wrap the content of the destructor in a try { code(); } catch(...) {} block. Which may not always be the desired behavior, depending on what you want to achieve in that destructor.
Short answer: no.
Any global object that throws an exception in its constructor will cause an unhandled exception (that is, terminate be called).
Any class that throws an exception in its destructor is a broken class.
Using the singleton pattern rather than globals will give you more options.