为什么我无法使用gcc链接具有C接口的混合C/C++静态库?

4
我有一个混合的C/C++库。 外部提供C接口,使用extern C。内部有模板和类。使用"ar"创建库没有问题。文件名为libo-client.a。 然而,当使用gcc(而不是g++)链接.a文件时,会出现许多如下的错误:
libo-client.a(mysocket.o):(.rodata._ZTV7mStream[vtable for mStream]+0x10): undefined reference to `__cxa_pure_virtual'
...
mysocket.cpp:(.text+0x15ad): undefined reference to `operator new[](unsigned long)'
mysocket.cpp:(.text+0x15c1): undefined reference to `operator delete(void*)'
mysocket.cpp:(.text+0x167a): undefined reference to `__cxa_allocate_exception'
mysocket.cpp:(.text+0x16a6): undefined reference to `__cxa_throw'
...

我的编译/链接行看起来像这样:

gcc $(CFLAGS) $(INCLUDES) test2.c libo-client.a -o test2 

这里的 test2 是我的测试框架。

当我使用 g++ 时,不会出现这个问题。然而,我要将这个库接口到使用 gcc 编译的 C 项目中。如何解决这个问题?它的原因是什么?

编辑:

即使我没有使用标准的 C++ 库,它也需要一些像 operator new/delete 等东西,内部也有异常。

我正在将这个东西链接到 Xen hypervisor 上,所以我不确定我有什么其他选择,除了彻底重写这个东西或尝试使用 G++ 编译 Xen?


1
将其制作为动态库可能会有所帮助。你有研究过这个吗? - Chris Lutz
谢谢Chris。它实际上作为动态共享对象起作用。 - hookenz
5个回答

11

解决你问题最简单的方法是链接 g++; 这会将正确的库放在适当的位置。

问题在于,C++有许多C所没有的要求,并在调用 main() 之前完成更多的工作以确保事情被正确初始化。

如果你坚持使用C编译器进行链接,至少你需要在链接命令中包含C++支持库。


将一个C++库包含到C项目中不仅仅是简单地将目标文件扔进去。 - Carl Norum
切换到g++对我也起作用了,但有更便携的方法吗?如何为非Linux系统编写Makefile? - Edward Falk
@EdwardFalk:比使用C++编译器链接(部分)C++程序更具可移植性?不是的;那将是最可移植的方法。另一种选择是找出C++编译器如何调用链接器来完成C++工作,然后找出如何扭曲C编译器以以与C++编译器自动为您执行的方式相同的方式调用链接器。如果您愿意尝试,请随便。但是这会更加困难,并且更加不可移植。 - Jonathan Leffler
如果编译器不叫"g++",GNU并不是唯一的编译器。在make中是否有${CC++}宏,相当于${CC}? - Edward Falk
2
通常由宏${CXX}表示。如果您的C编译器是gcc,则C++编译器通常是g++。如果您使用不同的编译器,则仍然需要链接使用用于构建C++对象模块的C++编译器。 - Jonathan Leffler
完美;这正是我在寻找的。 - Edward Falk

2

你的C++库在内部仍然是一个C++库,这意味着它包含对C++标准库各种housekeeping函数的外部引用。为了解决这些链接,你需要将最终可执行文件链接到C++标准库。 gcc 不会自动完成这个过程。你可以显式地向 gcc 提供 C++ 标准库,或者使用 g++ 编译器。


1

一个存档仍然只是一堆目标文件(.o),因此在将它们链接到可执行文件或共享库时,您仍然需要使用g++链接以解析符号到C++运行时(例如您在错误消息中看到的符号)。

为了使您的C函数具有C链接,将它们包装在extern "C"块中应该就足够了。在某些情况下可能会更加复杂,例如Windows,它有无数的链接宏(STDCALL、WINAPI等),这些宏会扩展为其他内容(一些编译器特定的),如__stdcall、__declspec(export)等。


1
你看到的错误似乎是由于缺少与标准C++库的链接导致的,该库具有operator new、operator delete等符号定义。尝试链接stdc++,例如:gcc $(CFLAGS) $(INCLUDES) test2.c libo-client.a -o test2 -lstdc++。更好的选择是像Jonathan Leffler建议的那样使用g++而不是gcc。

0

用C++编写可以从C中使用而不需要任何C++要求的额外内容是非常困难的。可能甚至取决于编译器。您的库需要C++运行时,并且在调用程序中的main()之前初始化C++运行时可能需要特殊支持。即使您将C文件链接到其中,您的chimaera仍然是一个C++程序,即使它是main()和库之外的所有其他内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接