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.
Structures (also called structs) are a way to group several related variables into one place. Each variable in the structure is known as a member of the structure. Unlike an array, a structure can contain many different data types (int, float, char, etc.).
Syntax of structstruct structureName { dataType member1; dataType member2; ... }; For example, struct Person { char name[50]; int citNo; float salary; }; Here, a derived type struct Person is defined.
We use structures to overcome the drawback of arrays. We already know that arrays in C are bound to store variables that are of similar data types. Creating a structure gives the programmer the provision to declare multiple variables of different data types treated as a single entity.
Structure is a user-defined data type. It works similarly like arrays. Structures help you in grouping items of different types in a single group. It stores the collection of different data types. Each element of structure is known as a member.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With