std :: async如何“存储”任意异常?

时间:2015-02-23 22:52:27

标签: c++ exception c++11 asynchronous future

我无法理解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(),然后在int内抛出f。奇怪的是,这个intstd::async返回的未来捕获并存储。我理解catch(...)中的std::async异常是可能的,但后者如何在不知道异常类型的情况下存储它?异常不是从某个基类派生的(在这种情况下,可能可以通过某些Base::clone“克隆”它),但可以是任何异常。我们能以某种方式神奇地“推断”异常类型吗?

总结一下,我的问题是:

如何在对象内部存储任意异常,然后在以后重新抛出它,而不知道异常类型?

2 个答案:

答案 0 :(得分:4)

std::async可以在std::threadstd::packaged_task之上实施。

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

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

答案 1 :(得分:3)

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

我编译了以下内容:

int main()
{
    throw 1;
}

使用命令

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

gimple(gcc&#39; s中间输出)是:

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!)部分。

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