如何在C ++中使用MSVC定义外部C结构返回函数? [英] How to define an extern, C struct returning function in C++ using MSVC?

查看:333
本文介绍了如何在C ++中使用MSVC定义外部C结构返回函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  / * stest。 c * / 
#ifdef __cplusplus
externC{
#endif

struct Test;

/ * NB:当由另一个模块导入时,这可能是extern。 * /
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


b $ b

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

  stest.c(8):错误C2526:'make_Test':C链接函数不能返回C ++类'Test'
stest.c $ b

无需 / Tp $ c> cl 将文件视为C ++)工作正常。该文件还编译在DigitalMars C和GCC(从mingw)在C和C ++模式。我也用了 -ansi -pedantic -Wall 和GCC,没有投诉。



到下面,我们需要编译这个文件作为C ++的MSVC(而不是其他),但函数被编译为C.本质上,我们想要一个正常的C编译器...除了大约六行。是否有开关或属性或者我可以添加的东西,这将允许这个工作?








作为其中的一部分,我们需要 生成浮点数和无穷大作为常量(长故事),这意味着我们必须使用MSVC在C ++模式下编译,才能真正做到这一点。我们只找到一个有效的解决方案,而且只有 在C ++模式下工作。



我们将代码包装在 externC{...} ,因为我们想要控制整理和调用约定,以便我们可以与现有的C代码接口。 ...也因为我信任C ++编译器关于我可以抛出一个小型百货公司。我也尝试在 externC ++{...} 中包含 reinterpret_cast >,但当然不起作用。 Pity。



我发现一个潜在的解决方案,它需要重新排序声明,使得完整的结构体定义在函数foward声明之前但是这是非常不方便的,因为codegen的执行方式,所以我会真的喜欢避免走下去的道路,如果我可以。

解决方案

这是一个有趣的问题。正如你所说,编译代码为C代码正确地产生没有错误。并且只有MSVC似乎在编译为C ++代码时有麻烦。



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

  struct Test; 

这是一个不完整的声明 struct Test 编译器不知道 struct Test 的完整定义是否包含C ++特定项目(虚函数,继承等)。请注意, externC块中的类型仍然可以使用所有C ++设施; externC语言链接规范仅适用于声明引入的所有函数声明符,函数名和变量名的函数类型(7.5 / 4因此我可以看到当MSVC的C ++编译器遇到一个 externC时,函数返回一个不完整的类型,它可能会决定它需要返回一个错误,如果类型不是一个简单的C风格的POD类型。



C ++标准确实表示(7.5 / 9链接规范):


从C ++到其他语言定义的对象在C ++中从其他语言定义的对象是实现定义的和语言相关的。只有在两个语言实现的对象布局策略相似的情况下才能实现这种连接。


因此MSVC可能有一些余地如果它有一个原因不允许 externC函数从返回非POD对象,虽然我不知道为什么MSVC会有一个问题,当其他Windows编译器不。如果任何人知道细节(或者如果他们知道我只是在这里的基地),我会感谢一个笔记。



不是这些都有助于你 - 这只是我的猜测的理由。



不知道更多关于你的codegen过程,以及你如何能够影响它,我不知道什么体面的选择,可能具有 - 可能对生成的文件进行后处理以分割需要的东西以被编译为C(或重新排列声明)。但我可以想象,这可能是一个噩梦,工作,特别是维护。


The following source file will not compile with the MSVC compiler (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

Compiling with cl /c /Tpstest.c produces the following error:

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

Compiling without /Tp (which tells cl to treat the file as C++) works fine. The file also compiles fine in DigitalMars C and GCC (from mingw) in both C and C++ modes. I also used -ansi -pedantic -Wall with GCC and it had no complaints.

For reasons I will go into below, we need to compile this file as C++ for MSVC (not for the others), but with functions being compiled as C. In essence, we want a normal C compiler... except for about six lines. Is there a switch or attribute or something I can add that will allow this to work?


The code in question (though not the above; that's just a reduced example) is being produced by a code generator.

As part of this, we need to be able to generate floating point nans and infinities as constants (long story), meaning we have to compile with MSVC in C++ mode in order to actually do this. We only found one solution that works, and it only works in C++ mode.

We're wrapping the code in extern "C" {...} because we want to control the mangling and calling convention so that we can interface with existing C code. ... also because I trust C++ compilers about as far as I could throw a smallish department store. I also tried wrapping just the reinterpret_cast line in extern "C++" {...}, but of course that doesn't work. Pity.

There is a potential solution I found which requires reordering the declarations such that the full struct definition comes before the function foward decl., but this is very inconvenient due to the way the codegen is performed, so I'd really like to avoid having to go down that road if I can.

解决方案

This is an interesting question. As you say, compiling the code as C code rightly produces no error. And only MSVC seems to have trouble with it when compiled as C++ code.

Since other C++ compilers don't have a problem with the code, this might be a bug in MSVC, but I can see how MSVC might have a rationale for this error. When the C++ compiler hits the line:

struct Test;

That's an incomplete declaration of struct Test - the compiler doesn't know if the complete definition of struct Test will contain C++ specific items (virtual functions, inheritance, etc). Note that types in an extern "C" block can still use all C++ facilities; the extern "C" language linkage specification applies only to "function types of all function declarators, function names, and variable names introduced by the declaration(s)" (7.5/4 "Linkage specifications").

So I could see how when MSVC's C++ compiler comes across an extern "C" function that's returning an incomplete type, it might decide that it needs to return an error at that point in case the type turns out to not be a plain C-style POD type.

The C++ standard does say (7.5/9 "Linkage specifications"):

Linkage from C++ to objects defined in other languages and to objects defined in C++ from other languages is implementation-defined and language-dependent. Only where the object layout strategies of two language implementations are similar enough can such linkage be achieved.

So MSVC might have some leeway (standards-wise) if it has a reason to not permit extern "C" functions from returning non-POD objects, though I'm not sure why MSVC would have a problem when other Windows compilers don't. If anyone knows details (or if they know I'm just plain off-base here), I'd appreciate a note.

Not that any of this this helps you - it's just my guess at a rationale.

Without knowing more about your codegen process and how you might be able to influence it, I'm not sure what decent options you might have - maybe a post-processing of the generated files to split out the stuff that needs to be compiled as C (or rearranges the declarations). But I can imagine that that might be a nightmare to get working and especially to maintain.

这篇关于如何在C ++中使用MSVC定义外部C结构返回函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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