web-dev-qa-db-ja.com

C ++ライブラリへのC APIの提供と厳密なエイリアス

C APIを提供するときの一般的なパターンは、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
Sam Kellett

これは厳密なエイリアス違反ではありません。まず、厳密なエイリアシングとは、間違ったタイプのglvalueを介してオブジェクトにアクセスすることです。

あなたの質問では、LLVMContextを作成し、LLVMContext lvalueを使用してそれにアクセスします。そこには違法なエイリアシングはありません。

発生する可能性がある唯一の問題は、ポインター変換が同じポインターを返さない場合です。しかし、reinterpret_castは往復変換で同じポインタを返すことが保証されているため、これも問題ではありません。ポインター型との間で変換が行われる限り、適切に調整されたデータに変換されます(つまり、元の型よりも厳密ではありません)。

物事を進める良い方法か悪い方法かは議論の余地があります。個人的にはLLVMOpaqueContextを気にせず、struct LLVMContext*を返します。それは依然として不透明なポインタであり、Cヘッダーがstructを使用して宣言することは問題ではありませんが、型定義はclassを使用します。 2つは、型定義の時点まで交換可能です。