【问题标题】:How to get the actual type of a template typed class member with Clang?如何使用 Clang 获取模板类型类成员的实际类型?
【发布时间】:2019-11-09 13:51:24
【问题描述】:

例如,我有以下课程:

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

private:
    T bar_;
};

实例化为:

Foo<Bar> foo;

我提取class Fooclang::CXXRecordDecl 节点,并遍历其字段:

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

我想要一些可以提供fieldDecl-&gt;getInstantiatedType() 的东西,它可以提供Bar

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


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

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

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

【问题讨论】:

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


    【解决方案1】:

    这确实是一种“非 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;
    }
    

    对于下面的sn-p:

    // 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 进行黑客攻击!

    【讨论】:

      【解决方案2】:

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

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-05-25
        • 2021-07-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-31
        • 1970-01-01
        相关资源
        最近更新 更多