代码之家  ›  专栏  ›  技术社区  ›  Sam Kellett

为C++库提供C API和严格别名

  •  25
  • Sam Kellett  · 技术社区  · 8 年前

    提供C API时的一种常见模式是在公共标头中向前声明一些不透明类型,这些类型将传递给API方法,然后 reinterpret_cast 一旦在翻译单元内(因此回到C++land),就可以将它们转换为您定义的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 对象来执行对其调用的任何方法。

    我的问题是:这是否违反了严格的别名规则?如果没有,为什么没有?如果是这样的话,如何在公共接口边界合法地实现这种类型的抽象?

    1 回复  |  直到 8 年前
        1
  •  22
  •   StoryTeller - Unslander Monica    8 年前

    这不是严格的别名冲突。首先,严格别名是指通过错误类型的glvalue访问对象。

    在您的问题中,您创建了 LLVMContext ,然后使用 LLVMContext 访问它的左值。那里没有非法的别名。

    唯一可能出现的问题是指针转换不会返回相同的指针。但这也不是问题,因为 reinterpret_cast 保证在往返转换中返回相同的指针。只要我们转换和转换的指针类型是适当对齐的数据(即不比原始类型严格)。

    这是一种好还是坏的做事方式仍有争议。我个人不会为 LLVMOpaqueContext 并返回 struct LLVMContext* 。它仍然是一个不透明的指针,C头用 struct 而类型定义与 class 。就类型定义而言,这两者是可互换的。

    推荐文章