2011年9月29日木曜日

CLangでクラスの中身を調べる(1)

C++において、ある特定の型で宣言されたメンバ変数を持つクラスとその変数名を列挙したい。
CLangを使ってそれを実現する(か挫折する)まで頑張る。

現在の環境:
・Visual Studio 2005 with SP1
・llvm 2.9
・CLang 2.9

まずCLangのドキュメントを読んでみたがよくわからない。さかんにASTという単語が出てくるが、これはたぶんAbstract Syntax Treeの略(=構文解析結果)だと思うので、そこまでたどり着ければ何か見えてくるだろう。

まずはプリプロセッサ。ドキュメントには「The Lexer and Preprocessor Library」という節があるので、ここを見ればいいはず。ちなみにLexer = Lexical Analyzerであるらしい。これによるとプリプロセッサのクラスはclang::Preprocessorというのがある。ドキュメントを読み進める前にこいつのオブジェクトを作ってみよう。
clang::Preprocessorはコンストラクタで多数のインスタンスが要求される。
  • clang::Diagnostic
  • clang::LangOptions
  • clang::TargetInfo
  • clang::SourceManager
  • clang::HeaderSearch
以下、1つずつ潰す。

■clang::Diagnostic
コンパイルエラーなどを通知する?
こいつはllvm::IntrusiveRefCntPtrを要求する。clang::DiagnosticIDsはデフォルト生成可能。


■clang::LangOptions
言語オプション?RTTIを有効にしたりする様子。
コンストラクタにて全オプションがデフォルト指定されているので、まずはデフォルトのまま利用する。

■clang::TargetInfo
ターゲットとは、たぶんバイナリの動作環境の事だろう。
こいつはコンストラクタは利用できず、staticなファクトリ関数を使う。clang::Diagnosticとclang::TargetOptionを要求する。
clang::TargetOptionは単なる構造体である。そのメンバにTripleというのがあって最初躓いたが、どうも以下の4つをまとめたものであるらしい:
  • CPUアーキテクチャ(alpha, ppc, x86, x86_64等)
  • ベンダ(UnknownとAppleとPCしかねぇー)
  • OS(Cygwin, Linux, Win32等)
  • 環境(オプション? GNU, MachO等)
そしてllvm::sysにgetHostTriple()というAPIが用意されている。今回は別にバイナリを作るわけではないで、ホスト環境をそのまま使うことにする。それ以外のオプションは設定しなくても大丈夫っぽい。Featuresがちょっと気になるが・・・。

■clang::SourceManager
ソースコードをファイルから読み込んでメモリ上に保持管理する役割を持つようだ。
こいつはclang::Diagnosticとclang::FileManagerを要求する。
clang::FileManagerはファイルやフォルダを検索する機能、またファイルをキャッシングする機能を提供する。clang::FileSystemOptionsを要求する。
clang::FileSystemOptionsは単なる構造体で、作業フォルダの指定しか属性がない。しかもデフォルトでは指定する必要がないようだ。

■clang::HeaderSearch
その名の通りヘッダ検索をサポートするようだ。
こいつもclang::FileManagerを要求する。

■clang::Preprocessor
こんな感じになった。
#include "llvm/Support/Host.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/TargetOptions.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/Preprocessor.h"

int main(int, char*[])
{
	llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagIds(new clang::DiagnosticIDs());
	clang::Diagnostic diag(diagIds);

	clang::LangOptions langOpts;

	clang::TargetOptions targetOpts;
	targetOpts.Triple = llvm::sys::getHostTriple();

	clang::TargetInfo* targetInfo
		= clang::TargetInfo::CreateTargetInfo(diag, targetOpts);

	clang::FileSystemOptions fileSysOpts;
	clang::FileManager fileMgr(fileSysOpts);
	clang::SourceManager srcMgr(diag, fileMgr);

	clang::HeaderSearch headerSearch(fileMgr);

	clang::Preprocessor pp(diag, langOpts, *targetInfo, srcMgr, headerSearch);

	getchar();
	return 0;
}

ビルドすると当然リンクエラーが出る。今はclang/Basicとclang/Lexに依存しているので、それぞれclangBasicプロジェクトとclangLexプロジェクトに依存するよう、プロジェクト依存関係を追加すればOK。
※LLVM.slnに自分のテストプロジェクトを追加している。
実行してエラーも何もでないことを確認した。

0 件のコメント:

コメントを投稿