Error in C++ code linkage: warning C4190: type has C-linkage specified, but returns UDT which is incompatible with C
Solution 1
The reason is when you compile that code with a C++ compiler, the preprocessed code looks like this:
typedef struct vec3 {
float x;
float y;
float z;
vec3(float x, float y, float z) : x(x), y(y), z(z) {}
} vec3;
extern "C" {
vec3 vec3_add(vec3 a, vec3 b);
}
So what the compiler sees is the function 'vec3_add' declared as having C linkage, but using the type 'vec3' which has as a constructor which a C compiler won't understand. The C++ compiler doesn't know that a C compiler won't see the constructor so emits the warning. Remember, preprocessing happens before compilation, so the #ifdef __cplusplus
lines are not visible to the compiler at the time when the warning is reported.
A common pattern for this sort of issue is:
extern "C" {
typedef struct vec3 {
float x;
float y;
float z;
} vec3;
vec3 vec3_add(vec3 a, vec3 b);
}
#ifdef __cplusplus
struct CVec3 :vec3 {
CVec3(float X, float Y, float Z) { x = X; y = Y; z = Z; }
};
#endif
In this way, C++ code can use the 'CVec3' type instead of the 'vec3' type in calls to 'vec3_add'. C code will only see the 'vec3' POD type.
Solution 2
The error message is pretty clear; when you are using extern "C"
you can only use code that is compatible with the C ABI. Non-POD structs (e.g. struct with a user-defined constructor) is not compatible with the C ABI.
You should remove this from the struct definition:
#ifdef __cplusplus
vec3(float x, float y, float z) : x(x), y(y), z(z) {}
#endif
It probably causes undefined behaviour having it there anyway, since adding this in may cause the struct layout to change. If you want to be able to tidily construct a vec3
in C++ code then write a function to do so, e.g.
vec3 make_vec3(float x, float y, float z) { vec3 v; v.x=x; v.y=y; v.z.z; return v; }
Also you should wrap the entire header in extern "C"
(except for standard headers included from it), not just bits and pieces of it.
kin
Updated on June 15, 2022Comments
-
kin almost 2 years
I am having a hard time understanding why the following code (UDT with standard layout) gives a C-linkage warning in visual C++ 2012:
warning C4190: 'vec3_add' has C-linkage specified, but returns UDT 'vec3' which is incompatible with C typedef struct vec3 { float x; float y; float z; #ifdef __cplusplus vec3(float x, float y, float z) : x(x), y(y), z(z) {} #endif } vec3; #ifdef __cplusplus extern "C" { #endif vec3 vec3_add(vec3 a, vec3 b); #ifdef __cplusplus }
The definition of the function is in a C++ file:
vec3 vec3_add(vec3 a, vec3 b) { static_assert(std::is_standard_layout<vec3>::value == true, "incompatible vec3 type"); return vec3(a.x + b.x, a.y + b.y, a.z + b.z); }
-
Cheers and hth. - Alf about 10 yearsthe compiler is apparently reacting to the constructor. maybe it (a bit paradoxically) could help to add a default constructor. i wouldn't place too much weight on what
is_standard_layout
reports. this stuff was often dummies in the original Boost implementation, and may not be correctly implemented or "really" implemented. -
Cheers and hth. - Alf about 10 yearsNote that by upping the compiler version one notch, you get support for curly braces initialization, where the constructor provides very little in the way of convenience. For your current compiler you can replace the constructor with an inline object factory function.
-
kin about 10 yearsFrom the definition of the class it is indeed a standard layout class. The question is should it be a POD type for it work ?! - POD are trivial and standard layout.
-
-
kin about 10 yearsNop, this will generate the same warning message twice
-
M.M almost 10 yearsAs long as the function is declared with
extern "C"
, the definition "inherits" that. -
singingsingh almost 7 yearswhat does the single colon do in the line
struct CVec3 :vec3
-
alvion over 3 yearsIt's a derived type. CVec3 is a subclass of vec3.
-
JohnAl almost 2 yearsIs the solution from the OP objectively wrong and undefined behavior, or is it purely a matter of this common pattern you suggested (a C++ struct that inherits from the C struct and adds a constructor) being prettier and not causing a warning? I think as of C++20, it can be said that OPs solution is valid as long as
std::is_standard_layout
for the struct istrue
, right? -
JohnAl almost 2 yearsWhy would "adding this in may cause the struct layout to change" and "probably cause undefined behaviour"? Could you give some example for why it could ever happen with a simple constructor? As far as I know, I think it's impossible for a constructor like this to change the layout. As of C++20, as long as
std::is_standard_layout
for the struct istrue
(which it is in this case) it should be defined behavior. -
M.M almost 2 years@JohnAl at time of writing this answer I thought that would render it non-standard layout. It no longer does as of c++20 as you say (and not sure if it ever did)