对于指向C+的不透明C指针,正确的typedef是什么+;上课? 有几十个这样的问题和博客文章,描述用C API包装C++类。范例
大多数答案和博客帖子都是这样写的:对于指向C+的不透明C指针,正确的typedef是什么+;上课? 有几十个这样的问题和博客文章,描述用C API包装C++类。范例,c++,c,typedef,C++,C,Typedef,大多数答案和博客帖子都是这样写的: typedef void* CMyClass; 但也有人说这很糟糕,因为它没有提供类型安全性。他们提出了不透明结构的各种变体,没有任何解释。我可以复制上面的片段,继续我的生活(我将在这段时间内继续),但我想一劳永逸地知道 哪种形式最好 它在void*上提供了哪些保证 它是如何工作的 void*的问题在于,它无法防止意外分配不兼容的指针 typedef void *CMyClass; int i = 1; CMyClass c = &i; // No
typedef void* CMyClass;
但也有人说这很糟糕,因为它没有提供类型安全性。他们提出了不透明结构的各种变体,没有任何解释。我可以复制上面的片段,继续我的生活(我将在这段时间内继续),但我想一劳永逸地知道
- 哪种形式最好
- 它在
上提供了哪些保证void*
- 它是如何工作的
typedef void *CMyClass;
int i = 1;
CMyClass c = &i; // No complaints
如果您将typedef改为某个唯一的不透明类型,编译器将帮助您
typedef struct MyClass *CMyClass;
int i = 1;
CMyClass c = &i; // BOOM!
我认为在C语言中这不是一个错误,但Clang6.0警告我(即使没有启用任何警告)
在C++中使用<代码> struct MyType <代码> > < /P> 使用
typedef struct MyType*pMyType代码>作为您的公共句柄
<>你的“C”API应该在C++和C++中(在C++中使用<代码>外部代码> C > <代码>包装,以获得正确的链接。您将接近max type safety
现在,struct MyHandle{void*private_ptr;}“代码>”是另一种选择:这避免了将C++类型的名称暴露给C.,只要隔离了与“代码> PrimeTyPPTR <代码>的直接交互,就可以实现一小部分功能,它在其他任何地方都是类型安全的。第一件事是,void*
确实不是一个好选择,因为它通过静默地接受任何不相关的指针使API更容易出错。因此,更好的办法是向某个结构添加一个转发声明,并接受指向该结构的指针:
#ifdef __cplusplus
extern "C"
{
#endif
struct CMyClassTag;
typedef struct CMyClassTag CMyClass;
void CMyClass_Work(CMyClass * p_self);
#ifdef __cplusplus
}
#endif
下一步是显式地告诉用户该指针是不透明的,不应该通过将指针隐藏为不必要的实现细节来取消引用:
typedef struct CMyClassTag * CMyClassHandle;
void CMyClass_Work(CMyClassHandle h_my_class);
另外,依靠用户正确地使用这个接口,您可以创建真正的句柄类型,而不是不透明的指针。这可以通过几种方式完成,但主要思想是传递一些模糊的整数标识符,并在运行时将其映射到库端的实际指针:
typedef uintptr_t CMyClassHandle;
void CMyClass_Work(CMyClassHandle h_my_class);
// impl
void CMyClass_Work(CMyClassHandle h_my_class)
{
auto it{s_instances_map.find(h_my_class)};
if(s_instances_map.end() != it)
{
auto & self{it->second};
// ...
}
}
问题是,如果您声明typedef struct CMyClass\u*CMyClass代码>您需要显式强制转换。示例:相关:不要键入def指针!那么,那些批评家们还推荐哪种方式呢?为什么它对你不起作用?C是从C++支持的,但是反过来不是真的。这意味着没有有效的通用机制可用于管理C中的类。例如,假设C程序中使用了全局实例(static
)。。。谁调用那个对象的构造函数?+ 1,用于提及C++声明。code>struct T
和class T
是不同的声明,例如,如果混合使用struct
和class
来引用相同的名称,则Clang将发出错误。@MatthieuM。如何触发此错误(假设-pedantic errors-Wno error-Wall-Wextra
)?我最多只能得到一个带有clang 6.0的-Wmismatched标记,但这显然是一个警告,而不是一个错误。@Ruslan:问得好。我只使用-Werror
进行编译,所以这可能是一个警告。对于可移植性来说也是非常有用的,因为在MSVC的ABI中有不同的链接。我最后用私有指针来构造Strut,因为我的C++ API返回了SyrdY-PTR,这是我唯一能找到的明智方法。对于C程序员来说,将指针隐藏在typedef
后面被广泛认为是不好的风格。在这种特殊情况下,它可能是合理的,因为指针指向C中未知的东西,所以您可以认为只提供了一个“不透明句柄”。不过,严格来说,“C++结构”可能不同于C中的结构(比如vtables),所以我总是选择第二个选项。
typedef uintptr_t CMyClassHandle;
void CMyClass_Work(CMyClassHandle h_my_class);
// impl
void CMyClass_Work(CMyClassHandle h_my_class)
{
auto it{s_instances_map.find(h_my_class)};
if(s_instances_map.end() != it)
{
auto & self{it->second};
// ...
}
}