简体   繁体   English

如何在库之间传递对象和调用成员函数?

[英]How does passing objects and calling member functions between libraries work?

I am trying to understand what happens, when I include a class file of the core application to the compilation of a library (Qt-Plugin). 当我将核心应用程序的类文件包含在库的编译(Qt-Plugin)中时,我试图理解会发生什么。 Assume I have a plugin - a handler - and a Query( h , cpp ) (with private implementation) - the object to be handled. 假设我有一个插件 - 一个处理程序 - 和一个Query( hcpp )(带有私有实现) - 要处理的对象。

Edit 编辑

query.h (from link) 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;
};

I presumed that the compiler, at least at the linking stage, takes the object file and puts it into the shared object file. 我假设编译器,至少在链接阶段,获取目标文件并将其放入共享对象文件中。 But actually the name query does not appear in the output of the cmake compilation and linking process(essentially the g++ commands executed), just the includes of its directory. 但实际上名称查询没有出现在cmake编译和链接过程的输出中(实际上是执行的g ++命令),只是它的目录的包含。

When I compile the plugin/library does the compiler/linker do anything else but checking the interface/header? 当我编译插件/库时,编译器/链接器除了检查接口/头之外还做了什么吗? How can the plugin know anything about the Query at runtime? 插件如何在运行时了解有关Query的任何信息? How does the pluging call functions on an object at runtime? 插入调用如何在运行时对对象起作用?

How can the plugin know about the query at runtime? 插件如何在运行时了解查询?

Sharing information between different compilation units (dlls, shared objects, executables), is a problematic piece of design. 在不同的编译单元(dll,共享对象,可执行文件)之间共享信息是一个有问题的设计。

  • There is no standard for a C++ ABI. C ++ ABI没有标准。 That allows different compiler providers to layout their objects (eg where the vtable is, where the virtual destructor is in the vtable), and how to call methods differently even on the same machine. 这允许不同的编译器提供者布置它们的对象(例如,vtable的位置,虚拟析构函数在vtable中的位置),以及如何在同一台机器上以不同方式调用方法。
  • The .h file is a weak interface definition, and suffers that #define 's may be different between different compilers of the same thing. .h文件是一个弱接口定义,并且在相同事物的不同编译器之间可能存在#define的不同。 (eg Microsoft debug STL does not work with release STL). (例如,Microsoft调试STL不适用于发行版STL)。
  • inline functions stored in the .h may result in different implementations being called between the library and the plugin. 存储在.h中的内联函数可能导致在库和插件之间调用不同的实现。
  • Memory management may be compromised as the code freeing an object may not understand where and how it was allocated. 内存管理可能会受到影响,因为释放对象的代码可能无法理解分配的位置和方式。

Modifying the data 修改数据

Assuming a class has public members, (and both modules share a compiler) these can be modified in the library which created the object and the library which implemented it. 假设一个类有公共成员(并且两个模块共享一个编译器),可以在创建该对象的库和实现它的库中修改它们。

class Example1 {
    public:
      int value1;
};

in executable. 在可执行文件

example1.value1 = 12;

in plugin 在插件中

if( this->value1 == 12 ){
}

This does not work for complex objects eg std::string . 这不适用于复杂对象,例如std::string

Calling functions 调用函数

class Example2 {
      public:
      void AFunction();
};

Any caller of AFunction needs an implementation available. 任何AFunction调用者AFunction需要一个可用的实现。 This will be called statically, and may be shared between the binary and the shared-object 这将被静态调用,并且可以在二进制文件和共享对象之间共享

 +-------------------+          +-----------------------+
 | 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     |                       |
 +-------------------+          +-----------------------+

If the shared object had an implementation (it was an inline function, or the query.cpp was included in the shared-object makefile ), then the implementation of AFunction may be distinct. 如果共享对象具有实现(它是内联函数,或者query.cpp包含在共享对象makefile ),那么AFunction的实现可能是不同的。

