読者です 読者をやめる 読者になる 読者になる

可変幅のバイトスワップ

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利用する方法を応用してテンプレートにしてみた。

考え方としては以下のとおり。

  1. unionを定義して8bitの配列と任意の変数を同じ領域に割り当てる。
  2. 配列の長さはテンプレート引数のsizeof()で取得する。
  3. unionの変数の方に値を入れる。
  4. unionの配列の方をひっくり返す。
  5. ひっくり返した変数の方を戻り値で返す。
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に置いてみた。