【问题标题】:How to embed a C++ library in a C library?如何在 C 库中嵌入 C++ 库?
【发布时间】:2014-05-24 02:17:35
【问题描述】:

我有一个关于将一个库嵌入另一个库的问题。

我有一个纯 C 代码,我的用户依赖它,他们不想依赖 C++ 库。但是,需要将第 3 方库 (ICU) 嵌入到我的库中。不会导出任何 ICU 功能,它们只会在我的库内部使用。不幸的是,ICU 是一个 C++ 库,尽管它确实有一个 C 包装器。 ICU 不使用异常,但确实使用 RTTI(抽象基类)。

问题是如何创建我的静态库,以便

  1. ICU 嵌入在我的库中(所有对 ICU 函数的引用都在我的库中解析)
  2. 所有对 libstdc++ 的引用也已解决,必要的代码已嵌入到我的库中
  3. 如果用户甚至没有在他们的系统上安装 libstdc++,则一切正常
  4. 如果用户碰巧在 C++ 项目中使用了我的库,那么与他使用的任何 libstdc++(可能是系统 libstdc++)都不会发生冲突。

这可能吗?目标平台几乎是一切:windows(我的库是动态的)和各种 unix 版本(linux、solaris、aix、hpux - 我的库需要是静态的)。

gcc-4.5 及更高版本确实有 --static-libstdc++,但据我了解,它仅用于创建共享库或可执行文件,而不是静态库。

感谢您的帮助!

【问题讨论】:

  • 这可能是不可能的.. 至少在“我的经验”中。使用特定版本的 libstdc++ 编译的库似乎并不总是与另一个版本兼容。通常会给出链接器错误。即使对于 Visual Studio。我注意到像 OpenCV 这样的库提供了不同的版本。 VC10、VC11、VC12 等(我昨晚构建的).. 我以前做的是将所有内容编译成它们的 .o 文件。然后运行ar -rcs *.o o 文件将是所有依赖项,如 zlib、libpng 等。但不要认为你可以摆脱 libstd ......我也很想回答这个问题。
  • dlopen+dlsym 可能是在不创建编译时依赖项的情况下从 C 中使用 ICU 的方式。
  • @ArtemGr :这很好,除了一件事:我需要分发 icu 库,因为我不能假设用户拥有它。但是,当然,我需要编译它。并且(假设我在 linux 上)我在构建机器上的编译器的 C++ ABI 可能与用户在他的机器上的 C++ ABI 不兼容(例如,对于 RTTI 分辨率)。因此,如果我对已分发的 icu 库进行 dlopen+dlsym,则在开始运行时可能会遇到问题。这就是为什么我想以这种方式解决和绑定所有内容并创建我的库。
  • 在 UNIX 相关操作系统上正确的做法是安装系统 ICU。如果你绝对不能这样做,那么人迹罕至的路径是从源代码构建 ICU(构建指令的一个示例:ceph.com/docs/master/install/build-ceph),但这是错误的路径,除非你想要您的用户可以使用未来的安全补丁手动升级 ICU。
  • 我并不是说使用系统范围的 ICU 安装不是最好的。我是说这对我来说不是一个选项:-(。鉴于这不是一个选项,我想让我的用户免于任何可能的 C++ ABI 冲突。

标签: c++ c linker static-linking


【解决方案1】:

这个问题的解决方法很简单,但可能不在你设置的参数范围内。

简单的规则是:

  1. 可以将 C++ 库动态链接到 C 调用程序,但您必须将库包装在外部 C 层中。您设计外部 C API 并使用 C++ 内部实现它,这些内部永远隐藏在视图之外。 [您也可以使用 COM 或 .NET 在 Windows 上执行此操作。]
  2. 不能将 C++ 库静态链接到 C 调用程序。库不同,调用序列/链接器符号不同。 [您通常甚至无法在同一编译器的不同版本之间进行静态链接,而且几乎从不可以在不同编译器之间进行链接。]

换句话说,解决方案很简单:使用动态链接。如果这不是正确的答案,那么我认为没有。

为了让事情变得有趣,您甚至可以实现自己的插件架构。这只是动态链接的另一个名称,但您可以选择 API。


为了清楚起见,我能看到的唯一可行的可移植选项是将 ICU 链接到它自己的动态库(DLL 或 SO)中。它的符号、C++ 库、RTTI 和异常都保留在其中。您的静态库通过 extern C 链接到 ICU 动态库。这正是构建了多少 Windows:DLL 中的 C++、extern C。

可以跨界调试,但不能导出类​​型信息。如果您需要这样做,则必须使用不同的 API,例如 .NET 或 COM。

【讨论】:

  • 是的,这就是 ICU C 包装器的工作方式,它将 ICU C++ 库包装到外部 C 接口。这就是我想在我的 C 库中使用的接口。所以那部分很清楚。尚不清楚在创建库时是否可以绑定 ICU 库、其 C 包装器和 libstdc++ 的符号?恐怕是不可能的
  • 如果我希望我的库是静态的,我希望如果我的库是共享的(如果是,如何?)。请注意,绑定必须包含所有 RTTI 信息,这不是没有问题的,因为可以保证我已经拥有所有可能的调用实例,也许我可以向活页夹指出这一点。另一方面,ICU 不使用异常(到目前为止),所以我不需要处理它。
  • 从您的编辑看来,如果我可以摆脱对我的库的静态库要求,然后在一个共享库中我可以放入我所有的东西、ICU、ICU 的符号、C++ 库、RTTI 和以某种方式隐藏所有 C++ 部分,并且当运行时链接发生在执行开始时(或执行期间的任何时间)时,与我的共享库链接的可执行文件不会访问 C++ 库。您能否提供一些提示,我应该在 RTFM 中查看哪些命令行标志?
  • 如果两者都使用相同的编译器编译,您只能将您的“东西”放在与 ICU 共享的库中。除此之外,我真的不清楚你在说什么。
  • 是的,我有这两个库的源代码,我可以使用相同的编译器进行编译(好吧,我的代码将使用 C 前端编译,ICU 使用 C++ 前端编译)。我想说的是,最终目标是将所有东西都放在一个 libfoo.so 中,它只提供我的 C 接口,尽管里面有 C 和 C++(我的和 ICU 的)。但是,与内部 C++ 部分相关的所有内容都已解决,并正确绑定在 libfoo.so 中,如果我的用户链接到 libfoo.so,则不会有系统 C++ 库依赖项。
【解决方案2】:

我不知道这是否可行,但至少让我建议您尝试一下!

优秀的LLVM project(clang编译器的起源)有很多不同语言的前端和后端,比如C++和C。根据this S.O. question,LLVM应该可以将C++编译成C反过来可以正常编译。

我想这条路线是一条崎岖不平的路线,但如果它有效,它可能会解决您的问题而无需动态链接。这完全取决于 ICU 是否会使用 LLVM C++ 进行编译。

如果您决定试一试,请告诉我们您的情况!

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2013-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-20
  • 2014-05-12
  • 1970-01-01
相关资源
最近更新 更多