【问题标题】:My code may be crashing because the 'class' isn't C compatible?我的代码可能会因为“类”与 C 不兼容而崩溃?
【发布时间】:2012-03-10 03:33:22
【问题描述】:

-edit- 我缩小了范围。 Reproducible: Why does passing this object in C break my code?

在我进行更改后,我的应用程序无法正常运行。我在 msvc 中收到警告,但在 gcc 中没有。 这是警告的回购。

警告 C4190:'fnA' 指定了 C 链接,但返回与 C 不兼容的 UDT 'Test'

#include <type_traits> 

template<class T>
class Test{
        T t;
};
typedef Test<int> A;
//static_assert(std::is_pod<A>::value, "Not a POD"); //fails in msvc 2010
static_assert(sizeof(A) == sizeof(int), "");
static_assert(sizeof(A) == sizeof(void*), "");
extern "C" {
        A fnA(A v) { return v; }
}
int main() {
        A a;
        fnA(a);
}

AFAIK 我应该没有理由不能使用 Test 作为返回值;这可能不是问题,但这可能是问题。我无法找出问题所在,但我得到了无法重现的运行时异常(gcc 和 msvc)。我怀疑问题可能是 MyString 已损坏,但似乎情况并非如此,这让我很困惑。这是我的包装。 MyString 大于 8 字节,我需要将它交给 C 代码,不幸的是,它返回所有内容。这就是为什么我放置静态断言来查看班级规模是否符合我的预期。既然处理了尺寸/拼接,我还是完全喜欢WTF!?!为什么会出现该警告?有什么办法可以解决吗?

即使写 class Test{ T t; }; 也会导致警告,但 struct 会修复它。带有私有的结构会破坏它,我需要保持私有。

好的!在我删除 WrappedPointer 中的构造函数并将类更改为 struct (这使得 lhs 公开)之后。它在 GCC 和 MSVC 中完美运行。将 struct WrappedPointer 更改为 class WrappedPointer 会破坏我的代码。哇!?!这也是一个调试版本,没有优化。为什么将关键字 struct 更改为 class 会破坏代码!?!哇!?!但是这个改变并没有破坏 gcc。使用非默认构造函数会破坏 gcc...

template <class T>
struct WrappedPointer {
//private:
    T* lhs;
public:
    void SetLHS(T*v) { lhs=v; }
    //WrappedPointer(){}
    //WrappedPointer(T*value) : lhs(value){}
    //WrappedPointer(const WrappedPointer&v) : lhs(v.lhs){}
    T* operator->() const { return lhs; }
    T* operator*() const { return lhs; }
    template<class TT>
    bool operator==(TT t) const { return *lhs==t; }
    template<class TT>
    bool operator!=(TT t) const { return *lhs!=t; }

    bool operator==(int v) const { myassert2(v==0); return lhs==0; }
    bool operator!=(int v) const { myassert2(v==0); return lhs!=0; }
    bool operator==(const WrappedPointer&t) const { return *lhs==*t; }
    bool operator!=(const WrappedPointer&t) const { return *lhs!=*t; }
}
typedef WrappedPointer<MyString> String;
//typedef MyString* String;
static_assert(sizeof(String) == sizeof(int), "");
static_assert(sizeof(String) == sizeof(void*),"");

【问题讨论】:

  • 调用fnA的C代码对它接收到的对象做了什么?由于 C 中没有类,因此您应该编写一个不返回类的函数并在 C 代码中使用它。
  • @jogojapan:fnA 只是导致错误的一个例子。你是什​​么意思不返回一个类!?!你是说把它放在一个全局变量中!?!也没用,因为代码可能会在调用我的代码之前多次调用此函数。
  • 我的意思是:在external "C" 中使用C++ 是可以的,但是如果你在那里使用的C++ 类型最终返回到C 代码,应该怎么做? C代码对它们做什么?它不能处理一个类。
  • 根据here,如果函数的定义是在C++中,代码是合法的,但不是。
  • @Jesse 还有第二个条件:“对这个函数的所有调用都来自 C++。”我的理解是必须满足两个条件。还有,函数is在C++中的定义不是吗。

标签: c++ crash undefined-behavior


【解决方案1】:

external "C" 将函数标记为具有 C 链接并且禁用名称修改。现在你的函数中的问题是参数是一个 templatetypedef 只在当前翻译单元中创建一个别名,A 仍然是Test&lt;int&gt; 用于所有目的),并且名称​​必须被破坏。

【讨论】:

  • 在所有答案中,这是唯一一个有帮助的答案。但是没有错误。我希望该结构可以在任何地方使用以进行运算符重载(对于 vector::find)和偶尔的隐式转换。我能做些什么来拥有自己的类型吗?使它成为 POD 似乎可以解决问题,但是就像我说的那样,如果我将 lhs 设为私有,它就会崩溃,这非常奇怪。我不在任何地方进行任何 POD 检查,使一个成员成为私有成员不应该破坏代码,但它确实会破坏代码(实际上它不适用于 GCC,但当我使用非默认构造函数时 gcc 会破坏)
  • 为什么要把函数设为extern "C"?这种需求可能会推动可用的选项。
【解决方案2】:
extern "C" {
    A fnA(A v) { return v; }
}

这表示fnA 是一个 C 函数。 C 没有面向对象的编程或模板。该函数同时使用两者。因此,它不能作为 C 函数链接。

【讨论】:

  • 严格来说,extern "C" 表示目标文件中使用的名称 mangling 将与 C 兼容。您可以在函数定义中使用 C++。但是如果函数原型(即返回类型和参数类型的定义)涉及到C++数据类型,就会出现问题,尤其是在实际的C代码调用函数时。
  • 感谢您的提醒。我在想它应该与 C 完全兼容,但我猜它只是在接口区域。
【解决方案3】:
  • 类在 C 中无效。
  • 模板在 C 中无效。
  • 模板类在 C 中肯定是无效的。

选择你的语言并坚持下去!

【讨论】:

  • 您听起来好像从来没有充分的理由将 C++ 代码和 C 代码结合起来。我相信可以有充分的理由。有时即使理由不是很好,人们也必须这样做。
  • 结合 C 和 C++ 可以很好地从 C++ 或类似代码中调用 C 库,但是 OP 尝试做的事情毫无意义 - 他希望用 C 中的模板类做什么?正如@Chris 下面所说,您真正能做的最好的事情就是将其转换为 void*,然后在其他地方恢复为真实类型 - 但如果您必须这样做,我会质疑设计!
  • 我正在使用一个 C 库,它要求我在所有函数中返回 int 和 voids*,因为这就是 C 所知道的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-31
  • 2017-05-12
  • 2020-01-25
  • 1970-01-01
相关资源
最近更新 更多