怒涛の勢いで開発が進むclangですが、c++だけでなく、コンパイラとして必要な汎用的な機能を提供しています。
これらの機能はc++APIとして公開されていて、Pythonインターフェース等もあります。
なので、clangを使えば「ぼくのかんがえたさいきょうのぷろぐらみんぐげんご」を少ないコストで作ることができます。
今回はこのclangのc++APIの使い方を学んでいきたいと思います。
最初の一回はファイルIO周りです。
続くかは分かりません。
概要
clangはファイルIOを管理するクラスとしてFileManagerクラスとSourceManagerクラスを提供しています。
FileManagerは基本的なファイルIOやファイル名のキャッシング機能等を提供しています。
SourceManagerはさらにFileManagerに加えてCプリプロセッサーとコンパチなソースロケーション機能を備えています。
またSourceManagerはファイルそのもののキャッシング機能も持っています。
初回はFileManagerから見ていきましょう。
FileManager
FileManagerクラスは
$CLANG_ROOT/include/clang/Basics/FileManager.h
に宣言してあります。
早速FileManagerを作ってみましょう。
コンストラクタをみると
FileManager (const FileSystemOptions &FileSystemOpts, IntrusiveRefCntPtr< vfs::FileSystem > FS=nullptr)
となっています。
FileSystemOptionsは今の所、作業ディレクトリの情報のみを格納します。($CLANG_ROOT/include/clang/Basics/FileSystemOptions.h)
IntrusiveRefCntPtr< vfs::FileSystem >は気にしなくて良いです。
気になる人のために少しだけ補足しておくと、IntrusiveRefCntPtrはクラステンプレートでstd::shared_ptr等の参照カウント方式のスマートポインタをラップします。
vfs::FileSystemは基本的なファイルIOのヘルパー関数を備えた抽象クラスです。
FileManagerはデフォルトでRealFileSystemを選択します。
これはOSが提供するファイルシステムを使います。
もちろんその気になればメモリ上にファイルシステムを展開して、物理デバイスをシミュレートする事もできます。
もっともこういう使い方はOSのファイルシステム側でも簡単にできるので(少なくともLinuxは)、使うことはあまりないでしょう。
ではFileManagerを作ります。
# include <string>
# include "clang/Basic/FileManager.h"
# include "clang/Basic/FileSystemOptions.h"
int main()
{
clang::FileSystemOptions fso;
fso.WorkingDir = "./";
clang::FileManager fm(fso);
return 0;
}
ちゃんとコンパイルできましたか?
リンクエラーがでている様なら以前に、リンクオプションの見分け方のページを書きましたので、参考にしてください。
FileSystemOptionsのWorkingDirを設定しなくても上手く行きますが、FileManagerが相対パスを解決できなくなります。
それではFileManagerを使ってみましょう。
まず良くつかうのが
const DirectoryEntry* getDirectory (StringRef DirName, bool CacheFailure=true)
const FileEntry* getFile (StringRef Filename, bool OpenFile=false, bool CacheFailure=true)
の2つです。
DirectoryEntryやFileEntryはディレクトリやファイルを表すクラスで、ファイル名、サイズ、アクセス可能かどうかを取得できるハンドラーを提供します。
getDirectory(DiraName)やgetFile(FileName)で、DirectoryEntryやFileEntryを取得します。
また、FileManagerはファイルパスのキャッシング機能を持っていて、一度参照したパスは次回から高速に検索できます。
ファイルの中身をキャッシュする訳ではありません。
ディレクトリやファイルの検索に失敗するとnullを返します。
それではFileManagerからFileEntryを受け取り、ファイルの情報を参照してみましょう。
1 clang::FileSystemOptions fso;
2 fso.WorkingDir = "./";
3 clang::FileManager fm(fso);
4 auto fe_ptr = fm.getFile("test.cpp");
5 std::cout << fe_ptr << std::endl;
6 if(fe_ptr != nullptr){
7 std::cout << fe_ptr->getName() << std::endl;
8 std::cout << fe_ptr->getSize() << std::endl;
9 std::cout << fe_ptr->isValid() << std::endl;
10 std::cout << fe_ptr->getDir()->getName() << std::endl;
11 std::cout << fm.getCanonicalName(fe_ptr->getDir()).str() << std::endl;
12 }
意味は見ての通りです。
4行目で $WorkingDir/test.cppのFileEntryを受け取り、成功していれば、
ファイル名(7行目)
ファイルサイズ(8行目)
ファイルが存在するかどうか(9行目)
相対ディレクトリ名(10行目)
絶対ディレクトリ名(11行目)
を出力します。
FileEntryのgetDirメンバ関数はDirEntryへのポインタを返します。(10行目)
FileSystemのgetCanonicalName(DirEntry*)メンバ関数はllvm::StringRefという文字列参照のラッパクラスを返しますが、.str()でstd::stringを返します。(11行目)
以上がFileManagerの使い方です。