本家の Planet Qt にはたびたび woboq という会社の記事が出てきます。Nokia/Trolltech で Qt の開発に携わっていたメンバーが立ち上げた小さな会社ですが、Qt および C++ などの確かな技術力を元にこれまでにもさまざまな面白い記事を提供しています。古い記事もご紹介したいものはありますが、今回はその中から「Proof Of Concept: Re-implementing Qt moc using libclang」を紹介しようと思います。
moc(Meta-Object Compiler) というと Qt のメタオブジェクトシステムの核となる要素であり、シグナルやスロットの実現に欠かせないコマンドであります。moc は C++ のヘッダーファイルやソースファイルをパースして QObject を継承したクラスの宣言(正確には Q_OBJECT マクロのあるクラス宣言)を見つけ出して、必要なメタオブジェクトやシグナル、メソッドの実装を作成します。
このため、moc は独自実装による C++ のプリプロセッサおよびパーサを合わせたような機能を持っています。が、C++ のすべての仕様の実装までは行っておらず、複雑な文法になる場合や C++11 特有の書式には対応していません。そこでパーサーの実装に libclang を用いて、moc を(moc-ng として)再設計することで C++ の仕様への追従度や、moc 自身のメンテナンス性を向上させようというのがこの Proof Of Concept (概念実証) の目的となります。
実装の詳細については 元記事 や ソースコード をみてください。基本的には clang のパース結果から生成された 抽象構文木(AST) を元に必要なコードを生成しています。ただし、従来のままでは Qt の各種マクロが展開されて空となるため、(moc-ng の中で) マクロを __attribute()__ などを使ったものに変更し、必要な属性を取得可能にしています。
また、この moc-ng では二つの方法で moc を動作させることができます。
- 従来の moc の置き換えとして、独立したコマンドとして実行する
- clang のプラグインとして、通常のコンパイル時に内部的に moc を実行する
前者は単純にコマンドが置き換わるだけです。clang は moc では簡略化されている処理も行うため、コンパイル時間そのものは遅くなりますが、より複雑な、あるいは新しい文法への対応やエラーメッセージの改善などがメリットとなります。
後者では従来のビルドプロセスには必要であった moc_*.cpp や *.moc を生成せずに、コンパイル時に直接 moc が生成していたコードをオブジェクトとして生成します。このため、前者のメリットに加え、コンパイル時間や中間ファイルの削減などがメリットとなります。
libclang への依存が必要になるため Qt 本体への取り込みは簡単には行われないと思いますが、非常に面白いアプローチだと思います。特にプラグインとして実行する場合にはプリプロセッサコマンドとしての moc が不要になるため、Qt を用いたプロジェクトのビルドプロセスがわかりやすくなるのは大きいでしょう。今後が楽しみなプロジェクトです。