Specifically, we have a C++ source file like this:
template <int n>
struct N {};
struct B {
template <typename M>
using A = typename std::conditional<std::is_same<M, N<4>>::value,
int*, void*>::type;
};
template <typename T, T value>
struct F : B {};
template <>
struct F<decltype(&fopen), &fopen> : B {
template <typename M>
using A = double*;
};
template <>
struct F<decltype(&fclose), &fclose> : B {
template <typename M>
using A = typename std::conditional<std::is_same<M, N<16>>::value,
void*, char**>::type;
};
// More specialization of 'F' follows.
It is easy to find the ClassTemplateDecls of N
and F
, and the QualType and FunctionDecl of the function pointers &fopen
, &fclose
, etc. But the problem is how to substitute these arguments into N, F and F::A without modifying the source code.
The question is:
F<decltype(&fprintf), &fprintf>::A<N<4>>
and know that it is an int*
?F<decltype(&fopen), &fopen>::A<N<7>>
and know that it is a double*
?I've got a partial solution, the only caveat is that, I can't get std::is_same<N<4>, N<4>>::value
to return true
. Well I can live with that, as I could just define a constexpr
method which operates on the values directly. But I hope someone could provide a correct answer for this.
I have put the complete solution and the modified input to https://gist.github.com/4178490.
I've found that to substitute arguments into a class template and instantiate it, one would:
The method Sema::RequireCompleteType does indirectly call InstantiateClass, and requires less input, so I call this method instead. Therefore, we would write:
/**
* Instantiate a class template.
*/
ClassTemplateSpecializationDecl* instantiate(ASTContext& ast, Sema& sema,
DeclContext* parent,
ClassTemplateDecl* decl,
ArrayRef<TemplateArgument> args) {
void* ins_point;
auto retval = decl->findSpecialization(args.data(), args.size(), ins_point);
if (retval == nullptr) {
retval = ClassTemplateSpecializationDecl::Create(ast, TTK_Class, parent,
{}, {}, decl,
args.data(), args.size(),
nullptr);
decl->AddSpecialization(retval, ins_point);
}
bool is_incomplete = sema.RequireCompleteType({}, ast.getTypeDeclType(retval),
diag::err_incomplete_type);
return is_incomplete ? nullptr : retval;
}
This method only works for ClassTemplateDecl. In the question we also have a TypeAliasTemplateDecl. For this, I am going to invoke the TemplateDeclInstantiator directly, since this is the only object which knows a TypeAliasTemplateDecl. Perhaps this method also works with ClassTemplateDecl, but I can't be sure as it seems not enough work is done using TemplateDeclInstantiator alone.
/**
* Instantiate a template alias (`template <...> using Foo = ...`).
*/
TypeAliasDecl* instantiate(ASTContext& ast, Sema& sema, DeclContext* parent,
TypeAliasTemplateDecl* decl,
ArrayRef<TemplateArgument> args) {
auto args_count = static_cast<unsigned>(args.size());
TemplateArgumentList arg_list {TemplateArgumentList::OnStack,
args.data(), args_count};
MultiLevelTemplateArgumentList multi_arg_list {arg_list};
TemplateDeclInstantiator instantiator {sema, parent, multi_arg_list};
auto instantiated = instantiator.Visit(decl);
if (auto inst_decl = dyn_cast<TypeAliasTemplateDecl>(instantiated)) {
return inst_decl->getTemplatedDecl();
}
return nullptr;
}
(I skipped FunctionTemplateDecl, it is out of the scope of my question.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With