提供C API时的一个常见模式是在您的公共标头中转发声明一些opaque类型,这些类型传递给您的API方法,然后在翻译单元内reinterpret_cast
将它们转换为您定义的C ++类型(因此回到C ++领域。)
在Types.h中声明了这个typedef:
typedef struct LLVMOpaqueContext *LLVMContextRef;
LLVMOpaqueContext
未在项目中的任何其他位置引用。
在Core.h中声明了以下方法:
LLVMContextRef LLVMContextCreate(void);
在Core.cpp中定义:
LLVMContextRef LLVMContextCreate() {
return wrap(new LLVMContext());
}
wrap
(和unwrap
)由CBindingWrapping.h中的宏定义:
#define DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ty, ref) \
inline ty *unwrap(ref P) { \
return reinterpret_cast<ty*>(P); \
} \
\
inline ref wrap(const ty *P) { \
return reinterpret_cast<ref>(const_cast<ty*>(P)); \
}
并在LLVMContext.h中使用:
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(LLVMContext, LLVMContextRef)
因此我们看到C API基本上采用指向LLVMOpaqueContext
的指针并将其转换为llvm::LLVMContext
对象,以执行在其上调用的任何方法。
我的问题是:这不违反严格的别名规则吗?如果没有,为什么不呢?如果是这样,那么公共界面边界上的这种抽象如何能够合法地实现呢?
答案 0 :(得分:22)
这不是严格的别名违规行为。首先,严格别名是通过错误类型的glvalue访问对象。
在您的问题中,您创建了一个LLVMContext
,然后使用LLVMContext
左值来访问它。那里没有非法走样。
唯一可能出现的问题是指针转换是否会产生相同的指针。但这也不是问题,因为reinterpret_cast
保证在往返转换中返回相同的指针。只要我们转换为和返回的指针类型是适当对齐的数据(即不比原始类型更严格)。
无论是好事还是坏事都是有争议的。我个人不会打扰LLVMOpaqueContext
并返回struct LLVMContext*
。它仍然是一个不透明的指针,当类型定义为struct
时,C标头用class
声明它并不重要。这两者可以互换,直到类型定义。