Обычным шаблоном при предоставлении API-интерфейса C является пересылка объявления некоторых непрозрачных типов в ваш общедоступный заголовок, которые передаются вашим API-методам, а затем reinterpret_cast
их в определенные типы C++ один раз внутри единицы перевода (и, следовательно, обратно в C++ земельные участки).
Использование LLVM в качестве примера:
В 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
и LLVMOpaqueContext
его в объект llvm::LLVMContext
для выполнения любого метода, вызываемого на нем.
Мой вопрос: разве это не нарушает строгие правила псевдонимов? Если нет, почему бы и нет? И если да, то как этот тип абстракции на границе публичного интерфейса должен быть законным?