C++ 在变量中存储异常的正确方法

C++ 在变量中存储异常的正确方法,c++,exception,C++,Exception,我有一个API,它内部有一些错误报告的例外情况。基本结构是它有一个根异常对象,该对象继承自std::exception,然后它将抛出该对象的一些子类 因为捕获在一个库或线程中抛出的异常并在另一个库或线程中捕获它可能会导致未定义的行为(至少Qt会抱怨它,并且在许多上下文中不允许它)。我希望将库调用封装在函数中,这些函数将返回状态代码,如果发生异常,则返回异常对象的副本 存储异常(具有多态行为)以供以后使用的最佳方法是什么?我相信c++0x future API会使用类似的东西。那么,最好的方法是什

我有一个API,它内部有一些错误报告的例外情况。基本结构是它有一个根异常对象,该对象继承自
std::exception
,然后它将抛出该对象的一些子类

因为捕获在一个库或线程中抛出的异常并在另一个库或线程中捕获它可能会导致未定义的行为(至少Qt会抱怨它,并且在许多上下文中不允许它)。我希望将库调用封装在函数中,这些函数将返回状态代码,如果发生异常,则返回异常对象的副本

存储异常(具有多态行为)以供以后使用的最佳方法是什么?我相信c++0x future API会使用类似的东西。那么,最好的方法是什么

我能想到的最好方法是在每个异常类中都有一个
clone()
方法,该方法将返回指向相同类型异常的指针。但这不是很一般,根本不处理标准异常

有什么想法吗

编辑:似乎c++0x将具有。它被描述为“图书馆魔法”。这是否意味着is不需要c++0x的任何语言特性?如果没有,是否有与c++03兼容的实现

编辑:看起来boost有一个。对于任何非
boost::copy\u异常的答案,我都会保留该问题


编辑:解决j_random_hacker对异常的根本原因是内存不足错误的担忧。对于这个特定的库和一组异常,情况并非如此。从根异常对象派生的所有异常表示由无效用户输入引起的不同类型的分析错误。与内存相关的异常只会导致抛出一个单独处理的
std::bad_alloc

您拥有我认为最好的、唯一的答案。您不能保留对原始异常的引用,因为它将离开作用域。您只需复制它,唯一通用的方法是使用类似clone()的原型函数


对不起。

你可以扔任何东西,包括指针。你可以一直这样做:

throw new MyException(args);
然后在异常处理程序中存储捕获的指针,该指针将是完全多态的(下面假设
MyException
派生自
std::exception
):

这样做时,只需小心内存泄漏,这就是通常使用按值抛出和按引用捕获的原因


更多关于指针捕获的信息:

捕获一个库中抛出的异常并在另一个库中捕获它会导致未定义的行为的原因是这些库可以与不同的运行库链接。如果从函数返回异常而不是抛出异常,则无法避免该问题。

我的实用程序库有一个
AnyException
类,基本上与没有强制转换支持的
boost::any
类相同。相反,它有一个
Throw()
成员,用于抛出存储的原始对象

struct AnyException {
  template<typename E>
  AnyException(const E& e) 
    : instance(new Exception<E>(e))
  { }

  void Throw() const {
    instance->Throw();
  }

private:
  struct ExceptionBase {
    virtual void Throw() const =0;
    virtual ~ExceptionBase() { }
  };

  template<typename E>
  struct Exception : ExceptionBase {
    Exception(const E& e)
      : instance(e)
    { }

    void Throw() const {
      throw std::move(instance);
    }

  private:
    E instance;
  };
  ExceptionBase* instance;
};

我还没有真正测试过第二点。。。我可能遗漏了一些显而易见的东西,这些东西会阻止它工作。

从C++11开始,这可以使用std::exception\u ptr来完成

(我在使std::thread可中断的类中使用它,前提是底层线程实现是POSIX线程。为了处理可能在用户代码中抛出的异常(如果在实现的某个关键部分抛出,则会导致问题),我使用std::exception_ptr存储异常,然后抛出它稍后,在关键部分完成后。)

要存储异常,需要捕获它并将其存储在ptr变量中

std::exception_ptr eptr;
try {
    ... do whatever ...
} catch (...) {
    eptr = std::current_exception();
}
然后,您可以将eptr传递到任何您喜欢的地方,甚至传递到其他线程中(根据文档,我自己还没有尝试过)

if (eptr) {
    std::rethrow_exception(eptr);
}
如果要检查异常,只需捕获它即可

try {
    if (eptr) {
        std::rethrow_exception(eptr);
    }
} catch (const std::exception& e) {
    ... examine e ...
} catch (...) {
    ... handle any non-standard exceptions ...
}

我相信你是对的,我认为Qt的问题更多的是关于线程之间的传递(我也在我的问题中添加了这一微妙之处)。在我看来,此解决方案的问题在于,您可能会由于内存不足的根本原因而引发异常,在这种情况下,
new
将失败。链接中提到了这一点,但从理论上讲,您可以通过预分配或静态内存不足异常来解决此问题这种情况。但是你需要确保你不会像删除其他异常那样删除它。这是可以做到的,但你必须小心很多这样的陷阱。我们能不能检查一下异常是否表示内存不足,然后简单地设置一个int/boolean来表示这一点?+1:对于我的特殊用途,这不是吗一个坏主意。类似于克隆方法,但在抛出时执行。但请注意,异常的根本原因可能是内存不足,在这种情况下,
clone()
如果尝试调用
new
,则将失败。这可以通过重载
操作符new()来解决
对于要从静态缓冲区分配的每个异常类型,但也不一定很好。@j_random_hacker:好吧,因为我们越来越迂腐,所以你所谓的内存不足最好称为bad alloc,而不必是实际的内存不足。bad alloc的另一个原因是请求太多内存,在这种情况下不小的拨款将succeed@sbk考虑到内存耗尽的可能性真的很迂腐吗?这里的简单答案是指出,用BADOL OLLC,你是一个“螺丝钉”,B)得到一个特殊的例外,你将无法克隆,因为它不是你自己的。
if (eptr) {
    std::rethrow_exception(eptr);
}
try {
    if (eptr) {
        std::rethrow_exception(eptr);
    }
} catch (const std::exception& e) {
    ... examine e ...
} catch (...) {
    ... handle any non-standard exceptions ...
}