I am not able to understand how is it possible for std::async
to store any exception, not just something derived from std::exception
. I played around with the code below
#include <iostream>
#include <future>
#include <chrono>
void f()
{
std::cout << "\t\tIn f() we throw an exception" << std::endl;
throw 1; // throw an int
}
int main()
{
std::future<void> fut = std::async(std::launch::async, f);
std::cout << "Main thread sleeping 1s..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1)); // sleep one second
std::cout << "Main thread waking up" << std::endl;
try
{
fut.get();
}
catch(...)
{
std::cout << "We caught an exception!" << std::endl;
throw; // rethrow
}
}
I launch f()
asynchronously, then throw an int
inside f
. Magically, this int
is caught and stored by the future returned by std::async
. I understand that is possible to catch(...)
the exception in std::async
, but how can the latter store it without knowing the exception type? The exception is not derived from some base class (in this case one perhaps can "clone" it via some Base::clone
), but can be any exception. Can we somehow magically "deduce" the exception type?
To summarize, my question is:
How can we store an arbitrary exception inside an object then re-throw it at some later time, without knowing the exception type?
std::async
could be implemented on top ofstd::thread
andstd::packaged_task
.std::packaged_task
could be implemented (partly) on top ofstd::exception_ptr
and related function (except that thread-exit ready function).std::exception_ptr
and related functions cannot be written in C++.I'm not sure if this exactly answers your question, but this example might be helpful.
I compiled the following:
with the command
The gimple (gcc's intermediate output) is:
So it calls
__cxa_throw
with the address of the a symbol that represents a type. In this case the type is_ZTIi
, which is the mangled type of an integer.Types not available at compile time
The type symbols only need to be available at run time. In a dynamic library that is trying to hide as many symbols as it can, it needs to make sure that any exceptions that aren't caught and dealt with internally are available. For more info, see https://gcc.gnu.org/wiki/Visibility, particularly the section
Problems with C++ exceptions (please read!)
.It would be interesting to see how this worked between dynamic libraries compiled with different compilers that had different naming schemes.