以下源文件无法使用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一起使用,并且没有任何抱怨。
由于我将在下面进行的原因,我们需要将此文件编译为C ++ for MSVC(不是其他用户),但是将函数编译为C.本质上,我们需要一个普通的C编译器...除了大约六行。是否有可以添加的开关或属性或其他内容可以使其正常工作?
有问题的代码(虽然不是上面的代码;这只是一个简化的例子)是由代码生成器生成的。
作为其中的一部分,我们需要能够生成浮点数和无穷大作为常量(长篇故事),这意味着我们必须在C ++模式下使用MSVC进行编译才能实际执行此操作。我们只发现了一个有效的解决方案,仅在C ++模式下工作。
我们将代码包装在extern "C" {...}
中,因为我们想要控制修改和调用约定,以便我们可以与现有的C代码进行交互。 ...也因为我信任C ++编译器,因为我可以抛出一个小型的百货商店。我还尝试在<{1}}中包装 reinterpret_cast
行,但当然这不起作用。可惜。
我找到了一个潜在的解决方案,它需要重新排序声明,以便在函数foward decl之前完整的结构定义。但由于执行codegen的方式,这非常不方便,所以,如果可以的话,我真的喜欢避免走这条路。
答案 0 :(得分: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
答案 1 :(得分:2)
这是一个有趣的问题。正如您所说,将代码正确编译为C代码不会产生任何错误。当编译为C ++代码时,只有MSVC似乎遇到了麻烦。
由于其他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可能有一些余地(标准),但我不确定为什么MSVC在其他Windows时会出现问题编译器没有。如果有人知道细节(或者如果他们知道我在这里只是偏离基础),我会很感激。
这并不是对你有任何帮助 - 这只是我对理由的猜测。
在不了解您的codegen进程以及如何影响它的情况下,我不确定您可能拥有哪些不错的选项 - 可能是对生成的文件进行后处理以分割出的内容需要编译为C(或重新排列声明)。但我可以想象,这可能是一个噩梦,需要工作,尤其是维持。
答案 2 :(得分:0)
为什么你有
中的externextern struct Test make_Test(int x)
{
struct Test r;
r.x = x;
return r;
}
这不是外在的,你在那里定义它。