Error in C++ code linkage: warning C4190: type has

2019-06-21 02:52发布

问题:

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);
}

回答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.



回答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.



回答3:

I believe you need to state again in the cxx file that vec3_add is a C linkage function:

extern "C" 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);
}


标签: c++ c dll