How to define an extern, C struct returning function in C++ using MSVC?
- by DK
The following source file will not compile with the MSVC compiler (v15.00.30729.01):
/* stest.c */
#ifdef __cplusplus
extern "C" {
#endif
struct Test;
extern struct Test make_Test(int x);
struct Test { int x; };
extern 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.