Consider this code:
int main()
{
try
{
throw std::range_error("");
}
catch (std::bad_alloc)
{
std::cout << "AAAA" << std::endl;
throw;
}
catch (std::range_error)
{
std::cout << "BBB" << std::endl;
throw;
}
catch (std::exception)
{
std::cout << "CCC" << std::endl;
}
std::cout << "DDD" << std::endl;
}
Here I throw an exception of type std::range_error and trying to catch it.
Logically the first catch block cannot catch it because of type mismatch(std::bad_alloc and std::range_error).
The second catch block must catch it because they are the same types of std::range_error.
And also, when I rethrow the exception in second catch block it must be caught in the third catch block.
So my output must be
BBB
CCC
DDD
But I only get the BBB output with the termination.
Can anybody please explain me the behavior??
When you re-throw an exception, you are throwing it completely out of the context of the current exception handling block.... So,
try
{
throw std::range_error("");
}
catch (std::bad_alloc)
{
std::cout << "AAAA" << std::endl;
throw;
}
catch (std::range_error)
{
std::cout << "BBB" << std::endl;
throw;
}
catch (std::exception)
{
std::cout << "CCC" << std::endl;
}
is one exception handling block. Therefore, on meeting the first throw in any of the catch blocks, it leaves the whole block to look for another handling block (try-catch) outside the current scope. if not found, the program terminates.
Please see try-catch block in C++
To print as you initially thought ... Live On Coliru ...
int main()
{
try{
try{
try{
throw std::range_error("");
}
catch (std::bad_alloc) {
std::cout << "AAAA" << std::endl;
throw;
}
}
catch (std::range_error) {
std::cout << "BBB" << std::endl;
throw;
}
}
catch (std::exception){
std::cout << "CCC" << std::endl;
}
std::cout << "DDD" << std::endl;
}
Prints:
BBB
CCC
DDD
For the record: please avoid using exceptions for control flow that could be done with simple if-else ladder in production code
To re-catch the range_error and new outer try catch block is required.
#include <iostream>
int main()
{
//outer try catch ------------------------
try {
// inner try catch ---------------------
try
{
throw std::range_error("");
}
catch (std::bad_alloc)
{
std::cout << "AAAA" << std::endl;
throw;
}
catch (std::range_error)
{
std::cout << "BBB" << std::endl;
throw;
}
// -------------------------------
}
catch (std::exception)
{
std::cout << "CCC" << std::endl;
}
// --------------------------------
std::cout << "DDD" << std::endl;
}
Output
BBB
CCC
DDD
This throw:
catch (std::range_error)
{
std::cout << "BBB" << std::endl;
throw; // <== this one
}
isn't itself within the body of a try, so when the exception handling mechanism will keep going out one scope at a time until it finds one. Since there is no other outer try, the exception isn't caught at all.
There is no reentrance in exception handling.
Related
Today I met a weird behaviour in catching exceptions in C++, could anyone clarify it to me? Code snippe
#include <iostream>
#include <string>
#include <exception>
int main() {
try {
std::stod("notanumber");
} catch (const std::invalid_argument&) {
std::cerr << "std::invalid_argument" << std::endl;
} catch (const std::out_of_range&) {
std::cerr << "std::out_of_range" << std::endl;
} catch (const std::exception&) {
std::cerr << "Caught by ancestor" << std::endl;
} catch (...) {
auto ptr = std::current_exception();
auto type = __cxxabiv1::__cxa_current_exception_type();
std::cerr << type->name() << std::endl;
std::cerr << "..." << std::endl;
}
return 0;
}
Writes to output
St16invalid_argument
...
Environment details
C++ 14, disabled RTTI
Clang 13.1.6 arm64-apple-darwin-21.6.0
macOS Monterey 12.6
I expect exception to be caught on the very first catch block
Upd. Even simplest catch doesn't work for me on the environment
try {
std::stod("notanumber");
} catch (const std::invalid_argument&) { // not caught
std::cerr << "std::invalid_argument" << std::endl;
}
The following code will print 0 and ends:
// version 1
#include<future>
#include<iostream>
using namespace std;
int main(){
promise<int> p;
auto f = p.get_future();
p.set_exception(current_exception());
try {
cout << f.get() << endl;
} catch (const exception& e) {
cout << "caught future set exception\n";
}
return 0;
}
Well, seems p.set_exception(current_exception()); didn't cause any issue. But when I change it to be:
// version 2
#include<cassert>
#include<future>
#include<iostream>
using namespace std;
int main(){
promise<int> p;
auto f = p.get_future();
string msg = "test promise";
async([&] { // new code which version 1 didn't have
try {
throw runtime_error(msg);
} catch (...) {
cout << "caught exception 1\n";
try {
p.set_exception(current_exception());
} catch (...) {
cout << "caught exception 2\n";
}
}
}).wait(); // end new code
try {
cout << f.get() << endl;
} catch (const exception& e) {
cout << "caught future set exception\n";
assert(e.what() == msg);
}
return 0;
}
It will run and print:
caught exception 1
caught future set exception
Just wonder how/when does a std::promise fall into error/exception state so that its get_future().get() will throw exception?
What's the main reason of my version 2 code that p.set_exception() causes f.get() issue, while version 1 code has no problem?
Thanks.
In which cases do Options 1 and 2 give different results/behaviour?
Are they equivalent in all respects?
I tried with a non-existing in_out/sample2.txt to force an exception and they behave the same.
int main() {
string fnamein2 = "in_out/sample2.txt";
ifstream ifstr;
try {
cout << "Reading " << fnamein2 << endl;
ifstr.open(fnamein2);
ifstr.exceptions( ifstream::eofbit | ifstream::failbit | ifstream::badbit );
} catch(const exception &e) { // <-- Option 1
//} catch(const ifstream::failure &e) { // <-- Option 2
cout << "There was an error: " << e.what() << endl;
}
return 0;
}
There is no difference in your scenario.
std::ifstream::failure is specialized version of std::exception (contains more details) but in your case you do not used them.
std::ifstream::failure has code method which gives you more information about an error. But if you do not need it, you can use base class.
Problem : I am using both std::exception and std::bad_alloc to catch exception. Something is wrong with the order of the try catch that I am using. I attached sample code for reference.
Expected : If my error is bad_alloc then the bad_alloc exception is thrown.
Observed : My error is bad_alloc, but exception is thrown.
Sample Code :
#include "stdafx.h"
#include <iostream>
#include <exception>
using namespace std;
void goesWrong()
{
bool error1Detected = true;
bool error2Detected = false;
if (error1Detected)
{
throw bad_alloc();
}
if (error2Detected)
{
throw exception();
}
}
int main()
{
try
{
goesWrong();
}
catch (exception &e)
{
cout << "Catching exception: " << e.what() << endl;
}
catch (bad_alloc &e)
{
cout << "Catching bad_alloc: " << e.what() << endl;
}
return 0;
}
You have to put your exceptions in reverse order, regarding their inheritance relationship. std::exception is the parent class of std::bad_alloc, that is why it is found before in the catch list. So you have to transform your code to be:
try {
goesWrong();
}
catch (bad_alloc &e)
{
cout << "Catching bad_alloc: " << e.what() << endl;
}
catch (exception &e)
{
cout << "Catching exception: " << e.what() << endl;
}
You're not limited to catch objects: you can throw integers, chars... whatever. In that case, catch(...) is the only secure way to catch them all.
That said, using objects from the standard class library is the advised way to do it. And in this case, since std::exception is the base class for all (standard) exceptions, it will catch all possible exceptions thrown.
You can create your own exception classes deriving them from std::exception, or from std::runtime_error, for example, my personal choice.
Hope this helps.
In C++, the order in which exception handlers are listed is taken into account when matching handlers to exceptions. The first handler which can handle the exception will be called, even if there is a better match further down the list. This is different from Java or C#, where only the best match will be called (and the compiler forces you to put it at the top of the list).
As the exception is passed by reference, polymorphism applies; this means that a subclass can be passed to a handler that expects its parent class. Since std::bad_alloc is a subclass of std::exception, it will be handled by the first catch block.
To get the behaviour you expected, put the catch blocks the other way round:
catch (bad_alloc &e)
{
cout << "Catching bad_alloc: " << e.what() << endl;
}
catch (exception &e)
{
cout << "Catching exception: " << e.what() << endl;
}
This way round, std::bad_alloc will match the first handler, while std::exception and all its other subclasses will match the second.
After a successful catch , does the function that caught the exception exit ?
For example :
foo()
{
try
{
valid(name, name1, name2, name3, name3);
}
catch (int error)
{
std::cout << "Error number :" << error << std::endl;
}
std::cout << "Valid names" << std::endl;
}
If I catch an exception , will "Valid names" be printed ?
A caught exception will continue after the catch block provided you have not exited in the catch block once the commands in the catch block are finished.
The answer to your question is yes.