3

以下源文件将无法使用 MSVC 编译器 (v15.00.30729.01) 进行编译:

/* stest.c */
#ifdef __cplusplus
extern "C" {
#endif

struct Test;

/* NB: This may be extern when imported by another module. */
struct Test make_Test(int x);

struct Test { int x; };

struct Test make_Test(int x)
{
    struct Test r;
    r.x = x;
    return r;
}

#ifdef __cplusplus
}
#endif

编译cl /c /Tpstest.c产生以下错误:

stest.c(8) : error C2526: 'make_Test' : C linkage function cannot return C++ class 'Test'
        stest.c(6) : see declaration of 'Test'

不编译/Tp(告诉cl将文件视为 C++)工作正常。该文件还可以在 C 和 C++ 模式下在 DigitalMars C 和 GCC(来自 mingw)中正常编译。我也-ansi -pedantic -Wall与 GCC 一起使用,它没有任何抱怨。

由于我将在下面讨论的原因,我们需要将此文件编译为 MSVC 的 C++(而不是其他文件),但函数被编译为 C。本质上,我们想要一个普通的 C 编译器......除了大约六行. 是否有一个开关或属性或我可以添加的东西可以让它工作?


有问题的代码(虽然不是上面的;这只是一个简化的例子)是由代码生成器生成的。

作为其中的一部分,我们需要能够生成浮点 nan 和无穷大作为常量(长话短说),这意味着我们必须在 C++ 模式下使用 MSVC 编译才能真正做到这一点。我们只找到了一种可行的解决方案,而且它只适用于 C++ 模式。

我们包装代码是extern "C" {...}因为我们想要控制修改和调用约定,以便我们可以与现有的 C 代码交互。...也因为我信任 C++ 编译器,就我可以扔一个小百货商店。我也尝试将行包含reinterpret_cast在 中extern "C++" {...},但这当然行不通。遗憾。

我发现了一个潜在的解决方案,它需要重新排序声明,以便完整的结构定义出现函数 foward decl 之前,但是由于 codegen 的执行方式,这非常不方便,所以我真的很想避免不得不如果可以的话,沿着那条路走下去。

4

3 回答 3

3

这有点令人费解的错误消息,但调用者需要知道结构的大小才能进行调用。它需要在堆栈上为返回值预留空间。或者如果结构足够小,则期望寄存器中的返回值。

您必须在标题中声明结构。当你这样做时,错误消失了:

#ifdef __cplusplus
extern "C" {
#endif

struct Test { int x; };
struct Test make_Test(int x);

struct Test make_Test(int x)
{
    struct Test r;
    r.x = x;
    return r;
}

#ifdef __cplusplus
}
#endif
于 2010-04-27T05:28:14.257 回答
2

这是个有趣的问题。正如您所说,将代码编译为 C 代码正确不会产生错误。只有 MSVC 在编译为 C++ 代码时似乎有问题。

由于其他 C++ 编译器的代码没有问题,这可能是 MSVC 中的一个错误,但我可以看到 MSVC 可能有这个错误的基本原理。当 C++ 编译器运行时:

struct Test;

这是一个不完整的声明struct Test- 编译器不知道完整的定义是否struct Test将包含 C++ 特定项(虚拟函数、继承等)。请注意,extern "C"块中的类型仍然可以使用所有 C++ 工具;语言链接规范仅适用于“由声明引入的所有函数声明符、函数名和变量名的extern "C"函数类型”(7.5/4“链接规范”)。

所以我可以看到,当 MSVC 的 C++ 编译器遇到一个extern "C"返回不完整类型的函数时,它可能会决定此时需要返回一个错误,以防该类型不是纯 C 风格的 POD 类型。

C++ 标准确实说(7.5/9“链接规范”):

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

因此,如果 MSVC 有理由不允许extern "C"函数返回非 POD 对象,它可能会有一些余地(标准方面),但我不确定为什么 MSVC 会在其他 Windows 编译器没有问题的情况下出现问题。如果有人知道详细信息(或者如果他们知道我在这里只是单纯的偏离基地),我会很感激。

并不是说这对您有任何帮助-这只是我对基本原理的猜测。

在不了解您的代码生成过程以及您可能如何影响它的更多信息的情况下,我不确定您可能有哪些不错的选择 - 也许是对生成的文件进行后处理以分离出需要编译为 C的内容(或重新排列声明)。但我可以想象,这可能是一场噩梦,开始工作,尤其是维护。

于 2010-04-27T06:32:30.547 回答
0

为什么你有外部

extern struct Test make_Test(int x)
{
    struct Test r;
    r.x = x;
    return r;
}

它不是外部的,您正在那里定义它。

于 2010-04-27T05:03:04.300 回答