当我将核心应用程序的类文件包含在库的编译(Qt-Plugin)中时,我试图理解会发生什么.假设我有一个插件 - 一个处理程序 - 和一个Query(h,cpp)(带有私有实现) - 要处理的对象.
query.h(来自链接)
class Query final
{
public:
friend class ExtensionManager;
Query(const QString &term);
~Query();
void addMatch(shared_ptr<AlbertItem> item, short score = 0);
void reset();
void setValid(bool b = true);
bool isValid();
private:
QueryPrivate *impl;
};
Run Code Online (Sandbox Code Playgroud)
我假设编译器,至少在链接阶段,获取目标文件并将其放入共享对象文件中.但实际上名称查询没有出现在cmake编译和链接过程的输出中(实际上是执行的g ++命令),只是它的目录的包含.
当我编译插件/库时,编译器/链接器除了检查接口/头之外还做了什么吗?插件如何在运行时了解有关Query的任何信息?插入调用如何在运行时对对象起作用?
插件如何在运行时知道查询?
在不同编译单元(dll、共享对象、可执行文件)之间共享信息是一个有问题的设计。
.h文件是一个弱接口定义,并且#define同一事物的不同编译器之间可能会有所不同。(例如,Microsoft 调试STL 不能与发布STL 一起使用)。假设一个类具有公共成员(并且两个模块共享一个编译器),可以在创建该对象的库和实现该对象的库中修改这些成员。
class Example1 {
public:
int value1;
};
Run Code Online (Sandbox Code Playgroud)
在可执行文件中。
example1.value1 = 12;
Run Code Online (Sandbox Code Playgroud)
在插件中
if( this->value1 == 12 ){
}
Run Code Online (Sandbox Code Playgroud)
这不适用于复杂的对象,例如std::string。
class Example2 {
public:
void AFunction();
};
Run Code Online (Sandbox Code Playgroud)
任何调用者都AFunction需要一个可用的实现。这将被静态调用,并且可以在二进制文件和共享对象之间共享
+-------------------+ +-----------------------+
| binary | | shared object |
| Query::AFunction()| | |
| { | | Process( Query &q ) |
| } | | { |
| | o--> | q.AFunction(); | <<< may be in
| main() | | | | shared object
| { | | | | could call binary
| Query q; | | | |
| Process( q ); | ===o | |
+-------------------+ +-----------------------+
Run Code Online (Sandbox Code Playgroud)
如果共享对象有一个实现(它是一个内联函数,或者 query.cpp 包含在共享对象中makefile),那么 的实现AFunction可能是不同的。
**使用STL - 两个二进制文件都有自己的实现,如果它们在不同时间编译,可能会不同(并且不兼容)。**
共享对象的行为是这样的:如果它具有未解析的外部对象(加载它的二进制文件满足这些外部对象),它将使用它们的实现。在 Windows 上情况并非如此,可以使用 生成 Windows 行为-z, defs。
为了调用非虚函数,调用者需要在编译时了解该类。该方法是固定调用,第一个(通常)参数是 this 指针。因此,为了生成代码,编译器直接(或通过修复表)调用该函数。
虚函数总是通过 this 指针调用,这意味着类的虚函数是由构造对象的代码“选择”的。这在 Windows 中用于 COM 实现,并且是一种有用的对象共享技术 - 允许在框架编译后交付具有不同功能的新类,而无需任何知识来调用实现对象。
vtable 需要稳定才能正常工作。当调用者和被调用者编译时,基类或接口应该相同,以便这一切都能正常工作。
设计库时,可以生成接口对象。
class ICallback {
virtual void Funcion1( class MyData * data ) = 0;
};
Run Code Online (Sandbox Code Playgroud)
当编译库时,它不知道什么实现了ICallback 及其任何函数,但它确实知道如何调用它们。
所以一个函数定义
class Plugin {
bool Process( ICallback * pCallback );
};
Run Code Online (Sandbox Code Playgroud)
允许在不知道回调 ( ) 的实现的情况下声明和实现函数ICallback。这不会创建未解析的符号,也不要求插件在编译插件之前了解该项目。它所需要的只是它的调用者(m_pluginObject.Process( &myQueryImplementation );)创建了一个具体类型来传递。
当编译器编译代码时,它会创建一个目标文件(.obj对于 Windows 和.oUNIX)。
该文件中包含链接该文件所需的所有代码和数据定义。
<dictionary>
int SomeIntValue = Address1
bool Class1::SomeFunction( char * value ) = Address2
</dictionary>
<Requires>
std::ostream::operator<<( const char *);
std::cout
</Requires>
<Data>
Address1 : SomeIntValue = 12
</Data>
<Code>
Address2 .MangledSomeFunctionCharStarBool
// some assembly
call ostream::operator<<(char*)
</Code>
Run Code Online (Sandbox Code Playgroud)
该 objectcf 文件中应包含足够的信息来满足部分编译过程的需要。虽然通常情况下,诸如 之类的文件MyClass.cc可能具有实现所需的所有功能MyClass,但它不需要具有所有这些功能。
当编译器读取头文件或任何类声明时,它会创建一个稍后需要的未解析的外部列表。
class Class1 {
int ClassData;
public:
bool SomeFunction( char * value);
....
};
Run Code Online (Sandbox Code Playgroud)
描述 Class1 有一个成员函数,它接受char *一个值,并且返回值为bool. 当继续编译 C++ 程序时,当编译器看到诸如以下内容时,可能会实现此未解析的函数
bool Class1::SomeFunction( char * value )
{
bool success = false;
cout << value;
// some work
return success;
}
Run Code Online (Sandbox Code Playgroud)
这个实现的功能被添加到已实现的字典中,并且它需要的功能和数据被添加到需求中。
UNIX 和 Windows 上的库文件略有不同。最初,unix 库文件是 .o 文件的容器。这些只是ar.o 的串联项 ( )。然后,为了找到正确的项目,对库进行索引 ( ranlib) 以生成工作库。最近,我相信档案的标准已经改变,但概念必须保留。
在windows中,链接库是在构建DLL时创建的,在unix中,链接库被构建到共享对象中。
链接库是动态加载对象的可交付成果的列表以及交付该对象的.dll名称.so。这会导致信息被添加到二进制文件中,例如:-
<SharedObjects>
printf : glibc:4.xx
</SharedObjects>
Run Code Online (Sandbox Code Playgroud)
描述需要加载的共享对象以及它们提供的功能(该程序的子集)。
当编译器生成二进制文件(.so、.dll或.exeunix 二进制文件)时,命令行上指定的目标文件将绑定到该二进制文件中。这创建了一组已实现的功能(例如main)和一组未解决的需求。
然后搜索每个库(.a、.lib)以查看它们是否提供完成完整过程所需的功能。如果它们确实提供了任何功能,那么这将被视为已解决。实现已解析函数的单个目标文件已完全添加到二进制文件中。
他们可能也有要求,这些要求是:-
请注意,库的顺序很重要,因为仅将所需库的部分添加到二进制文件中。
在 Windows 上,如果此过程成功,则所有所需的功能都已添加。
在 unix 上,您可能需要传递-z,defs SO : unresolved externals。这允许加载二进制文件来满足 unix .so 的一些要求,但可能会导致二进制文件不完整。
二进制文件具有:-
shared objects交付工作计划所需的列表及其功能。