2012-11-14 13 views
9

Robię wyliczenie w C++, aby utworzyć skończoną maszynę stanów za pomocą flag binarnych. Wygląda na to:Jak dokonać wyliczenia z wartościami powyżej maksymalnej dozwolonej int?

enum VStates 
{ 
    NEUTRAL   = 0x00000000,  // 000000 
    // Physical Status 
    DRY    = 0x00000001,  // 000001 
    WET    = 0x00000002,  // 000010 
    HOT    = 0x00000004,  // 000100 
    COLD   = 0x00000008,  // 001000 
    BURNED   = 0x00000016,  // etc.. 
    FROZEN   = 0x00000032, 
    EROS   = 0x00000064,  // 
    THANATOS  = 0x00000128,  // 
    SLEEP   = 0x00000256, 
    STUNNED   = 0x00000512, 
    PARALYZED  = 0x00001024, 
    POISONED  = 0x00002048,  // 
    BLIND   = 0x00004096, 
    SOFT   = 0x00008192,  // Flexible 
    TOUGH   = 0x00016384,  // Resistent 
    MAGNETIZED  = 0x00032768, 
    POSSEDERUNT  = 0x00131072,  // 
    // Mental Status 
    ANGRY   = 0x00262144, 
    DRUGGED   = 0x00524288, // Drugs Meaning 
    HORNY   = 0x01048576, // Sexual Meaning 
    // Material Status 
    METAL   = 0x02097152, 
    WOOD   = 0x04194304, 
    GLASS   = 0x08388608, 
    AIR    = 0x16777216, 
    EARTH   = 0x33554432, 
    DUST   = 0x67108864, 
    LIGHT   = 0x134217728, 
    SHADOW   = 0x268435456, 
    WATER   = 0x536870912, 
    // Total Status 
    PROTECTED  = 0x1073741824, 
    INVULNERABLE = 0x2147483648 

}; 

Niektóre statusy są niekompatybilne, więc używam operatorów Bitwise do zarządzania nimi. Teraz mój kompilator mówi:

warning: integer constant is too large for 'long' type 

Czy to jest właściwy sposób deklarowania tego wyliczenia? Lubię unikać ostrzeżenia, Jak mogę rozwiązać ten problem?

+1

Nie jest to odpowiedź, ale byłoby to o wiele prostsze w pisaniu i czytaniu, jeśli wartości zostały zapisane w postaci stałych heksadecymalnych. –

+9

@PeteBecker: Faktycznie * są * zapisane jako stałe heksadecymalne - ale wygląda na to, że używa cyfr, które powinien podać dla stałych dziesiętnych (np. Ma 0x00000512, co powinno być naprawdę 512 lub 0x200). –

+0

@JerryCoffin - oops. –

Odpowiedz

10

(Uwaga: aby moja odpowiedź pełna dodam coś Nie miałem Nie zwlekajmy z zauważeniem, ale inni zwrócili uwagę: używasz prefiksu 0x, co oznacza, że ​​twoje liczby byłyby interpretowane jako szesnastkowe, w rzeczywistości nie byłyby potęgą dwóch, a twoje testy na bitach nie zadziałałyby!)

Jeśli wyliczenia wymykają się spod kontroli w ten sposób, nie należy używać typu wyliczeniowego. Użyj czegoś takiego jak std::bitset. Wtedy twoje wyliczenie może być zwykłą numerowaną listą nazw pozycji bitów w zbiorze ... i nie będziesz wyczerpująco wyliczał przestrzeni wyliczeniowej!

Na przykład:

enum VState { 
    NEUTRAL, 
    DRY, 
    WET, 
    COLD, 
    BURNED, 
    FROZEN, 
    /* ... */ 
    VState_Max 
}; 

bitset<VState_Max> state; 

state[COLD] = true; 
if (state[COLD]) { 
    cout << "I am cold\n"; 
} 

Teraz Twój enum jest tak małe, numery podlegające konserwacji i nie trzeba się martwić o ich na platformie 64-bitowej lub etażerka.

Zauważam, że podałeś wartość "0" dla NEUTRAL w swoim oryginalnym przykładzie. Jeśli twoim zamiarem było, aby było możliwe użycie tego w połączeniu z innymi rzeczami ... takimi jak możliwość bycia state = NEUTRAL | INVULNERABLE | SHADOW i samodzielne testowanie pod kątem , które nie działałyby wcześniej. Teraz ... po prostu zatrzymasz go w wyliczaniu, aby zindeksować bitset.

