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