4

我一直在尝试用 C++ 开发一个可以在应用程序中运行时加载的动态库。我终于让它工作了,但它有点难看。我有一个函数,它将指向 C++ 类的指针作为参数,如下所示:

bool registerGrindPlugin( Grind::PluginManager* mgr );

但当然它被导出为:

_Z19registerGrindPluginPN5Grind13PluginManagerE

我尝试了一个带有简单函数的 .c 文件,它导出为“registerGrindPlugin”,但当然我不能以这种方式将 C++ 类作为参数传递。

Soo ...我的问题是,有没有办法解开或别名导出的符号,这样我就不必在我的 dlsym 调用中使用像 Z19registerGrindPluginPN5Grind13PluginManagerE 这样的怪物?

我确实看到了一些关于 -alias_list 作为链接器选项的信息,但我还没有完全弄清楚如何在 XCode 中使用它。如果这是解决方案,有人可以提供有关如何使用它的更多详细信息吗?

4

3 回答 3

4

从长远来看,您尝试这样做的方式不会奏效。

您不能指望任何特定的 C++ 修饰/去修饰算法。不同的编译器——甚至同一编译器的不同版本——使用了不同的编译器。因此,您可以这样做,并切换到新版本的 Xcode,然后陷入糟糕的境地。

此外,C++ 还存在脆弱的二进制接口问题。为了避免这种情况,Grind::PluginManager 实例内部的所有操作,从创建到成员访问到删除,都需要在同一个动态库中进行。

解决这些问题是 Objective C 的消息传递系统和 Windows OLE 系统背后的一些基本原理。

C++ 解决方案是使用包装系统。

首先,您需要定义一个不透明的指针类型来代替 Grind::PluginManager*。C 语言 Cocoa 绑定做了很多这样的事情。

typedef void* MyGrindPlugInManagerOpaqueHandle;

其次,对于您想从动态库外部对 Grind::PluginManager 执行的每个操作,您需要使用extern "C"未损坏的 C 绑定定义一个函数,并将这些不透明指针之一作为参数。例如:

#ifdef __cplusplus
extern "C" {
#endif

void foo_wrapper(MyGrindPlugInManagerOpaqueHandle *bar);

#ifdef __cplusplus
}
#endif

第三,C++ 文件中的实现如下所示:

void foo_wrapper(MyGrindPlugInManagerOpaqueHandle *bar)
{
    Grind::PluginManager* baz = (Grind::PluginManager*)bar;
    baz->foo();
}
于 2010-12-22T06:16:39.350 回答
2

通常,插件接口是用 c 命名和调用约定定义的。名称修饰可能取决于编译器(非标准)。

所以最简单的解决方案是定义一些接口函数并将其声明为extern "C"

extern "C"  {
    bool registerGrindPlugin( Grind::PluginManager* mgr );
}

您可能必须将 Grid::PluginManager 替换为 void* 类型和内部转换。这必须是一个简单的结构,而不是一个具有虚函数的类。

如果您确实坚持要处理损坏的名称,您可以查看“c++filt”的源代码,它是一个 gnu 实用程序,在 OS X 上也可用。

于 2010-12-22T05:49:39.973 回答
1

您可以通过在项目窗口的左侧窗格中选择要构建的目标并单击“信息”按钮来在 XCode 中设置链接器标志。在“构建”选项卡中,有一整个链接器设置部分,包括一个称为“其他链接器标志”的部分;这应该让您指定要尝试的任何链接器选项。

于 2010-12-22T05:47:27.583 回答