I am trying to understand how the exceptions can be propagated between different functions and returned to main function in C++. I have a small setup:
main.cpp:
int run () {
.
.
try {
testException(file);
} catch (const std::exception &e) {
std::cerr << "Error: " << e.what() << std::endl;
}
return 0;
}
test.cpp
std::exception_ptr g_exceptionPtr = nullptr;
void testThread() {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
.
.
}
void testException(const std::string &file) {
TestCSV csv(file);
try {
std::thread input(testThread);
csv.writeToCSV(file, a, b, c);
input.join();
} catch (const std::runtime_error &e) {
g_exceptionPtr = std::current_exception();
std::rethrow_exception(g_exceptionPtr);
}
}
In test_csv.cpp
:
TestCSV::writeToCSV(const std::string &file, const std::string &a, const std::string &b, const std::string &c) {
.
.
std::ofstream outFile(file);
if (!outFile.is_open()) {
throw std::runtime_error("Unable to open file for writing.");
}
}
Now I want to propagate the error from writeToCSV
function and handle it in main.cpp
. But, currently, this exception is caught in test.cpp
but is not re-thrown to main.cpp
.
What is the issue and how can I resolve it?
P.S: Above code is just an example and please let me know if any info is missing
You are calling writeToCSV()
in the context of the same thread that is calling run()
, so any exception that writeToCSV()
throws and is not caught will already propagate to run()
as expected. You don't need to do anything extra for that. Simply don't catch
the exception at all, or if you do then just re-throw
it (not std::rethrow_exception()
). Your usage of std::thread
is irrelevant in this situation.
int run () {
...
try {
testException(file);
} catch (const std::exception &e) {
// this should catch whatever testException() throws...
std::cerr << "Error: " << e.what() << std::endl;
}
return 0;
}
void testException(const std::string &file) {
TestCSV csv(file);
csv.writeToCSV(file, a, b, c);
// or:
try {
TestCSV csv(file);
csv.writeToCSV(file, a, b, c);
}
catch (const std::exception &e) {
...
throw;
}
}
On the other hand, if your question is about propagating exceptions across threads, then you should change your test to reflect that. Moving writeToCSV()
into the std::thread
would be a better test of std::current_exception
and std::rethrow_exception
.
std::current_exception
allows you to capture a caught exception so you can access it outside of the catch
. You can capture the exception in the original thread that threw it, move it to the desired thread, and then call std::rethrow_exception
in that thread. In this case, after you have join
'ed the std::thread
if the exception_ptr
was assigned, eg:
void testThread(const std::string &file, std::exception_ptr &exceptionPtr) {
try {
TestCSV csv(file);
csv.writeToCSV(...);
} catch (const std::exception &) {
exceptionPtr = std::current_exception();
}
}
void testException(const std::string &file) {
std::exception_ptr exceptionPtr = nullptr;
std::thread writer(testThread, file, std::ref(exceptionPtr));
...
writer.join();
if (exceptionPtr)
std::rethrow_exception(exceptionPtr);
}
int run () {
...
try {
testException(file);
} catch (const std::exception &e) {
// this should catch whatever testThread() throws...
std::cerr << "Error: " << e.what() << std::endl;
}
return 0;
}