24 kwietnia 2013

COTW - Pola i flagi bitowe w C++

Każdy pewnie korzystał z flag bitowych w C/C++. Wygodne i zgrabne narzędzie do przekazywania parametrów, które szczególnie ukochali sobie programiści Microsoftu projektujący WinApi.
Czasem zdarza się jednak, że przekazane kombinacje mogą być bardzo finezyjne, a parametry dla instrukcji warunkowych niekoniecznie muszą mieścić się na jednym ekranie (nazwy flag często są dość długie). W takiej sytuacji idealnym wyjściem byłoby przypisanie wyniku sumy bitowej każdej flagi do osobnej zmiennej typu bool i na niej wykonywać operacje warunkowe. Nie ma jednak nic za darmo. W najbardziej pesymistycznym przypadku będziemy musieli stworzyć 32 lub 64 takie zmienne i dla każdej z nich dokonać przypisania. Narzut jest dość spory. Można to rozwiązać lepiej? Można. Z pomocą przychodzą pola bitowe. 


#include 
 
struct Data
{
 bool a : 1;
 bool b : 1;
 bool c : 1;
};
 
enum Flags
{
 FLAG_1 = 0x1,
 FLAG_2 = 0x2,
 FLAG_3 = 0x4
};
 
int main()
{
 unsigned int uFlags = FLAG_1 | FLAG_3;
 Data f;
 
 f = *((Data*)&uFlags);
 
 printf("%s\n", f.a == true ? "a == true" : "a == false");
 printf("%s\n", f.b == true ? "b == true" : "b == false");
 printf("%s\n", f.c == true ? "c == true" : "c == false");
 
 return 0;
}

Struktura Data przechowuje pola bitowe. Każde pole zajmuje jeden bit. Ich kolejność jest zgodna z flagami enuma Flags. Dzięki przypisaniu zmiennej przechowującej flagi (uFlags) do owej struktury typu Data (f = *((Data*)&uFlags);), możemy w szybki sposób zacząć pracować z flagami bitowymi, tak samo jak ze zwykłymi zmiennymi typu logicznego. 


2 komentarze:

  1. Zamiast brzydkiego rzutowania można tę strukturę zapakować w unię z pojedynczym unsigned int - wtedy zwykłe przypisanie wystarczy, a mamy dodatkową kontrolę typów (warning przy automatycznej konwersji typów).

    union Data {
    unsigned int Flags;
    struct {
    bool a : 1;
    bool b : 1;
    bool c : 1;
    };
    };

    // use:
    Data d;
    d.Flags = uFlags;

    OdpowiedzUsuń