【发布时间】:2021-11-17 06:24:31
【问题描述】:
假设我有两个库,我们随意称它们为“CUDA”和“HIP”。这两个库恰好有非常相似的接口,我想将这些接口包装到一个模板化类中,其中模板参数定义调用哪个库。我目前的实现是(以简略形式):
#define ALIAS_FUNCTION(Alias_,Original_) \
template <typename... Args> Alias_(Args&&... args) \
-> decltype(Original_(std::forward<Args>(args)...)) \
{ return Original_(std::forward<Args>(args)...); }
enum class Selector {CUDA,HIP};
template <Selector T> struct Interface;
#if (havecuda)
# include <cuda_headers.h>
template <>
struct Interface<Selector::CUDA>
{
using error_type = cudaError_t;
static const auto success = cudaSuccess;
ALIAS_FUNCTION(static constexpr getLastError,cudaGetLastError);
};
#endif
#if (havehip)
# include <hip_headers.h>
template <>
struct Interface<Selector::HIP>
{
using error_type = hipError_t;
static const auto success = hipSuccess;
ALIAS_FUNCTION(static constexpr getLastError,hipGetLastError);
};
#endif
虽然这有效,但维护起来非常痛苦,因为我需要复制每个别名,并确保相当一部分样板文件是正确的。更不用说宏了。理想情况下,我想要的是将所有内容保持在一个定义中,例如:
template <Selector T>
struct Interface
{
using error_type = std::conditional<T==Selector::CUDA,cudaError_t,hipError_t>;
// do something similar to solve the function and variable aliasing
};
但是唉std::conditional 要求至少声明这两种类型。这里有更聪明的解决方案吗?理想情况下,它也应该是 C++11。
编辑:(只是为了更明显地回答 cmets 的问题)
Interface 类的想法是,还有其他派生自它的类(假设 havecuda 或 havehip 之一为真)以便获得对这些类型的访问:
template <Selector T>
class DoWork : Interface<T>
{
using typename Interface<T>::error_type;
using Interface<T>::success;
using Interface<T>::getLastError;
void work()
{
error_type err;
if (getLastError() != success) {
// something else
}
}
};
// instantiate the work classes if available
#if havecuda
template class DoWork<Selector::CUDA>;
#endif
#if havehip
template class DoWork<Selector::HIP>;
#endif
【问题讨论】:
-
std::conditional真的需要定义类型吗?声明可能就足够了。 -
前向声明就足够了,但我还必须处理另一个既没有定义也没有声明的情况。例如。
havecuda = 1和havehip = 0。在这种情况下,std::conditional崩溃了…… -
编译时
havecuda和havehip都可以吗? -
是的,因此我使用模板而不是纯宏。这个想法是第三个类
DoWork<Selector T> : Interface<T>继承了using Interface<T>::success的定义。