| (同じ利用者による、間の5版が非表示) | |||
| 1行目: | 1行目: | ||
== 概要 == | == 概要 == | ||
QML6およびC++を組み合わせて使用する場合、ライブラリを用いることにより、コードの再利用性、モジュール性、更新の容易さを向上させることができる。<br> | |||
ただし、プロジェクトの複雑性が増すため、デバッグや依存関係の管理に注意が必要である。<br> | |||
<br> | |||
適切に設計および管理することにより、大規模なアプリケーションの開発効率を向上させることが可能となる。<br> | |||
<br> | |||
ライブラリを使用するメリットを、以下に示す。<br> | |||
* 再利用性 | |||
*: ライブラリとして共通機能を分離することにより、他のプロジェクトでも同じライブラリを再利用できる。 | |||
* モジュール性 | |||
*: 大規模なプロジェクトを複数のモジュールに分割することにより、コードの管理と保守が容易になる。 | |||
* 更新の容易さ | |||
*: ライブラリを更新する場合、実行ファイルプロジェクトを再コンパイルする必要がなく、ライブラリを置き換えるだけで済む。 | |||
* ロード時の選択性 | |||
*: 必要な時にのみライブラリを読み込むことにより、メモリ使用量を削減できる。 | |||
*: また、動的ロードを活用してプラグイン機構を構築することも可能である。 | |||
<br> | |||
ライブラリを使用するデメリットを、以下に示す。<br> | |||
* 複雑性の増加 | |||
*: 動的ライブラリを使用することにより、プロジェクト構成やビルド設定が複雑になる。 | |||
*: また、動的リンクに関する知識が必要である。 | |||
* デバッグの難しさ | |||
*: ライブラリ内をデバッグする時、実行ファイルとライブラリの間で問題を追跡するのが難しくなることがある。 | |||
* パフォーマンスオーバーヘッド | |||
*: ライブラリを動的ロードする時に、多少のパフォーマンスオーバーヘッドが発生する場合がある。 | |||
*: ただし、これは通常ごく僅かである。 | |||
* 依存関係の管理 | |||
*: ライブラリのバージョン管理や依存関係の管理が必要になる。 | |||
*: 特に異なるバージョンのライブラリを使用するプロジェクト間での互換性を維持することが課題となる場合がある。 | |||
<br><br> | <br><br> | ||
| 17行目: | 44行目: | ||
<br> | <br> | ||
以下の例では、動的ライブラリとしてQMLの<code>Window</code>コンポーネントを作成している。<br> | 以下の例では、動的ライブラリとしてQMLの<code>Window</code>コンポーネントを作成している。<br> | ||
<syntaxhighlight lang="cmake"> | |||
# CMakeLists.txtファイル | |||
# ...略 | |||
project(LibSample LANGUAGES CXX) | |||
set(CMAKE_CXX_STANDARD 17) | |||
set(CMAKE_CXX_STANDARD_REQUIRED ON) | |||
set(CMAKE_AUTOMOC ON) | |||
find_package(Qt6 REQUIRED COMPONENTS Core Quick) | |||
add_library(LibSample SHARED | |||
LibSample_global.h | |||
LibSample.cpp | |||
LibSample.h | |||
) | |||
target_link_libraries(LibSample PRIVATE | |||
Qt6::Core | |||
Qt6::Quick | |||
) | |||
qt_add_qml_module(LibSample | |||
URI Lib | |||
VERSION 1.0 | |||
QML_FILES LibSample.qml | |||
) | |||
target_compile_definitions(LibSample PRIVATE | |||
LIBSAMPLE_LIBRARY | |||
) | |||
</syntaxhighlight> | |||
<br> | |||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
// LibSample_global.hファイル | // LibSample_global.hファイル | ||
| 50行目: | 113行目: | ||
#include <QQmlComponent> | #include <QQmlComponent> | ||
#include <QtQml/qqmlengine.h> | #include <QtQml/qqmlengine.h> | ||
#include " | #include "LibSample_global.h" | ||
class LIBSAMPLE_EXPORT LibSample : public QObject | class LIBSAMPLE_EXPORT LibSample : public QObject | ||
| 81行目: | 144行目: | ||
void LibSample::showQmlWindow() | void LibSample::showQmlWindow() | ||
{ | { | ||
QQmlComponent component(m_engine, "qrc:/ | QQmlComponent component(m_engine, "qrc:/qt/qml/LibSample.qml"); | ||
QObject *object = component.create(); | QObject *object = component.create(); | ||
| 93行目: | 156行目: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | <br> | ||
<syntaxhighlight lang="qml"> | |||
// LibSample.qmlファイル | |||
import QtQuick | |||
import QtQuick.Window | |||
import QtQuick.Controls | |||
import QtQuick.Layouts | |||
Window { | |||
id: libWindow | |||
width: 300 | |||
height: 200 | |||
visible: true | |||
title: qsTr("LibSample QML Window") | |||
ColumnLayout { | |||
anchors.fill: parent | |||
anchors.margins: 10 | |||
Text { | |||
Layout.alignment: Qt.AlignHCenter | |||
text: "Hello from Hoge QML" | |||
font.pixelSize: 18 | |||
color: "blue" | |||
} | |||
Button { | |||
Layout.alignment: Qt.AlignHCenter | |||
text: "Click Me" | |||
onClicked: { | |||
messageText.visible = true | |||
libWindow.close() | |||
} | |||
} | |||
Text { | |||
id: messageText | |||
Layout.alignment: Qt.AlignHCenter | |||
text: "Button was clicked!" | |||
color: "green" | |||
font.pixelSize: 16 | |||
visible: false | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
動的ライブラリのプロジェクトをビルドして、デバッグディレクトリまたはリリースディレクトリに動的ライブラリ (.soファイル) が生成される。<br> | |||
<br> | <br> | ||
<u>※注意</u><br> | <u>※注意</u><br> | ||
<u>Windowsの場合、.dllファイルと.aファイルが生成されるため、.aファイルの<code>.a</code>拡張子を<code>.lib</code>拡張子に変更する必要がある。</u><br> | <u>Windowsの場合、.dllファイルと.aファイルが生成されるため、.aファイルの<code>.a</code>拡張子を<code>.lib</code>拡張子に変更する必要がある。</u><br> | ||
<br> | <br> | ||
==== 動的ライブラリのリンク ==== | ==== 動的ライブラリのリンク ==== | ||
動的ライブラリをQtプロジェクトにリンクするには、以下の手順を行う。<br> | 動的ライブラリをQtプロジェクトにリンクするには、以下の手順を行う。<br> | ||
| 202行目: | 313行目: | ||
Text { | Text { | ||
anchors.centerIn: parent | anchors.centerIn: parent | ||
text: libSample. | text: libSample.showQmlWindow() | ||
wrapMode: Text.WordWrap | wrapMode: Text.WordWrap | ||
} | } | ||
| 213行目: | 324行目: | ||
<br> | <br> | ||
明示的リンクを使用して、動的ライブラリの関数を呼び出す手順は、以下の通りである。<br> | 明示的リンクを使用して、動的ライブラリの関数を呼び出す手順は、以下の通りである。<br> | ||
# | # 動的ライブラリのメソッドにおいて、C言語のグローバル関数 (Cスタイルのインターフェース) として定義することにより、ライブラリの動的ロードを容易にする。 | ||
# | # 実行ファイル側において、<code>QLibrary</code>クラスを使用して動的にライブラリを読み込み、動的ライブラリのクラスのインスタンスを生成する。 | ||
# | # 生成したインスタンスをQMLコンテキストにセットして、QMLファイルから動的ライブラリのクラスのメソッドを呼び出せるようにする。 | ||
# | # 実行ファイル側のQMLファイルから動的ライブラリのオブジェクトのメソッドを呼び出して、動的ライブラリ内のQMLファイルを表示する。 | ||
<br> | |||
<u>※注意</u><br> | |||
<u>Windowsでは、動的ライブラリからメソッドを呼び出すには、<code>__declspec(dllexport)</code>を関数の前に付加すること。</u><br> | |||
<br> | <br> | ||
上記セクションにある動的ライブラリの変更点のみを、以下に示す。<br> | |||
<br> | <br> | ||
まず、動的ライブラリのクラスおよびメソッドの変更点を記述する。<br> | |||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
// | // LibSample.h (動的ライブラリ側) | ||
#ifndef LIBSAMPLE_H | |||
#define LIBSAMPLE_H | |||
#include <QObject> | |||
#include <QString> | |||
#include <QQmlComponent> | |||
#include <QtQml/qqmlengine.h> | |||
#include "LibSample_global.h" | |||
class LIBSAMPLE_EXPORT LibSample : public QObject | |||
{ | |||
Q_OBJECT | |||
public: | |||
explicit LibSample(QQmlEngine *engine, QObject *parent = nullptr); | |||
Q_INVOKABLE void showQmlWindow(); | |||
private: | |||
QQmlEngine *m_engine; | |||
}; | |||
extern "C" { | |||
LibSample* createLibSample(); | |||
void destroyLibSample(LibSample *instance); | |||
} | |||
#endif // | #endif // LIBSAMPLE_H | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | <br> | ||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
// | // LibSample.cpp (動的ライブラリ側) | ||
#include " | #include <QQmlComponent> | ||
#include <QtQuick/QQuickWindow> | |||
#include "LibSample.h" | |||
LibSample::LibSample(QQmlEngine *engine, QObject *parent) : QObject(parent), m_engine(engine) | |||
{ | |||
qDebug() << "LibSample lib"; | |||
} | |||
void LibSample::showQmlWindow() | |||
{ | { | ||
QQmlComponent component(m_engine, "qrc:/Hoge/Hoge.qml"); | |||
{ | |||
QObject *object = component.create(); | |||
if (object) { | |||
QQuickWindow *window = qobject_cast<QQuickWindow*>(object); | |||
if (window) { | |||
window->show(); | |||
} | |||
} | } | ||
} | |||
LibSample* createLibSample() | |||
{ | |||
return new LibSample(); | |||
} | |||
void destroyLibSample(LibSample *instance) | |||
{ | |||
delete instance; | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | <br> | ||
次に、実行ファイル側 (動的ライブラリを使用する側) から動的ライブラリを読み込み、動的ライブラリのクラスのインスタンスを生成する。<br> | |||
生成したインスタンスをQMLコンテキストにセットして、QMLファイルから動的ライブラリのクラスのメソッドを呼び出す。<br> | |||
<br> | <br> | ||
<code>QLibrary</code>クラスから呼び出す関数は、必ず、C言語のグローバル関数として定義すること。<br> | <code>QLibrary</code>クラスから呼び出す関数は、必ず、C言語のグローバル関数として定義すること。<br> | ||
これは、マングリングしていない元の名前から関数を呼び出すためである。<br> | これは、マングリングしていない元の名前から関数を呼び出すためである。<br> | ||
<br> | <br> | ||
<code>QLibrary</code>クラスのコンストラクタには、<code>.so</code>拡張子または<code>.dll</code>拡張子を付加しないライブラリ名を渡す。<br> | <code>QLibrary</code>クラスのコンストラクタには、<code>.so</code>拡張子または<code>.dll</code>拡張子を付加しないライブラリ名を渡す。<br> | ||
動的ライブラリをリンクするには、<code>load</code>メソッドを実行する。<br> | 動的ライブラリをリンクするには、<code>load</code>メソッドを実行する。<br> | ||
<br> | |||
次に、<code>resolve</code>メソッドを使用して呼び出す関数名を渡して、関数が存在する場合は関数ポインタが返る。<br> | 次に、<code>resolve</code>メソッドを使用して呼び出す関数名を渡して、関数が存在する場合は関数ポインタが返る。<br> | ||
メソッド名や型が間違っている場合は、<code>0</code>が返る。<br> | |||
<br> | <br> | ||
もし、メソッド内でグローバル変数の値が変更された場合、次の関数を呼び出した時でも、グローバル変数の値を保持し続ける。<br> | |||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
// main.cpp | // main.cpp (実行ファイル側) | ||
#include < | #include <QGuiApplication> | ||
#include <QQmlApplicationEngine> | |||
#include <QQmlContext> | |||
#include <QLibrary> | #include <QLibrary> | ||
#include "LibSample.h" | |||
using | |||
using CreateLibSampleInstance = LibSample* (*)(); | |||
using DestroyLibSampleInstance = void (*)(LibSample*); | |||
int main(int argc, char * argv[]) | int main(int argc, char *argv[]) | ||
{ | { | ||
QGuiApplication app(argc, argv); | |||
/ | QQmlApplicationEngine engine; | ||
QLibrary lib(" | const QUrl url(u"qrc:/main.qml"_qs); | ||
lib.load(); | QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, | ||
[url](QObject *obj, const QUrl &objUrl) { | |||
if (!obj && url == objUrl) | |||
QCoreApplication::exit(-1); | |||
}, Qt::QueuedConnection); | |||
engine.load(url); | |||
QLibrary lib("LibSample"); | |||
if (lib.load()) { | |||
CreateLibSampleInstance createInstance = (CreateLibSampleInstance) lib.resolve("createLibSample"); | |||
DestroyLibSampleInstance destroyInstance = (DestroyLibSampleInstance) lib.resolve("destroyLibSample"); | |||
if (createInstance && destroyInstance) { | |||
LibSample *sample = createInstance(); | |||
engine.rootContext()->setContextProperty("libSample", sample); | |||
QObject::connect(&app, &QCoreApplication::aboutToQuit, [sample, destroyInstance]() { | |||
{ | destroyInstance(sample); | ||
}); | |||
} | |||
else { | |||
qWarning() << "インスタンスメソッドの作成 / 破棄の解決に失敗"; | |||
} | |||
} | |||
else { | |||
qWarning() << "動的ライブラリの読み込みに失敗"; | |||
} | } | ||
return | return app.exec(); | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||