Modern C++ has many features to aid multi-thread programming that allow applications to be faster and more responsive. The C++11 standard offers the possibility of moving an exception from one thread to another. This type of movement is called propagating exceptions, exception propagation; also known as rethrow exception in multi-threading. To do that, some modifications have been made to the <exception>
header in C++ and there is a nullable pointer-like type std::exception_ptr
. In this post, we explain std::exception_ptr
and how to use the rethrow exception method in modern C++.
What are propagating exceptions (exception propagation) in modern C++?
The C++11 standard offers a nullable pointer-like type std::exception_ptr
. The exception_ptr
is used to refer to an exception and manages an exception object that has been thrown and captured with std::current_exception()
. Here is how we can use both:
1 2 3 |
std::exception_ptr e = std::current_exception(); |
In modern C++, a concurrency support library is designed to solve problems in multi-thread operations. This library includes built-in support for threads (std::thread
), atomic operations (std::atomic
), mutual exclusion (std::mutex
), condition variables (std::condition_variable
), and many other features. In addition to these useful features, std::exception_ptr
is useful in exception handling between threads. They may be passed to another function, possibly to another thread function, so that the exception can be rethrown and handled in another thread with a catch clause.
According to open-std, “An exception_ptr
that refers to the currently handled exception or a copy of the currently handled exception, or a null exception_ptr
if no exception is being handled. If the function needs to allocate memory and the attempt fails, it returns an exception_ptr
that refers to an instance of bad_alloc. It is unspecified whether the return values of two successive calls to current_exception
refer to the same exception.”
The exception_ptr
can be DefaultConstructible
, CopyConstructible
, Assignable
and EqualityComparable
. By default, it is constructed as a null pointer, doesn’t point to an exception, and operations from exception_ptr
do not throw.
Is there a simple example of how to use propagating exceptions in modern C++?
Here is a simple example that outputs exceptions coming from a thread.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
std::mutex m; void throw_exception( std::exception_ptr e) { try { if (e) std::rethrow_exception(e); } catch(const std::exception& e) { m.lock(); std::cout << "Exception from " << t << ":" << e.what() << std::endl; m.unlock(); } }; |
As you see, here we used mutex m
variable to lock()
and unlock()
printing out exceptions. Thus, we can see exceptions in order.
Is there a full example of how to use propagating exceptions in modern C++?
Here is a very good example where we use lambdas and threads to throw exceptions and to define a string s
which is empty. The mylambda
construct is trying to reach the 5th character and that will throw an exception. We simulate 2 threads throwing same exceptions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
#include <iostream> #include <string> #include <exception> #include <stdexcept> #include <thread> #include <mutex> std::mutex m; auto lambda = []( std::exception_ptr e, int t ) { try { if (e) std::rethrow_exception(e); } catch(const std::exception& e) { m.lock(); std::cout << "Exception from " << t << ":" << e.what() << std::endl; m.unlock(); } }; auto mylambda = [](int t) { std::string s; try { char c=s.at(5); // exception : invalid string position } catch(...) { lambda( std::current_exception(), t ); } }; int main() { std::exception_ptr e = std::current_exception(); std::thread t3( mylambda, 3); // assume third thread is running then throwing exception std::thread t5( mylambda, 5); // assume fifth thread is running then throwing exception t3.join(); t5.join(); system("pause"); } |
For more information about the Propagating Exceptions feature, please see Propagating Exceptions Proposal document.
Exceptions are one of the most important parts of error handling methods. If you want to learn more about exceptions here are more detailed documents.
- C++ Exception Optimizations. An experiment https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1947r0.pdf
- C++ exceptions and alternatives by Bjarne Stroustrup https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1676r0.pdf
- Handling Concurrent Exceptions with Executors https://open-std.org/JTC1/SC22/WG21/docs/papers/2018/p0797r1.pdf
- Exception Propagation across Threads https://www.open-std.org/JTC1/sc22/wg21/docs/papers/2006/n2107.html
- Removing Deprecated Exception Specifications from C++17 https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0003r1.html
C++ Builder is the easiest and fastest C and C++ IDE for building simple or professional applications on the Windows, MacOS, iOS & Android operating systems. It is also easy for beginners to learn with its wide range of samples, tutorials, help files, and LSP support for code. RAD Studio’s C++ Builder version comes with the award-winning VCL framework for high-performance native Windows apps and the powerful FireMonkey (FMX) framework for cross-platform UIs.
There is a free C++ Builder Community Edition for students, beginners, and startups; it can be downloaded from here. For professional developers, there are Professional, Architect, or Enterprise version.