2011年10月27日木曜日

メモ:頭にCXXがついているClangのクラス

clang/lib/AST/CXXABI.h(26):class CXXABI {
clang/include/clang/AST/CXXInheritance.h(69):class CXXBasePath : public SmallVector<CXXBasePathElement, 4> {
clang/include/clang/AST/CXXInheritance.h(117):class CXXBasePaths {
clang/include/clang/AST/CXXInheritance.h(361):class CXXFinalOverriderMap 
clang/include/clang/AST/CXXInheritance.h(365):class CXXIndirectPrimaryBaseSet
clang/include/clang/AST/DeclCXX.h(164):class CXXBaseSpecifier {
clang/include/clang/AST/DeclCXX.h(265):class CXXRecordDecl : public RecordDecl {
clang/include/clang/AST/DeclCXX.h(1315):class CXXMethodDecl : public FunctionDecl {
clang/include/clang/AST/DeclCXX.h(1447):class CXXCtorInitializer {
clang/include/clang/AST/DeclCXX.h(1693):class CXXConstructorDecl : public CXXMethodDecl {
clang/include/clang/AST/DeclCXX.h(1924):class CXXDestructorDecl : public CXXMethodDecl {
clang/include/clang/AST/DeclCXX.h(1993):class CXXConversionDecl : public CXXMethodDecl {
clang/include/clang/AST/DeclFriend.h(130):class CXXRecordDecl::friend_iterator {
clang/include/clang/AST/ExprCXX.h(48):class CXXOperatorCallExpr : public CallExpr {
clang/include/clang/AST/ExprCXX.h(91):class CXXMemberCallExpr : public CallExpr {
clang/include/clang/AST/ExprCXX.h(157):class CXXNamedCastExpr : public ExplicitCastExpr {
clang/include/clang/AST/ExprCXX.h(206):class CXXStaticCastExpr : public CXXNamedCastExpr {
clang/include/clang/AST/ExprCXX.h(237):class CXXDynamicCastExpr : public CXXNamedCastExpr {
clang/include/clang/AST/ExprCXX.h(271):class CXXReinterpretCastExpr : public CXXNamedCastExpr {
clang/include/clang/AST/ExprCXX.h(302):class CXXConstCastExpr : public CXXNamedCastExpr {
clang/include/clang/AST/ExprCXX.h(327):class CXXBoolLiteralExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(357):class CXXNullPtrLiteralExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(386):class CXXTypeidExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(467):class CXXUuidofExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(549):class CXXThisExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(586):class CXXThrowExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(639):class CXXDefaultArgExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(725):class CXXTemporary {
clang/include/clang/AST/ExprCXX.h(753):class CXXBindTemporaryExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(796):class CXXConstructExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(928):class CXXFunctionalCastExpr : public ExplicitCastExpr {
clang/include/clang/AST/ExprCXX.h(984):class CXXTemporaryObjectExpr : public CXXConstructExpr {
clang/include/clang/AST/ExprCXX.h(1013):class CXXScalarValueInitExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(1051):class CXXNewExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(1258):class CXXDeleteExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(1380):class CXXPseudoDestructorExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(2252):class CXXUnresolvedConstructExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(2356):class CXXDependentScopeMemberExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(2762):class CXXNoexceptExpr : public Expr {
clang/include/clang/AST/StmtCXX.h(25):class CXXCatchStmt : public Stmt {
clang/include/clang/AST/StmtCXX.h(61):class CXXTryStmt : public Stmt {
clang/include/clang/AST/StmtCXX.h(128):class CXXForRangeStmt : public Stmt {
clang/lib/AST/DeclarationName.cpp(31):class CXXSpecialName
clang/lib/AST/DeclarationName.cpp(49):class CXXOperatorIdName : public DeclarationNameExtra {
clang/lib/AST/DeclarationName.cpp(62):class CXXLiteralOperatorIdName
clang/lib/AST/ItaniumMangle.cpp(137):class CXXNameMangler {
clang/lib/CodeGen/CGBlocks.cpp(1363):class CXXByrefHelpers : public CodeGenModule::ByrefHelpers {
clang/include/clang/Sema/CXXFieldCollector.h(25):class CXXFieldCollector {
clang/include/clang/Sema/DeclSpec.h(60):class CXXScopeSpec {
clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h(703):class CXXThisRegion : public TypedValueRegion {
clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h(847):class CXXTempObjectRegion : public TypedValueRegion {
clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h(876):class CXXBaseObjectRegion : public TypedValueRegion {

CLangでクラスの中身を調べる: クラスとそのメンバ関数の列挙

クラスのメンバ関数はclang::CXXRecordDecl::method_begin(), method_end()で反復できる。この反復でclang::CXXMethodDeclが得られる。
CXXMethodDeclはヘッダを見ると、各関数の属性、例えば可視性であったり、戻り値の型であったりを取得できるようだ。

とりあえず、自分で明示的に定義したメンバ関数をリストアップしてみよう。

コード:
clang::ASTContext::const_type_iterator t = astContext.types_begin();
clang::ASTContext::const_type_iterator eot = astContext.types_end();
for(; t != eot; ++t)
{
    clang::CXXRecordDecl* pDecl = (*t)->getAsCXXRecordDecl();
    if( (pDecl != nullptr) && pDecl->hasDefinition() && pDecl->isClass() )
    {
        pDecl->getDeclName().dump();
        //pDecl->getDeclName().printName(llvm::outs());

        // 基本クラスを確認する。
        std::printf(">>>> base class >>>>\n");
        clang::CXXRecordDecl::base_class_iterator base = pDecl->bases_begin();
        clang::CXXRecordDecl::base_class_iterator eobase = pDecl->bases_end();
        for(; base != eobase; ++base)
        {
            // CXXRecordDecl::base_class_iterator は CXXBaseSpecifier* 。
            base->getType().dump();
        }
        std::printf("<<<<\n");

        // public なメンバ関数を列挙する。
        std::printf(">>>> method >>>>\n");
        clang::CXXRecordDecl::method_iterator method = pDecl->method_begin();
        clang::CXXRecordDecl::method_iterator eomethod = pDecl->method_end();
        for(; method != eomethod; ++method)
        {
            if( !method->isImplicit() && (method->getAccess() == clang::AS_public) )
            {
                method->dump();
            }
        }
        std::printf("<<<<\n");

        std::printf("\n---------------------------------------------------------\n");
    }
}

調べるcppはこんな感じ。
#include <cstdio>

namespace SuperNs
{
    class Super
    {
    public:
        Super()
        {
        }

        void super_public_method()
        {
        }
        
        virtual void super_public_virtual_method()
        {
        }

        virtual void super_public_virtual_method_nooverride()
        {
        }

    private:
        void super_private_method()
        {
        }
    };
}

namespace SubNs
{
    class Sub
        : public SuperNs::Super
    {
    public:
        Sub()
            : Super()
        {
        }

        void sub_public_method()
        {
        }
        
        virtual void super_public_virtual_method()
        {
        }

    private:
        void sub_private_method()
        {
        }
    };
}

int main(int, char**)
{
    std::printf("Hello,world\n");
    std::getchar();
    return 0;
}
結果:
_Lockit
>>>> base class >>>>
<<<<
>>>> method >>>>
_Lockit()_Lockit(int)void ~_Lockit() noexceptstatic void _Lockit_ctor(int)static void _Lockit_dtor(int)<<<<

---------------------------------------------------------
_Mutex
>>>> base class >>>>
<<<<
>>>> method >>>>
_Mutex()void ~_Mutex() noexceptvoid _Lock()void _Unlock()<<<<

---------------------------------------------------------
_Init_locks
>>>> base class >>>>
<<<<
>>>> method >>>>
_Init_locks()void ~_Init_locks() noexcept<<<<

---------------------------------------------------------
Super
>>>> base class >>>>
<<<<
>>>> method >>>>
Super() {
}

void super_public_method() {
}

virtual void super_public_virtual_method() {
}

virtual void super_public_virtual_method_nooverride() {
}

<<<<

---------------------------------------------------------
Sub
>>>> base class >>>>
: SuperNs::Super identifier
<<<<
>>>> method >>>>
Sub() : SuperNs::Super() {
}

void sub_public_method() {
}

virtual void super_public_virtual_method() {
}

<<<<

---------------------------------------------------------
Super
>>>> base class >>>>
<<<<
>>>> method >>>>
Super() {
}

void super_public_method() {
}

virtual void super_public_virtual_method() {
}

virtual void super_public_virtual_method_nooverride() {
}

<<<<

---------------------------------------------------------

なんでか、Superが2回反復されている(コピペミスではない)。その他は一応意図通り動いているようだ。ただ「親クラスのメンバ関数は子クラスで反復されない」のはちょっと困った。

C++コード表示の調整テスト中・・・

C++コードが見にくいので調整中。 以前からPrettyPrintを導入しようとしていたが、うまくいかずにモヤモヤしていた。どうもBloggerのテンプレートとして動的ビューを選択しているとうまくいかないようなので、シンプルに戻してみた。
#include <iostream>

#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Host.h"

#include "clang/Frontend/DiagnosticOptions.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"

#include "clang/Basic/LangOptions.h"
#include "clang/Basic/FileSystemOptions.h"

#include "clang/Basic/SourceManager.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Basic/FileManager.h"

#include "clang/Frontend/HeaderSearchOptions.h"
#include "clang/Frontend/Utils.h"

#include "clang/Basic/TargetOptions.h"
#include "clang/Basic/TargetInfo.h"

#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/PreprocessorOptions.h"
#include "clang/Frontend/FrontendOptions.h"
#include "clang/Frontend/CompilerInstance.h"

#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/Builtins.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/Sema/Sema.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/Type.h"
#include "clang/AST/Decl.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Ownership.h"
#include "clang/AST/DeclGroup.h"
#include "clang/AST/CXXInheritance.h"

#include "clang/Parse/Parser.h"

#include "clang/Parse/ParseAST.h"

int main()
{
    // 言語オプションをセットアップする。
    clang::LangOptions langOpts;
    langOpts.MicrosoftExt            = true;
    langOpts.MicrosoftMode            = true;
    langOpts.CPlusPlus                = true;
    langOpts.CPlusPlus0x            = true;
    langOpts.Bool                    = true;
    langOpts.GNUMode                = false;
    langOpts.GNUKeywords            = false;
    langOpts.LaxVectorConversions    = false;
    langOpts.Exceptions                = true;
    langOpts.CXXExceptions            = true;
    langOpts.MSBitfields            = true;
    langOpts.NeXTRuntime            = false;
    langOpts.MSCVersion                = _MSC_VER;

    // Diagnostic をセットアップする。
    clang::DiagnosticOptions diagOpts;
    clang::TextDiagnosticPrinter diagPrinter(llvm::outs(), diagOpts);
    diagPrinter.BeginSourceFile(langOpts, nullptr);

    llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>
        diagIds(new clang::DiagnosticIDs());
    clang::DiagnosticsEngine diagEngine(diagIds, &diagPrinter, false);

    // 動作環境オプションをセットアップする。
    llvm::Triple triple;
    triple.setArch(llvm::Triple::x86);
    triple.setVendor(llvm::Triple::PC);
    triple.setOS(llvm::Triple::Win32);
    clang::TargetOptions targetOpts;
    targetOpts.Triple = triple.getTriple();
    clang::TargetInfo* targetInfo
        = clang::TargetInfo::CreateTargetInfo(diagEngine, targetOpts);

    // ソースファイル管理をセットアップする。
    clang::FileSystemOptions fileSysOpts;
    clang::FileManager fileMgr(fileSysOpts);
    clang::SourceManager srcMgr(diagEngine, fileMgr);

    // ヘッダ検索をセットアップする。
    clang::HeaderSearch headerSearch(fileMgr);
    clang::HeaderSearchOptions headerSearchOpts;
    headerSearchOpts.AddPath("C:\\Program Files (x86)\\Microsoft Visual Studio 8\\VC\\include",
                                clang::frontend::Angled, false, false, false);
    headerSearchOpts.AddPath("C:\\Program Files (x86)\\Microsoft Visual Studio 8\\VC\\PlatformSDK\\include",
                                clang::frontend::Angled, false, false, false);
    clang::ApplyHeaderSearchOptions(headerSearch, headerSearchOpts, langOpts,
                                    triple);

    // プリプロセッサをセットアップする。
    clang::CompilerInstance compiler;
    clang::Preprocessor pp(diagEngine, langOpts, targetInfo, srcMgr, headerSearch, compiler);
    clang::PreprocessorOptions ppOpts;
    clang::FrontendOptions frontendOpts;
    clang::InitializePreprocessor(pp, ppOpts, headerSearchOpts, frontendOpts);


    const clang::FileEntry *pFile = fileMgr.getFile("D:\\develop\\Clang\\temp.cpp");
    srcMgr.createMainFileID(pFile);

    clang::IdentifierTable identifierTable(langOpts);
    clang::SelectorTable selectorTable;
    clang::Builtin::Context builtinContext;
    clang::ASTContext astContext(langOpts, srcMgr, targetInfo, identifierTable,
                                    selectorTable, builtinContext, 0);

    clang::ASTConsumer astConsumer;
    clang::Sema sema(pp, astContext, astConsumer);
    sema.Initialize();

    clang::ParseAST(pp, &astConsumer, astContext);

    // 構文解析結果を確認する。
    clang::ASTContext::const_type_iterator t = astContext.types_begin();
    clang::ASTContext::const_type_iterator eot = astContext.types_end();
    for(; t != eot; ++t)
    {
        clang::CXXRecordDecl* pDecl = (*t)->getAsCXXRecordDecl();
        if( (pDecl != nullptr) && pDecl->hasDefinition() && pDecl->isClass() )
        {
            //pDecl->getDeclName().printName(llvm::outs());

            // 基本クラスを確認する。
            std::printf(">>>> base class >>>>\n");
            clang::CXXRecordDecl::base_class_iterator base = pDecl->bases_begin();
            clang::CXXRecordDecl::base_class_iterator eobase = pDecl->bases_end();
            for(; base != eobase; ++base)
            {
                // CXXRecordDecl::base_class_iterator は CXXBaseSpecifier* 。
                base->getType().dump();
            }
            std::printf("<<<<\n");

            // public なメンバ関数を列挙する。
            std::printf(">>>> method >>>>\n");
            clang::CXXRecordDecl::method_iterator method = pDecl->method_begin();
            clang::CXXRecordDecl::method_iterator eomethod = pDecl->method_end();
            for(; method != eomethod; ++method)
            {
                method->dump();
                std::printf("\n-#-#-#-#\n");
            }
            std::printf("<<<<\n");

            std::printf("\n---------------------------------------------------------\n");
        }
    }

    std::getchar();
    return 0;
}

2011年10月26日水曜日

まとめ:CLangをビルド(Release3.0 RC1)

先の文章だとわかりづらいので、新しくわかった内容も含めて2.9→3.0の変更点をまとめておく。


clang::LangOptionsは実装や利用可能オプションが結構変わっているので、clang/Basic/LangOptions.defファイルを見て内容を確認したほうがよい。
Windows/VisualC++関連として、MicrosoftオプションがMicrosoftExtとMicrosoftModeの2つに分割されている。


旧clang::Diagnosticのほとんどの機能はclang::DiagnosticsEngineに分離された。clang::Diagnosticクラスもまだ残っているが、構文解析においては単純に置換(Diagnostic→DiagnosticsEngine)するだけでいい様子。


clang::Preprocessorがclang::ModuleLoaderを要求するようになった。clang::ModuleLoaderは抽象クラスであり、例えばclang::CompilerInstanceが利用できる。


clang::Builtin::Contextがclang::TargetInfoを要求しなくなった。clang::Builtin::Context::InitializeTarget()というAPIがあるが、コールしなくても構文解析は動くようだ。


他、細かなインターフェースの変更や、リンクするlibの増加などがあるが、エラーに対して普通に対処すれば問題ない。

CLangをビルド(Release3.0 RC1)


リポジトリを覗いたら、LLVM共々3.0 RC1が10/18に出ていた。リリースノートを見ると色々改善されている様子なので、環境を入れ替えてみる。

llvmとclangは普通にビルドが通ったが、自分のサンプルはエラーが多発した。
それぞれの内容と対処は以下の通り。

■dyn_castがビルドエラー
→llvm名前空間に入った。

→llvm::dyn_castに変更。

■clang::LangOptions.Microsoftがない
→おそらくMicrosoftExtとMicrosoftModeの2つに分割されたのだと思われる。

→両方trueに設定。

■clang::Diagnosticのコンストラクタの引数が変わった
→DiagnosticsEngineなるクラスができたようだ。
DiagnosticsEngineのコンストラクタは
・llvm::IntrusiveRefCntPtr
・DiagnosticConsumer
・bool ShouldOwnClient
の3引数を受け取る。第1引数は今までと同じだが、第2引数のDiagnosticConsumerはまた知らないクラスだ。ただしこっちはコンストラクタに引数がない。またDiagnosticsEngineはConsumerを受け取らなくてもいいっぽい。

→DiagnosticsEngineはConsumerなしで生成。
→後述の理由(実行時アサーション)により、やっぱり受け取らないと駄目なようだ。
よくよく確認すると、TextDiagnosticPrinterがDiagnosticConsumerの派生クラスになっている!
これを指定するよう修正。

■clang::TargetInfo::CreateTargetInfo()の引数が変わった。
→第1引数がclang::Diagnosticからclang::DiagnosticsEngineに変更されている。

→上記で作ったEngineをそのまま指定。

■clang::SourceManagerのコンストラクタの引数が変わった。
→上記と同様clang::Diagnosticからclang::DiagnosticsEngineに変更されている。

→上記で作ったEngineをそのまま指定。

■clang::Preprocessorのコンストラクタの引数が変わった。
→上記と同様clang::Diagnosticからclang::DiagnosticsEngineに変更されている。
・・・clang::Diagnosticの一部がDiagnosticsEngineに分割されたのかな?
第3引数のTargetInfoが参照からポインタに。
さらに以下の引数が追加された:
・ModuleLoader
・IdentifierInfoLookup
clang::ModuleLoaderはインターフェースであり、それを実装しているのはclang::ASTUnitとclang::CompilerInstanceである。

→上記で作ったEngineをそのまま指定。
ModuleLoaderにはCompilerInstanceを指定。
IdentifierInfoLookupはオプションっぽいのでnullptr指定。

■clang::Builtin::Contextのコンストラクタの引数が変わった。
→引数なしに。ただしInitializeTarget()なるメンバ関数がある。

→InitializeTarget()をコールするよう変更。

■clang::ASTContextのコンストラクタの引数が変わった。
→第3引数のTargetInfoが参照からポインタに。


▲clang::ASTReader::ReadAST()がリンクエラー。
→clangSerialization.libをリンク。

▲clang::driver::Arg::getAsString()がリンクエラー。
→clangDriver.libをリンク。


●実行時、DiagnosticIDs::ProcessDiag()でアサーション。
→DiagnosticsEngineにDiagnosticConsumerがセットされてないと判定NG。

→TextDiagnosticPrinterをDiagnosticsEngineのコンストラクタでConsumerとして設定。


以上の対応で動作した。今のコードはこんな感じ:

#include <iostream>

#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Host.h"

#include "clang/Frontend/DiagnosticOptions.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"

#include "clang/Basic/LangOptions.h"
#include "clang/Basic/FileSystemOptions.h"

#include "clang/Basic/SourceManager.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Basic/FileManager.h"

#include "clang/Frontend/HeaderSearchOptions.h"
#include "clang/Frontend/Utils.h"

#include "clang/Basic/TargetOptions.h"
#include "clang/Basic/TargetInfo.h"

#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/PreprocessorOptions.h"
#include "clang/Frontend/FrontendOptions.h"
#include "clang/Frontend/CompilerInstance.h"

#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/Builtins.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/Sema/Sema.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/Type.h"
#include "clang/AST/Decl.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Ownership.h"
#include "clang/AST/DeclGroup.h"
#include "clang/AST/CXXInheritance.h"

#include "clang/Parse/Parser.h"

#include "clang/Parse/ParseAST.h"

class MyASTConsumer : public clang::ASTConsumer
{
public:
 MyASTConsumer() : clang::ASTConsumer() { }
 virtual ~MyASTConsumer() { }

 virtual void HandleTopLevelDecl( clang::DeclGroupRef d)
 {
  static int count = 0;
  clang::DeclGroupRef::iterator it;
  for( it = d.begin(); it != d.end(); it++)
  {
   count++;
   clang::VarDecl *vd = llvm::dyn_cast<clang::VarDecl>(*it);
   if(!vd)
   {
    continue;
   }
   std::cout << vd << std::endl;
   if( vd->isFileVarDecl() && vd->hasExternalStorage() )
   {
    std::cerr << "Read top-level variable decl: '";
    std::cerr << vd->getDeclName().getAsString() ;
    std::cerr << std::endl;
   }
  }
 }
};

int main()
{
 // 言語オプションをセットアップする。
 clang::LangOptions langOpts;
 langOpts.BCPLComment = true;
 langOpts.Bool   = true;
 langOpts.MicrosoftExt = true;
 langOpts.MicrosoftMode = true;
 langOpts.CPlusPlus  = true;
 langOpts.CPlusPlus0x = true;
 langOpts.Exceptions  = true;
 langOpts.CXXExceptions = true;
 langOpts.MSBitfields = true;
 langOpts.NeXTRuntime = false;
 langOpts.NoBuiltin  = true;
 langOpts.MSCVersion  = _MSC_VER;

 // Diagnostic をセットアップする。
 clang::DiagnosticOptions diagOpts;
 clang::TextDiagnosticPrinter diagPrinter(llvm::outs(), diagOpts);
 diagPrinter.BeginSourceFile(langOpts, NULL);

 llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>
  diagIds(new clang::DiagnosticIDs());
 clang::DiagnosticsEngine diagEngine(diagIds, &diagPrinter, false);
 clang::Diagnostic diag(&diagEngine);

 // 動作環境オプションをセットアップする。
 llvm::Triple triple;
 triple.setArch(llvm::Triple::x86);
 triple.setVendor(llvm::Triple::PC);
 triple.setOS(llvm::Triple::Win32);
 clang::TargetOptions targetOpts;
 targetOpts.Triple = triple.getTriple();
 clang::TargetInfo* targetInfo
  = clang::TargetInfo::CreateTargetInfo(diagEngine, targetOpts);

 // ソースファイル管理をセットアップする。
 clang::FileSystemOptions fileSysOpts;
 clang::FileManager fileMgr(fileSysOpts);
 clang::SourceManager srcMgr(diagEngine, fileMgr);

 // ヘッダ検索をセットアップする。
 clang::HeaderSearch headerSearch(fileMgr);
 clang::HeaderSearchOptions headerSearchOpts;
 headerSearchOpts.AddPath("C:\\Program Files (x86)\\Microsoft Visual Studio 8\\VC\\include",
        clang::frontend::Angled, false, false, false);
 headerSearchOpts.AddPath("C:\\Program Files (x86)\\Microsoft Visual Studio 8\\VC\\PlatformSDK\\include",
        clang::frontend::Angled, false, false, false);
 clang::ApplyHeaderSearchOptions(headerSearch, headerSearchOpts, langOpts,
         triple);

 // プリプロセッサをセットアップする。
 clang::CompilerInstance compiler;
 clang::Preprocessor pp(diagEngine, langOpts, targetInfo, srcMgr, headerSearch, compiler);
 clang::PreprocessorOptions ppOpts;
 clang::FrontendOptions frontendOpts;
 clang::InitializePreprocessor(pp, ppOpts, headerSearchOpts, frontendOpts);



 const clang::FileEntry *pFile = fileMgr.getFile("D:\\develop\\Clang\\temp.cpp");
 srcMgr.createMainFileID(pFile);
 //pp.EnterMainSourceFile();

 clang::IdentifierTable identifierTable(langOpts);
 clang::SelectorTable selectorTable;

 clang::Builtin::Context builtinContext;
 builtinContext.InitializeTarget(*targetInfo);
 clang::ASTContext astContext(
  langOpts,
  srcMgr,
  targetInfo,
  identifierTable,
  selectorTable,
  builtinContext,
  0 /* size_reserve*/);
   // clang::ASTConsumer astConsumer;
   MyASTConsumer astConsumer;

 clang::Sema sema(
  pp,
  astContext,
  astConsumer);
 sema.Initialize();

   //MySemanticAnalisys mySema( pp, astContext, astConsumer);

 //clang::Parser parser( pp, sema);
 //parser.ParseTranslationUnit();
 clang::ParseAST(pp, &astConsumer, astContext);

 clang::ASTContext::const_type_iterator t = astContext.types_begin();
 clang::ASTContext::const_type_iterator eot = astContext.types_end();
 for(; t != eot; ++t)
 {
  clang::CXXRecordDecl* pDecl = (*t)->getAsCXXRecordDecl();
  if( (pDecl != nullptr) && pDecl->isClass() )
  {
   if(pDecl->hasDefinition())
   {
    clang::CXXRecordDecl::base_class_iterator base = pDecl->bases_begin();
    clang::CXXRecordDecl::base_class_iterator eobase = pDecl->bases_end();
    for(; base != eobase; ++base)
    {
     base->getType().dump();
    }
   }
  }
 }

 getchar();
 return 0;
}

2011年10月25日火曜日

CLangでクラスの中身を調べる:Rebuild(5)

clang::RecordTypeにはgetAsCXXRecordDecl()というAPIもあり、clang::CXXRecordDeclが取得できる。このCXXRecordDeclのヘッダを眺めると、なかなか魅力的なAPIがある。

base_class_iterator bases_begin() { return data().getBases(); }

名前から、当該型の基本クラスを反復できることは容易に想像できる。

ところがこのbases_begin()をコールするとアサーションされてしまう。どうもメンバ変数が設定されてない様子。
CXXRecordDecl::hasDecinition()にて、このメンバ変数が設定されているかどうかを判定できるようだ。

こんなコードで

clang::ASTContext::const_type_iterator t = astContext.types_begin();
clang::ASTContext::const_type_iterator eot = astContext.types_end();
for(; t != eot; ++t)
{
 clang::CXXRecordDecl* pDecl = (*t)->getAsCXXRecordDecl();
 if( (pDecl != nullptr) && pDecl->isClass() )
 {
  if(pDecl->hasDefinition())
  {
   clang::CXXRecordDecl::base_class_iterator base = pDecl->bases_begin();
   clang::CXXRecordDecl::base_class_iterator eobase = pDecl->bases_end();
   for(; base != eobase; ++base)
   {
    base->getType().dump();
   }
  }
 }
}

こんな結果

: class Super identifier

正しく判定できている様子。
このhasDefinition()が何を意味するのか・・・。

2011年10月24日月曜日

CLangでクラスの中身を調べる:Rebuild(4)

Type.hを眺めていたら、isClassType()なるAPIを発見。さっそくgetClassType()の代わりに利用。きちんと動作した。

このclang::Type::isClassType()の実装や、すぐ下のisStructureType()の実装は興味深い。

bool Type::isClassType() const {
  if (const RecordType *RT = getAs<recordtype>())
    return RT->getDecl()->isClass();
  return false;
}
bool Type::isStructureType() const {
  if (const RecordType *RT = getAs<recordtype>())
    return RT->getDecl()->isStruct();
  return false;
}

まずTypeの派生クラスとしてRecordTypeがあり、反復中の型がクラスや構造体であるのなら、その実体はRecordTypeとして生成される。
かつRecordTypeにはgetDecl()なるAPIがあり、RecordDeclオブジェクトを返却してくれる。

ということで、Intellisenceにまかせてテキトーにコードを書いて実行してみた。

for(; t != eot; ++t)
{
 const clang::RecordType* pType = (*t)->getAs<clang::RecordType>();
 if(pType != nullptr)
 {
  const clang::RecordDecl* pDecl = pType->getDecl();
  if( (pDecl != nullptr) && pDecl->isClass() )
  {
   pDecl->getDeclName().dump();
  }
 }
}

結果。

type_info
_Lockit
_Mutex
_Init_locks

クラス名が取得できるようになった。

CLangでクラスの中身を調べる:Rebuild(3)

環境をVS2010に移行したら、リンクエラーが大量に・・・プロジェクトの依存関係を構築しただけではlibをリンクしてくれないらしい。以下のようにリンク設定を記述した。

..\build\lib\debug\LLVMSupport.lib
..\build\lib\debug\LLVMCodeGen.lib
..\build\lib\debug\LLVMMC.lib
..\build\lib\debug\clangBasic.lib
..\build\lib\debug\clangFrontend.lib
..\build\lib\debug\clangAnalysis.lib
..\build\lib\debug\clangLex.lib
..\build\lib\debug\clangSema.lib
..\build\lib\debug\clangAST.lib
..\build\lib\debug\clangParse.lib


前回のコードで、clang::Type::Recordに該当するもののみをダンプするよう修正してみた。こんなコードで

clang::ASTContext::const_type_iterator t = astContext.types_begin();
clang::ASTContext::const_type_iterator eot = astContext.types_end();
for(; t != eot; ++t)
{
 if(clang::Type::Record == (*t)->getTypeClass())
 {
  (*t)->dump();
 }
}

こんなソースコードを食わせると

#include <cstdio>

int main(int, char**)
{
 std::printf("Hello,world\n");
 std::getchar();
 return 0;
}

こんな結果。

: class type_info identifier
: struct threadlocaleinfostruct identifier
: struct threadmbcinfostruct identifier
: struct __lc_time_data identifier
: struct localeinfo_struct identifier
: struct tagLC_ID identifier
: struct threadlocaleinfostruct::<anonymous (x86)\microsoft="" 8\vc\include="" at="" c:\program="" crtdefs.h:2063:9="" files="" studio="" visual=""> identifier
: struct lconv identifier
: class std::_Lockit identifier
: class std::_Mutex identifier
: class std::_Init_locks identifier
: struct _iobuf identifier

2011年10月23日日曜日

CLangでクラスの中身を調べる:Rebuild(2)

ちょっと進展があったのでメモ。

clangにはTypeというクラスとその派生クラスがたくさんある。
構文解析結果としてそれを得られれば、その内容も調べられるかもしれない。

構文解析はclang::ParseAST()という関数にて実施する。このとき引数にclang::ASTContextのインスタンスを指定するのだが、このASTContextにTypeを反復するtypes_begin(), types_end()メンバ関数がある。

さて、Typeにはdump()という関数があるので、反復した要素に対してdump()してみると・・・

clang::ASTContext::const_type_iterator t = astContext.types_begin();
clang::ASTContext::const_type_iterator eot = astContext.types_end();
for(; t != eot; ++t)
{
 (*t)->dump();
}

int等の組み込み型やincludeしたC/C++の標準構造体などがすべて出力される。まあ当たり前か。

他にTypeで何かいいものがないかなぁと調べると、getTypeClass()というのがある。これをダンプ結果と組み合わせると、自作クラスでは値として22が返却される。getTypeClassName()を使うと、これはRecordという種別であるようだ。

こんなプログラムで

clang::ASTContext::const_type_iterator t = astContext.types_begin();
clang::ASTContext::const_type_iterator eot = astContext.types_end();
for(; t != eot; ++t)
{
 (*t)->dump();
 std::printf("%u %s\n", (*t)->getTypeClass(), (*t)->getTypeClassName());
 std::printf("-----------------------------------------------------------------------------------\n");
}

こんなのをパースして

#include 

class Temp
{
};
class Hoge;
int main(int, char**)
{
 std::printf("Hello,world\n");
 std::getchar();
 return 0;
}

こんな結果が出る(末尾付近を一部抜粋)

: class Temp identifier
ほげ    22      Record
-----------------------------------------------------------------------------------
: class Hoge identifier
ほげ    22      Record
-----------------------------------------------------------------------------------
: char **identifier
ほげ    2       Pointer
-----------------------------------------------------------------------------------
: int (identifier)(int, char **)
ほげ    14      FunctionProto
-----------------------------------------------------------------------------------

2011年10月2日日曜日

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

こんなの見つけた。

今までの苦労は・・・と一瞬思ったが、バージョン違いのせいかそのままではビルドが通らなかったり、変なビルドエラーが出たりする。
こいつのTutorial6を元にしてビルドが通るよう修正しながら動作を見ていくことにする。

まず気づいた点:
  • LangOptionsをきちんと設定しなければならない。たとえばMicrosoft拡張仕様をtrueにしないと__int64が正しく認識されなかったりする。
  • 色々とグローバル関数があって、それを利用して各インスタンスを初期化していく。ヘッダ検索はApplyHeaderSearchOptions()、プリプロセッサはInitializePreprocessor()など。
  • TextDiagnosticPrinter::BeginSourceFile()は、やっぱり自分でコールしなければならない。

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

さて、ようやくプリプロセッサを実行するところまで行った。と、プリプロセッサがエラーを出している。
In file included from D:\develop\Clang\temp.cpp:1:
In file included from C:\Program Files (x86)\Microsoft Visual Studio 8\VC\INCLUDE/cstdio:5:
In file included from C:\Program Files (x86)\Microsoft Visual Studio 8\VC\INCLUDE/yvals.h:6:
C:\Program Files (x86)\Microsoft Visual Studio 8\VC\INCLUDE/crtdefs.h:40:2: error: #error ERROR: Only Win32 target supported!
#error ERROR: Only Win32 target supported!

どうも_WIN32をdefineせずにcstdioをインクルードしてはいけないらしい。ということで、predefineを追加してみる。
色々調べていくと、clang::PreprocessorにsetPredefines()というAPIがあり、またその設定値をclang::MacroBuilderで生成できることがわかった。
	std::string predefinesBuf;
	llvm::raw_string_ostream predefines(predefinesBuf);
	clang::MacroBuilder macroBuilder(predefines);
	macroBuilder.defineMacro("_WIN32");
	pp.setPredefines(predefines.str());

これでビルドして実行するとプリプロセッサがエラーを出さなくなった。ブレークポイントでPreprocessor::Lex()の結果を確認すると、何かしらデータは入っているようだ。ようやく先に進めそうだ。

2011年10月1日土曜日

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

前回の続き。アサーションではなくエラーメッセージにする方法の検討。

どうもclang::DiagnosticのメンバにClientというのがあり、それがnullptrであるときにアサーションされるようだ。

ってことで、改めてマジメにclang::Diagnosticsについて調べてみる。.hのドキュメントを見ると、clang::DiagnosticClientを出力先に指定しなければならないようだ。もし自分が診断結果の出力先のクラスを作るなら、きっと出力先を切り替えられるよう抽象化するだろうと思ってpublic DiagnosticClientで検索すると、以下のようなものがヒットした:
  • ChainedDiagnosticClient
  • TextDiagnosticBuffer
  • TextDiagnosticPrinter
  • VerifyDiagnosticsClient
  • StoredDiagnosticClient
  • FixItRewriter
  • IgnoringDiagClient
  • PathDiagnosticClient
最もそれっぽいclang::TextDiagnosticPrinterを使ってみる。

clanng::TextDiagnosticPrinterのコンストラクタはllvm::raw_ostreamとclang::DiagnosticOptionsを要求する。
前者は出力先ストリームであるからさらに抽象化されているのではないかと思って検索すると、やはり派生クラスがいくつか出てくる。ひとまずstd::ostreamに出力すると書いてあるllvm::raw_os_ostreamを使ってみる。
後者は色々設定できるようだが、まずはデフォルト設定で使ってみる。
インクルードに
#include <iostream>
#include "llvm/Support/raw_os_ostream.h"
#include "clang/Frontend/DiagnosticOptions.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
を追加して、以下のコードを記述。
	llvm::raw_os_ostream diagOStream(std::cout);
	clang::DiagnosticOptions diagOpts;
	clang::TextDiagnosticPrinter diagPrinter(diagOStream, diagOpts);

	llvm::IntrusiveRefCntPtr<clang::diagnosticids>
		diagIds(new clang::DiagnosticIDs());
	clang::Diagnostic diag(diagIds, &diagPrinter);

ビルドするとclang::TextDiagnosticPrinterがリンクエラーになるので、clangFrontendプロジェクトを依存関係に追加。

で、実行すると・・・今度はTextDiagnosticPrinterでアサーションされるorz
メンバにLangOptionsがいて、そいつがnullptrであるとのこと。そのメンバ変数を設定するAPIはTextDiagnosticPrinter::BeginSourceFile()のみであるようだ。親クラスであるclang::DiagnosticClientの.hに記載のドキュメントによると、ソースファイルの処理が開始されたこと通知するためのコールバックであると書かれている。

そこでBeginSourceFile()の呼び出しルートを調べる。

■clang::DiagnosticClient::BeginSourceFile()の呼び出し元
  • clang::ASTMergeAction::ExecuteAction()
    ※ASTMergeActionはclang::FrontendActionの派生クラス。
  • clang::FrontendAction::BeginSourceFile()
■clang::FrontendAction::ExecuteAction()の呼び出し元
  • clang::FrontendAction::Execute()
■clang::FrontendAction::BeginSourceFile()の呼び出し元
  • clang::ASTUnit::Parse()
  • clang::ASTUnit::getMainBufferWithPrecompiledPreamble()
  • clang::ASTUnit::CodeComplete()
  • clang::CompilerInstance::ExecuteAction()
■clang::FrontendAction::Execute()の呼び出し元
  • clang::ASTUnit::Parse()
  • clang::ASTUnit::getMainBufferWithPrecompiledPreamble()
  • clang::ASTUnit::CodeComplete()
  • clang::CompilerInstance::ExecuteAction()
■clang::CompilerInstance::ExecuteAction()の呼び出し元
  • clang::ExecuteCompilerInvocation()

うーん、CompilerInstanceやASTUnitはプリプロセッサより後段であると思う。では自前でコールするのがいいのか?
正しいやり方かどうか怪しくなってきたが、試してみると・・・アサーションされなくなり、プリプロセッサのエラーが確認できるようになった。
#include <iostream>
#include "llvm/Support/raw_os_ostream.h"
#include "llvm/Support/Host.h"
#include "clang/Frontend/DiagnosticOptions.h"
#include "clang/Frontend/TextDiagnosticPrinter.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*[])
{
	// Preprocessor をセットアップする。

	llvm::raw_os_ostream diagOStream(std::cout);
	clang::DiagnosticOptions diagOpts;
	clang::TextDiagnosticPrinter diagPrinter(diagOStream, diagOpts);

	llvm::IntrusiveRefCntPtr<clang::diagnosticids>
		diagIds(new clang::DiagnosticIDs());
	clang::Diagnostic diag(diagIds, &diagPrinter, false);

	clang::LangOptions langOpts;
	diagPrinter.BeginSourceFile(langOpts, NULL);

	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);


	// 標準インクルードパスを設定する。

	llvm::StringRef stdHeaderPath1("C:\\Program Files (x86)\\Microsoft Visual Studio 8\\VC\\INCLUDE");
	llvm::StringRef stdHeaderPath2("C:\\Program Files (x86)\\Microsoft Visual Studio 8\\VC\\PlatformSDK\\include");
	std::vector<clang::directorylookup> stdHeaderPaths;
	{
		const clang::DirectoryEntry* p = fileMgr.getDirectory(stdHeaderPath1);
		clang::DirectoryLookup d(p, clang::SrcMgr::C_System, false, false);
		stdHeaderPaths.push_back(d);
	}
	{
		const clang::DirectoryEntry* p = fileMgr.getDirectory(stdHeaderPath2);
		clang::DirectoryLookup d(p, clang::SrcMgr::C_System, false, false);
		stdHeaderPaths.push_back(d);
	}
	headerSearch.SetSearchPaths(stdHeaderPaths, 0, false);


	// コンパイル対象のファイルを指定する。

	llvm::StringRef cppPath("D:\\develop\\Clang\\temp.cpp");
	const clang::FileEntry* cppEntry = fileMgr.getFile(cppPath);
	clang::FileID fileId = srcMgr.createMainFileID(cppEntry);
	pp.EnterMainSourceFile();

	clang::Token token;
	pp.Lex(token);

	diagPrinter.EndSourceFile();

	getchar();
	return 0;
}

しばらくこれで進めてみよう・・・。