如何使用 Clang 获取类型为 class 的模板成员的实际类型?
How to get the actual type of a template typed class member with Clang?
比如我有下面的class:
template<typename T>
class Foo {
public:
T getBar();
private:
T bar_;
};
实例化为:
Foo<Bar> foo;
我提取 class Foo
的 clang::CXXRecordDecl
节点,并遍历其字段:
for (const clang::FieldDecl *fieldDecl: fooRecordDecl->fields()) {
// fieldDecl->getType() gives T
// fieldDecl->getNameAsString() gives bar_
}
我想要的东西 fieldDecl->getInstantiatedType()
可以 Bar
我知道 Foo
的 CXXRecordDecl
的 AST 不应包含任何关于实例化类型的信息。 我想知道这个 linking 信息是否存储在 AST 的其他地方,以及如何检索它。
我目前的解决方案包括按顺序获取未初始化的模板参数,比如 {A, B, C}
对应 template<typename A, typename B, typename C> class Baz {};
并将它们存储在 std::vector
中。然后找到实例化调用Baz<Foo, Bar, Baz>
,将实例化的类型按顺序存储在另一个std::vector
中,link按索引一起得到:
{ A: Foo, B: Bar, C: Baz}
这看起来很复杂,"un-Clang" 喜欢。
这确实是一种 "un-Clang" 方式。 Clang 通常单独存储所有实例化,获得正确的 class 声明很重要。在你的情况下,我猜你在 "I extract clang::CXXRecordDecl
..." 部分的某个地方转错了。
我放了一个小的访客解决方案,它有点笨拙,但很容易根据您的需要进行调整:
bool VisitVarDecl(VarDecl *VD) {
// VD->getType() dump would look like smth like this:
//
// > TemplateSpecializationType 0x7ffbed018180 'Foo<class Bar>' sugar Foo
// > |-TemplateArgument type 'class Bar'
// > `-RecordType 0x7ffbed018160 'class Foo<class Bar>'
// > `-ClassTemplateSpecialization 0x7ffbed018078 'Foo'
//
// The following code unwraps types to get to that ClassTemplateSpecialization
auto SpecializationDecl = VD->getType()
->getAs<TemplateSpecializationType>()
->desugar()
->getAs<RecordType>()
->getDecl();
// these fields will have specialized types
for (const auto *Field : SpecializationDecl->fields()) {
Field->getType().dump();
}
return true;
}
对于以下片段:
// test.cpp
class Bar {};
template <typename T> class Foo {
public:
T getBar();
private:
T bar_;
};
int main() { Foo<Bar> foo; }
它产生这个输出:
SubstTemplateTypeParmType 0x7ffbed0183b0 'class Bar' sugar
|-TemplateTypeParmType 0x7ffbed017900 'T' dependent depth 0 index 0
| `-TemplateTypeParm 0x7ffbed017890 'T'
`-RecordType 0x7ffbed017750 'class Bar'
`-CXXRecord 0x7ffbed0176b0 'Bar'
如您所见,它有一个 T
的加糖版本,其中包含对 Bar
.
的引用
希望这就是您要找的。祝您使用 Clang 愉快!
对于 class 模板的每个实例化,AST 中的 ClassTemplateDecl 节点下都会有一个 ClassTemplateSpecializationDecl。通常,您在问题中访问的 CXXRecordDecl 将是 ClassTemplateDecl 下的第一个节点,然后是 CXXRecordDecl 的子 class 的那些 ClassTemplateSpecializationDecl(s)。您需要的信息在那些 ClassTemplateSpecializationDecl(s) 中。
但 RecursiveASTVisitor 默认不访问这些 ClassTemplateSpecializationDecl(s)。如果您需要拜访他们,您需要:
class MyVisitor: public RecursiveASTVisitor<MyVisitor> {
...
bool shouldVisitTemplateInstantiations() const { return true;}
...
}
然后对于模板中的每个元素,如 field/member 函数,相关的 Visit*** 函数将为模板元素(即在问题中)调用一次,并为它的每个实例调用一次。
比如我有下面的class:
template<typename T>
class Foo {
public:
T getBar();
private:
T bar_;
};
实例化为:
Foo<Bar> foo;
我提取 class Foo
的 clang::CXXRecordDecl
节点,并遍历其字段:
for (const clang::FieldDecl *fieldDecl: fooRecordDecl->fields()) {
// fieldDecl->getType() gives T
// fieldDecl->getNameAsString() gives bar_
}
我想要的东西 fieldDecl->getInstantiatedType()
可以 Bar
我知道 Foo
的 CXXRecordDecl
的 AST 不应包含任何关于实例化类型的信息。 我想知道这个 linking 信息是否存储在 AST 的其他地方,以及如何检索它。
我目前的解决方案包括按顺序获取未初始化的模板参数,比如 {A, B, C}
对应 template<typename A, typename B, typename C> class Baz {};
并将它们存储在 std::vector
中。然后找到实例化调用Baz<Foo, Bar, Baz>
,将实例化的类型按顺序存储在另一个std::vector
中,link按索引一起得到:
{ A: Foo, B: Bar, C: Baz}
这看起来很复杂,"un-Clang" 喜欢。
这确实是一种 "un-Clang" 方式。 Clang 通常单独存储所有实例化,获得正确的 class 声明很重要。在你的情况下,我猜你在 "I extract clang::CXXRecordDecl
..." 部分的某个地方转错了。
我放了一个小的访客解决方案,它有点笨拙,但很容易根据您的需要进行调整:
bool VisitVarDecl(VarDecl *VD) {
// VD->getType() dump would look like smth like this:
//
// > TemplateSpecializationType 0x7ffbed018180 'Foo<class Bar>' sugar Foo
// > |-TemplateArgument type 'class Bar'
// > `-RecordType 0x7ffbed018160 'class Foo<class Bar>'
// > `-ClassTemplateSpecialization 0x7ffbed018078 'Foo'
//
// The following code unwraps types to get to that ClassTemplateSpecialization
auto SpecializationDecl = VD->getType()
->getAs<TemplateSpecializationType>()
->desugar()
->getAs<RecordType>()
->getDecl();
// these fields will have specialized types
for (const auto *Field : SpecializationDecl->fields()) {
Field->getType().dump();
}
return true;
}
对于以下片段:
// test.cpp
class Bar {};
template <typename T> class Foo {
public:
T getBar();
private:
T bar_;
};
int main() { Foo<Bar> foo; }
它产生这个输出:
SubstTemplateTypeParmType 0x7ffbed0183b0 'class Bar' sugar
|-TemplateTypeParmType 0x7ffbed017900 'T' dependent depth 0 index 0
| `-TemplateTypeParm 0x7ffbed017890 'T'
`-RecordType 0x7ffbed017750 'class Bar'
`-CXXRecord 0x7ffbed0176b0 'Bar'
如您所见,它有一个 T
的加糖版本,其中包含对 Bar
.
希望这就是您要找的。祝您使用 Clang 愉快!
对于 class 模板的每个实例化,AST 中的 ClassTemplateDecl 节点下都会有一个 ClassTemplateSpecializationDecl。通常,您在问题中访问的 CXXRecordDecl 将是 ClassTemplateDecl 下的第一个节点,然后是 CXXRecordDecl 的子 class 的那些 ClassTemplateSpecializationDecl(s)。您需要的信息在那些 ClassTemplateSpecializationDecl(s) 中。
但 RecursiveASTVisitor 默认不访问这些 ClassTemplateSpecializationDecl(s)。如果您需要拜访他们,您需要:
class MyVisitor: public RecursiveASTVisitor<MyVisitor> {
...
bool shouldVisitTemplateInstantiations() const { return true;}
...
}
然后对于模板中的每个元素,如 field/member 函数,相关的 Visit*** 函数将为模板元素(即在问题中)调用一次,并为它的每个实例调用一次。