Reputation: 15
I would like to have a template function to copy data in a special way. There is an easy way if the data element type size is a multiple of 4 Bytes, i.e., (sizeof(T) % 4 == 0):
template <typename T, typename Idx, uint32 dimensions>
void loadData4BWords(T *target, const T *source, const Idx eleCount);
and there is a more complex way to copy the array if that is not the case:
template <typename T, typename Idx, uint32 dimensions>
void loadDataNo4BWords(T *target, const T *source, const Idx eleCount);
How do I write a caller template function that makes this decision at compile time and transparent to the user? For example:
template <typename T, typename Idx, uint32 dimensions>
void loadData(T *target, const T *source, const Idx eleCount);
which is supposed to call one of the two above versions depending on the compile time condition multipleOf4BWord = (sizeof(T) % 4 == 0). More accurately, loadData is supposed to be translated to one of the two above versions at compile time.
Upvotes: 0
Views: 535
Reputation: 66200
In C++17 if constexpr
, as suggested by uneven_mark, is the simplest and clearer solution (IMHO).
Before C++17 (C++11 and C++14) you can use overloading and SFINAE (with std::enable_if
)
I mean... you can simplify a lot the problem if, instead a loadData4BWords()
and a loadDataNo4BWords()
functions ever enabled, you create a loadData()
enabled only when 0u == sizeof(T) % 4u
(loadData4BWords()
equivalent) and a loadData()
enabled only when 0u != sizeof(T) % 4u
(loadDataNo4BWords()
equivalent).
The following is a full C++11 working example (simplified: only one parameter)
#include <iostream>
#include <type_traits>
template <typename T>
typename std::enable_if<0u == sizeof(T) % 4u>::type loadData (T *)
{ std::cout << "4 version" << std::endl; }
template <typename T>
typename std::enable_if<0u != sizeof(T) % 4u>::type loadData (T *)
{ std::cout << "no 4 version" << std::endl; }
int main ()
{
char ch;
int i;
loadData(&ch);
loadData(&i);
}
In C++14 (and C++17, if you want) you can use std::enable_if_t
to simplify a little
template <typename T>
std::enable_if_t<0u == sizeof(T) % 4u> loadData (T *)
{ std::cout << "4 version" << std::endl; }
template <typename T>
std::enable_if_t<0u != sizeof(T) % 4u> loadData (T *)
{ std::cout << "no 4 version" << std::endl; }
p.s.: see also the tag dispatching way in Jeff Garrett's answer.
Upvotes: 0
Reputation: 7383
if constexpr
is nicest. But old school tag dispatch also works, and maybe it could be clearer in some instances (particularly pre-C++17), so I'll contribute this option to the discussion:
template <typename T, typename Idx, uint32 dimensions>
void loadData(T *target, const T *source, const Idx eleCount, std::true_type)
{
loadData4BWords(target, source, eleCount);
}
template <typename T, typename Idx, uint32 dimensions>
void loadData(T *target, const T *source, const Idx eleCount, std::false_type)
{
loadDataNo4BWords(target, source, eleCount);
}
template <typename T, typename Idx, uint32 dimensions>
void loadData(T *target, const T *source, const Idx eleCount)
{
loadData(target, source, eleCount,
std::integral_constant<bool, sizeof(T) % 4 == 0>{});
}
Upvotes: 1
Reputation: 15164
You could make the function a member of a struct and use partial template specialization:
template<typename T, bool is_even_multiple=0==(sizeof(T)%4)>
struct load_data_helper;
template<typename T>
struct load_data_helper<T, true>
{
template<uint32_t Dimensions, typename Idx>
static void apply(T * dest, T const * src, Idx const & index)
{ ... }
};
template<typename T>
struct load_data_helper<T, false>
{
template<uint32_t Dimensions, typename Idx>
static void apply(T * dest, T const * src, Idx const & index)
{ ... }
};
template<uint32_t Dimensions, typename T, typename Idx>
void load_data(T * dest, T const * src, Idx const & index)
{
load_data_helper<T>::apply<Dimensions>(dest, src, index);
}
and then the call would be:
load_data<3>(dest, src, index);
Note I have not actually compiled the above so there may be errors in the provided code but the outlined method should work.
Upvotes: 0
Reputation: 22152
You can use if constexpr
since C++17 to call one or the other:
template <typename T, typename Idx, uint32 dimensions>
void loadData(T *target, const T *source, const Idx eleCount) {
if constexpr(sizeof(T) % 4 == 0)
loadData4BWords<T, Idx, dimensions>(target, source, eleCount);
else
loadDataNo4BWords<T, Idx, dimensions>(target, source, eleCount);
}
In contrast to if
, if constexpr
is tested at compile-time and only the matching branch is compiled.
Upvotes: 4