2016-04-29 12 views
15

Come scrivere una funzione constexpr per scambiare l'endianess di un intero, senza fare affidamento sulle estensioni del compilatore e puoi dare un esempio su come farlo?Come scrivere la funzione di scambio constexpr per cambiare l'endianess di un intero?

+6

Qual è la "endianità di un intero"? Qual è il endianness di 15? –

+0

@KerrekSB Qualunque cosa sia. Non ho fatto quella domanda. La mia domanda è come scambiare big-endian con little-endian e viceversa. – user1095108

+3

@KerrekSB: Nel contesto del C++ (e della maggior parte della programmazione in generale), quando si dice intero, di solito si riferiscono a un oggetto intero. Cioè, una regione in memoria utilizzata per memorizzare i dati interi, di solito uno dei tipi interi fondamentali (char, short, int, long e long long, insieme alle loro varianti senza segno). Non hai mai incontrato questo utilizzo? –

risposta

31

Sì, è piuttosto semplice; ecco un'implementazione ricorsiva (C++ 11-compatibile) (solo tipi interi senza segno):

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

Qui sto usando j come l'accumulatore e n come il contatore di ciclo (indicizzazione bytes) .

Se si dispone di un compilatore di supporto C++17 fold expressions, è possibile scrivere qualcosa che si espande fuori in esattamente quello che ci si scrive 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)>{}); 
} 

Il vantaggio di questa forma è che perché non usa loop o ricorsione, sei praticamente garantito per ottenere un output di assemblaggio ottimale - su x86-64, clang arriva anche a work out to use the bswap instruction.

2

Ispirato da ecatmur suggerisco la seguente soluzione, che ha prestazioni potenzialmente migliori quando bswap non viene rilevato dal compilatore (O (log (n)) vs O (N)). Dato che N è di solito < = 8 questo è probabilmente irrilevante, ancora:

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

Poiché questa forma è più complessa di quanto la soluzione di ecatmur il compilatore ha un difficile ottimizzazione del lavoro, ma clang trova ancora che intendiamo bswap.

+1

Questa soluzione ha in realtà una complessità temporale di Θ (N), poiché il ciclo interno (senza contare le ottimizzazioni) ha una complessità ammortizzata di Θ (N/log N) (per iterazione del ciclo esterno). Per ottenere l'effettivo Θ (log N), le maschere di bit dovrebbero essere memoizzate, ad es. precompilato in un array. –

+0

@ArneVogel questo è vero, ho appena assunto che le maschere di bit saranno costanti di tempo di compilazione in quanto la funzione che le genera è un constexpr. – Lykos

Problemi correlati