关于如何通过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);
...
}
(注意:要求普遍接受的惯例,约定和/或工具不是要征求意见,而是要提供实际反馈)
答案 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();
};