Veroderung von enum-Werten
-
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.
-
camper schrieb:
Ich fände es fragwürdig, wenn der numerische Wert jemals im Programm relevant werden sollte...
Muss ja nicht.
foo blubb = do_something(); switch(blubb) { ... }
reicht schon.
Eine Aufzählung ist eine Aufzählung. Wenn da in eine Variable - wie auch immer - Werte reinkommen, die in der Aufzählung nicht vorhanden sind, dann ist das bestenfalls ein Hack. Wenn C++ Bitmasken unterstützen will, warum dann nicht richtig? Diese "Lösung" ist doch wieder dem übertriebenen bloß-keine-reservierte-Bezeichner-einführen-Wahn geschuldet. Wer das gut findet, der leidet am Stockholm-Syndrom.
Edit: was du da gepostet hast, sieht ja fast aus wie der
KotCode von Nathan. Hat sein Compiler unrecht, wenn er warnt?
-
#include <iostream> enum NIBBLEBITS {BIT1 = 1, BIT2 = 2, BIT3 = 4, BIT4 = 8}; namespace { struct Ored { int mret; explicit Ored(NIBBLEBITS a, NIBBLEBITS b) : mret(a|b) {} }; } Ored operator|(NIBBLEBITS n1, NIBBLEBITS n2) { return Ored(n1, n2); } void testfunc(NIBBLEBITS stuff) { std::cout << stuff << std::endl; } void testfunc(Ored stuff) { std::cout << stuff.mret << std::endl; } int main() { testfunc(BIT1); testfunc(BIT1|BIT2); testfunc(1); //geht nat. nicht... }
Wie wärs damit? Jetzt die Klasse und den anonymen Namensraum in einen andere Übersetzungseinheit und bei belieben natürlich noch ergänzen und fertig...
-
Hallo,
als Threadstarter melde ich mich nochmal.
Danke an alle! Habe wieder was dazu gelernt.
Und wie camper schon schrieb, bilde ich mit enums Flags ab. Dafür sind enums auf jedenfall besser als ints. (siehe Beitrag von camper)
Gruss Ludger.
-
dd++ schrieb:
Vorschlag:
+1
Wieso die Funktion nur auf das eine Enum begrenzen? Kann man doch auch wunderbar für andere numerische Werte benutzen.
#include <iostream> enum NIBBLEBIT {BIT0 = 1, BIT1 = 2, BIT2 = 4, BIT3 = 8}; void bitProof(int value) { if ((value & BIT0) == BIT0) std::cout << "Bit 0 ist gesetzt" << std::endl; if ((value & BIT1) == BIT1) std::cout << "Bit 1 ist gesetzt" << std::endl; if ((value & BIT2) == BIT2) std::cout << "Bit 2 ist gesetzt" << std::endl; if ((value & BIT3) == BIT3) std::cout << "Bit 3 ist gesetzt" << std::endl; } int main() { bitProof(BIT0); bitProof(BIT0 | BIT1); return 0; }
Enums mag ich ehe nur als reine Aufzählung benutzen. Der Wert dahinter ist meistens egal, bei der BIT Sache drücke ich aber jedesmal ein Auge zu. Ist so doch halt schicker
-
KasF schrieb:
Wieso die Funktion nur auf das eine Enum begrenzen?
Typsicherheit (siehe camper). Man möchte nur Kombinationen der Enumeratoren erlauben.
-
@Captain Obvious
Guck mal die Sache ist doch wirklich denkbar einfach.C++ erlaubt dir enum Typen zu definieren. Per Default haben die keinen operator | und keinen operator & definiert. Wenn man dann
enumA | enumB
schreibt werden die beiden Operanden erstmal in einenint
verwandelt, und dann verodert. Das Ergebnis ist dann einint
, und um wieder nen enum draus zu machen müsste man mitstatic_cast
draufhauen.Da man mit
static_cast
ziemlich viel Unsinn anstellen kann, erübrigt sich mMn. die Frage ob das nun eine weitere Sache ist wo man mitstatic_cast
Unsinn machen kann oder nicht.Wenn man nen enum für Werte braucht deren Veroderung nicht sinnvoll ist, dann ist die Sache damit erledigt. Man definiert eben keine Operatoren für den enum Typ. Und wenn irgendwer meint mit
static_cast
einen (nicht im enum enthaltenen) Wert erzwingen zu müssen, dann ist er hübsch selbst schuld falls dabei Unsinn rauskommt.Wenn man aber typsichere Flags haben will, dann definiert man die besagten Operatoren eben, und der User kann ohne
static_cast
die Werte verodern oder verunden.Wenn man sich nun Sorgen macht ob der zugrundeliegende Typ wohl auch immer gross genug sei, dann kann man ja durchaus was machen. In C++ 03 kann man Dummy-Werte aufnehmen um einen minimalen Wertbereich zu erzwingen. Und in C++ 11 kann man den Typ explizit angeben.