Veroderung von enum-Werten
-
Hallo,
ich habe mir Werte per enum definiert. Diese Werte übergebe ich einer Testfunction. Das klappt auch dann, wenn ich nur einen Wert übergebe, wenn ich allerdings 2 Werte binär verodere, gibt mir MS Visual Studio 2010 in Zeile 13 eine Fehlermeldung (siehe unten) aus.
Folgendes vereinfachtes Beispiel zeigt das Verhalten
#include <iostream> enum NIBBLEBITS {BIT0 = 1, BIT1 = 2, BIT2 = 4, BIT3 = 8}; void testfunc(const NIBBLEBITS bit) { std::cout << bit << std::endl; } int main() { testfunc(BIT0); testfunc(BIT0 | BIT1); std::cin.get(); return 0; }
Es kommt zur folgender Fehlermeldung:
: error C2664: 'testfunc': Konvertierung des Parameters 1 von 'int' in 'const NIBBLEBITS' nicht möglich
1> Konvertierung in einen Enumerationstypen erfordert explizite Typumwandlung (static_cast-Operator oder eine Typumwandlung im C- oder Funktionsformat)Wie kann ich diese enum-Werte verodern?
Wenn ich die Zeile 13 durch die folgende Zeile ersetze, funktioniert es
testfunc(static_cast<NIBBLEBITS>(BIT0 | BIT1));
Ich würde gerne auf den static_cast verzichten.
Gruss
Ludger.
-
Achso schrieb:
Wenn ich die Zeile 13 durch die folgende Zeile ersetze, funktioniert es
testfunc(static_cast<NIBBLEBITS>(BIT0 | BIT1));
es funktioniert nicht, es kompiliert. Welchen Integer-Wert hat "BIT0|BIT1" (also "1|2")? Und welchem NIBBLEBITS-Wert sollte das Ergebnis entsprechen?
Spricht irgendwas gegen std::bitset?
-
Hallo,
es funktioniert nicht, es kompiliert. Welchen Integer-Wert hat "BIT0|BIT1" (also "1|2")? Und welchem NIBBLEBITS-Wert sollte das Ergebnis entsprechen?
BIT0 = 1 => 0001 binär
BIT1 = 2 => 0010 binär
BIT0 | BIT1 = 3 => 0011 binär Bitweise Verknüpfung von BIT0 und BIT1.Das Beispielprogramm gibt bei mir 3 aus, es funktioniert mit static_cast wie gewünscht.
Spricht irgendwas gegen std::bitset?
Ich will den Hintergrund verstehen und meine C-affinen Kollegen (Hardware) sollen den Code grundsätzlich verstehen können. Ansonsten kenne ich std::bitset und verwende es auch.
Gruss
Ludger.
-
Achso schrieb:
Das Beispielprogramm gibt bei mir 3 aus, es funktioniert mit static_cast wie gewünscht.
dumm nur, dass es kein NIBBLEBITS mit dem Wert "3" gibt. Es funktioniert, weil enums intern wie integer behandelt werden.
Eigentlich ist die Semantik von enums, dass es eine eingeschränkte Menge von Möglichkeiten gibt (Monate, Wochentage etc.). So eine Veroderung habe ich bisher noch nicht gesehen und fände es auch sehr merkwürdig.
-
Spricht was dagegen die Funktion umzuschreiben, so dass sie einen int erwartet?
Zur Not kannst du von dieser Funktion ja casten und weiter delegieren
so also:#include <iostream> enum NIBBLEBITS { BIT0 = 1, BIT1 = 2, BIT2 = 4, BIT3 = 8 }; void testfunc(const NIBBLEBITS bit) { std::cout << bit << std::endl; } void testfunc(const int bit) { testfunc(static_cast<NIBBLEBITS>(bit)); } int main() { testfunc(BIT0); testfunc(BIT0 | BIT1); std::cin.get(); return 0; }
-
Hallo,
dumm nur, dass es kein NIBBLEBITS mit dem Wert "3" gibt. Es funktioniert, weil enums intern wie integer behandelt werden.
Direkt brauche ich den Wert 3 nie, weil ich innerhalb der Funktion nur die verschiedenen Bits einzeln abfrage.
void testfunc(const NIBBLEBITS bit) { if ((bit & BIT0) == BIT0) std::cout << "Bit 0 ist gesetzt" << std::endl; if ((bit & BIT1) == BIT1) std::cout << "Bit 1 ist gesetzt" << std::endl; }
Aber sehe natürlich ein, dass ich durch eine Veroderung den Wertebereich meines enums verlasse. Schade!
Früher habe ich es auch schon mal anders gelöst.
#include <iostream> const int BIT0 = 1; const int BIT1 = 2; const int BIT2 = 4; const int BIT3 = 8; void testfunc(const int bit) { if ((bit & BIT0) == BIT0) std::cout << "Bit 0 ist gesetzt" << std::endl; if ((bit & BIT1) == BIT1) std::cout << "Bit 1 ist gesetzt" << std::endl; } int main() { testfunc(BIT0); testfunc(BIT0 | BIT1); std::cin.get(); return 0; }
Gruss
Ludger.
-
Hallo,
Skym0sh0 schrieb:
Spricht was dagegen die Funktion umzuschreiben, so dass sie einen int erwartet?
Zur Not kannst du von dieser Funktion ja casten und weiter delegieren
so also:nein, das ist die Lösung, die ich auch schon eingebaut habe!
Ich hatte auch schon mit einer Überladung des |-Operators für NIBBLEBITS experimentiert.
NIBBLEBITS operator|(const NIBBLEBITS& lhs, const NIBBLEBITS& rhs) { return static_cast<NIBBLEBITS>(static_cast<int>(lhs) | static_cast<int>(rhs)); }
Ohne die static_casts kommt es zur einer Rekursion.
Gruss Ludger.
-
Hallo Ludger,
du kannst deinen Code so benutzen, da spricht nichts dagegen.
Statt dem static_cast<> könntest du auch einfach den Konstruktor verwenden:testfunc(NIBBLEBITS(BIT0 | BIT1));
s. z.B. http://www.parashift.com/c++-faq/enumeration-is-its-own-type.html bzw. http://www.parashift.com/c++-faq/enumeration-type-ops.html
P.S. Will man enums wirklich nur auf vordefinierte Werte zulassen, so kann man das Type Safe Enum Idiom benutzen.
PPS. @daddy_felix, diese Art von Enumerations nennt man Flags und ist sehr üblich
-
@Achso:
Was spricht den gegen den |-Operator?#include <iostream> enum NIBBLEBITS {BIT0 = 1, BIT1 = 2, BIT2 = 4, BIT3 = 8}; constexpr NIBBLEBITS operator|(const NIBBLEBITS& lhs, const NIBBLEBITS& rhs) { return NIBBLEBITS(static_cast<int>(lhs) | static_cast<int>(rhs)); } void testfunc(const NIBBLEBITS bit) { std::cout << bit << std::endl; switch(bit) { case BIT0: //... case BIT1: //... //... case BIT0 | BIT3: //... } int main() { testfunc(BIT0); testfunc(BIT0 | BIT1); std::cin.get(); return 0; }
Dank des constexpr kannst du den überall verwenden...
-
Warum zwängt ihr den Wert 3 in ein NIBBLEBITS? Das Ergebnis von BIT0|BIT1 ist kein NIBBLEBITS, es ist ein int. Dass testfunc hier NIBBLEBITS erwartet, ist extrem irreführend und meines Erachtens schlichtweg falsch.
Der Vorschlag von Nathan sollte erst gar nicht kompilieren. Man kann ein NIBBLEBITS doch nicht aus einem (x-beliebigem) int konstruieren.
-
Er tuts aber.
Es kommt zwar eine Warnung, dass die Zahl nicht im enum-Value drin ist, aber wenn man das nach int castet, klappt es ohne Probleme.
Für Flags ist das aber trotz allem eine gute Lösung finde ich, es ist ja egal, welchen Wert das intern represäntiert.
-
Nathan schrieb:
Er tuts aber.
Es kommt zwar eine Warnung, dass die Zahl nicht im enum-Value drin ist, aber wenn man das nach int castet, klappt es ohne Probleme.
Für Flags ist das aber trotz allem eine gute Lösung finde ich, es ist ja egal, welchen Wert das intern represäntiert.Furchtbar. Und was soll dann der Vorteil gegenüber int sein?
-
Enum Bezeichnungen?
Ja, der Funktionsparameter ist vielleicht ungünstig gewählt.
Ich finde aber, dass der verdeutlicht welche Enum Werte (und Kombinationen) die Funktion erwartet.
Außerdem nenne mir bitte einen anderen Sinn für den |-Operator für enums.
-
Captain Obvious schrieb:
Warum zwängt ihr den Wert 3 in ein NIBBLEBITS? Das Ergebnis von BIT0|BIT1 ist kein NIBBLEBITS, es ist ein int. Dass testfunc hier NIBBLEBITS erwartet, ist extrem irreführend und meines Erachtens schlichtweg falsch.
Der Vorschlag von Nathan sollte erst gar nicht kompilieren. Man kann ein NIBBLEBITS doch nicht aus einem (x-beliebigem) int konstruieren.Schonmal ios_base::fmtflags angeschaut? (Allgemein 17.5.2.1.3 Bitmask types)
-
camper schrieb:
Schonmal ios_base::fmtflags angeschaut? (Allgemein 17.5.2.1.3 Bitmask types)
Nein, hab auch keinen Bock mir das jetzt zu suchen. Wenn da drin steht, dass man mit üblem rumcasten enum-Werte basteln soll/darf, die nicht mal im enum vorkommen, dann ist es Müll.
-
Captain Obvious schrieb:
camper schrieb:
Schonmal ios_base::fmtflags angeschaut? (Allgemein 17.5.2.1.3 Bitmask types)
Nein, hab auch keinen Bock mir das jetzt zu suchen. Wenn da drin steht, dass man mit üblem rumcasten enum-Werte basteln soll/darf, die nicht mal im enum vorkommen, dann ist es Müll.
Dann solltest du dich evtl in
Captain Oblivious umbenennen? j/k
-
Th69 schrieb:
Statt dem static_cast<> könntest du auch einfach den Konstruktor verwenden:
testfunc(NIBBLEBITS(BIT0 | BIT1));
Das ist kein Konstruktor, sondern ein Function Style Cast, und damit semantisch equivalent zum C-Cast. Würde ich vermeiden.
Nathan schrieb:
constexpr NIBBLEBITS operator|(const NIBBLEBITS& lhs, const NIBBLEBITS& rhs) void testfunc(const NIBBLEBITS bit)
Const-Wahn? Übergib Enumeratoren bitte per Value und zwar ohne const. Den Aufrufer interessiert es genau gar nicht, ob die Kopie konstant ist oder nicht.
<
>
-
Beitrag bitte löschen.
-
Hahaha, ja.
Ernsthaft, willst du damit sagen, dass dieser Haufen Müll seit neuestem ok sein soll?
constexpr NIBBLEBITS operator|(const NIBBLEBITS& lhs, const NIBBLEBITS& rhs) { return NIBBLEBITS(static_cast<int>(lhs) | static_cast<int>(rhs)); }
-
Captain Obvious schrieb:
Hahaha, ja.
Ernsthaft, willst du damit sagen, dass dieser Haufen Müll seit neuestem ok sein soll?
constexpr NIBBLEBITS operator|(const NIBBLEBITS& lhs, const NIBBLEBITS& rhs) { return NIBBLEBITS(static_cast<int>(lhs) | static_cast<int>(rhs)); }
Nein.
Ich behaupte, dass es seit Einführung von Operatorüberladung (noch vor der letzten Eiszeit) ein übliches Mittel ist, um (einigermaßen) sichere Flagtypen zu erhalten.