C APIを提供するときの一般的なパターンは、APIメソッドに渡されるいくつかの不透明な型を転送ヘッダーに転送して宣言し、次にreinterpret_cast
を翻訳単位内で定義済みのC++型に転送することです(したがって、C++に戻ります)。土地)。
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
オブジェクトにキャストして、呼び出されるメソッドを実行します。
私の質問は、これは厳密なエイリアシング規則に違反していないのですか?そうでない場合、なぜそうではないのですか?もしそうなら、パブリックインターフェイスの境界でこの種の抽象化をどのようにして合法的に達成できるでしょうか。
これは厳密なエイリアス違反ではありません。まず、厳密なエイリアシングとは、間違ったタイプのglvalueを介してオブジェクトにアクセスすることです。
あなたの質問では、LLVMContext
を作成し、LLVMContext
lvalueを使用してそれにアクセスします。そこには違法なエイリアシングはありません。
発生する可能性がある唯一の問題は、ポインター変換が同じポインターを返さない場合です。しかし、reinterpret_cast
は往復変換で同じポインタを返すことが保証されているため、これも問題ではありません。ポインター型との間で変換が行われる限り、適切に調整されたデータに変換されます(つまり、元の型よりも厳密ではありません)。
物事を進める良い方法か悪い方法かは議論の余地があります。個人的にはLLVMOpaqueContext
を気にせず、struct LLVMContext*
を返します。それは依然として不透明なポインタであり、Cヘッダーがstruct
を使用して宣言することは問題ではありませんが、型定義はclass
を使用します。 2つは、型定義の時点まで交換可能です。