**with STL - both binaries will have their own implementation, which if they are compiled at different times, may be different (and incompatible). **使用STL - 两个二进制文件都有自己的实现,如果它们在不同的时间编译,可能会有所不同(并且不兼容)。 ** **

The behavior of a shared object is such that if it has unresolved externals, which are satisfied by the binary which is loading it, it will use their implementation. 共享对象的行为是这样的,如果它具有未解析的外部,它正在加载它的二进制文件,它将使用它们的实现。 This is not true on windows, and windows behavior can be generated using -z, defs . 在Windows上不是这样,并且可以使用-z, defs生成窗口行为。

In order to call a non-virtual function, the caller needs to know about the class at compile time. 为了调用非虚函数,调用者需要在编译时了解该类。 The method is a fixed call, with the first (generally) parameter being the this pointer. 该方法是固定调用,第一个(通常)参数是this指针。 Thus to generate the code, the compiler calls directly (or through a fix-up table) the function. 因此,为了生成代码,编译器直接(或通过修复表)调用该函数。

Calling virtual functions 调用虚函数

Virtual functions are always called through the this pointer, which means that a virtual function for a class is 'chosen' by the code which constructs the object. 虚函数总是通过this指针调用,这意味着类的虚函数由构造对象的代码“选择”。 This is used in Windows for COM implementations, and is a useful technique for object sharing - allows new classes with different functionality to be delivered after a framework's compilation, yet without any knowledge call the implementation object. 这在Windows中用于COM实现,并且是一种用于对象共享的有用技术 - 允许在框架编译之后提供具有不同功能的新类,但是没有任何知识调用实现对象。

The vtable needs to be stable for this to work. vtable需要稳定才能工作。 The base class, or interface should be the same when the caller and the callee are compiled for this all to work. 当编译调用者和被调用者以使其全部起作用时,基类或接口应该是相同的。

When designing a library, it is possible to produce an interface object. 在设计库时,可以生成接口对象。

class ICallback {
     virtual void Funcion1( class MyData * data ) = 0;
};

When the library is being compiled, it does not know what implements ICallback and any of its functions, but it does know how to call those. 在编译库时,它不知道什么实现了 ICallback及其任何功能,但它确实知道如何调用它们。

So a function definition 所以一个函数定义

class Plugin {
     bool Process( ICallback * pCallback );
};

Allows a function to be declared and implemented, without knowing the implementation of the callback ( ICallback ). 允许声明和实现函数,而不知道回调的实现( ICallback )。 This does not create an unresolved symbol, nor does it require that the plugin knows about the item before the plugin is compiled. 这不会创建一个未解析的符号,也不需要插件在编译插件之前知道该项目。 All it requires, is that its caller ( m_pluginObject.Process( &myQueryImplementation ); ) has a concrete type created to pass in. 它需要的只是它的调用者( m_pluginObject.Process( &myQueryImplementation ); )具有一个传入的具体类型。

Compilation 汇编

When a compiler compiles code, it creates an object file ( .obj for windows and .o on unix). 当编译器编译代码时,它会创建一个目标文件(Windows上的.obj和unix上的.o )。

Within this file, is all the code and data definitions required to link the file. 在此文件中,是链接文件所需的所有代码和数据定义。

Notional object file 名义对象文件

<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>

This objecf file should have sufficient information within it to satisfy a part of the compilation process. 该objecf文件应该在其中包含足够的信息以满足编译过程的一部分。 Whilst normally a file such as MyClass.cc may have all of the functions needed to implement MyClass , it does not need to have all of these things. 虽然通常像MyClass.cc这样的文件可能具有实现MyClass所需的所有功能,但它不需要具备所有这些功能。

When the compiler is reading a header file, or any class declarations, it is creating a list of unresolved externals which it will need later. 当编译器正在读取头文件或任何类声明时,它正在创建一个未解析的外部列表,稍后将需要它。

 class Class1 {
       int ClassData;
    public:
        bool SomeFunction( char * value);
        ....
 };

