std::async "store" 如何任意异常?

How does std::async "store" an arbitrary exception?

我无法理解 std::async 怎么可能存储任何异常,而不仅仅是从 std::exception 派生的东西。我玩弄了下面的代码

#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
    }
}

我异步启动 f(),然后在 f 中抛出一个 int。神奇的是,这个 intstd::async 返回的未来捕获并存储。我知道 catch(...) std::async 中的异常是可能的,但是后者如何在不知道异常类型的情况下存储它?异常不是从某些基础 class 派生的(在这种情况下,也许可以通过某些 Base::clone "clone" 它),但可以是任何异常。我们能以某种方式神奇地 "deduce" 异常类型吗?

总而言之,我的问题是:

我们如何在对象中存储任意异常,然后在不知道异常类型的情况下稍后重新抛出它?

我不确定这是否能准确回答您的问题,但这个示例可能会有所帮助。

我整理如下:

int main()
{
    throw 1;
}

使用命令

g++ -fdump-tree-gimple -std=c++11 main.cpp -o main

gimple(gcc 的中间输出)是:

int main() ()
{
  void * D.1970;
  int D.1974;

  D.1970 = __cxa_allocate_exception (4);
  try
    {
      MEM[(int *)D.1970] = 1;
    }
  catch
    {
      __cxa_free_exception (D.1970);
    }
  __cxa_throw (D.1970, &_ZTIi, 0B);
  D.1974 = 0;
  return D.1974;
}

所以它调用 __cxa_throw 与代表一个类型的符号的地址。在这种情况下,类型是 _ZTIi,这是整数的 mangled 类型。

类型在编译时不可用

类型符号只需要在 运行 时间可用。在试图隐藏尽可能多的符号的动态库中,它需要确保任何未在内部捕获和处理的异常都是可用的。有关详细信息,请参阅 https://gcc.gnu.org/wiki/Visibility,尤其是 Problems with C++ exceptions (please read!).

部分

看看这在使用具有不同命名方案的不同编译器编译的动态库之间如何工作会很有趣。

std::async 可以在 std::threadstd::packaged_task.

之上实现

std::packaged_task 可以(部分)在 std::exception_ptr 和相关函数(线程退出就绪函数除外)之上实现。

std::exception_ptr及相关函数不能用C++编写。