Blog 2024 01 17 C++23: bitwise operations
Post
Cancel

C++23: bitwise operations

While C++ is getting increasingly expressive with each new standard, we must not forget its origins. It is inherently a low-level language which operates close to the hardware level and allows operations that languages such as Javascript cannot even express.

A part of providing low-level functionalities is to be able to work on a byte or even on a bit level. In this post, we are going to see what related features C++23 brings or modifies.

constexpr std::bitset

To be fair, we already covered this earlier last year in C++23: Even more constexpr. But as I’m using this feature to demonstrate the next one, I decided to mention it again.

P2417R2 extends the constexpr interface of std::bitset. So far, only one of the constructors and operator[] was marked as constexpr. However, since std::string can be constexpr, all the internals - and therefore the full API - of std::bitset can be constexpr.

If you’re unfamiliar with std::bitset, it represents the object you pass in as a sequence of bits. You have to pass the number of bits as a non-type template argument.

1
2
3
4
5
6
7
8
9
10
11
12
#include <bitset>
#include <iostream>

int main()
{
    constexpr short i = 15;
    constexpr int numberOfBitsInInt = sizeof(i) * 8;
    std::cout << "i:" << i << ", i as binary: " << std::bitset<numberOfBitsInInt>(i) << '\n';
}
/*
i:15, i as binary: 0000000000001111
*/

It’s more capable than that, it also offers several ways to query the individual bits or set them. These features are out of the scope of this article, but you can check them out on C++ Reference.

std::byteswap

std::byteswap is a new library function introduced by P1272R4.

1
2
3
namespace std {
  constexpr auto byteswap (integral auto value) noexcept;
}

As you can see from its signature, it only accepts integral values.

Personally, I don’t find the name “byteswap” very informative, but maybe it’s just me. I’m not working in the world of byte-level operations. Probably, I would have called it reverse_bytes. By writing that, you probably understood what this function does. It takes an integral and returns another instance of the same type, where the bytes are in the reverse order compared to the input.

The following code snippet (godbolt) represents what is happening.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <bit>
#include <bitset>
#include <iostream>

int main()
{
    int i = 1;
    int j = std::byteswap(i);
    constexpr int numberOfBitsInInt = sizeof(i) * 8;
    std::cout << "i:        " << i << ", i as binary: " << std::bitset<numberOfBitsInInt>(i) << '\n';
    std::cout << "j: " << j << ", j as binary: " << std::bitset<numberOfBitsInInt>(j) << '\n';
}
/*
i:        1, i as binary: 00000000000000000000000000000001
j: 16777216, j as binary: 00000001000000000000000000000000
*/

I think it’s worth emphasizing that the reverse is not happening on the level of bits but on the levels of bytes. It’s even clearer in the following example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <bit>
#include <bitset>
#include <iostream>

int main()
{
    constexpr char i = 1;
    constexpr char j = std::byteswap(i);
    constexpr int numberOfBitsInChar = sizeof(i) * 8;
    static_assert(numberOfBitsInChar == 8);
    std::cout << "i as binary: " << std::bitset<numberOfBitsInChar>(i) << '\n';
    std::cout << "j as binary: " << std::bitset<numberOfBitsInChar>(j) << '\n';
    static_assert(i == j);
}

Conclusion

In this article, we had a look at C++23 changes affecting bit and byte manipulations. We had a reminder on std::bitset that received a full constexpr API. Then we learned about the new library function, std::byteswap that helps reversing the bytes - and not the bits - in an object.

Connect deeper

If you liked this article, please

This post is licensed under CC BY 4.0 by the author.