如何使用Clang获取模板类型的类成员的实际类型?

时间:2019-06-27 14:01:06

标签: c++ c++11 clang abstract-syntax-tree libtooling

例如,我有以下课程:

template<typename T>
class Foo {
public:
    T getBar();

private:
    T bar_;
};

它被实例化为:

Foo<Bar> foo;

我提取clang::CXXRecordDecl的{​​{1}}节点,并遍历其字段:

class Foo

我想要做某事for (const clang::FieldDecl *fieldDecl: fooRecordDecl->fields()) { // fieldDecl->getType() gives T // fieldDecl->getNameAsString() gives bar_ } 并得到fieldDecl->getInstantiatedType()

我知道Bar的{​​{1}}的AST不应包含有关实例化类型的任何信息。 我想知道此链接信息是否存储在AST的其他位置,以及如何检索它。


我当前的解决方案包括按顺序获取未初始化的模板参数,例如将CXXRecordDecl设为Foo并将其存储在{A, B, C}中。然后找到实例化调用template<typename A, typename B, typename C> class Baz {};,并将实例化的类型按顺序存储在另一个std::vector中,并通过索引将它们链接在一起以获得:

Baz<Foo, Bar, Baz>

这看起来很令人费解,像“ un-Clang”。

2 个答案:

答案 0 :(得分:2)

这确实是一种“非C语”的方式。 Clang通常将所有实例化单独存储,因此获取正确的类声明很重要。就您而言,我想您在“我提取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进行快乐的黑客入侵!

答案 1 :(得分:0)

对于类模板的每个实例,在AST的ClassTemplateDecl节点下都有一个ClassTemplateSpecializationDecl。通常,您在问题中访问过的CXXRecordDecl将是ClassTemplateDecl下的第一个节点,然后是CXXRecordDecl的子类ClassClassSpecializationDecl。您需要的信息在这些ClassTemplateSpecializationDecl中。

但是,默认情况下,RecursiveASTVisitor不会访问这些ClassTemplateSpecializationDecl。如果您需要访问它们,则需要:

class MyVisitor: public RecursiveASTVisitor<MyVisitor> {
...
bool shouldVisitTemplateInstantiations() const { return true;}
...
}

然后,对于模板中的每个元素(例如字段/成员函数),相关的Visit ***函数将分别为模板元素(即问题中)调用一次,并为每个实例化调用一次。