C++11下封装CUDA、HIP相似接口的类型与函数别名方案咨询
C++11 下统一封装CUDA/HIP异构接口的低维护方案
核心思路
C++模板的全定义检查规则决定了直接在主模板中用std::conditional分支选择符号的方案无法适配单后端未引入的场景:主模板中出现的所有符号必须在实例化时可见,即使某分支不会被实际使用,未声明的符号依然会触发编译错误。
我们可以将所有接口的映射关系集中定义,通过宏批量生成不同后端的特化代码,既避免了重复编写样板代码,又完美适配单后端未引入的场景,全程符合C++11标准,维护成本和你预期的单模板写法完全一致。
具体实现
第一步:定义枚举与接口映射列表
先把所有需要封装的接口统一列在一个宏里,后续新增/修改接口只需要改这一处,不需要调整任何其他代码:
enum class Selector { CUDA, HIP }; // 所有需要封装的接口统一在这里声明,格式: // MACRO(接口类型, 统一别名, CUDA原名, HIP原名) // 接口类型支持TYPE(类型别名)、CONST(常量)、FUNC(函数) #define FOR_EACH_HETERO_INTERFACE(MACRO) \ MACRO(TYPE, error_type, cudaError_t, hipError_t) \ MACRO(CONST, success, cudaSuccess, hipSuccess) \ MACRO(FUNC, getLastError, cudaGetLastError, hipGetLastError) // 新增接口仅需在此追加一行即可
第二步:定义特化代码生成逻辑
分别定义三种接口类型的展开宏,以及函数别名的通用宏:
// 函数别名通用宏,兼容C++11 #define ALIAS_FUNCTION(Alias_, Original_) \ template <typename... Args> \ static auto Alias_(Args&&... args) \ -> decltype(Original_(std::forward<Args>(args)...)) \ { return Original_(std::forward<Args>(args)...); } // 生成CUDA后端特化的单条接口 #define GEN_CUDA_INTERFACE(Type, Alias, CudaName, HipName) \ GEN_CUDA_##Type(Alias, CudaName) #define GEN_CUDA_TYPE(Alias, CudaName) using Alias = CudaName; #define GEN_CUDA_CONST(Alias, CudaName) static const auto Alias = CudaName; #define GEN_CUDA_FUNC(Alias, CudaName) ALIAS_FUNCTION(Alias, CudaName) // 生成HIP后端特化的单条接口 #define GEN_HIP_INTERFACE(Type, Alias, CudaName, HipName) \ GEN_HIP_##Type(Alias, HipName) #define GEN_HIP_TYPE(Alias, HipName) using Alias = HipName; #define GEN_HIP_CONST(Alias, HipName) static const auto Alias = HipName; #define GEN_HIP_FUNC(Alias, HipName) ALIAS_FUNCTION(Alias, HipName)
第三步:按需生成后端特化
template <Selector T> struct Interface; #if (havecuda) # include <cuda_headers.h> template <> struct Interface<Selector::CUDA> { FOR_EACH_HETERO_INTERFACE(GEN_CUDA_INTERFACE) }; #endif #if (havehip) # include <hip_headers.h> template <> struct Interface<Selector::HIP> { FOR_EACH_HETERO_INTERFACE(GEN_HIP_INTERFACE) }; #endif
方案优势
- 维护成本极低:新增/修改接口只需要修改
FOR_EACH_HETERO_INTERFACE一处,自动同步到两个后端,不会出现两边定义不一致的问题 - 完美适配单后端场景:只有开启对应后端编译开关时才会生成对应特化,不会用到未引入库的任何类型/符号,完全规避了符号未声明的编译错误
- 完全兼容C++11标准,无额外依赖
- 和现有业务代码完全兼容,不需要修改任何继承
Interface的业务类实现
内容的提问来源于stack exchange,提问作者Jacob Faib
相关产品推荐
相关产品推荐

