2

例如,我有以下课程:

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

private:
    T bar_;
};

它被实例化为:

Foo<Bar> foo;

我提取 的clang::CXXRecordDecl节点class Foo,并遍历其字段:

for (const clang::FieldDecl *fieldDecl: fooRecordDecl->fields()) {
    // fieldDecl->getType() gives T
    // fieldDecl->getNameAsString() gives bar_
}

我想要一些能fieldDecl->getInstantiatedType()提供的东西Bar

我了解CXXRecordDeclof的 ASTFoo不应包含有关实例化类型的任何信息。我想知道这个链接信息是否存储在 AST 的其他地方,以及如何检索它。


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

{ A: Foo, B: Bar, C: Baz}

这看起来很复杂,而且像“un-Clang”一样。

4

2 回答 2

2

这确实是一种“非 Clang”方式。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 一起愉快地进行黑客攻击!

于 2019-06-27T17:56:55.493 回答
0

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

但是 RecursiveASTVisitor 默认不会访问这些 ClassTemplateSpecializationDecl(s)。如果您需要拜访他们,您需要:

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

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

于 2020-01-01T05:56:41.500 回答