【问题标题】:Can C++ functions marked as Extern "C" throw?标记为 Extern "C" 的 C++ 函数可以抛出吗?
【发布时间】:2013-04-06 00:31:09
【问题描述】:

我有想要使用extern "C" 声明的 C++ 函数,即使它们只在 C++ 代码中调用。是的,我知道这很奇怪,但为了保持一致性,我想这样做,因为我们混合了 C 和 C++ 声明。我只是想确保将 C++ 函数声明为 extern "C" 不会影响抛出的行为。

看起来像这样:

extern "C" void foo() {throw exception;}

int bar()
{
    try
    {
        foo();
    } catch (exception e) { return 1; }
}

【问题讨论】:

  • 答案断言,如果异常从 C++ 代码抛出到实际的 C 代码(不知道如何处理异常),则您正在调用未定义的行为。我认为这是正确的,但是您是在问从 C++ 还是从 C 调用 extern "C" 函数?如果您从 C++ 调用它们(而不是从 C 调用),那么不可避免的问题是“如果函数从未从另一种语言调用,为什么是函数 extern "C"?”,但我认为只需使用 @987654327 调用 C++ 函数来自 C++ 的 @ 表示没有跨越语言边界,因此没有未定义的行为。
  • 感谢所有为回答此问题做出贡献的人。如果我理解正确,我的问题的最终答案是:extern "C" 不会改变处理异常的方式。但是,抛出未被捕获并跨越语言边界的异常具有未定义的行为。
  • q:"extern "C" 不会改变处理异常的方式"——我不会那样阅读答案。具体/EHsc 另有说法。
  • @JonathanLeffler 每个编译器版本的 cpp 都是另一种语言。如果您有一个 msvc 静态库,除非它是使用 extern "C" 的 C 库,否则它将无法与 GCC 一起使用。每个编译器都可以根据他们的感觉来修改名称,因此当引用实际存在时,您最终会得到未定义的引用。我认为并且看不到任何不应该标准化名称修改的理由;但这是仅将 extern "C" 用于 C++ 的常见原因。例如,我必须创建 dll 以在 gcc 中使用来自 msvc 的代码,因为该代码需要 msvc 特定的东西。然后我必须使用 extern "C" 来抑制名称修改。

标签: c++ extern-c


【解决方案1】:

“标记为外部“C”的 C++ 函数可以抛出吗?”

是的,无论是语言还是编译器都不会阻止您这样做。

,从某种意义上说,如果你抛出,这将是一个未定义的行为,因为 C++ 异常跨越了语言边界。

在实践中:不要这样做。捕获异常并将其转换为错误代码,或其他语言可以理解的方式。

所以底线是:不要从标记为 extern "C" 的函数中抛出异常。

【讨论】:

  • 当它是undefined behavior 时,你不能说YES。当您调用 undefined behavior 时,您的代码基本上已损坏。
  • @LokiAstari,有人问他们能不能扔,答案是肯定的。如果直接从具有 c++ 链接的函数调用 extern "C" 函数,则没有未定义的行为。从 C 调用的相同函数,未定义的行为。不一定因为链接而存在语言交叉,但它肯定是一种气味。
  • @LokiAstari,我必须对你的说法表示异议,“你不能说YES,而它是undefined behaviour。” “未定义的行为”对于它是否可以或应该发生并不重要。它仅指定结果未定义。它没有指定它永远不会发生。跨越边界的异常总是发生。 C++->C; C++->Java; C++->Python。不同之处在于,大多数语言翻译库都提供了一种工具来捕获、翻译和重新抛出目标语言中的异常。
  • 您的第一个假设是错误的:If an extern "C" function was invoked directly from a function with c++ linkage。如果它抛出未定义的行为。 C 函数的 ABI 不包含允许堆栈展开所需的信息。
  • 我必须同意@LokiAstari。在我的代码中,我有 C++ 调用一个引发异常的 extern c 函数,并且它没有被捕获(即使使用 catch(...))。编译器是英特尔编译器 (icpc)。所有代码都是用 C++ 编写的,没有 C。链接差异似乎足以破坏堆栈展开。
【解决方案2】:

对于GCC the answer seems inconclusive

MSVC documentation,不过在这个主题上比较清楚:

  • /EHa/EHs ... 告诉编译器假设声明为 extern "C" 的函数可能会抛出异常。
  • /EHsc ... 告诉编译器假设声明为 extern "C" 的函数永远不会抛出 C++ 异常

所以对于 Visual-C++,是否获得定义的行为取决于编译器选项。

【讨论】:

  • +1,但您错过了其中一个链接 cmets 中的 gcc 答案:-fexceptions。来自 gcc 文档:“...在编译需要与用 C++ 编写的异常处理程序正确互操作的 C 代码时,您可能需要启用此选项”。
【解决方案3】:

它会编译,但从标记为具有 C 链接的函数抛出是未定义的行为。 C 没有异常,因此通常您应该只返回一个错误代码和/或提供一个返回有关最后一个错误的信息的函数。

#include <exception>
extern "C" void foo() {throw std::exception();}

编译良好​​p>

【讨论】:

    【解决方案4】:

    这里是您问题的答案:http://yosefk.com/c++fqa/mixing.html#fqa-32.6

    基本上你无法捕捉到它。 (但为什么你不只是编译并尝试呢?:))

    【讨论】:

    • “编译并尝试”对于具有未定义行为的语言来说绝不是一个好主意。
    • 但它可能会导致你对代码的行为做出错误的假设,而实际上你根本不应该假设任何东西,因为它是未定义的,编译器可以用它做任何事情。
    • @PiotrJaszkowski:它可能不会摧毁宇宙,但龙会喷出你的鼻子。尝试undefined behavior 是没有意义的,因为测试没有任何意义。
    猜你喜欢
    • 2017-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    • 2012-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多