【问题标题】:Error in C++ code linkage: warning C4190: type has C-linkage specified, but returns UDT which is incompatible with CC++ 代码链接错误:警告 C4190:类型已指定 C 链接,但返回与 C 不兼容的 UDT
【发布时间】:2014-05-19 01:31:03
【问题描述】:

我很难理解为什么以下代码(具有标准布局的 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);
}

【问题讨论】:

  • 编译器显然对构造函数有反应。也许它(有点自相矛盾)可以帮助添加一个默认构造函数。我不会对is_standard_layout 报告的内容过于重视。这些东西在最初的 Boost 实现中通常是假的,可能没有正确实现或“真正”实现。
  • 请注意,通过将编译器版本提高一个档次,您将获得对花括号初始化的支持,其中构造函数提供的便利很少。对于您当前的编译器,您可以将构造函数替换为内联对象工厂函数。
  • 从类的定义来看确实是一个标准的布局类。问题是它应该是 POD 类型吗? - POD 是简单而标准的布局。

标签: c++ c dll


【解决方案1】:

原因是当您使用 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 类型。

【讨论】:

  • struct CVec3 :vec3行中的单冒号是做什么的
  • 它是一个派生类型。 CVec3 是 vec3 的子类。
【解决方案2】:

错误信息很清楚;当您使用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" 中(其中包含的标准标头除外),而不仅仅是它的零碎部分。

【讨论】:

    【解决方案3】:

    我相信你需要在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);
    }
    

    【讨论】:

    • 不,这将生成两次相同的警告消息
    • 只要函数用extern "C"声明,定义“继承”它。
    猜你喜欢
    • 2012-10-08
    • 1970-01-01
    • 2013-03-17
    • 1970-01-01
    • 2021-08-29
    • 1970-01-01
    • 2011-04-14
    • 2014-04-12
    • 2014-07-18
    相关资源
    最近更新 更多