Вопрос: Предоставление 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 и бросает его в llvm::LLVMContext объект для выполнения любого метода, вызываемого на нем.

Мой вопрос: разве это не нарушает строгие правила псевдонимов? Если нет, почему бы и нет? И если да, то как этот тип абстракции на границе публичного интерфейса должен быть законным?


25


источник


Ответы:


Это не строгое нарушение псевдонимов. Начнем с того, что строгое сглаживание связано с доступом к объекту с помощью значения glvalue неправильного типа.

В вашем вопросе вы создаете LLVMContext, а затем использовать LLVMContext lvalue для доступа к нему. Никакого незаконного наложения не существует.

Единственная проблема, которая может возникнуть, заключается в том, что преобразование указателя не возвращает один и тот же указатель. Но это тоже не проблема, поскольку reinterpret_cast гарантированно возвращает один и тот же указатель в конверсии туда и обратно. До тех пор, пока тип указателя, который мы преобразуем в и обратно, является подходящим образом выровненными данными (т. Е. Не более строгим, чем исходный тип).

Является ли это хорошим или плохим способом заниматься вещами, является спорным. Я лично не стал бы беспокоиться LLVMOpaqueContext и вернуть struct LLVMContext*, Это все еще непрозрачный указатель, и не имеет значения, что заголовок C объявляет его struct в то время как определение типа с class, Эти два взаимозаменяемы до точки определения типа.


22