在C中,我习惯于编写一个可以从任何客户端代码调用的共享库,只需链接库并包含相关的头文件即可使用它。但是,我read that C++'s ABI过于易变,非标准,无法从其他来源可靠地调用函数。
这会让我相信在C ++中创建像C一样普遍的真正共享库是不可能的,但实际的实现似乎表明不是这样。例如,Node.js公开了一个非常简单的模块系统,该系统允许使用extern "C"
函数的普通C ++函数(NODE_SET_METHOD
} exported dynamically。
C ++ API的哪些元素可以公开,如果有的话,以及允许C ++代码与其他C ++代码交互的常用方法是什么?是否可以创建可以公开C ++类的共享库?或者由于ABI不一致,必须为每个程序单独重新编译这些类吗?
答案 0 :(得分:4)
是的,C ++互操作很困难并且充满了陷阱。冷硬的规则是你必须使用完全相同的编译器版本和完全相同的编译器设置来构建模块并确保它们共享完全相同的CRT和标准C ++库。当一个模块使用与删除对象的模块不同的分配器分配对象时,打破这些规则往往会使得C ++类在分界的任何一端都没有相同的布局和内存管理的麻烦。当代码使用错误的偏移来访问类成员并泄漏内存或破坏堆时,导致很难诊断运行时失败的问题。
Node.js首先通过不导出任何内容来避免这些问题。 NODE_SET_METHOD()不会按照您的想法执行操作,它只是在Javascript引擎的符号表中添加一个符号,以及在脚本中调用函数时调用的函数指针。此外,它是一个开源项目,因此使用相同的编译器和运行时库构建所有不是问题。
答案 1 :(得分:1)
此
例如,Node.js公开了一个允许的非常简单的模块系统 简单的C ++函数(没有extern“C”)可以动态导出 使用NODE_SET_METHOD函数。
错了,你可以看到他们在extern "C"
函数中使用init()
,这显然是node.js调用的函数,然后将函数转发到C ++他们想要的功能,没有曝光。
正如本问题How does an extern "C" declaration work?中所解释的那样 - 当编译器编译代码时,它会破坏函数名,类名和命名空间名。这样做的原因是因为很容易出现名称冲突,例如重载函数。
在此处阅读更多内容:http://en.wikipedia.org/wiki/Name_mangling
引用和查找函数的唯一方法是使用extern "C"
声明,这会强制编译器不破坏名称。即在上面的示例中,函数init
将被称为init
,其中函数foo
将被称为_ugAGE
(我做了这个,因为它没有重要的是,它不适合人类消费)
总之,您可以将任何C ++公开给任何其他语言,但是库的入口点必须是一个或多个extern "C"
全局函数,因为它们是引用未编码名称的唯一方法。
答案 2 :(得分:0)
C和C ++标准都没有定义ABI。这完全取决于实施。使C ++共享/动态库变得更难的原因是C ++添加了诸如类,多态,模板,异常,函数重载,STL等内容......
所以,真正的信息来源是你的编译器'文档,以及库API的一组相应指南,以避免与您的库构建的任何实现有任何问题。它在C ++中更难(指南集可能比C大得多,你可能不得不使用C ++的一个子集),但并非不可能。