Как написать функцию constexpr swap для изменения endianess целого числа?

Как написать функцию constexpr для замены endianess целого числа, не полагаясь на расширения компилятора, и можете ли вы привести пример о том, как это сделать?

Ответы

Ответ 1

Да, это довольно легко; здесь рекурсивная (С++ 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);
}

Пример.

Здесь я использую j в качестве аккумулятора и n как счетчик циклов (индексирование байтов).

Если у вас есть компилятор с поддержкой С++ 17-кратных выражений, можно написать что-то, что будет раскрываться именно в том, что вы пишете вручную:

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 даже справляется с выполните команду bswap.

Ответ 2

Вдохновленный 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.