2016-04-29 7 views
15

constexprコンパイラの拡張機能に頼らずに、整数のエンディアンを入れ替える機能を書くにはどうすればいいですか?整数のエンディアンを変更するためのconstexprスワップ関数の記述方法は?

+6

「整数のエンディアン」とは何ですか? 15のエンディアンは何ですか? –

+0

@KerrekSBそれは何でも。私はその質問をしなかった。私の質問はビッグエンディアンをリトルエンディアンにスワップする方法、そしてその逆の方法です。 – user1095108

+3

@KerrekSB:C++(一般的なほとんどのプログラミング)の文脈では、整数と言うとき、通常は整数オブジェクトを参照します。つまり、整数データを格納するために使用されるメモリ内の領域で、通常は基本整数型(char、short、int、longおよびlong long、およびそれらの符号なしバリアント)の1つです。あなたは本当にその使用法を見いだすことはありませんか? –

答えて

31

はい、かなり簡単です。ここでは、再帰(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まで管理します。

2

私は、bswapがコンパイラによって検出されない場合(O(log(n))対O(N))、潜在的により良いパフォーマンスを持つ次の解決策を提案します。 Nは、これはまだ、おそらく無関係であり、通常は= 8 <であることを考える:このフォームはecatmurのソリューションよりも複雑であるため

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

コンパイラは難しい仕事の最適化を持っていますが、打ち鳴らすは、まだ我々はBSWAPを意味していることを知ります。

+1

内側ループ(カウント最適化ではない)がΘ(N/logN)(アウターループの反復ごとに)の償却された複雑さを有するため、この解は実際にはΘ(N)の時間複雑さも有する。実際のΘ(log N)を得るためには、ビットマスクをメモする必要がある。配列にあらかじめ計算されています。 –

+0

@ArneVogelこれは当てはまりますが、私はビットマスクを生成する関数がconstexprであるため、ビットマスクは時定数をコンパイルすると仮定しました。 – Lykos

関連する問題