2014-04-04

Variadic C++ functions for packing and unpacking bits

Shall you need variadic C++11 functions for packing and unpacking (a few) bits, here is what I have done for myself.

In most cases it's probably better to use std::bitset or any other container, but these two functions may provide a quick and easy way to pack some bits into an integer and the other way around, without using any specific class or container.

#include <type_traits>

template<typename T>
constexpr T pack(bool b)
{
    return b;
}

template<typename T, typename... Types>
constexpr T pack(bool b, Types... args)
{
    static_assert(std::is_integral<T>::value, "The pack type is not an integral type");
    static_assert(sizeof(T) * 8 >= 1 + sizeof...(Types), "The pack type is not large enough to store the bits you have passed");
    return (b << sizeof...(Types)) | pack<T>(args...);
}

template<typename T>
void unpack(T packed, bool& b)
{
    b = packed & 1;
}

template<typename T, typename... Types>
void unpack(T packed, bool& b, Types&... args)
{
    static_assert(std::is_integral<T>::value, "The pack type is not an integral type");
    static_assert(sizeof(T) * 8 >= 1 + sizeof...(Types), "The pack type is not large enough to provide the bits you have requested");
    b = packed & (1 << sizeof...(Types));
    unpack(packed, args...);
}

Usage examples:

std::cout << pack<int>(1, 0, 0, 1, 0, 1, 1, 0) << std::endl; // will print 150

bool b1 = true, b2 = false, b3 = true;
std::cout << pack<int>(b1, b2, b3) << std::endl; // will print 5

int val = 2;
unpack(val, b1, b2, b3);
std::cout << b1 << " " << b2 << " " << b3 << std::endl; // 0 1 0

Compile-time checks ensure that T is an integral type and is able to store/provide the number of bits you are providing/requesting, even though it is really unlikely that you want to pass tens of arguments to a variadic function.

std::cout << pack<char>(1, 0, 0, 1, 0, 1, 1, 0, 1) << std::endl; // will not compile, char is too small
std::cout << pack<double>(1, 0, 0) << std::endl; // will not compile, double is not an integral type

It's good to remark that, as pack is a constexpr, you can use its result as a compile time constant (for example in a case), provided the passed arguments are known at compile time.

Tags: cpp, cpp11
comments powered by Disqus
Fork me on GitHub