如何在clang库中执行模板替换?

时间:2012-11-18 08:30:16

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

具体来说,我们有一个像这样的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.

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

问题是:

  • 如何评估F<decltype(&fprintf), &fprintf>::A<N<4>>并知道它是int*
  • 如何评估F<decltype(&fopen), &fopen>::A<N<7>>并知道它是double*
  • 等......

1 个答案:

答案 0 :(得分:5)

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

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


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

  1. 使用参数将ClassTemplateDecl转换为ClassTemplateSpecializationDecl
  2. 使用Sema::InstantiateClass方法实例化专业化。
  3. 方法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,这超出了我的问题的范围。)