I would like to provide a templated function, that varies its implementation (->specialization) according to the sizeof the template type.
Something similar to this (omi
I can propose the following method:
Its benefit is that you don't have to throw
an exception if the operand isn't of the valid size. It just won't link. So that you have the error checking at build time.
template<int size>
void byteswapInPlace(void* p);
template<> void byteswapInPlace<1>(void* p) { /* do nothing */ }
template<> void byteswapInPlace<2>(void* p)
{
_byteswap_ushort((ushort*) p);
}
template<> void byteswapInPlace<4>(void* p)
{
_byteswap_ulong((ulong*) p);
}
template<> void byteswapInPlace<8>(void* p)
{
_byteswap_uint64((uint64*) p);
}
template<class T>
T byteswap(T & swapIt)
{
byteswapInPlace<sizeof(T)>(&swapIt);
return swapIt;
}
Just for the sake of demonstrating enable_if
in action, since you spoke about it:
template <class T>
typename boost::enable_if_c< sizeof(T) == 2, T >::type
swapIt(T& rhs) { return _byteswap_short(rhs); }
template <class T>
typename boost::enable_if_c< sizeof(T) == 4, T >::type
swapIt(T& rhs) { return _byteswap_long(rhs); }
etc...
And of course, instead of throwing, there is just no implementation if the type doesn't meet any of the requirement and thus you have a compile time error.
Two notes:
typename
and ::type
are mandatoryenable_if_c
because my expression evaluates to a boolean value directly, whereas enable_if
requires a type containing a ::value
member which is a boolean.You don't need SFINAE or type traits. Vanilla template specialization is enough. Of course it must be specialized on structs as C++(98) doesn't support function template partial specialization.
template <typename T, size_t n>
struct ByteswapImpl
/*
{
T operator()(T& swapIt) const { throw std::exception(); }
}
*/ // remove the comments if you need run-time error instead of compile-time error.
;
template <typename T>
struct ByteswapImpl<T, 2> {
T operator()(T& swapIt) const { return _byteswap_ushort (swapIt); }
};
// ...
template <typename T>
T byteswap(T& swapIt) { return ByteswapImpl<T, sizeof(T)>()(swapIt); }
Simply make an auxiliary class that takes the size as a template argument:
#include <cstddef>
#include <iostream>
template<std::size_t Size>
struct ByteSwapper { };
template<>
struct ByteSwapper<2> {
static unsigned short swap(unsigned short a) {
return 2 * a;
}
};
template<typename T>
T byteswap(const T& a) {
return ByteSwapper<sizeof(T)>::swap(a);
}
int main() {
unsigned short s = 5;
std::cout << byteswap(s) << std::endl;
unsigned int i = 7;
// std::cout << byteswap(i) << std::endl; // error
}