LLVM源码阅读之四---TrailingObjects

2022-04-27 10:08:53
标签: llvm trailingobjects crtp

LLVM源码阅读之四 --- TrailingObjects

今天来研究一下这个类TrailingObjects,这也是奇葩的类。

class TemplateParameterList final

: private llvm::TrailingObjects

Expr *>

第一,它必须被别人私有继承,然后Derived类会被当成第一个模板参数传递给TrailingObjects模板类。

这是一个CRTP的模板,奇异递归模板模式。Curiously Recurring Template Pattern。

它会把子类的类型传递给模板基类,然后在基类实现时,直接static_cast来调用方法。

可以参考https://blog.csdn.net/sinat_21107433/article/details/123145236

template

class TrailingObjects : private trailing_objects_internal::TrailingObjectsImpl<</p>

trailing_objects_internal::AlignmentCalcHelper<</p>

TrailingTys...>::Alignment,

BaseTy, TrailingObjects,

BaseTy, TrailingTys...>

它从TrailingObjectsImpl继承。其中BaseTy类是Derived子类。然后TrailingObjectsImpl同样是个CRTP模板。

把自己传进去,然后又把类型传一遍。

template class AlignmentCalcHelper {

private:

enum {

FirstAlignment = alignof(First),

RestAlignment = AlignmentCalcHelper::Alignment,

};

public:

enum {

Alignment = FirstAlignment > RestAlignment ? FirstAlignment : RestAlignment

};

};

template class AlignmentCalcHelper {

public:

enum { Alignment = alignof(First) };

};

用来计算alignment,这个在创建对象时候用。

template

typename NextTy, typename... MoreTys>

class TrailingObjectsImpl

MoreTys...>

: public TrailingObjectsImpl

MoreTys...>

它会从

frame #0:

0x000000010a960928

clang`llvm::trailing_objects_internal::TrailingObjectsImpl<8, clang::TemplateParameterList, llvm::TrailingObjects,

clang::Expr*>::additionalSizeToAllocImpl(SizeSoFar=8) at TrailingObjects.h:199:12

frame #1:

0x000000010a960914

clang`llvm::trailing_objects_internal::TrailingObjectsImpl<8, clang::TemplateParameterList, llvm::TrailingObjects,

clang::NamedDecl*,

clang::Expr*>::additionalSizeToAllocImpl(SizeSoFar=8, Count1=0) at TrailingObjects.h:179:12

frame #2:

0x000000010a9608dc

clang`llvm::trailing_objects_internal::TrailingObjectsImpl<8, clang::TemplateParameterList, llvm::TrailingObjects,

clang::TemplateParameterList,

clang::NamedDecl*,

clang::Expr*>::additionalSizeToAllocImpl(SizeSoFar=0, Count1=1, MoreCounts=0) at TrailingObjects.h:179:12

frame #3:

0x000000010a957de4

clang`std::__1::enable_if::Foo, llvm::TrailingObjects::Foo >::value, unsigned long>::type

llvm::TrailingObjects::totalSizeToAlloc(Counts=1, Counts=0) at TrailingObjects.h:328:29

frame #4: 0x000000010a957d44 clang`clang::TemplateParameterList::Create(C=0x000000013380f800, TemplateLoc=(ID = 14342), LAngleLoc=(ID = 14350), Params=ArrayRef @ 0x000000032fdab200, RAngleLoc=(ID = 14361), RequiresClause=0x0000000000000000) at DeclTemplate.cpp:123:26

从调用栈上看,很明显它一层层继承

clang::TemplateParameterList,

clang::NamedDecl*,

clang::Expr*

clang::NamedDecl*,

clang::Expr*

再到

clang::Expr*

逐渐去掉前面的Base类。

// The base case of the TrailingObjectsImpl inheritance recursion,

// when there's no more trailing types.

template

class alignas(Align) TrailingObjectsImpl

: public TrailingObjectsBase

这是最后一层基类,它从TrailingObjectsBase继承

static constexpr size_t additionalSizeToAllocImpl(size_t SizeSoFar) {

return SizeSoFar;

}

当最后一个类型的时候,它直接返回SizeSoFar.没有后续的类了。

template

static constexpr std::enable_if_t<</span>

std::is_same, Foo>::value, size_t>

totalSizeToAlloc(typename trailing_objects_internal::ExtractSecondType<</span>

TrailingTys, size_t>::type... Counts) {

return sizeof(BaseTy) + ParentType::additionalSizeToAllocImpl(0, Counts...);

}

template

static constexpr std::enable_if_t<</span>

std::is_same, Foo>::value, size_t>

additionalSizeToAlloc(typename trailing_objects_internal::ExtractSecondType<</span>

TrailingTys, size_t>::type... Counts) {

return ParentType::additionalSizeToAllocImpl(0, Counts...);

}

也很有意思,它通过

std::enable_if_t<</span>

std::is_same, Foo>::value, size_t>判断是不是和定义的类型一样,来确定size_t。

否则应该报错了。

typename trailing_objects_internal::ExtractSecondType<</span>

TrailingTys, size_t>::type... Counts

这个模板类方法,用来返回相同个数的size_t对像。

totalSizeToAlloc = sizeof(BaseTy) + ParentType::additionalSizeToAllocImpl(0, Counts...);

additionalSizeToAlloc = ParentType::additionalSizeToAllocImpl(0, Counts...);

这样一层一层统计。最后就会到了上面一个参数的TrailingObjectsBase。

最后来说一下。

Expr** exprTemp = getTrailingObjects();

