来自 MSVC extern “C"的故事 [英] Tales from the MSVC extern "C"

查看:33
本文介绍了来自 MSVC extern “C"的故事的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

[这个问题有一个重复的我可以找到,但该答案完全错误,请参阅下面的 C 代码.]

[this question has one SO duplicate I can find, but that answer is plain wrong, see the C code below.]

我了解 extern "C" 不会在您的 C++ 中间生成 C 代码.它只是一个链接指令.

I understand extern "C" does not produce C code in the middle of your C++. It is just a linkage directive.

我有一些 extern "C" 故事要讲,但今天有一个困扰我.这是一个完全最新的VS2019,代码如下:

I have a few of these extern "C" tales to tell, but here is one that bothers me today. This is a completely up-to-date VS2019, and this is the code:

#ifdef __cplusplus
extern "C" {
 #endif

// NOTE: in here it is still C++ code, 
// extern "C" is a linkage directive

typedef struct Test Test;

struct Test { 

    const  /* remove this const and MSVC makes no warning */
        uint32_t x; 
} ;

/*
MSVC throws warning C4190:  'make_Test' has C-linkage specified, 
   but returns UDT 'Test' which is incompatible with C
:  see declaration of 'Test'
*/
inline constexpr Test make_Test(uint32_t x)
{
    return Test{ x };
}

#ifdef __cplusplus
  }
#endif

int main(const int argc, const char * argv[])
{
    constexpr auto test_{ make_Test(42) };
    return 42 ;
}

关于那个 const 的评论是我问题的要点.

That comment about that const is the gist of my question.

MSVC extern "C" 很大程度上(完全?)没有记录.因此,我无法判断我是否违反了这个无证区域的某些规则?许多人声称这是某种未完全实施"的 C11 吗?

MSVC extern "C" is largely (completely?) undocumented. Thus I am unable to tell if, I am breaking some rule in this undocumented zone? Many claim this is some kind of "not fully implemented" C11 in there?

AFAIK 具有 C11(或任何其他 C)结构成员类型的 const 非常好.好的老 G++ 不在乎:https://wandbox.org/permlink/7XfH2i21Yfnb7BDw当然.

AFAIK having that const for a C11 (or any other C) struct member type is quite OK. And good old G++ could not care less: https://wandbox.org/permlink/7XfH2i21Yfnb7BDw of course.

这只是 VS2019 中的一个错误,还是我犯了一个错误?

Is this just a bug in VS2019, or I made a bug?

更新

即使我将 make_Test 的实现移动到单独的 C 文件中,并将其显式编译为 C,此警告将保持不变.

Even if I move the implementation of make_Test into separate C file, and explicitly compile it as C, this Warning will stay the same.

关于那个答案",来自 之前的同一个问题.C 可以有 const struct 数据成员,当然,C 结构可以在创建时进行列表初始化.看下面的代码:

About that 'answer' from the same question from before. C can have const struct data members, and of course, C structs can be list initialized when made. See the code below:

// gcc prog.c -Wall -Wextra -std=gnu11 "-Wno-unused-parameter" "-Wno-unused-variable"

#include <stdlib.h>

typedef struct Test { const long x; } Test;

static struct Test make_Test(long x)
{
    struct Test  test_ = { x } ;
    return test_;
 }
 int main(const int argc, const char * argv[])
 {
  struct Test test_ = make_Test(42) ;
   return 42;
 }

推荐答案

x64 调用约定文档 解释了如果 UDT 足够小且符合某些条件,则在 eax 中返回:

The x64 calling convention docs explain that UDTs are returned in eax if they are small enough and fit some criteria:

要在 RAX 中按值返回用户定义的类型,它的长度必须为 1、2、4、8、16、32 或 64 位.它还必须没有用户定义的构造函数、析构函数或复制赋值运算符;没有私有或受保护的非静态数据成员;没有引用类型的非静态数据成员;没有基类;没有虚函数;并且没有不满足这些要求的数据成员.

To return a user-defined type by value in RAX, it must have a length of 1, 2, 4, 8, 16, 32, or 64 bits. It must also have no user-defined constructor, destructor, or copy assignment operator; no private or protected non-static data members; no non-static data members of reference type; no base classes; no virtual functions; and no data members that do not also meet these requirements.

虽然 TestStandardLayout 类型(因此我们希望它可以工作),const 非静态数据成员使得复制赋值运算符已删除,这可能就是它们的意思,即使它说用户定义".顺便说一下,这使它成为一个非 TrivialType,因此不是 C++03 意义上的 POD.

While Test is a StandardLayout type (and as such we would expect it to work), the const non-static data member makes the copy assignment operator deleted, which is probably what they mean, even if it says "user-defined". This makes it, by the way, a non TrivialType and therefore not a POD in the C++03 sense.

同样,x86 调用约定文档 解释了类似的内容:

Similarly, the x86 calling convention docs explain something similar:

非 POD 的结构不会在寄存器中返回.

Structures that are not PODs will not be returned in registers.

例如,像下面这样的函数:

For instance, a function like the following:

Test f(void)
{
    Test test = { 12345 };
    return test;
}

在 x86/x64 C++ 模式下编译时,Test 被认为是非 POD,因此 eax/rax 包含文档引导我们期望的对象.

When compiled under x86/x64 C++ mode, Test is considered a non-POD and therefore eax/rax contains the address of the object as the docs lead us to expect.

但是,在x86/x64 C模式下编译时,Test被认为是一个POD(我们正在编译C),因此你会直接在uint32_t中得到uint32_t的值代码>eax.

However, when compiler under x86/x64 C mode, Test is considered a POD (we are compiling C) and therefore you will get the uint32_t value directly in eax.

因此,即使我们将语言链接设置为 C,从 C 调用 f 也不起作用,这就是出现警告的原因.

Therefore, calling f from C won't work, even if we set the language linkage to C, which is why the warning appears.

这篇关于来自 MSVC extern “C"的故事的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