A common pattern when providing a C API is to forward declare some opaque types in your public header which are passed to your API methods and then reinterpret_cast
them into your defined C++ types once inside the translation unit (and therefore back in C++ land).
Using LLVM as an example:
In Types.h this typedef is declared:
typedef struct LLVMOpaqueContext *LLVMContextRef;
LLVMOpaqueContext
is not referenced anywhere else in the project.
In Core.h the following method is declared:
LLVMContextRef LLVMContextCreate(void);
Which is defined in Core.cpp:
LLVMContextRef LLVMContextCreate() {
return wrap(new LLVMContext());
}
wrap
(and unwrap
) is defined by a macro in 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)); \
}
And used in LLVMContext.h:
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(LLVMContext, LLVMContextRef)
So we see that the C API basically takes a pointer to an LLVMOpaqueContext
and casts it into an llvm::LLVMContext
object to perform whatever method is called on it.
My question is: isn't this in violation of the strict aliasing rules? If not, why not? And if so, how can this type of abstraction at the public interface boundary be acheived legally?