暗黙の変換でキャストを回避できます。
uint32_t pack_helper(uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3) {
return c0 | (c1 << 8) | (c2 << 16) | (c3 << 24);
}
uint32_t pack(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3) {
return pack_helper(c0, c1, c2, c3);
}
考え方は、「各パラメーターについて、正しく変換し、シフトして結合する」ではなく、「すべてのパラメーターを正しく変換する。シフトして結合する」ということです。ただし、それほど多くはありません。
それで:
template <int N>
uint8_t unpack_u(uint32_t packed) {
// cast to avoid potential warnings for implicit narrowing conversion
return static_cast<uint8_t>(packed >> (N*8));
}
template <int N>
int8_t unpack_s(uint32_t packed) {
uint8_t r = unpack_u<N>(packed);
return (r <= 127 ? r : r - 256); // thanks to caf
}
int main() {
uint32_t x = pack(4,5,6,-7);
std::cout << (int)unpack_u<0>(x) << "\n";
std::cout << (int)unpack_s<1>(x) << "\n";
std::cout << (int)unpack_u<3>(x) << "\n";
std::cout << (int)unpack_s<3>(x) << "\n";
}
出力:
4
5
249
-7
uint32_tこれは、、uint8_tおよびint8_tタイプと同じくらいポータブルです。これらはいずれもC99では必要ありません。また、ヘッダーstdint.hはC++またはC89で定義されていません。ただし、タイプが存在し、C99要件を満たしている場合、コードは機能します。もちろん、Cでは、アンパック関数にはテンプレートパラメーターの代わりに関数パラメーターが必要です。解凍するための短いループを記述したい場合は、C++でもそれを好むかもしれません。
uint_least32_tタイプがオプションであるという事実に対処するために、C99で必須のを使用できます。同様にuint_least8_tそしてint_least8_t。pack_helperとunpack_uのコードを変更する必要があります。
uint_least32_t mask(uint_least32_t x) { return x & 0xFF; }
uint_least32_t pack_helper(uint_least32_t c0, uint_least32_t c1, uint_least32_t c2, uint_least32_t c3) {
return mask(c0) | (mask(c1) << 8) | (mask(c2) << 16) | (mask(c3) << 24);
}
template <int N>
uint_least8_t unpack_u(uint_least32_t packed) {
// cast to avoid potential warnings for implicit narrowing conversion
return static_cast<uint_least8_t>(mask(packed >> (N*8)));
}
正直なところ、これは価値がない可能性があります。アプリケーションの残りの部分は、int8_tetcが存在することを前提として作成されている可能性があります。これは、8ビットと32ビットの2の補数型を持たないまれな実装です。