Veroderung von enum-Werten
-
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.
-
camper schrieb:
Schonmal ios_base::fmtflags angeschaut? (Allgemein 17.5.2.1.3 Bitmask types)
sieht nicht gut aus, File include\xiosbase
#define _IOSskipws 0x0001 #define _IOSunitbuf 0x0002 #define _IOSuppercase 0x0004 ... enum _Dummy_enum {_Dummy_enum_val = 1}; // don't ask enum _Fmtflags { // constants for formatting options _Fmtmask = 0xffff, _Fmtzero = 0}; static const _Fmtflags skipws = (_Fmtflags)_IOSskipws; static const _Fmtflags unitbuf = (_Fmtflags)_IOSunitbuf; static const _Fmtflags uppercase = (_Fmtflags)_IOSuppercase;
-
camper schrieb:
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.
Habe ich so noch nicht gesehen, bzw. nicht wahrgenommen. Ich finde es höchst fragwürdig, wenn bei einer Aufzählung wie
enum foo { bar = 1, baz = 2 };
hin und wieder auch mal ein
foo
mit dem Wert drei auftauchen darf. Das kann Standard sein wie es will, es bleibt komisch.
-
... denke, denke, denke ...
Vorschlag:
#include <iostream> enum NIBBLEBIT {BIT0 = 1, BIT1 = 2, BIT2 = 4, BIT3 = 8}; typedef int NIBBLEBITS; void testfunc(NIBBLEBITS bits) { std::cout << bits << std::endl; } int main() { testfunc(BIT0); testfunc(BIT0 | BIT1); std::cin.get(); return 0; }
-
Captain Obvious schrieb:
Ich finde es höchst fragwürdig, wenn bei einer Aufzählung wie
enum foo { bar = 1, baz = 2 };
hin und wieder auch mal ein
foo
mit dem Wert drei auftauchen darf.Ich fände es fragwürdig, wenn der numerische Wert jemals im Programm relevant werden sollte...
dd++ schrieb:
... denke, denke, denke ...
Vorschlag:
#include <iostream> enum NIBBLEBIT {BIT0 = 1, BIT1 = 2, BIT2 = 4, BIT3 = 8}; typedef int NIBBLEBITS; void testfunc(NIBBLEBITS bits) { std::cout << bits << std::endl; } int main() { testfunc(BIT0); testfunc(BIT0 | BIT1); std::cin.get(); return 0; }
Das hindert niemanden
testfunc(3);
zu schreiben, was sinnlos wäre.
enum Flag1 { a = 1 << 0, b = 1 << 1, c = 1 << 2 }; enum Flag2 { x = 1 << 0, y = 1 << 1, z = 1 << 2 }; ... überladene Operatoren ... testfunc1(Flag1); testfunc2(Flag2); testfunc1(a|b); // ok testfunc1(x|y); // error: wäre sinnlos testfunc2(a|b); // error: wäre sinnlos testfunc2(x|y); // ok
C++03 schrieb:
17.3.2.1.2 Bitmask types [lib.bitmask.types]
1 Several types defined in clause 27 are bitmask types. Each bitmask type can be
implemented as an enumer- ated type that overloads certain operators, as an integer type, or as a
bitset (23.3.5).2 The bitmask type bitmask can be written:
enum bitmask {
V0 = 1 << 0, V1 = 1 << 1, V2 = 1 << 2, V3 = 1 << 3, .....
};static const bitmask C0(V0); static const bitmask C1(V1); static const bitmask C2(V2); static const
bitmask C3(V3);
.....bitmask operator& (bitmask X, bitmask Y)
// For exposition only.
// int_type is an integral type capable of
// representing all values of bitmask
{ return static_cast<bitmask>( static_cast<int_type>(X) & static_cast<int_type>(Y)); }bitmask operator| (bitmask X, bitmask Y)
{ return static_cast<bitmask>( static_cast<int_type>(X) | static_cast<int_type>(Y)); }
bitmask operatorˆ (bitmask X, bitmask Y)
{ return static_cast<bitmask>( static_cast<int_type>(X) ˆ static_cast<int_type>(Y)); }
bitmask operator˜ (bitmask
{ return static_cast<bitmask>(static_cast<int_type>(˜X)); }bitmask& operator&=(bitmask& X, bitmask Y)
{ X = X & Y; return X; }
bitmask& operator|=(bitmask& X, bitmask Y)
{ X = X | Y; return X; }
bitmask& operatorˆ=(bitmask& X, bitmask Y)
{ X = X ˆ Y; return X; }3 Here, the names C0, C1, etc. represent bitmask elements for this particular bitmask
type. All such ele- ments have distinct values such that, for any pair Ci and Cj, Ci & Ci is
nonzero and Ci & Cj is zero.4 The following terms apply to objects and values of bitmask types:
— To set a value Y in an object X is to evaluate the expression X ï= Y.
— To clear a value Y in an object X is to evaluate the expression X &= ˜Y.
— The value Y is set in the object X if the expression X & Y is nonzero.
Der Standard verlangt nat. nicht, dass eine Implementation tatsächlich enums hierfür verwendet.