While going through some of the type traits, I was searching how does std::is_const<type_name>
work. According to the definition in it is
#if __has_builtin(__is_const)
template <class _Tp>
struct _LIBCPP_TEMPLATE_VIS is_const : _BoolConstant<__is_const(_Tp)> { };
// ...
#else
// "normal" type trait
#endif
I know it's a compiler builtin, but was wondering on a high level as to how it's implemented?
It's implemented via an X macro that populates a map of type llvm::SmallDenseMap<IdentifierInfo *, tok::TokenKind>
.
From clang/lib/Parse/ParseExpr.cpp
:
#define RTT_JOIN(X,Y) X##Y
#define REVERTIBLE_TYPE_TRAIT(Name) \
RevertibleTypeTraits[PP.getIdentifierInfo(#Name)] \
= RTT_JOIN(tok::kw_,Name)
// ...
REVERTIBLE_TYPE_TRAIT(__is_const);
// ...
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) \
REVERTIBLE_TYPE_TRAIT(RTT_JOIN(__, Trait));
#include "clang/Basic/TransformTypeTraits.def"
#undef REVERTIBLE_TYPE_TRAIT
#undef RTT_JOIN
... and then when parsing your code, the RevertibleTypeTraits
map is consulted:
// If we find that this is in fact the name of a type trait,
// update the token kind in place and parse again to treat it as
// the appropriate kind of type trait.
llvm::SmallDenseMap<IdentifierInfo *, tok::TokenKind>::iterator Known
= RevertibleTypeTraits.find(II);
if (Known != RevertibleTypeTraits.end()) {
Tok.setKind(Known->second);
return ParseCastExpression(ParseKind, isAddressOfOperand,
NotCastExpr, isTypeCast,
isVectorLiteral, NotPrimaryExpression);
}
}
// ... and a lot more
This is internal compiler details. It'll likely look very different when looking inside another compiler's code.