Suppose I have two libraries, let's arbitrarily call them "CUDA" and "HIP". These two libraries just so happen to have eerily similar interfaces, and I want to wrap these interfaces into a templated class where the template parameter defines which library is called. My current implementation is (in an abridged form):
#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
While this works it is extremely painful to maintain, as I need to duplicate every alias, as well as make sure a fair chunk of boilerplate is correct. Not to mention the macros. What I want ideally is keep it all in one definition so something like:
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
};
but alas std::conditional requires both types to be at least declared. Is there a cleverer solution here? It should ideally also be C++11.
edit: (just to answer the comments questions more visibly)
The idea of the Interface class is that there are other classes which derive from it (assuming one of havecuda or havehip or both is true) in order to gain access to the types:
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