8

I have dll written on language X with C-ABI interface. And I want to use this C-ABI from my c++ program.

I wrote in main.cpp:

extern "C" {
struct Foo {
  const char * const data;
  unsigned len;
};
struct Foo f(void);
}

int main()
{
}

And got warning from compiler (visual c++/15.7.5/windows 7/32bit):

(7): warning C4190: 'f' has C-linkage specified, but returns UDT 'Foo' which is incompatible with C

(7): note: see declaration of 'Foo'

Here godbolt link: https://godbolt.org/g/ztx1kf

I read Error in C++ code linkage: warning C4190: type has C-linkage specified, but returns UDT which is incompatible with C, but in my case I have no "c++ code" at all in my POD struct.

How can I convince compiler that this is not C++ struct Foo, but C struct Foo?

I try to move it to separate header file (.h), but this is change nothing.

If I replace const char * const data with const char * warning disappear, what I also don't understand, but I don't want to change definition of struct.

user1244932
  • 7,352
  • 5
  • 46
  • 103

2 Answers2

1

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

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.

While Foo 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.

It also exceeds the maximum size, but even if we removed len, the above would still prevent it to be a "POD".

Similarly, the x86 calling convention docs explain something similar:

[...] except for 8-byte structures, which are returned in the EDX:EAX register pair. Larger structures are returned in the EAX register as pointers to hidden return structures. [...] Structures that are not PODs will not be returned in registers.

For instance, a function like the following:

struct Foo
{ 
    const uint32_t x;
};

Foo f(void)
{
    Foo foo = { 12345 };
    return foo;
}

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

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

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

Acorn
  • 24,970
  • 5
  • 40
  • 69
0

C, of course, can have const struct members

Some kind of a solution (as of today): Simply disable the warning 4190, if using MSVC.

Chef Gladiator
  • 902
  • 11
  • 23