You can avoid casting with implicit conversions:
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); }
The idea is that you see “correctly convert all parameters. Shift and combine them”, and not “for each parameter, correctly convert, shift and combine”. However, not so much.
Then:
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"; }
Conclusion:
4 5 249 -7
This is just as portable as the uint32_t , uint8_t and int8_t . None of these are required on C99, and the stdint.h header is not defined in C ++ or C89. If types exist and meet the requirements of C99, the code will work. Of course, in C, for decompression functions, instead of a template parameter, a function parameter is required. You may also prefer C ++ if you want to write short loops for unpacking.
To eliminate the fact that types are optional, you can use uint_least32_t , which is required on C99. Similar to uint_least8_t and int_least8_t . You will need to change the pack_helper and unpack_u code:
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) {
Honestly, it is unlikely to be worth it - the chances are that the rest of your application is written under the assumption that int8_t , etc., exists. This is a rare implementation that does not have 8-bit and 32-bit add-on types.