我很难理解为什么以下代码(具有标准布局的 UDT)在 Visual C++ 2012 中给出 C 链接警告:
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
}
函数的定义在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++ 编译器编译该代码时,预处理后的代码如下所示:
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);
}
因此,编译器看到的是函数“vec3_add”声明为具有 C 链接,但使用类型“vec3”,该类型具有 C 编译器无法理解的构造函数。 C++ 编译器不知道 C 编译器不会看到构造函数,因此会发出警告。请记住,预处理发生在编译之前,因此报告警告时编译器看不到
#ifdef __cplusplus
行。
此类问题的常见模式是:
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
通过这种方式,C++ 代码可以在调用“vec3_add”时使用“CVec3”类型而不是“vec3”类型。 C 代码只能看到“vec3”POD 类型。
错误信息非常清楚;当您使用
extern "C"
时,您只能使用与 C ABI 兼容的代码。非 POD 结构(例如具有用户定义构造函数的结构)与 C ABI 不兼容。
您应该从结构定义中删除它:
#ifdef __cplusplus
vec3(float x, float y, float z) : x(x), y(y), z(z) {}
#endif
无论如何,它可能会导致未定义的行为,因为添加它可能会导致结构布局发生变化。如果您希望能够在 C++ 代码中整齐地构造
vec3
,那么可以编写一个函数来执行此操作,例如
vec3 make_vec3(float x, float y, float z) { vec3 v; v.x=x; v.y=y; v.z.z; return v; }
此外,您还应该将整个标头包装在
extern "C"
中(其中包含的标准标头除外),而不仅仅是其中的一部分。
刚刚遇到类似的问题。我通过将结构包装到联合中来解决这个问题,就 ABI 而言,联合将构造函数从其中剥离出来。
#if !defined(__cplusplus)
#define VEC3_RETURN_VALUE vec3
#else
union VEC3_RETURN_VALUE {
vec3 vec;
operator vec3() const { return vec; }
};
#endif
vec3_add(vec3 a, vec3 b);
我相信你需要在cxx文件中再次声明vec3_add是一个C链接函数:
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);
}