我有使用C-ABI接口以X语言编写的dll。 我想从我的c ++程序中使用此C-ABI。
我在main.cpp中写道:
extern "C" {
struct Foo {
const char * const data;
unsigned len;
};
struct Foo f(void);
}
int main()
{
}
并从编译器得到警告(Visual c ++ / 15.7.5 / windows 7 / 32bit):
(7):警告C4190:'f'已指定C链接,但返回 与C不兼容的UDT'Foo'
(7):注意:请参见“ Foo”的声明
在这里链接:https://godbolt.org/g/ztx1kf
我读了Error in C++ code linkage: warning C4190: type has C-linkage specified, but returns UDT which is incompatible with C,但就我而言,我没有“ c ++代码” 完全在我的POD结构中。
如何使编译器相信它不是 C ++ struct Foo
,而是 C struct Foo
?
我尝试将其移动到单独的头文件(.h),但这没什么改变。
如果我将const char * const data
替换为const char *
警告消失,
我还不了解的内容,但我不想更改struct的定义。
答案 0 :(得分:2)
extern "C"
不允许您在C ++程序中嵌入任意C代码。它做的唯一一件事就是抑制C ++特定的名称修饰和调用声明的约定,以便可以用其他方式调用用extern "C"
(或等效地在extern "C"
块中声明)的函数。语言。 (变量声明也可以是extern "C"
,尽管在正常使用中这无效。)类型定义不受此类限制的影响。
在您的情况下,问题是声明const char * const data
,它是指向const char*
的const指针。 C不支持const struct成员(如果没有成员初始值设定项列表,它实际上没有任何意义),因此Foo
不是合法的C struct定义。删除第二个const
,一切都会好起来。
没有办法不更改结构定义,因为您的C编译器不会接受该结构。如果您需要保持与C ++结构的兼容性,那么唯一的选择就是定义两个结构,然后将数据复制过来。
答案 1 :(得分:0)
C, of course, can have const struct member
某种解决方案(截至今天):如果使用MSVC,只需禁用警告4190。
答案 2 :(得分:0)
x64 calling convention docs解释说,如果UDT足够小并符合某些条件,则它们将在eax
中返回:
要在RAX中按值返回用户定义的类型,其长度必须为1、2、4、8、16、32或64位。它还必须没有用户定义的构造函数,析构函数或复制分配运算符;没有私有或受保护的非静态数据成员;没有引用类型的非静态数据成员;没有基类;没有虚拟功能;并且没有不满足这些要求的数据成员。
尽管Foo
是 StandardLayout 类型(因此我们希望它可以工作),但const
非静态数据成员会删除副本分配运算符,即使它说的是“用户定义”,这也可能是它们的意思。顺便说一下,这使其成为非 TrivialType ,因此不是C ++ 03意义上的POD。
它也超过了最大大小,但是即使我们删除了len
,上面的内容仍然会阻止它成为“ POD”。
类似地,x86 calling convention docs解释了类似的内容:
[...]除了8字节结构(在EDX:EAX寄存器对中返回)之外。较大的结构作为指向隐藏的返回结构的指针在EAX寄存器中返回。 [...]不是POD的结构将不会在寄存器中返回。
例如,如下所示的函数:
struct Foo
{
const uint32_t x;
};
Foo f(void)
{
Foo foo = { 12345 };
return foo;
}
在x86 / x64 C ++模式下编译时,Foo
被认为是非POD,因此eax
/ rax
包含了对象的地址,正如文档所指引的那样。
但是,当在x86 / x64 C模式下进行编译时,Foo
被视为POD(我们正在编译C),因此您将直接在uint32_t
中获得eax
的值。 / p>
因此,即使我们将语言链接设置为C,也无法从C调用f
,这就是警告出现的原因。