I've developed a simple template function for swapping the byte order of a single field:
template <typename T> inline void SwapEndian(T& ptr) {
char *bytes = reinterpret_cast<char*>(&ptr);
int a = sizeof(T) / 2;
while (a--) {
char tmp = bytes[a];
int b = sizeof(T) - 1 - a;
bytes[a] = bytes[b];
bytes[b] = tmp;
}
}
I'll often use it where T = int
or float
. Both of these types are represented by 4 bytes on the target platforms, and can be processed by the same specialization of the template.
Because this function sometimes is responsible for processing large buffers of raw data, I've created an optimized specialization:
template<> inline void SwapEndian(float& ptr) {
#if defined(__GNUC__)
*reinterpret_cast<unsigned*>(&ptr) = __builtin_bswap32(*reinterpret_cast<unsigned*>(&ptr));
#elif defined(_MSC_VER)
*reinterpret_cast<unsigned*>(&ptr) = __byteswap_ulong(*reinterpret_cast<unsigned*>(&ptr));
#endif
}
This specialization also works with 32-bit integers, signed or unsigned, so I have a big smelly pile of duplicates with only the type name different.
How do I route all instantiations of 4 byte POD types through this one template? (PS. I'm open to solving this in a different way, but in that case I'd like to know definitively whether or not it's possible to build these kind of meta-specialized templates.)
EDIT: Thanks everyone, after reading the answers and realizing that arithmetic is a better restriction than pod, I was inspired to write something. All the answers were useful but I could only accept one, so I accepted the one that appears to be structurally the same.
template<bool, bool> struct SwapEndian_ { template<typename T> static inline void _(T&); };
template<> template<typename T> inline void SwapEndian_<true, true>::_(T& ptr) {
// ... stuff here ...
}
// ... more stuff here ...
template<typename T> inline void SwapEndian(T& ptr) {
static_assert(is_arithmetic<T>::value, "Endian swap not supported for non-arithmetic types.");
SwapEndian_<sizeof(T) & (8 | 4), sizeof(T) & (8 | 2)>::template _<T>(ptr);
}
When in doubt, tag dispatch.
This implementation has 2 traits -- is_pod
and get_sizeof_t
. The base override dispatches to SwapEndian
s with those two traits tagged. There is also a is_pod
override, and an override (which I'd advise =delete
ing) for non-pod types.
Extension to new traits and types is relatively easy.
template<size_t n>
using sizeof_t = std::integral_constant<size_t, n>;
template<class T>
using get_sizeof_t = sizeof_t<sizeof(T)>;
template <class T>
void SwapEndian(T& t, std::true_type /*is pod*/, sizeof_t<4>) {
std::cout << "4 bytes!\n";
// code to handle 32 bit pods
}
template <class T, size_t n>
void SwapEndian(T& t, std::true_type /*is pod*/, sizeof_t<n>) {
std::cout << "pod\n";
// code to handle generic case
}
template <class T, size_t n>
void SwapEndian(T& t, std::false_type /*is pod*/, sizeof_t<n>) {
std::cout << "not pod\n";
// probably want to =delete this overload actually
}
template<class T>
void SwapEndian(T& t) {
SwapEndian(t, std::is_pod<T>{}, get_sizeof_t<T>{});
}
I am not sure if this is a good idea, but the above should do it.
Uses some C++14 features. Assumes CHAR_BIT
is 8
.
You should only rarely specialize template functions. Instead overload. Tag dispatching gives you the power of overload resolution to dispatch what code to run at compile time.
live example
I'm using a separate SwapEndian
and SwapEndianImpl
so that we can use template deduction and partial specialization.
template<bool> struct SwapEndianImpl
{
template<typename t> static inline void Func(t& n);
};
template<> template<typename t> void SwapEndianImpl<false>::Func(t& n)
{
std::cout << "not 32bit pod" << std::endl;
}
template<> template<typename t> void SwapEndianImpl<true>::Func(t& n)
{
std::cout << "32bit pod" << std::endl;
}
template<typename t> inline void SwapEndian(t& n)
{
SwapEndianImpl<std::is_pod<t>::value && sizeof(t) == (32 / CHAR_BIT)>::template Func<t>(n);
}
I believe that this is a better way to go than SFINAE if you specialize to more than two conditions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With