【问题标题】:How to perform template substitution in the clang library?如何在 clang 库中进行模板替换?
【发布时间】:2012-11-06 11:29:04
【问题描述】:

具体来说,我们有一个这样的 C++ 源文件:

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.

很容易找到NFClassTemplateDecls,以及函数指针&amp;fopen&amp;fclose等的QualTypeFunctionDecl。但问题是如何在不修改源代码的情况下将这些参数替换为 N、F 和 F::A。

问题是:

  • 如何评估 F&lt;decltype(&amp;fprintf), &amp;fprintf&gt;::A&lt;N&lt;4&gt;&gt; 并知道它是 int*
  • 如何评估F&lt;decltype(&amp;fopen), &amp;fopen&gt;::A&lt;N&lt;7&gt;&gt; 并知道它是double*
  • 等等……

【问题讨论】:

  • 我不完全确定我理解你想要做什么,但你为什么不能使用 typeid?
  • @AndreiTita:我们正在尝试使用 C++ 解析器 (clang) 对此进行评估。

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


【解决方案1】:

我有一个部分解决方案,唯一需要注意的是,我无法让std::is_same&lt;N&lt;4&gt;, N&lt;4&gt;&gt;::value 返回true。好吧,我可以忍受,因为我可以定义一个直接对值进行操作的 constexpr 方法。但我希望有人可以为此提供正确的答案。

我已将完整的解决方案和修改后的输入放到https://gist.github.com/4178490


我发现要将参数替换为类模板并实例化它,可以:

  1. 使用参数将ClassTemplateDecl 变成ClassTemplateSpecializationDecl,并且
  2. 使用Sema::InstantiateClass 方法实例化专业化。

方法Sema::RequireCompleteType确实间接调用了InstantiateClass,需要的输入较少,所以我改用这个方法。因此,我们会写:

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

此方法仅适用于 ClassTemplateDecl。在这个问题中,我们还有一个TypeAliasTemplateDecl。为此,我将直接调用TemplateDeclInstantiator,因为这是唯一知道 TypeAliasTemplateDecl 的对象。也许这种方法也适用于 ClassTemplateDecl,但我不能确定,因为单独使用 TemplateDeclInstantiator 似乎还不够。

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

(我跳过了 FunctionTemplateDecl,它超出了我的问题范围。)

【讨论】:

    猜你喜欢
    • 2021-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-13
    相关资源
    最近更新 更多