【问题标题】:Throwing C++ exceptions outside static library?在静态库之外抛出 C++ 异常?
【发布时间】:2012-12-21 13:14:30
【问题描述】:

通常,异常不得传播模块边界,例如 Herb Sutters C++ 编码标准(第 62 项)中所述。当使用不同的编译器或仅使用编译器设置编译时,这可能会崩溃。

我可以理解这个问题,例如动态链接库。但我想知道它是否也适用于静态库。就上述规则而言,静态库是模块吗?如果库是使用其他编译器设置(例如对齐)编译的,如果从静态库中抛出异常并在应用程序中捕获,程序可能会崩溃?

【问题讨论】:

  • “作为一项规则”,这只是 Herbs 的个人观点,受 FUD 的影响,甚至更糟糕的经历是他过去面临的可怕的 dll 地狱和蹩脚的编译器场景。就像他设置一个“规则”一样,你应该永远只在你新的同一个模块中删除,这也只与他用一个非常糟糕的实现所带来的糟糕体验有关。如果您正在使用理智的实现,请放弃此规则。如果您必须使用损坏的实现,请不要将其作为规则,而是作为“常见的丑陋解决方法”。
  • @KitFisto 但这适用于所有对象,而不仅仅是异常 - 所以建议不要混合不兼容的编译器。 +1 等离子HH
  • @PlasmaHH 我经常听说在与new 相同的DLL 中有关delete 的限制,但它从来没有给我带来任何问题,无论是使用Microsoft 编译器还是使用g++。只要new 和delete 转发到malloc 和free,并且malloc 和free 在它们自己的DLL 中(所以你不会得到它们的不同副本),应该没有问题。另一方面,dynamic_cast 之类的东西可能无法跨 DLL 工作,即使它们的编译方式相同。当然,如果一个模块是用迭代器调试编译的,而另一个不是......
  • @JamesKanze:我不是 Windows 程序员,但是当您在一个使用 crt 版本的 dll 中新建/malloc 并在使用另一个版本的 dll 中释放/删除时,问题就来了crt 的版本,因为所有 crt 实例都有自己的“私有”堆
  • @JamesKanze try new std::string in a module 使用调试设置编译并在 Windows 上使用发布设置构建的模块中删除它。你不会走得太远......

标签: c++ exception static-libraries


【解决方案1】:

通常,静态库必须由相同的编译器和相同的编译器设置(大部分)编译才能与交付物(动态库或可执行文件)兼容。

然后,您可以在静态库的边界之外抛出异常,因为它与您的编译器生成的一组 .obj 文件没有太大区别。而且您显然可以在不同的 .obj 模块之间抛出异常。

编辑:

总结一下cmets:

  1. 只有在使用与编译库相同的编译器和编译器设置时,才能使用静态库。
  2. 您可以在使用相同编译器和编译器设置编译的模块之间引发异常。
  3. 从 1) 和 2) 可以看出,您可以从静态库中引发异常,因为如果您使用它,这意味着您使用的是相同的编译器和编译器设置,因此您可以引发异常。

【讨论】:

  • +1 表示“目标文件或多或少是静态库”。您实际上可以添加一个简短的评论有什么不同。
  • “显然可以在不同的 .obj 模块之间抛出异常”:如果 obj 由不同的编译器编译,你确定这也成立吗?
  • @KitFisto 当然不是。假设您不在编译器之间共享目标文件(或静态库)。事实上,目标文件和静态库的结构依赖于编译器,因此您无法使用 CodeGear 的 C++Builder 编译模块并将其传递给 Visual C++ 的链接器。此类模块可以通过动态库共享,其中二进制接口匹配(它们仅在一定程度上匹配,例如您不能直接共享类或任何需要名称修改的东西)。
  • @Alex 是的,当然,但是例如来自 VS 2008 的 AFAIK 库可以与来自 VS 2010 的代码链接。正如 Christopher 在他对 this question 的回答中所说的那样,内存中的表示并不相同。
  • @KitFisto 如果我们谈论的是两个特定的编译器(或编译器版本),它们可能兼容也可能不兼容,这完全取决于编译器供应商。正如您链接到的thread 所暗示的那样,Microsoft 不保证它们是兼容的。如果您只使用内部表示未更改的特定功能,它们可能兼容,但您将自行承担风险。
【解决方案2】:

Herb Sutters 的描述也适用于静态库:

C++ 异常处理没有普遍的二进制标准。 不允许异常在两段代码之间传播,除非 您控制用于构建两者的编译器和编译器选项 侧面;否则,模块可能不支持兼容 异常传播的实现。通常,这归结为 to:不要让异常跨模块/子系统边界传播。

【讨论】:

  • 你怎么知道他说“模块”时也谈到了静态库?我的问题是,如果静态库中的类在二进制级别上不兼容,编译器是否允许我链接到静态库。
  • 他提到二进制,剩下的就是我的理解
【解决方案3】:

这取决于 Herb 所说的“模块”是什么意思。问题不 只关注例外情况;他们可以使用 C++ 处理任何事情 界面。

异常交叉翻译肯定没有问题 源的单元边界编译为同一部分的一部分 零件。组件之间,如果它们都是相同的一部分 应用程序,并确保它们都是使用 相同的编译器,具有相同的编译器选项,它可能是安全的, 虽然在动态之间交叉时可能会出现问题 库,取决于库的加载方式。 (在 一般来说,这只是 Unix 系统上的问题,其中 动态加载的组件中符号的可见性是 由传递给动态加载器的选项控制。)如 一般规则:安排编译所有应用程序 使用相同的编译器和相同的编译器选项,并且您 在应用程序中应该没有真正的问题(尽管 您可能必须确保所有动态组件都是 显式加载,至少在 Unix 下)。之间 “应用程序”,您正在加载或被加载的地方 “外国”软件,Herb 的限制还远远不够。在 练习,您在应用程序之间交叉的界面 必须在 C 中定义。并且可能仍然存在限制, 取决于您的代码的加载方式以及其他动态 正在使用加载的组件。

静态链接将消除关于如何链接的问题 库已加载,但没有其他任何更改。

【讨论】:

  • 抱歉,我不明白您对这个问题的回答是什么:静态库是 Herb Sutters 规则意义上的模块吗?是还是不是?
  • 我不知道 Herb Sutter 的规则,但在这种情况下,模块是单独生成的:它可以是静态库。然而,我的全部观点是,库是静态的还是动态的不是问题。问题是它是否被编译为您的应用程序的一部分,使用相同的编译器和相同的选项。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-02
  • 1970-01-01
  • 2010-10-08
  • 1970-01-01
  • 2016-02-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多