Use Bitmask in C++

  1. Use struct to Define Bitmask and Implement Date Object in C++
  2. Use struct and union to Define Bitmask and Implement Date Object in C++
  3. Use std::bitset to Define Bitmask in C++

This article will demonstrate multiple methods of how to use bitmask in C++.

Use struct to Define Bitmask and Implement Date Object in C++

struct is a popular data structure in C++. Moreover, it is only slightly different from the class as opposed to the C language. In this case, we only need to define struct with several data members, but there’s a new notation : used after each variable name and an integer specified. As shown in the example code, we declared a struct with uint32_t type data members, and the sum of the numbers on the right is 32. This construction implies that the struct occupies the size of a single uint32_t data type in memory and the user can access its bitfield ranges(23:5:4) separately.

This method is mostly utilized to save memory utilization of data structures that might be squeezed into similar structs. It’s also one of the common ways to access smaller data than a byte in C++ programming language. Note that the first member year can only hold integer values up to 223-1 and other members follow similarly.

#include <iostream>

using std::cout;
using std::endl;

struct {
    uint32_t year:23;
    uint32_t day:5;
    uint32_t month:4;
} typedef Bitfield;

int main() {
    Bitfield date = {2020, 13, 12};

    cout << "sizeof Bitfield: " << sizeof(date) << " bytes\n";
    cout << "date: " << date.day << "/"
            << date.month << "/" << date.year << endl;

    return EXIT_SUCCESS;
}

Output:

sizeof Bitfield: 4 bytes
date: 13/12/2020

Use struct and union to Define Bitmask and Implement Date Object in C++

The previous method is a sufficient and correct way of implementing bitmask. Still, it has one downside - accessing and assigning values to members takes relatively more time than operations on built-in types. This can be solved by implementing a union type object consisting of a struct and a single built-in type variable. Note that, struct is layout is the same as we would construct in the previous method. In this case, the difference is the variableuint32_t ydm that occupies the same size as the struct.

The idea is to initialize/assign data members their values much faster. Namely, we construct an integer with bitwise operations that exactly match the layout of bits when they are stored in the struct and then assign it to a single uint32_t member. This saves time to access data one time rather than repeat the same operation for each of three members.

Bitwise arithmetic is relatively straightforward; namely, we start with the first member’s value in the struct, which is OR-ed with the result of the next member shifted left by the number of bits the previous member occupies, and it follows similarly.

#include <iostream>

using std::cout;
using std::endl;

union BitfieldFast{
    struct {
        uint32_t year:23;
        uint32_t day:5;
        uint32_t month:4;
    };
    uint32_t ydm;
};

int main() {
    BitfieldFast date_f{};
    date_f.ydm = 2020 | (13 << 23) | (12 << 28);

    cout << "sizeof BitfieldFast: " << sizeof(date_f) << " bytes\n";
    cout << "date: " << date_f.day << "/"
            << date_f.month << "/" << date_f.year << endl;

    return EXIT_SUCCESS;
}
sizeof BitfieldFast: 4 bytes
date: 13/12/2020

Use std::bitset to Define Bitmask in C++

std::bitset is a standard library feature that includes the class for storing the binary mask data. bitset has multiple useful manipulation functions built-in, and declaration is quite effortless. It can be initialized with a binary string value or multiple number values. Bitwise operations are built-in methods and can be called with traditional operators that are overloaded. Notice that, reset method permanently modifies the bitset it’s called from.

#include <iostream>
#include <bitset>

using std::cout;
using std::endl;

int main() {
    std::bitset<16> bs1("1100100011110010");
    std::bitset<16> bs2(0xfff0);
    cout << "bs1        : " << bs1 << endl;
    cout << "bs2        : " << bs2 << endl;
    cout << "NOT bs1    : " << ~bs1 << endl;
    cout << "bs1 AND bs2: " << (bs1 & bs2) << endl;
    cout << "bs1 reset  : " << bs1.reset() << endl;

    return EXIT_SUCCESS;
}

Output:

bs1        : 1100100011110010
bs2        : 1111111111110000
NOT bs1    : 0011011100001101
bs1 AND bs2: 1100100011110000
bs1 reset  : 0000000000000000