zurück zum Artikel

Bit-Manipulationen mit C++20

Rainer Grimm

Mit diesem Artikel schließe ich meine Artikel zu Features der C++20-Bibliothek ab. Den Abschluss bilden die Klasse std::source_location und die Funktionen zur Bit-Manipulation.

Nun schließe ich meine Artikelserie zu Features der C++20-Bibliothek ab. Den Abschluss bilden die Klasse std::source_location und die Funktionen zur Bit-Manipulation.

Bit-Manipulationen mit C++20

std::source_location bietet Informationen zum Sourcecode an. Diese umfassen den Dateinamen, die Zeilennummer und den Funktionsnamen. Diese Werte sind zum Debuggen, Loggen oder Testen sehr wertvoll. Damit ist die Klasse std::source_location die deutliche bessere Alternative zu den vordefinierten Makros __FILE__ und __LINE__ in C++11. Das heißt natürlich, dass std::source_location in C++20 zum Einsatz kommen sollte.

Die folgende Tabelle stellt das Interface von std::source_location kompakt dar.

Bit-Manipulationen mit C++20

Der Aufruf std::source_location::current() erzeugt eine neues source_location-Objekt src. Escstellt die Information des Aufrufers bereit. Zum jetzigen Zeitpunkt unterstützt noch kein C++-Compiler std::source_location. Konsequenterweise ist das folgende Beispiel sourceLocation.cpp direkt von der Online-Ressource cppreference.com/source_location [1]:

// sourceLocation.cpp
// from cppreference.com

#include <iostream>
#include <string_view>
#include <source_location>

void log(std::string_view message,
const std::source_location& location = std::source_location::current())
{
std::cout << "info:"
<< location.file_name() << ':'
<< location.line() << ' '
<< message << '\n';
}

int main()
{
log("Hello world!"); // info:main.cpp:19 Hello world!
}

Die Ausgabe des Programms ist Bestandteil des Sourcecodes.

Durch C++20 wird es sehr einfach, auf Bits oder Bit-Sequenzen zuzugreifen oder sie zu modifizieren.

Dank des neuen Datentyps std::endian ist es einfach, die Byte-Reihenfolge eines skalaren Datentyps zu ermitteln.

Byte-Reihenfolge

Die Klasse endian bietet die Byte-Reihenfolge für skalare Datentypen an:

enum class endian
{
little = /*implementation-defined*/,
big = /*implementation-defined*/,
native = /*implementation-defined*/
};

Selbst Sonderfälle werden unterstützt:

Das Ausführen des Programms getEndianness.cpp auf einer x86-Architektur gibt mir die Antwort little-endian zurück:

// getEndianness.cpp

#include <bit>
#include <iostream>

int main() {

if constexpr (std::endian::native == std::endian::big) {
std::cout << "big-endian" << '\n';
}
else if constexpr (std::endian::native == std::endian::little) {
std::cout << "little-endian" << '\n'; // little-endian
}

}

constexpr if erlaubt es, Sourcecode bedingt zu kompilieren. Das heißt in dem konkreten Fall, dass die Kompilierung von der Byte-Reihenfolge der Architektur abhängt. Mehr Information zur Byte-Reihenfolge gibt die gleichnamige Wikipedia-Seite [2].

Die folgenden Tabellen zeigen alle Funktionen im Überblick.

Bit-Manipulationen mit C++20

Die Funktionen benötigen mit Ausnahme der Funktion std::bit_cast eine vorzeichenlose Ganzzahl (unsigned char, unsigned short, unsigned int, unsigned long oder unsigned long long).

Bit-Manipulationen mit C++20

Die Funktion bit.cpp zeigt die Anwendung der Funktionen:

// bit.cpp

#include <bit>
#include <bitset>
#include <iostream>

int main() {

std::uint8_t num= 0b00110010;

std::cout << std::boolalpha;

std::cout << "std::has_single_bit(0b00110010): "
<< std::has_single_bit(num) << '\n';

std::cout << "std::bit_ceil(0b00110010): "
<< std::bitset<8>(std::bit_ceil(num)) << '\n';

std::cout << "std::bit_floor(0b00110010): "
<< std::bitset<8>(std::bit_floor(num)) << '\n';

std::cout << "std::bit_width(5u): "
<< std::bit_width(5u) << '\n';

std::cout << "std::rotl(0b00110010, 2): "
<< std::bitset<8>(std::rotl(num, 2)) << '\n';

std::cout << "std::rotr(0b00110010, 2): "
<< std::bitset<8>(std::rotr(num, 2)) << '\n';

std::cout << "std::countl_zero(0b00110010): "
<< std::countl_zero(num) << '\n';

std::cout << "std::countl_one(0b00110010): "
<< std::countl_one(num) << '\n';

std::cout << "std::countr_zero(0b00110010): "
<< std::countr_zero(num) << '\n';

std::cout << "std::countr_one(0b00110010): "
<< std::countr_one(num) << '\n';

std::cout << "std::popcount(0b00110010): "
<< std::popcount(num) << '\n';

}

Die folgende Ausgabe erzeugt das Programm:

Bit-Manipulationen mit C++20

Das nächste Programm zeigt den Einsatz und die Ausgabe der Funktionen std::bit_floor, std::bit_ceil, std::bit_width und std::bit_popcount für die Zahlen 2 bis 7:

// bitFloorCeil.cpp

#include <bit>
#include <bitset>
#include <iostream>

int main() {

std::cout << std::endl;

std::cout << std::boolalpha;

for (auto i = 2u; i < 8u; ++i) {
std::cout << "bit_floor(" << std::bitset<8>(i) << ") = "
<< std::bit_floor(i) << '\n';

std::cout << "bit_ceil(" << std::bitset<8>(i) << ") = "
<< std::bit_ceil(i) << '\n';

std::cout << "bit_width(" << std::bitset<8>(i) << ") = "
<< std::bit_width(i) << '\n';

std::cout << "bit_popcount(" << std::bitset<8>(i) << ") = "
<< std::popcount(i) << '\n';

std::cout << std::endl;
}

std::cout << std::endl;

}
Bit-Manipulationen mit C++20

Neben Coroutinen hat C++20 viele weitere Concurrency-Features anzubieten. Die neuen atomaren Variablen gibt es für Gleitkommazahlen und Smart Pointer. Atomare Variablen erlauben es darüber hinaus, auf Benachrichtigungen zu warten. Zur Koordination von Threads wird C++20 um Sempaphoren, Latches und Barriers erweitert. Zusätzlich wurde std::thread mit std::jthread verbessert. Die Ausführung eines std::jthread kann unterbrochen werden. Zusätzlich ruft ein std::jthread automatisch join in seinem Destruktor auf. ( [3])


URL dieses Artikels:
https://www.heise.de/-4981203

Links in diesem Artikel:
[1] https://en.cppreference.com/w/cpp/utility/source_location
[2] https://de.wikipedia.org/wiki/Byte-Reihenfolge
[3] mailto:rainer@grimm-jaud.de