clangにはリンク等の時点で判別する方法があると思うが、その機能を調べるのが非常にめんどくさい。そこで、各クラスの所属パッケージを調べておいて、その名前を文字列比較することで判定しようと思う。
自分の名前(クラス名)はCXXRecordDecl::getName()で取得できる。なので所属するパッケージ(名前空間)を取得できればよいはずである。
名前空間はclang::Type::dump()にてダンプされているので、そこで利用されているルーチンclang::TypePrinter::AppendScope()を調べてみた。以下のような実装である:
void TypePrinter::AppendScope(DeclContext *DC, std::string &Buffer) { if (DC->isTranslationUnit()) return; AppendScope(DC->getParent(), Buffer); unsigned OldSize = Buffer.size(); if (NamespaceDecl *NS = dyn_cast<NamespaceDecl>(DC)) { if (NS->getIdentifier()) Buffer += NS->getNameAsString(); else Buffer += "<anonymous>"; } else if (ClassTemplateSpecializationDecl *Spec = dyn_cast<ClassTemplateSpecializationDecl>(DC)) { IncludeStrongLifetimeRAII Strong(Policy); const TemplateArgumentList &TemplateArgs = Spec->getTemplateArgs(); std::string TemplateArgsStr = TemplateSpecializationType::PrintTemplateArgumentList( TemplateArgs.data(), TemplateArgs.size(), Policy); Buffer += Spec->getIdentifier()->getName(); Buffer += TemplateArgsStr; } else if (TagDecl *Tag = dyn_cast<TagDecl>(DC)) { if (TypedefNameDecl *Typedef = Tag->getTypedefNameForAnonDecl()) Buffer += Typedef->getIdentifier()->getName(); else if (Tag->getIdentifier()) Buffer += Tag->getIdentifier()->getName(); } if (Buffer.size() != OldSize) Buffer += "::"; }
本来の目的を考えると、上のルーチンをそのまま使えばいいのだが、それはできない。このTypePrinterは匿名の名前空間(namespace {} と宣言されているやつ)内で定義されており、かつAST/TypePrinter.cppにクラス定義があるのである。
なのでこのルーチンを解析して移植する。
std::string getPackagePath(clang::DeclContext* context) { if(context == nullptr) { return ""; } if(context->isTranslationUnit()) { return ""; } std::string path = getPackagePath(context->getParent()); std::string::size_type oldLen = path.length(); clang::NamespaceDecl* nsDecl = llvm::dyn_cast<clang::NamespaceDecl>(context); if(nsDecl == nullptr) { clang::TagDecl* tag = llvm::dyn_cast<clang::TagDecl>(context); if(tag != nullptr) { clang::TypedefNameDecl* tdef = tag->getTypedefNameForAnonDecl(); if(tdef == nullptr) { path += tag->getIdentifier()->getName(); } else { path += tdef->getIdentifier()->getName(); } } } else { if(nsDecl->getIdentifier() == nullptr) { path += "<anonymous>"; } else { path += nsDecl->getName(); } } if(path.length() != oldLen) { path += "::"; } return path; }
これで通常の名前空間やインナークラスは処理できるようになった。TypePrinter側の実装でテンプレート用?ルーチンがあるのが気になる。
追記:
クラステンプレートの定義はASTContextによるclang::Typeの反復においては反復されない。
0 件のコメント:
コメントを投稿