【问题标题】:Relax void * casting in C++在 C++ 中放松 void * 铸造
【发布时间】:2011-04-20 10:32:58
【问题描述】:

在 C 中,将指针转换为 void * 和从其转换不是错误。

移植到 C++ 的一个主要障碍是在从处理泛型指针(例如 malloc)的函数以及在我自己的代码中声明的函数(例如 void *block_get(Blkno const blkno);)返回时需要转换指针。

然而,我的代码旨在由 C C++ 编译器成功编译。如果我为了 C++ 而在任何地方都提供了显式转换,那么它们必须是 C 风格的转换,并且由于在两种语言的指针类型之间转换非指针类型,我可能会掩盖错误。

我的参考错误如下:

struct Cpfs *cpfs = calloc(1, sizeof(*cpfs));

在 MSVC 中产生:

错误 2 错误 C2440: 'initializing' : 无法从 'void *' 转换为 'Cpfs *' e:\src\cpfs\cpfs.c 179

显然我不能使用newstatic_cast,如果我不再使用C,我自然会使用它们。围绕void *为每种语言提供最大类型安全的最佳方法是最少的冗长?

【问题讨论】:

  • 为什么需要移植到 C 和 C++ 的公共子集?正如您所发现的,它严重限制了您可以使用的 C(或者 C++)。几乎所有有 C++ 可用的环境也有 C 可用,并且还允许您将 C++ 和 C 目标文件链接在一起。我看不出移植到公共子集有什么好处。
  • 为了 C 中的类型安全(这有点难),为每个结构创建一个函数,其工作是分配并返回一个指向新结构的指针。同样,创建一个释放结构的函数。
  • 我想知道尝试编写一个为两种不同语言编译的程序的基本原理。这通常是留给谜题的东西。
  • 顺便说一句,在 C++ 中,您应该使用 static_cast 转换为/从 void* 转换。
  • 10 次中有 9 次,头文件中的 static inline 是虚假的过早优化。

标签: c++ c casting void-pointers


【解决方案1】:

我建议要么简单地使用 C 样式转换,要么将转换包装在一个宏中,该宏要么扩展为空(在 C 中),要么在 C++ 中为 static_cast

【讨论】:

  • 来自void*reinterpret_cast 不是最好的演员。 static_cast 就足够了,而且不太可能将无效源静默转换为指针类型。
【解决方案2】:

如果您的编译器支持decltype(),您可以使用一些宏魔法来避免显式重复类型名称(并且感谢sizeof,元素大小):

#ifdef __cplusplus
#define my_calloc(VAR, COUNT) \
    static_cast<decltype(VAR)>(std::calloc(COUNT, sizeof *VAR))
#else
#define my_calloc(VAR, COUNT) calloc(COUNT, sizeof *VAR)
#endif

示例用法:

#ifdef __cplusplus
#include <cstdlib>
#else
#include <stdlib.h>
#endif

struct Cpfs *cpfs = my_calloc(cpfs, 42);

更简洁的解决方案可能是只使用 C 编译器并链接目标文件,不过...

【讨论】:

  • decltype 看起来类似于 typeof,不能使用 c 编译器,msvc 不支持 c99。 c++ 编译它比一个半熟的 c 编译器更好
  • @Matt:你可以使用 MinGW 来编译你项目的 C 部分,并且仍然使用 MSVC 来编译 C++ 部分(参见例如stackoverflow.com/questions/2096519/…
  • @Matt Joiner:对 C99 和 C++ 的交集进行编程与仅对 C89 进行编程没有本质区别,只是更加尴尬和烦人。您正在编写一个半笨拙的 C 版本,使用半笨拙的 C 编译器并没有更糟,至少 C89 是一个标准。
【解决方案3】:

创建一个替换分配器函数,您可以为 C 和 C++ 构建以不同方式定义:- 在头文件中类似这样的内容:

#ifdef __cplusplus
template<typename TypeT>
TypeT* MyAlloc(TypeT** pOut,size_t cb){
  *pOut = static_cast<TypeT*>(malloc(cb)); //aint c++ pretty.
  return *pOut;
}
#else
  extern void* MyAlloc(void** ppv, size_t cb);
#endif

现在,在 c++ 构建中,您有一个可以推断其处理的事物类型的函数,而在 C 构建中,它是一个返回 void* 的常规函数​​。

唯一的问题是需要传递指针以进行分配 - c++ 编译器不会尝试仅根据函数 afaik 的返回类型推断模板参数。所以你可以这样称呼它:-

int *p;
if(MyAlloc(&p,sizeof(int)*n)){
  ...

【讨论】:

  • "//aint c++ 漂亮。"是的,当你使用new
  • new 更糟糕,因为 c++ 编译器不会从构造函数调用中推断出类模板参数。所以前几天我不得不写这样的东西:SomeClass&lt;int(int)&gt;* pWrapper = new SomeOtherClass&lt;int(SomeOtherClass::*)(int)&gt;(this,&amp;SomeOtherClass::method);
  • @Chris:您在使用new 时没有将其放入包装器中,您的观点对我来说毫无意义。 C++0x 有auto,无论如何你的反应是红鲱鱼。
  • 现在最好的做法是始终在包装器中捆绑对 new 的调用? auto 只解决了一半的问题。并且 new 并不比 malloc() 更漂亮,特别是如果避免丑陋的推荐方法是包装它。
  • @Chris:嗯,是的。这甚至不是一个新想法,它相对较旧。 (例如,我们处于“现代 C++”时代,您无需手动管理资源。将它们包装起来(std::vectorshared_ptr 等);这是范围绑定的资源管理(SBRM ) 更为人所知的是它的丑陋名称 RAII) 任何时候你必须手动释放某些东西,你做错了什么。同样,这条切线是一条红鲱鱼。你在帖子中说的丑陋的代码只是丑陋的,因为它不是惯用的 C++,用一些 other 代码回复是无关紧要的。
【解决方案4】:

也许是这样的? (未经测试,没有可用的编译器,不经常使用宏):

#ifdef __cplusplus
    #define pointer_cast(type, pointer) reinterpret_cast<type>(pointer)
#else
    #define pointer_cast(type, pointer) (type)(pointer)
#endif

【讨论】:

  • __cplusplus 未定义时,为什么不完全避免无用的演员表?
  • 我最终创建了一个 VOID_CAST 宏,它只在必要时进行转换。
【解决方案5】:

我知道的唯一解决方案是进行显式转换:

struct Cpfs *cpfs = (Cpfs*)calloc(1, sizeof(*cpfs));

这里两个编译器都满意。 另外请记住,对于较旧的编译器,malloc 可能会返回 char*。

马里奥

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多