Ale jeśli on przeznaczony jako nazwa dla „nic ustaw”, a następnie będzie go usunąć z wyliczenia i zamiast testu na żadnym z wymienionych bitów:

if (state.none()) { 
    // we are in the "NEUTRAL" state of nothing set... 
} 

... i jeśli chciałeś aby ustawić wszystkie bity na fałszywe, należy podać:

state.reset(); 
+4

To jest znacznie lepsza odpowiedź niż moja, ponieważ rozwiązujesz prawdziwy problem. Użyj 'std :: bitset' do kolekcjonowania flag, a nie wyliczeń. Nawet lepsze niż użycie wyliczenia do indeksu byłoby zawarcie go w klasie, która ma 'std :: bitset' jako prywatny element członkowski z nazwanymi funkcjami dostępu do elementu. 'class VStates {public: bool is_dry() const {return bits [1]; } ...}; ' –

+0

@DavidStone Dzięki. A jeśli informacje o pakowaniu są interesujące dla tej publiczności, to moja [biblioteka Nstate] (http://hostilefork.com/nstate/) może być interesująca ... – HostileFork

+0

Może się to skończyć. Obecnie pracuję nad aplikacją, w której spędzam ~ 2/3 mojego czasu na kopiowaniu danych. Zmniejszenie o 50% moich istotnych struktur danych odpowiada nieco mniej niż 30-procentowemu zmniejszeniu czasu wykonywania, i wydaje się, że przynajmniej część mojego wniosku może podlegać temu rozwiązaniu. –

0

Użyj mniejszych cyfr. Wyliczenie może być tylko tak duże jak długie. Rozmiar długiego zależy od kompilatora, ale typowy rozmiar to 32 lub 64 bity. Widzę tam jakieś 10-cyfrowe liczby szesnastkowe, które są zbyt duże.

3

Jeśli używasz C++ 11, można zadeklarować silnie wpisany enum z określonym rodzaju co unsigned long long (_int64 w systemie Windows, choć powinieneś używać przenośnego uint64_t), jeśli rozszerza zakres wystarczająco daleko.

Dzięki Joachim dla linkiem do przykładów C++ 11 użycia ENUM: Strongly Typed Enums

+0

Zobacz np. [link do Wikipedii] (http://en.wikipedia.org/wiki/C++11#Strongly_typed_enumerations) na temat mocno napisanych wyrażeń i sposobu ich definiowania. z różnymi typami liczb całkowitych. –

+0

@JoachimPileborg dzięki, czy mogę użyć tego linku, aby poprawić odpowiedź? –

+0

Oczywiście, proszę bardzo. :) –

12

w C++ 11, można określić podstawowy rodzaj wyliczenia.

#include <cstdint> 

enum VStates : uint64_t { 
    // Values 
} 

Na marginesie, zalecam, aby nie obliczać wszystkich tych uprawnień dwóch. Popełniłeś błąd w swoich obliczeniach, pisząc stałą heksadecymalną, ale podając jej cyfry liczby 10. Jednak zamiast przeliczać wszystko, polecam coś takiego:

#include <cstdint> 

enum VStates : uint64_t { 
    NEUTRAL = 0ULL, 
    DRY = 1ULL << 0, 
    WET = 1ULL << 1, 
    HOT = 1ULL << 2, 
    COLD = 1ULL << 3, 
    // etc. 
} 

Wtedy na pewno się nie pomylisz. Przyrostek ULL zapewnia, że ​​literał jest akceptowany jako co najmniej 64-bitowa szerokość całkowita.

4

Wyliczenie ma 31 wartości niezerowych, więc wszystkie mogą zmieścić się w 32-bitowej wartości bez znaku. Problem polega na tym, że wartości tutaj nie są wartościami bitowymi. Albo zapisz je jako wartości dziesiętne (usuń z przodu 0x) lub zapisz je jako wartości szesnastkowe (0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40 itd.) Osobiście nie lubię tego, ale niektórzy piszą tego rodzaju stały z przesunięciami: 1 < < 0, 1 < < 1, 1 < < 2, 1 < < 3 itd

+2

Ciekawi mnie, co ci się nie podoba w zmianach? Są mniej podatne na błędy i szybko rzucają okiem na enum i od razu wiesz, że możesz używać operacji bitowych na tym. – Joe

+2

@Joe - to czysto estetyczne. Nie są mniej podatne na błędy niż wartości szesnastkowe i myślę, że są brzydkie. –

Powiązane problemy