c++ - que - como funciona swap en c
¿Cómo escribir la función de intercambio constexpr para cambiar la endianess de un entero? (2)
Inspirado por ecatmur, sugiero la siguiente solución, que tiene un rendimiento potencialmente mejor cuando bswap no es detectado por el compilador (O (log (n)) vs O (N)). Dado que N es generalmente <= 8, esto es probablemente irrelevante, aún así:
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;
}
Como esta forma es más compleja que la solución de ecatmur, el compilador tiene una tarea más difícil de optimizar, pero Clang todavía descubre que queremos decir bswap.
¿Cómo escribir una función constexpr
para intercambiar endianess de un entero, sin depender de las extensiones del compilador y puedes dar un ejemplo de cómo hacerlo?
Sí, es bastante fácil; Aquí hay una implementación recursiva (compatible con C ++ 11) (solo tipos integrales sin signo):
#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);
}
Aquí estoy usando j
como acumulador y n
como contador de bucles (bytes de indexación).
Si tiene un compilador que admite expresiones de C ++ 17 fold , es posible escribir algo que se expanda exactamente a lo que escribiría a mano:
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)>{});
}
La ventaja de esta forma es que debido a que no utiliza bucles ni recursividad, está prácticamente garantizado que obtendrá una salida de ensamblaje óptima: en x86-64, Clang incluso se las arregla para utilizar la instrucción bswap
.