この記事は The Qt Blog の Porting guide from Qt 1.0 to 5.11 を翻訳したものです。
執筆: Paul Olav Tvete, 2018年05月24日
Qt 5.11 がリリースされました。Qt 1.0 で書かれたアプリケーションをアップグレードする絶好の機会です ;-)。…という冗談はさておき 、今回は我々が最初の公式リリース以降の長い年月の間、いかに Qt の互換性を保ちつづけたのかを見てみたいと思います。
Qt は、ソースコードとバイナリの 互換性をマイナーリリース間で保証 していて、これは真剣に取り組んでいることの一つです。Qt のバージョンが上がった際にいちいち、アプリケーションの修正(や再コンパイル)を必要としないように非常に気を遣っています。しかし、時には Qt の改善にともなう大きな変更が必要な場合もあります。その場合は、メジャーリリースとして行います。1996年の Qt 1.0 のリリース以来(22年も前ですね)、2.0、3.0、4.0(この時はとても大変でしたね)、5.0 と4度ソースコードの互換性を破りました。
もちろん、メジャーリリースで互換性を破る場合でも影響を最小限に留めるよう努めてはいますが、変更は積み重なります。ここで「Qt 1.0 のアプリケーションを 5.11 で動かすのはどのくらい大変なんだろう?」という疑問が浮かび上がります。
この回答として、Qt 1.0 で提供していたチュートリアルのサンプルを題材にして、Qt 5 でコンパイルできるようにすることにしました。Qt のアーカイブは 1.41 までしか遡れないため、4つもの異なるソースコード管理システムに保存された古代の歴史から発掘する必要がありました。少しだけ脱線しましたが、そのサンプルの名前は t14 というもので、チュートリアルの最後、14番目のコードになります。
それでは実際に、これをビルドして実行するために必要なステップを紹介したいと思います。
- Qt 1.0 から tutorial 14 をインポート 10 files changed, 798 insertions(+)
これが 1996 年に Troll Tech(この時はまだ Trolltech ではありませんでした)が公式に推奨した Qt 1.0 での最先端のプログラムの書き方でした。
- qmake へ変換 3 files changed, 2 insertions(+), 148 deletions(-)
qmake より昔に、tmake というものが存在していました。この tmake は Perl で記述されていました。基本的な文法は一緒ですが、qmake ではプロジェクトファイルに Perl のコードを埋め込むことはできなくなっています。これはおそらく最善の結果でしょう…。 また、tmake は非公開だったため、リリース版では Makefile を生成していました。このまったく異なる内部向けと外部向けのビルドシステムの存在は、リリースプロセスをエキサイティングなものにしていました。
- include ファイルの名前の修正 4 files changed, 8 insertions(+), 8 deletions(-)
昔々、Windows にはファイル名は8文字という制限がありました。Unix 上では適切なインクルードファイルが提供されていましたが、コードの移植性を求めた場合には "qscrbar.h"
や "qbttngrp.h"
といった素敵な名前を利用する必要がありました。
- 足りない include を追加 1 file changed, 3 insertions(+)
依存による間接的なインクルードは昔も問題だったんですね。
- TRUE/FALSE を true/false に変更 1 file changed, 13 insertions(+), 13 deletions(-)
最近の若者は自分たちがどれだけ恵まれているのか知らないんだろうなぁ。昔は独自の bool
型を作成する必要があったんだよ。
- Qt:: namespace を必要なものにつける 3 files changed, 15 insertions(+), 15 deletions(-)
Qt という名前空間は 1998 年に導入されました。当時はまだ名前空間なんて使ってなかったので、クラスの名前でした。
- "name" 引数の削除 6 files changed, 26 insertions(+), 26 deletions(-)
QObject の派生クラスのコンストラクタはすべてオブジェクト名を引数としてとっていました。
- QScrollBar の API 変更対応 1 file changed, 5 insertions(+), 5 deletions(-)
たまーに、古い、悪い API を削除する必要があります。7つもの引数をとるコンストラクタより、パラメーターごとに設定関数がある方がだいぶ幸せですよね。
- const char * の代わりに QString を使う 2 files changed, 2 insertions(+), 2 deletions(-)
QString は 1994 年から Qt にあります。最初は 8 ビットの Latin1 で、const char * への自動キャスト機能があったため、API は const char * の引数になっていました。Unicode の対応は Qt 2.0 で行われました。
- warning() は qWarning() に変更 1 file changed, 1 insertion(+), 1 deletion(-)
色んなものをグローバルな名前空間に置くのは控えています。例外は ‘Q’です。’Q’ は私たちのもの。
- 昔の QApplication の関数呼び出しを削除 1 file changed, 2 deletions(-)
最近の Qt は親切に自動で対応してくれます。1996 年当時、ほとんどのディスプレイは8ビットカラーでした。256 の標準色以外を使いたい場合は、Qt にそれを伝える必要がありました。
- QAccel を QShortcut に置き換え 1 file changed, 4 insertions(+), 3 deletions(-)
QShortcut はより便利で簡単に使え、名前も省略形ではありません。これは 2004 年に登場しました。
- 再描画のロジックを修正 1 file changed, 7 insertions(+), 7 deletions(-)
90年代は、好きなタイミングで直接ウィジェットに描画をすることが可能でした。いまはもうすべてのものがバッファと合成になり、更新要求を発行し、適切なタイミングまで待ってウィジェットの再描画を行う必要があります。ラッキーなことに、これにより、コードが単純になります。
- QObject::killTimers() はもう存在しない 2 files changed, 3 insertions(+), 2 deletions(-)
この関数は、単に危険過ぎました。オブジェクトに属するすべてのタイマーを殺す機能で、Qt の内部で使われていたタイマーも対象でした。現在は個別に殺す必要があります。
- QWMatrix は QMatrix に 1 file changed, 2 insertions(+), 2 deletions(-)
単純な名前の変更です。
- QWidget::setBackgroundColor() も廃止されました 1 file changed, 3 insertions(+), 1 deletion(-)
背景色は独立した要素ではなくなりました。様々な役割の色と共に、QPalette という形になりました。また、子ウィジェットのデフォルトは透明色になりました。Qt に背景を描かせる場合はそれを伝える必要があります。
- ウィジェットの内容を QPixmap に移すことはできなくなりました 1 file changed, 1 insertion(+), 1 deletion(-)
代わりに、現在の Qt はサポートしている透明色を使います。
- Rectangle の描画が Qt 1.0 以後変わっています 1 file changed, 1 insertion(+), 1 deletion(-)
これがここまでの非互換で最悪なものですね。Qt 4.0 では QPainter::drawRect() が変わり、「縁のある矩形は rectangle.size() にペン幅を加えたサイズ」になりました。このため、ペン幅(ここでは 1)の引いたものを QPainter の矩形として渡す必要がります。
というわけで、チュートリアルのサンプルが動くようになりました。こんなスクリーンショットです。
あー、文字が収まってないものがありますね。このサンプルはほとんどのものが固定の位置とサイズになっていて、1996 年以降フォントのサイズも変わっています。この対応には Qt 1.0 にはなかった(Qt 1.1 ではじめて登場し、2.0 でゼロから書き直された)QLayout を使いましょう。
これで、すべての見た目が整いました。
というわけで、いかがでしたか?チュートリアル14を Qt 1.0 から Qt 5.11 に移植するのはそんなに大変な作業ではありませんでした。多分、この記事を書く方が時間がかかっています。1.0 のAPI のほとんどが、現在のAPIと対応付けができる形で生き残っています。非互換の部分に関しては、ユーザビリティや可読性、安全性のための改善です。
もちろん、今回のは簡単なサンプルを対象にしました。通常の規模のアプリケーションの複数のメジャーバージョンに渡る移植作業はおそらくもっと難易度が高いでしょう。世の中には、Qt のコンサルサービスを提供している会社 がたくさんありますので、ぜひご相談ください。