通过C绑定公开C ++库

时间:2019-02-14 16:33:44

标签: c++ c

关于如何通过C绑定(作为库的一部分进行编译)公开C ++库,是否有任何可用的指南,习惯和最佳实践……。

目标是修改C ++类。

假设以下两个C ++类

class B {
   public:
    std::vector<int> list();
    std::string toString();
};

class A {
   public:
    explicit A(size_t size);
    B getB();
};

将这些作为C API公开的“最佳”方法是什么?处理指针和实例化堆上对象的函数列表,回调?是否有公认的命名约定列表?

extern "C" {

    A* A_new(size_t size);
    void A_delete(A* a);

    B* A_getB(A *a);

    void B_delete(B* b);
    ...
}

(注意:要求普遍接受的惯例,约定和/或工具不是要征求意见,而是要提供实际反馈)

3 个答案:

答案 0 :(得分:2)

我的经验是,要回答的基本问题是C ++对象是某种单例还是在接口C端是否存在这些对象的多个实例。

如果它们是单例,则C接口可以是一组函数,并且所有管理,对象句柄和其他位都位于接口的C ++端。库的用户不需要任何方式来引用特定的C ++对象。

但是,如果存在C ++对象的多个实例,则必须确定如何处理这些多个对象及其生存期的决定。我看到的主要两种方式是(1)提供指向C ++对象的指针到接口的C侧,或者(2)提供C ++对象的句柄到接口的C侧,并且该句柄引用a C ++方面的管理区域。

我个人喜欢handle方法,因为这样现代C ++实践可以用于管理对象的生存期。这些对象存储在某些C ++容器中,并为C接口提供了唯一的句柄或标识符,而C ++一方拥有这些对象。 C端正在通过C接口请求对对象执行特定操作。

C接口既可以是映射到各个C ++对象方法的唯一函数,也可以是通过指定C ++对象方法的唯一标识符来占用相当于命令标志的通用函数。

在第一个替代方案中,C ++对象的方法作为C接口函数公开,在多个实例的情况下,它采用指向C ++对象的指针或指向该对象的句柄。然后,用C ++编写的C接口函数使用适当的参数进行实际的C ++对象方法调用。因此,这只是一个C接口函数,将参数转发到C ++对象。

在第二种方法中,C ++接口函数必须使用查找表或switch或类似的方法来确定要调用的方法。可以在接口中指定参数,也可以使用变量参数。如果使用了可变参数,则存在解析参数列表,将参数编组然后再调用C ++对象方法的问题。

另一个考虑因素是命名和名称空间混乱。在C语言中,使用外部名称的传统三字母前缀方法,尽管在大多数情况下,更多的字符更有意义。

但是,我真正喜欢的方法是具有一个外部结构,该结构中包含指向构成接口的C函数的函数指针。通过这样做,您可以为结构命名一些对您的库有意义的名称,然后您可以随意命名实际的函数指针。

实际的C接口函数位于源文件中的static中,该文件仅公开包含函数指针的结构。该结构本身是一个全局变量,当在包含C接口函数的源文件的底部定义该结构时便会对其进行初始化。

答案 1 :(得分:1)

您提出的建议通常是处理此问题的最佳方法。您有一个函数,该函数可以将指针返回到新分配的对象,一组函数可以使用该指针执行各种操作,还有一个函数可以使用该指针并释放内存。

您唯一需要添加的就是与C兼容的类型的声明:

typedef struct A A;
typedef struct A B;

答案 2 :(得分:1)

您不应该在C接口(类B中的公共成员)中公开std类型,因为C ++ ABI尚未标准化,并且最终可能会导致难以理解的错误,从而使API使用者在使用其他API时编译器版本。

例如,在您的代码中,我将重写B如下:

class B {
   public:
    int* list(); // or write your own implementation for a vector type which you ship with the api
    const char* toString();
};