Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define an extern, C struct returning function in C++ using MSVC?

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.

like image 971
DK. Avatar asked Oct 20 '25 06:10

DK.


1 Answers

It's a bit of a screwy error message, but the caller needs to know the size of the structure to be able to make the call. It needs to reserve space on the stack for the return value. Or expect the return value in registers if the structure is small enough.

You have to declare the structure in the header. When you do, the error disappears:

#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
like image 50
Hans Passant Avatar answered Oct 22 '25 20:10

Hans Passant