Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write constexpr swap function to change endianess of an integer?

Tags:

How to write a constexpr function to swap endianess of an integer, without relying on compiler extensions and can you give an example on how to do it?

like image 314
user1095108 Avatar asked Apr 29 '16 11:04

user1095108


2 Answers

Yes, it's pretty easy; here's a recursive (C++11-compatible) implementation (unsigned integral types only):

#include <climits> #include <cstdint> #include <type_traits>  template<class T> constexpr typename std::enable_if<std::is_unsigned<T>::value, T>::type bswap(T i, T j = 0u, std::size_t n = 0u) {   return n == sizeof(T) ? j :     bswap<T>(i >> CHAR_BIT, (j << CHAR_BIT) | (i & (T)(unsigned char)(-1)), n + 1); } 

Example.

Here I'm using j as the accumulator and n as the loop counter (indexing bytes).

If you have a compiler supporting C++17 fold expressions, it's possible to write something that expands out into exactly what you'd write by hand:

template<class T, std::size_t... N> constexpr T bswap_impl(T i, std::index_sequence<N...>) {   return ((((i >> (N * CHAR_BIT)) & (T)(unsigned char)(-1)) <<            ((sizeof(T) - 1 - N) * CHAR_BIT)) | ...); }; //                                        ^~~~~ fold expression template<class T, class U = typename std::make_unsigned<T>::type> constexpr U bswap(T i) {   return bswap_impl<U>(i, std::make_index_sequence<sizeof(T)>{}); } 

The advantage of this form is that because it doesn't use loops or recursion, you're pretty much guaranteed to get optimal assembly output - on x86-64, clang even manages to work out to use the bswap instruction.

like image 162
ecatmur Avatar answered Oct 05 '22 06:10

ecatmur


Inspired by ecatmur I suggest the following solution, that has potentially better performance when bswap is not detected by the compiler (O(log(n)) vs O(N)). Given that N is usually <=8 this is probably irrelevant, still:

using std::numeric_limits;  template <typename T> typename std::enable_if<std::is_unsigned<T>::value,T>::type constexpr alternating_bitmask(const size_t step){   T mask(0);   for (size_t i=0;i<numeric_limits<T>::digits;i+=2*step){     mask|=(~T(0)>>(numeric_limits<T>::digits-step))<<i;   }   return mask; }  template <typename T> typename std::enable_if<std::is_unsigned<T>::value,T>::type constexpr bswap(T n){   for (size_t i=numeric_limits<unsigned char>::digits;               i<numeric_limits<T>::digits;               i*=2) {     n = ((n&(~(alternating_bitmask<T>(i))))>>i)|         ((n&( (alternating_bitmask<T>(i))))<<i);   }   return n; } 

As this form is more complex than ecatmur's solution the compiler has a harder job optimizing, but clang still finds out that we mean bswap.

like image 29
Wolfgang Brehm Avatar answered Oct 05 '22 06:10

Wolfgang Brehm