C++;库交叉依赖关系-从llvm移植到gcc 我试图把我最初在Mac LLVM上写的一些C++代码移植到Windows CygWin GCC上。在这个项目中,我将一个exe静态链接到两个库(我使用的是cmake):
在C++;库交叉依赖关系-从llvm移植到gcc 我试图把我最初在Mac LLVM上写的一些C++代码移植到Windows CygWin GCC上。在这个项目中,我将一个exe静态链接到两个库(我使用的是cmake):,c++,gcc,cmake,llvm,clion,C++,Gcc,Cmake,Llvm,Clion,在lib1中有一个类,它声明了一个虚拟方法: lib1/Class1.h: class Class1 { public: void method1(); virtual void method2(); }; #include "Class1.h" class Class2 { private: Class1 c1; public: void call_c1(); }; lib1/Class1.cpp: #include "Class1.h" void Cl
lib1
中有一个类,它声明了一个虚拟方法:
lib1/Class1.h:
class Class1
{
public:
void method1();
virtual void method2();
};
#include "Class1.h"
class Class2
{
private:
Class1 c1;
public:
void call_c1();
};
lib1/Class1.cpp:
#include "Class1.h"
void Class1::method1() {
// do work
}
// Note that method2 is not defined!
#include "Class2.h"
void Class1::method2() {
// do some other work
}
void Class2::call_c1() {
c1.method2();
}
Class1::method2
不是从lib1
调用的,因此它可以正常工作
lib2
中定义了Class1::method2
:
lib2/Class2.h:
class Class1
{
public:
void method1();
virtual void method2();
};
#include "Class1.h"
class Class2
{
private:
Class1 c1;
public:
void call_c1();
};
lib2/Class2.cpp中的:
#include "Class1.h"
void Class1::method1() {
// do work
}
// Note that method2 is not defined!
#include "Class2.h"
void Class1::method2() {
// do some other work
}
void Class2::call_c1() {
c1.method2();
}
当我在MacOS下编译并将其与llvm链接时,所有这些都可以正常工作。当我尝试在Windows/Cygwin上使用gcc构建这个时,我遇到了各种链接器错误,比如未定义对vtable的引用或未定义对'Class1:method2'的引用。实际错误取决于target\u link\u库
call中lib的顺序
是否有任何命令行选项可以传递给gcc/cmake以使其正常工作?或者最好在Windows上考虑另一个工具链?事实上,我现在正在两个平台上使用IntelliJ CLion
提前感谢您的帮助。您在lib1中对Class1
的定义不完整-您将其定义为虚拟方法,而不是纯虚拟方法
尝试链接lib1时,必须为虚拟方法提供实现,否则链接将失败-链接器将无法找到任何void Class1::method2()
的实现,因为不存在任何实现。我不确定其他编译器是如何允许这一点的;也许这是其他编译器中的一个bug,或者您的构建环境与您所说的不完全一样
如果将该方法标记为纯虚拟方法,这将改善这种情况,因为它将允许编译lib1(尽管还有更多问题):
不过还有一些更大的问题-您试图在不同于Class1.cpp的文件中定义Class1的方法,然后尝试使Class2能够在Class1中编译和调用此方法
看起来您确实在尝试使用继承—您希望Class1::method2()是纯虚拟的,Class2从Class1继承,并让Class2为method2()提供一个实现
类别1.h:
class Class1
{
public:
void method1();
virtual void method2() = 0;
};
类别1.cpp:
#include "Class1.h"
void Class1::method1() {
// do work
}
类别2.h:
#include "Class1.h"
class Class2 : public Class1
{
public:
virtual void method2();
};
Class2.cpp:
#include "Class2.h"
// - Pay close attention to this small but important change.
// V
void Class2::method2() {
// do some other work
}
然后,您将直接调用method2(),而不是调用Class2::call_c1():
SomeFile.cpp:
#include "Class2.h"
int main() {
Class2 someInstance;
someInstance.method2();
}
编译器可能会向lib1发出Class1
的vtbl,其中包含对Class1::method2
的引用。如果lib2中的一个编译单元(即对象文件)定义了method1
,而另一个引用了Class1
,那么库就会相互依赖。由于链接器(至少GNULD)在默认情况下在一次传递模式下工作,因此必须两次指定lib2,一次在lib1之前,一次在lib1之后。因此,cmake指令是目标链接库(myexe lib2 lib1 lib2)
GNU ld还可以通过使用--start group
和--end group
来封装一组库,从而解决这些库之间的所有依赖关系,从而降低链接性能。您可以通过gcc通过-Wl、-start group
等方式传递它们。不过,我不知道如何让cmake做到这一点
这背后的原因是,库不是作为一个整体链接的,而是在编译单元的基础上链接的,因此,只有库中实际需要的部分最终会出现在可执行文件中。在本例中,当链接lib2时,从主程序引用Class2
会导致对Class1
vtbl的引用无法解析。链接lib1满足此引用,但会创建另一个未解析的lib1到Class1::method2
,因为此方法的地址是vtbl的一部分。所以链接器必须重新检查lib2才能解决这个问题
请注意,只有当lib2中引用vtbl的编译单元不是定义Class1::method2
的编译单元时,才会出现此问题;在这种情况下,符号定义已经存在,不需要第二次传递lib2。也许这就是为什么对你的问题的评论说你的例子很好
链接器以一次通过模式工作,从左到右检查库,因为这种类型的相互依赖性很少见,并且完全解析会降低性能(在核心内存不足且穿孔卡堆栈不容易随机访问的情况下,出于历史原因,这可能也是默认设置).您的示例在我的cygwin设置中运行良好,可能是您的cmake文件错误,或者您的示例没有正确地表示您的示例。请检查您在机器上发布的示例。同时发布你的cmake文件。提到您的g++版本可能会有所帮助。我不知道vtbl是在哪个编译单元中发出的,但这在clang和gcc之间可能有所不同。也许gcc会造成两个LIB相互依赖的情况。链接(至少使用GNU ld)是一个从左到右的过程,因此您必须将依赖的lib放在提供的lib之前,并且如果链接lib2引入了对lib1的新依赖,您可能需要多次指定lib,如-llib1-llib2-llib1
。@roland-w,非常感谢您的评论。您建议在cmake中的target\u link\u libraries()
中多次指定libs,这确实解决了问题。如果您将您的评论转换为答案,我会将其标记为正确。@tejas-您也是对的。我已经在我的cygwin环境中编写了这个示例,并进行了编译和链接。显然,没有链接的实际代码还有其他一些副作用,这些副作用导致了问题的出现。成员函数实际上是在哪个编译单元中定义的并不重要。无论如何,符号只能在链接时间解析。@RolandW-他的原始帖子似乎表明这是链接时间问题:“实际错误取决于顺序