unionに入れてひっくり返したり、マクロでセコセコとシフトしたりするのは よく見かけるけどどれも使いづらいし、バイト長毎に作ったりする必要があったりする。
unionの例
union { struct { uint8_t b4,b3,b2,b1; } b; uint32_t l; };
使いづらい。
マクロの例
#define SWAP32(v) ((0xff000000&v)>>24)|((0x00ff0000&v)>>8) | \ ((0xff00&v)<<8)|(v<<24)
分かりづらい。
テンプレートで。
union利用する方法を応用してテンプレートにしてみた。
考え方としては以下のとおり。
- unionを定義して8bitの配列と任意の変数を同じ領域に割り当てる。
- 配列の長さはテンプレート引数のsizeof()で取得する。
- unionの変数の方に値を入れる。
- unionの配列の方をひっくり返す。
- ひっくり返した変数の方を戻り値で返す。
template <class Type> inline Type swapBytes(Type val) { union U { std::array<std::uint8_t, sizeof(Type)> a; Type b; U(Type&& v) {b = v; v = 0;} U() : b(0){} }; U s(std::move(val)); U d; std::copy(s.a.rbegin(), s.a.rend(), d.a.begin()); return d.b; }
使い方
std::int64_t a = swapBytes(0x0123456789ABCDEF); std::printf("0x%08lx\n", a); std::int16_t b = swapBytes<std::int16_t>(0x0123); std::printf("0x%04x\n", b); b = swapBytes(b); std::int32_t c = swapBytes(0x0123ABCD); std::printf("0x%08x\n", c);
16bitに関しては、リテラルから推論できないので、 変数に入れるか、明示的に指定することが必要。
どうだろう。。
おまけ
Gistに置いてみた。