C++ 用c+设计接口+;

C++ 用c+设计接口+;,c++,interface,C++,Interface,我正在开发一个可以用作动态加载的接口。它应该独立于编译器。所以我想导出接口。 我现在面临以下问题 问题1:接口函数将一些自定义数据类型(基本上是类或结构)作为输入/输出参数。我想使用构造函数用默认值初始化这些类的成员。如果我这样做,则无法动态加载库,并且库依赖于编译器。如何解决这个问题 问题2:一些接口将元素的列表(或映射)返回给客户端。为此,我使用std容器。但这同样依赖于编译器(有时还依赖于编译器版本) 谢谢。如果对用于参数和返回值的类型集采用相同的方式,则以不同方式编译的代码只能一起工作。

我正在开发一个可以用作动态加载的接口。它应该独立于编译器。所以我想导出接口。 我现在面临以下问题

问题1:接口函数将一些自定义数据类型(基本上是类或结构)作为输入/输出参数。我想使用构造函数用默认值初始化这些类的成员。如果我这样做,则无法动态加载库,并且库依赖于编译器。如何解决这个问题

问题2:一些接口将元素的列表(或映射)返回给客户端。为此,我使用std容器。但这同样依赖于编译器(有时还依赖于编译器版本)


谢谢。

如果对用于参数和返回值的类型集采用相同的方式,则以不同方式编译的代码只能一起工作。ABI在更深层次上非常重要-名称混乱、虚拟调度表等,但我的观点是,如果有一个编译器支持使用简单类型调用函数,您至少可以考虑对更复杂的类型(如标准容器和用户定义类型的编译器特定实现)提供一些支持

您必须研究编译器提供的ABI支持,并推断出它们将继续提供什么

如果要支持相关ABI标准之外的其他类型,选项包括:

  • 使用更简单的类型公开更复杂类型的内部

    • 传递由
      my_std_string.data()
      &my_std_string[0]
      my_std_string.size()
      提取的[
      const
      字符*和
      my_std_string.size()
      ,类似于
      std::vector

    • 使用接收器的数据结构对数据进行序列化和反序列化(可能很慢)

  • 提供一组函数指针,指向创建数据类型的对象实现的简单访问器/变异器函数

    • e、 g.经典C
      qsort
      函数接受指向元素比较函数的指针的方式

由于我通常关注多线程,所以我将主要讨论第二个问题

您已经意识到,通过API传递容器的元素似乎依赖于编译器。实际上更糟糕的是:它依赖于头文件&C++-库,所以至少对于Linux来说,您已经被两个不同的集合所困扰:libstc++(源自gcc)和libcxx(源自clang)。 因为容器的一部分是头文件,而另一部分是库代码,所以获得ABI独立性几乎是不可能的

我更担心的是,您实际上想到了传递容器元素。这是一个巨大的线程安全问题:STL容器在设计上不是线程安全的

通过在接口上传递引用,就是在传递“指向封装知识的指针”——API的用户可以对内部结构进行假设,并开始修改指向的数据。这在单线程环境中通常已经很糟糕了,但在多线程环境中会变得更糟

其次,您提供的指针可能会过时,也不好

确保返回您的内部知识副本,以防止用户修改您的结构

传递东西
const
是不够的:const可以被丢弃,你仍然会暴露你的内脏


我的建议是:隐藏数据类型,只通过简单的类型和/或结构,即完全控制(即不依赖于STL或Boost)。

< P>设计具有最广泛ABI兼容性的API是一个极其复杂的主题,更重要的是当C++被涉及而不是C.</P> 然而,还有更多的理论类型的问题并不像在实践中听起来那么糟糕。例如,从理论上讲,调用约定和结构填充/对齐听起来可能是最头疼的问题。实际上,它们并没有那么多,您甚至可以在事后通过向第三方指定其他构建指令或使用指示适当调用约定的宏装饰SDK函数来解决这些问题。这里所说的“还不错”,我的意思是,他们可以让你绊倒,但他们不会让你回到绘图板,重新设计你的整个SDK作为回应

我想关注的“实际”问题是可以让您重新访问绘图板并重做整个SDK的问题。我的清单也不是详尽无遗的,但我认为你应该首先记住其中一些

您还可以将SDK视为两部分:动态链接部分,实际上导出了其实现是隐藏于客户端的功能,以及一个静态(内部)链接的便利库部分,上面添加了C++包装。如果你把SDK当成这两个不同的部分,那么你可以在静态链接库中使用更多的C++来使用更多的C++机制。 那么,让我们从这些实用的头痛诱因开始:

1。vtable的二进制布局在编译器之间不一定一致。

在我看来,这是最大的陷阱之一。我们通常关注两种在运行时从一个模块访问另一个模块的主要方法:函数指针(包括由dylib符号查找提供的指针)和包含虚拟函数的接口。后者在C++中更方便(既适用于实现者,又可以使用接口的客户端),然而不幸的是,在API中使用的虚拟函数,其目的是二进制兼容最广泛的编译器,就像在GoCHAs的土地上玩扫雷器。 我建议避免使用虚拟功能,除非您的团队由min组成
struct Interface
{
    void* opaque_private_data;
    void (*func1)(struct Interface* self, ...);
    void (*func2)(struct Interface* self, ...);
    void (*func3)(struct Interface* self, ...);
};