How to create a shared library

Symbols and their visibility marks

Symbols in the shared library represent functions, variables, and classes in the source code. Symbols need to be output before they can be used by the client. The client of the shared library refers to the application or other libraries. We can output all the symbols in the source code, or by selecting which symbols are publicly visible and which symbols are invisible, we need A special means to specify this attribute of the symbol. These visible ones are called public symbols. This process is called exporting or making the symbols public visible. Symbols other than these public symbols are not visible to the outside world.

The compilers of most platforms will hide symbols by default, and the compilers of a few platforms need to use compiler options to hide them. When we compile a shared library, variables must be marked as exported; when using a shared library, some compilers may also need to import (import) mark declaration.

Depending on your target platform, QT provides some special macros containing the necessary import and export definitions:

  • Q_DECL_EXPORT compile the dynamic library, add this macro in the declaration to indicate export
  • Q_DECL_IMPORT compile the client to add this macro in the statement to indicate import

In this way, we need to ensure that the correct macro is called at the right time, without worrying about whether we are compiling a dynamic library or using a dynamic library. Usually, we will add a special header file to solve this problem. Let's first assume that our dynamic library is called mysharedlib, and a special header file mysharedlib_global.hwill be created. Its content is roughly like this:

#include <QtCore/QtGlobal>

#if defined(MYSHAREDLIB_LIBRARY)
#  define MYSHAREDLIB_EXPORT Q_DECL_EXPORT
#else
#  define MYSHAREDLIB_EXPORT Q_DECL_IMPORT
#endif

In the pro file, we add a definition to indicate that this is a shared library compilation:

DEFINES += MYSHAREDLIB_LIBRARY

The header file in each library will contain this special header file, like this:

#include "mysharedlib_global.h"

MYSHAREDLIB_EXPORT void foo();
class MYSHAREDLIB_EXPORT MyClass...

In this way, we can guarantee Q_DECL_EXPORTand Q_DECL_IMPORTuse it correctly. You can see the application of this method in the source file of Qt.

Header file dependency problem

Normally, the client only includes the common header files of the shared library. When compiling a static library, other third-party libraries may be referenced, and the location of the third-party library may be incorrect or not found due to deployment. It is very necessary to remove the built-in header files used when compiling the dynamic library.

for example:

The static library may export a hardware-related handle, which is usually provided by a third party.

#include <footronics/device.h>
class MyDevice {
    
    
private:
    FOOTRONICS_DEVICE_HANDLE handle;
};

When you use this dynamic library, the dynamic library may not be found.

The same situation also appears in Qt Disigner, when we use multiple inheritance (multiple inheritance) or aggregation (aggregation):

#include "ui_widget.h"
class MyWidget : public QWidget {
    
    
private:
    Ui::MyWidget m_ui;
};

When we finish deploying a library, it may appear footronics/device.hor ui_widget.h cannot find the built-in header file. This can be avoided by using the implementation idiom pointer described in various C++ programming books. For classes with value semantics, consider using QSharedDataPointer.

Binary compatibility

For clients loading shared libraries to work properly, the memory layout of the classes used must exactly match the memory layout of the library version used to compile the client. In other words, the library found by the client at runtime must be binary compatible with the version used at compile time.

If the client is a standalone software package that provides all the libraries it needs, then this is usually not a problem.

However, if the client application depends on shared libraries belonging to different installation packages or operating systems, then we need to consider the version control scheme of the shared library and decide at which level to maintain binary compatibility. For example, Qt libraries with the same major version number are guaranteed to be binary compatible.

Maintaining binary compatibility imposes some restrictions on the changes that can be made to the class. A good explanation can be found using KDE strategy/binary compatibility issues in C++. These issues should be considered from the beginning of the dynamic library design. We recommend the use of information hiding principles and pointer implementation techniques as much as possible.

[1] QT Help Document

Guess you like

Origin blog.csdn.net/weixin_39258979/article/details/113768192