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 件のコメント:
コメントを投稿