我特地加了一个第二个模板参数来调,这个代码我加的,只为调试这个类的工作方式。

/// Returns a pointer to the trailing object array of the given type

/// (which must be one of those specified in the class template). The

/// array may have zero or more elements in it.

template T *getTrailingObjects() {

verifyTrailingObjectsAssertions();

// Forwards to an impl function with overloads, since member

// function templates can't be specialized.

return this->getTrailingObjectsImpl(

static_cast(this), TrailingObjectsBase::OverloadToken());

}

它会调

static NextTy *

getTrailingObjectsImpl(BaseTy *Obj,

TrailingObjectsBase::OverloadToken) {

auto *Ptr = TopTrailingObj::getTrailingObjectsImpl(

Obj, TrailingObjectsBase::OverloadToken()) +

TopTrailingObj::callNumTrailingObjects(

Obj, TrailingObjectsBase::OverloadToken());

if (requiresRealignment())

return reinterpret_cast(alignAddr(Ptr, Align::Of()));

else

return reinterpret_cast(Ptr);

}

可以看到

TrailingObjectsBase::OverloadToken(),这个是用来判断把这个方法实现成不同的override,如果传一个不存在的类,应该会报错。

它会得前一个类型指针,加上前一个类型对像的个数。

它是调用TopTrailingObj也就是本类的不同重载。得的时候就是从后向前推导类型和分配时候从前到后调用不同类型不一样。

static BaseTy *

getTrailingObjectsImpl(BaseTy *Obj,

TrailingObjectsBase::OverloadToken) {

return Obj;

}

// callNumTrailingObjects simply calls numTrailingObjects on the

// provided Obj -- except when the type being queried is BaseTy

// itself. There is always only one of the base object, so that case

// is handled here. (An additional benefit of indirecting through

// this function is that consumers only say "friend

// TrailingObjects", and thus, only this class itself can call the

// numTrailingObjects function.)

static size_t

callNumTrailingObjects(const BaseTy *Obj,

TrailingObjectsBase::OverloadToken) {

return 1;

}

template

static size_t callNumTrailingObjects(const BaseTy *Obj,

TrailingObjectsBase::OverloadToken) {

return Obj->numTrailingObjects(TrailingObjectsBase::OverloadToken());

}

它最后会进到基类为参数,getTrailingObjectsImpl(BaseTy *Obj,

TrailingObjectsBase::OverloadToken)的重载,直接返回指针,它的个数永远是1,+1相当于加了sizeof(BaseTy).

而其他对象时候,则会调用callNumTrailingObjects的其他类型的模板方法。

调用numTrailingObjects的对应版本,而这个通常是在最终类中实现的。神吧。

TemplateParameterList(const ASTContext& C, SourceLocation TemplateLoc,

SourceLocation LAngleLoc, ArrayRef Params,

SourceLocation RAngleLoc, Expr *RequiresClause);

size_t numTrailingObjects(OverloadToken) const {

return NumParams;

}

size_t numTrailingObjects(OverloadToken) const {

return HasRequiresClause ? 1 : 0;

}

现在原理分析完了,来验证一下call stack.

frame #0:

0x0000000105a4f068

clang`llvm::TrailingObjects

clang::NamedDecl*,

clang::Expr*>::getTrailingObjectsImpl(Obj=0x000000012d019b68, (null)=OverloadToken @ 0x000000032fdaafc8) at TrailingObjects.h:249:12

frame #1:

0x0000000105a4f035

clang`llvm::trailing_objects_internal::TrailingObjectsImpl<8, clang::TemplateParameterList, llvm::TrailingObjects,

clang::TemplateParameterList,

clang::NamedDecl*,

clang::Expr*>::getTrailingObjectsImpl(Obj=0x000000012d019b68, (null)=OverloadToken @ 0x000000032fdab008) at TrailingObjects.h:162:17

frame #2:

0x0000000105a4f0c5

clang`llvm::trailing_objects_internal::TrailingObjectsImpl<8, clang::TemplateParameterList, llvm::TrailingObjects,

clang::NamedDecl*,

clang::Expr*>::getTrailingObjectsImpl(Obj=0x000000012d019b68, (null)=OverloadToken @ 0x000000032fdab048) at TrailingObjects.h:162:17

frame #3:

0x0000000105a4f0a2

clang`clang::Expr** llvm::TrailingObjects::getTrailingObjects(this=0x000000012d019b68) at TrailingObjects.h:301:12

得Expr*地址,也就是先得到BaseTy* + 1个对像+ sizeof(NamedDecl*) * NumParams

传第一个参数的情况

frame #0:

0x0000000105a4f068

clang`llvm::TrailingObjects::getTrailingObjectsImpl(Obj=0x000000012d01d768, (null)=OverloadToken @ 0x000000032fdaafe8) at TrailingObjects.h:249:12

frame #1:

0x0000000105a4f035

clang`llvm::trailing_objects_internal::TrailingObjectsImpl<8, clang::TemplateParameterList, llvm::TrailingObjects,

clang::TemplateParameterList,

clang::NamedDecl*,

clang::Expr*>::getTrailingObjectsImpl(Obj=0x000000012d01d768, (null)=OverloadToken @ 0x000000032fdab028) at TrailingObjects.h:162:17

frame #2:

0x0000000105a4f002

clang`clang::NamedDecl**

llvm::TrailingObjects::getTrailingObjects(this=0x000000012d01d768) at TrailingObjects.h:301:12

 



阅读(0) 收藏(0) 转载(0) 举报/Report
相关阅读

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有