Describes that there is a member function of Class1 which accepts char * as a value, and that the return value will be a bool . 描述了Class1的成员函数接受char *作为值,并且返回值将是bool When contuing to compile a C++ program, this unresolved function may be implemented when the compiler sees such as 在编译C ++程序时,可以在编译器看到这样的时候实现这个未解析的函数

  bool Class1::SomeFunction( char * value )
  {
     bool success = false;
     cout << value;
       // some work
      return success;
  }  

This implemented function is added to the dictionary of what is implemented, and the functions and data it needs are added to the requirements. 这个实现的函数被添加到实现的字典中,并且它所需的函数和数据被添加到需求中。

Library files 库文件

A library file is slightly different on unix and windows. unix和windows上的库文件略有不同。 Originally the unix library file was a container of .o files. 最初,unix库文件是.o文件的容器。 These were simply the concatenated items ( ar ) of the .o. 这些只是.o的连接项( ar )。 Then in order to find the correct items, the library was indexed ( ranlib ) to produce a working library. 然后,为了找到正确的项目,将库编入索引( ranlib )以生成工作库。 More recently I believe the standard of an archive has changed, but the concepts have to remain. 最近我认为档案的标准已经改变,但概念必须保留。

link library 链接库

In windows a link library is created when building a DLL, in unix, the link library is built into the shared-object. 在windows中,在构建DLL时会创建一个链接库,在unix中,链接库内置在共享对象中。

The link library is a list of the deliverables from the dynamically loaded object and the name of the .dll , .so which delivers it. 链接库是动态加载对象的可交付成果列表,以及提供它的.dll.so的名称。 This results in information being added to the binary such as :- 这导致信息被添加到二进制文件中,例如: -

<SharedObjects>
     printf : glibc:4.xx
</SharedObjects>

Describing the shared objects which are needed to be loaded, and the functions that they provide (the subset for this program). 描述需要加载的共享对象及其提供的功能(该程序的子集)。

Linking 链接

When the compiler is producing a binary ( .so , .dll , .exe or unix binary), then the object files specified on the command line are bound into the binary. 当编译器生成二进制文件( .so.dll.exe或unix二进制文件)时,命令行中指定的目标文件将绑定到二进制文件中。 This creates a set of implemented functions (eg main ), and a set of unresolved requirements. 这将创建一组已实现的函数(例如main )和一组未解决的需求。

Each library ( .a , .lib ) is then searched to see if they offer the functions required to make a complete process. 然后搜索每个库( .a.lib )以查看它们是否提供完成整个过程所需的功能。 If they do offer any function, then this is treated as resolved. 如果他们提供任何功能,则将其视为已解决。 The single object file which implements the resolved function is completely added to the binary. 实现已解析函数的单个目标文件完全添加到二进制文件中。

They may also have requirements, and these are :- 他们也可能有要求,这些是: -

  1. Resolved by the already loaded binary 由已加载的二进制文件解决
  2. Added to the unresolved values. 添加到未解析的值。

Note here, that the order of libraries is important, as only parts of the library required are added to the binary. 请注意,库的顺序很重要,因为只有部分库需要添加到二进制文件中。

On windows if this process succeeds, then all the functions required have been added. 在Windows上,如果此过程成功,则添加了所需的所有功能。

On unix, you may need to pass -z,defs SO : unresolved externals . 在unix上,你可能需要传递-z,defs SO:unresolved externals This allows a unix .so to have some of its requirements to be satisfied by the loading binary, but can result in an incomplete binary. 这允许unix .so通过加载二进制文件来满足它的一些要求,但是可能导致二进制文件不完整。

In summary 综上所述

A binary has :- 二进制有: -

  1. All the object files from the link command line. 链接命令行中的所有目标文件。
  2. Any of the object files from static libraries required to satisfy unresolved externals 来自静态库的任何目标文件都需要满足未解析的外部
  3. A list of shared objects and their functions required to deliver the working program. 提供工作程序所需的shared objects及其功能的列表。
  4. Using interfaces and base classes, allows new classes to be added after the original design has completed. 使用接口和基类,允许在原始设计完成后添加新类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM