C++ Basics: Transport Worker Exceptions through a Future
Catch a worker failure, store it in a promise, and handle it where the future is consumed.
Prepare a scratch directory
Tested on Linux x86_64 with GCC 14.2.0. Install a C++20-capable GCC compiler and its standard-library development files, with g++ on PATH. You also need Bash, a text editor, and mktemp, rm, and rmdir. No third-party library is required.
Keep the same terminal open. Stop if setup fails before saving the source:
start_directory="$PWD"
work_directory="$(mktemp -d)" || exit 1
cd -- "$work_directory" || exit 1Run the example
Save this as promise-future-exception-demo.cpp:
#include <exception>
#include <future>
#include <iostream>
#include <stdexcept>
#include <thread>
int main()
{
std::promise<int> result_promise;
auto result_future = result_promise.get_future();
std::jthread worker([&] {
try {
throw std::runtime_error("invalid job input");
} catch (...) {
result_promise.set_exception(std::current_exception());
}
});
try {
const int result = result_future.get();
std::cout << "Result: " << result << '\n';
} catch (const std::runtime_error& error) {
std::cout << "Worker failed: " << error.what() << '\n';
}
worker.join();
}Compile and run it. The second command runs only after compilation succeeds:
g++ -std=c++20 -Wall -Wextra -Werror -pedantic -pthread promise-future-exception-demo.cpp -o promise-future-exception-demo &&
./promise-future-exception-demoObserved output:
Worker failed: invalid job inputRead the result
The exception does not escape the thread entry function. current_exception captures the active exception, and set_exception makes the shared state ready. The future get waits if necessary and rethrows that exception in main, where it is caught and printed.
Apply it carefully
An exception escaping a thread entry function terminates the program; it is not automatically forwarded to the joining thread. Fulfill a promise exactly once with either a value or an exception. In this example only one worker owns that responsibility. A regular future consumes its result on get, so do not call get twice.
The C++ working draft documents the relevant rules. This example uses C++20 facilities; the current draft also contains newer additions.
Clean up
Remove only the source and executable created here, then remove the empty practice directory:
rm -- promise-future-exception-demo.cpp promise-future-exception-demo
cd -- "$start_directory" || exit 1
rmdir -- "$work_directory"
unset start_directory work_directoryIf the directory is not empty, inspect its remaining contents before removing anything else.
Acknowledgements
This post was created with assistance from AI.