共有ライブラリの作成

以下のセクションでは、共有ライブラリを作成する際に注意すべき点を挙げています。

共有ライブラリのシンボルの使用

アプリケーションや他のライブラリなどのクライアントが使用することを意図した共有ライブラリに含まれるシンボル(関数、変数、クラス)は、特別な方法でマークする必要があります。これらのシンボルはパブリック・シンボルと呼ばれ、エクスポートされたり一般に公開されたりします。

残りのシンボルは外部から見えないようにする。ほとんどのプラットフォームでは、コンパイラーはデフォルトでこれらのシンボルを非表示にします。一部のプラットフォームでは、これらのシンボルを非表示にするには特別なコンパイラー・オプションが必要です。

共有ライブラリをコンパイルする際には、エクスポート用にマークしておく必要があります。クライアントから共有ライブラリを使用するには、プラットフォームによっては特別なインポート宣言も必要になります。

ターゲットプラットフォームによっては、Qt が必要な定義を含む特別なマクロを提供しています:

  • Q_DECL_EXPORT は、共有ライブラリをコンパイルする際に使用するシンボルの宣言に追加する必要があります。
  • Q_DECL_IMPORT 共有ライブラリを使用するクライアントをコンパイルするときに使用するシンボルの宣言に追加する必要があります。

ここで、共有ライブラリ自体をコンパイルする場合でも、共有ライブラリを使用するクライアントだけをコンパイルする場合でも、正しいマクロが呼び出されるようにする必要があります。通常、これは特別なヘッダーを追加することで解決できる。

例えば、mysharedlibという共有ライブラリを作りたいとします。このライブラリの特別なヘッダー、mysharedlib_global.h は次のようになります:

#include <QtCore/QtGlobal>

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

ライブラリーの各ヘッダーで、以下のように指定する:

#include "mysharedlib_global.h"

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

次に、ライブラリ自体をビルドする際に、MYSHAREDLIB_LIBRARY がコンパイラ用に定義されていることを確認する必要がある。これは、ライブラリーのビルド・システムで行う。CMakeを使用する場合は、共有ライブラリーのターゲットを拡張します:

target_compile_definitions(mysharedlib PRIVATE MYSHAREDLIB_LIBRARY)

qmakeを使う場合は、共有ライブラリーの ファイルに

DEFINES += MYSHAREDLIB_LIBRARY

を共有ライブラリーの.pro ファイルに追加します。

注意: Qt CreatorQt VS Toolsのライブラリウィザードは、これらの設定を行うスケルトンを提供します。

ヘッダーファイルに関する考察

通常、クライアントは共有ライブラリのパブリックヘッダーファイルのみをインクルードします。これらのライブラリは、デプロイ時に別の場所にインストールされるかもしれません。したがって、共有ライブラリのビルド時に使用された他の内部ヘッダーファイルを除外することが重要です。

例えば、共有ライブラリがハードウェア・デバイスをラップするクラスを提供し、サードパーティ・ライブラリによって提供されたそのデバイスへのハンドルを含んでいるかもしれません:

#include <footronics/device.h>

class MyDevice {
private:
    FOOTRONICS_DEVICE_HANDLE handle;
};

Qt Widgets Designer で作成されたフォームで集約や多重継承を使用する場合、同様の状況が発生します:

#include "ui_widget.h"

class MyWidget : public QWidget {
private:
    Ui::MyWidget m_ui;
};

ライブラリをデプロイする際、内部ヘッダfootronics/device.hui_widget.h への依存はあってはなりません。

これは、さまざまなC++プログラミングの本で説明されている実装へのポインタというイディオムを利用することで回避できます。値セマンティクスを持つクラスについては、QSharedDataPointer の使用を検討してください。

バイナリの互換性

共有ライブラリーをロードするクライアントが正しく動作するためには、使用されるクラスのメモリー・レイアウトが、クライアントのコンパイルに使用されたライブラリー・バージョンのメモリー・レイアウトと正確に一致していなければなりません。言い換えれば、クライアントが実行時に見つけるライブラリは、コンパイル時に使用されたバージョンとバイナリ互換性がなければなりません。

クライアントが自己完結型のソフトウェア・パッケージで、必要なライブラ リをすべて出荷している場合、これは通常問題にはなりません。

しかし、クライアント・アプリケーションが、別のインストール・パッケージやオペレーティング・システムに属する共有ライブラリに依存している場合は、共有ライブラリのバージョン管理方式を考え、どのレベルでバイナリ互換性を維持するかを決定する必要があります。例えば、同じメジャーバージョン番号のQtライブラリは、バイナリ互換性が保証されています。

バイナリ互換性を維持することで、クラスに加えられる変更に制限が生じます。KDE - Policies/Binary Compatibility Issues With C++ に良い説明があります。これらの問題は、ライブラリ設計の最初から考慮されるべきです。可能な限り、情報隠蔽の原則と実装へのポインタのテクニックを使用することを推奨します。

©2024 The Qt Company Ltd. 本書に含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。