2

For example, I have the following class:

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

private:
    T bar_;
};

It is instantiated as:

Foo<Bar> foo;

I extract the clang::CXXRecordDecl node of class Foo, and iterate through its fields:

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

I want something that does fieldDecl->getInstantiatedType() that gives Bar

I understand that the AST for the CXXRecordDecl of Foo shouldn't contain any information on the instantiated type. I was wondering if this linking information was stored somewhere else in the AST, and how I could retrieve it.


My current solution involves getting uninitialized template parameters in order, say {A, B, C} for template<typename A, typename B, typename C> class Baz {}; and storing them in an std::vector. Then finding the instantiation call Baz<Foo, Bar, Baz>, and store the instantiated types in order in another std::vector, and link them together by index to get:

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

This seems very convoluted and "un-Clang" like.

Valeriy Savchenko
  • 1,524
  • 8
  • 19
Eric
  • 730
  • 4
  • 16

2 Answers2

2

It is an "un-Clang" way indeed. Clang usually stores all instantiations separately and it is important to get to the right class declaration. In your case, I guess you took a wrong turn somewhere in the "I extract clang::CXXRecordDecl..." part.

I put a small visitor solution, it is a bit campy, but it's easy to adapt it for your needs:

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;
}

For the following snippet:

// test.cpp
class Bar {};

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

private:
  T bar_;
};

int main() { Foo<Bar> foo; }

it produces this ouput:

SubstTemplateTypeParmType 0x7ffbed0183b0 'class Bar' sugar
|-TemplateTypeParmType 0x7ffbed017900 'T' dependent depth 0 index 0
| `-TemplateTypeParm 0x7ffbed017890 'T'
`-RecordType 0x7ffbed017750 'class Bar'
  `-CXXRecord 0x7ffbed0176b0 'Bar'

As you can see, it has a sugared version of T that contains a reference to Bar.

I hope this is what you are looking for. Happy hacking with Clang!

Valeriy Savchenko
  • 1,524
  • 8
  • 19
0

For each instantiation of a class template there will be a ClassTemplateSpecializationDecl under the ClassTemplateDecl node in the AST. Commonly the CXXRecordDecl you visited in the question will be the first node under ClassTemplateDecl, then those ClassTemplateSpecializationDecl(s) which is a subclass of CXXRecordDecl. The information you need are in those ClassTemplateSpecializationDecl(s).

But RecursiveASTVisitor doesn't visit these ClassTemplateSpecializationDecl(s) by default. If you need to visit them, you need to:

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

then for each elements in the template like field/member function, the related Visit*** function will be called once for the template element (i.e. in the question) and once for each instantiation of it.

jw_
  • 1,663
  • 18
  • 32