17

函数能否extern "C"接受或返回特定于 C++ 的数据类型,例如引用、指向成员的指针或非 POD 类(按值)?我在 C++ 标准中找不到任何禁止这样做的内容。从逻辑上讲,我希望标准能说明这一点,因为 C ABI 不一定适合传递此类类型。

我想使用 C 链接的原因与 C 编译器无关。该函数只能从 C++ 代码中调用。我只想从我的动态库中导出未损坏的函数名称。

一个愚蠢的代码示例:

class Foo {
  public:
    virtual void doit() = 0;
};

class Bar : public Foo {
  public:
    void doit() { std::cout << "Bar" << std::endl; }
};

extern "C" Foo& getFoo() { static Bar bar; return bar; }

extern "C" Bar getBar() { return Bar(); }

这在 Linux 上使用 GCC 编译,并按预期工作。应该是标准的吗?

该问题是对该问题的评论中讨论的后续讨论。

更新我已经用 Comeau 编译器对此进行了测试,它没有抱怨。

4

4 回答 4

8

根据第 7.5.9 节链接规范(c++11 草案)http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf

“从 C++ 到其他语言定义的对象以及从其他语言到 C++ 定义的对象的链接是实现定义和语言相关的。只有在两种语言实现的对象布局策略足够相似的情况下,才能实现这种链接。”

于 2012-01-20T22:46:06.160 回答
4

5.2.2 函数调用[expr.call]

函数调用有两种:普通函数调用和成员函数62(9.3)调用。函数调用是一个后缀表达式,后跟括号,其中包含可能为空的逗号分隔的表达式列表,这些表达式构成函数的参数。对于普通函数调用,后缀表达式应该是一个指向函数的左值(在这种情况下,后缀表达式上会抑制函数到指针的标准转换(4.3)),或者它应该具有指向函数类型的指针. 通过函数类型具有与被调用函数定义的函数类型的语言链接不同的语言链接的表达式调用函数是未定义的 (7.5)

因此,如果您的函数在调用点的类型(即您正在调用的函数的类型)与定义点的函数类型不同,则结果是未定义的。

7.5 联动规范(第 1 段)[dcl.link]

所有函数类型、具有外部链接的函数名称和具有外部链接的变量名称都具有语言链接。

从这里我们看到语言链接是函数类型的一部分。所以调用站点和调用实现都必须具有完全相同的语言链接。

7.5 联动规范(第 9 段)[dcl.link]

从 C++ 到用其他语言定义的对象以及从其他语言到用 C++ 定义的对象的链接是实现定义和语言相关的。只有在两种语言实现的对象布局策略足够相似的情况下,才能实现这种联动。

由于 C 不支持 C++ 类型,因此不能以有意义的方式跨接口传递任何 C++ 对象。该函数不能有 C 语言链接,也不能被传递 C++ 对象。因此,我们打破了上面定义的类型规则。

但是:我们不仅要考虑对象的布局,还要考虑对象是如何传递/返回的。值是在堆栈上传递还是通过寄存器传递,这都是由 ABI 定义的,并且由于 C/C++ 具有不同的 API,因此无法保证对象如何从一个传递到另一个,或者函数清理的期望是相同的。

于 2012-01-20T23:27:20.850 回答
1

我对此没有明确的答案,但从Wikipedia推断,我会说这不能保证有效。这篇文章(我没有标准的副本)说extern "C"规范了 mangling 和 ABI。C ABI 可能不指定非 C 类型,因此您处于未指定的行为中。(文章还引用了 C 和 C++ ABI 不同的情况,尽管这似乎不是问题。)

因此,尽管我认为它可能在几乎所有情况下都有效,但我怀疑您是否可以从标准中引用必须的章节和经文。看到拒绝编译它的编译器,我不会感到非常惊讶。

于 2012-01-20T22:15:19.353 回答
1

我只想从我的动态库中导出未损坏的函数名称。

您是否考虑过使用 .def 文件,它允许您以不同的名称导出修饰命名函数?这样你就可以享受重载的好处,同时给你的函数“友好”的名字。

EXPORTS
funcOfInt=?func@a@@AAEXH@Z
funcOfSomethingElse=?func@a@@XYZ@W
于 2012-01-20T23:42:04.010 回答