如何编写constexpr swap函数来改变整数的endianess?

时间:2016-04-29 11:00:37

标签: c++ c++14 endianness constexpr c++17

如何编写constexpr函数来交换整数的endianess,而不依赖于编译器扩展,你能给出一个如何做的例子吗?

2 个答案:

答案 0 :(得分:38)

是的,这很容易;这是一个递归(C ++ 11兼容)实现(仅限无符号整数类型):

#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.

这里我使用j作为累加器,n作为循环计数器(索引字节)。

如果您有一个支持C++17 fold expressions的编译器,那么可以编写一些扩展到您手写的内容:

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)>{});
}

这种形式的优点是因为它不使用循环或递归,所以你几乎可以保证获得最佳的汇编输出 - 在x86-64上,clang甚至可以设法work out to use the bswap instruction

答案 1 :(得分:3)

受ecatmur的启发我建议使用以下解决方案,当编译器未检测到bswap时,该解决方案可能具有更好的性能(O(log(n))vs O(N))。鉴于N通常<= 8,这可能是无关紧要的,仍然是:

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<digits<T>();i+=2*step){
    mask|=(~T(0)>>(digits<T>()-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=digits<unsigned char>();i<digits<T>();i*=2){
    n = ((n&(~(alternating_bitmask<T>(i))))>>i)|
        ((n&( (alternating_bitmask<T>(i))))<<i);
  }
  return n;
}

由于这种形式比ecatmur的解决方案更复杂,编译器的优化工作更难,但clang仍然发现我们的意思是bswap。