kombinierbare Enums/Typen/Flags - wie?
-
wie kann man einen Datentypen, der im Wesentlichen Flags widerspiegelt generieren, der nur diese Flags bzw. deren Kombinationen zulässt? Ähnlich wie bei ios_base::openmode?
bsp:
enum MyFlags { a, b, c } void foo( MyFlags flags) {} ... foo( a | b );
Gerne auch mit boost-Bordmitteln
-
Enums sind integer, also solltest du auch integers erwarten.
int i = a | b | c; cout << i;
-
ja, sie werden idR intern als integer behandelt, aber bei der Typüberprüfung weigert sich der Compiler:
enum MyFlags {f1, f2} MyFlag flags = f1 | f1; //-> cannot convert from 'int' to 'MyFlags'
-
MyFlags flags = MyFlags(f1 | f2);
Edit:
(ios::out|ios::app) // funktioniert, da es enum _Openmode { // constants for file opening options _Openmask = 0xff}; static const _Openmode out = (_Openmode)0x02; static const _Openmode app = (_Openmode)0x08;
letzendlich so definiert ist.
-
hmm. okay.. I see. Ich ab auch "small flags" von Iain Denniston gefunden, die an sich genau das macht, was ich brauche. Allerdings nicht zu 100% syntax freundlich ist.
-
Du könntest dir auch den Operator überladen, allerdings ist das mit Vorsicht zu genießen, da du garantieren musst, dass das Ergebnis der Operation in den Enum passt, und ein Element des Enums ist.
enum Flag { zero, one, two, end = 5 }; Flag operator |(Flag lhs, Flag rhs) { int temp = (int)lhs | (int)rhs; assert(temp < end); return (Flag)temp; } int main() { Flag l = one | two; int test = one | two; return 0; }
Die erste Zeile wäre hier wohl undefiniertes Verhalten, von daher, arbeite daher lieber mit einem int und vergiss den eigenen Operator. Sprich mach es wie in Zeile zwei ohne den eigenen Operator oder wie Vicious Falcon es gezeigt hat.
-
Vicious Falcon schrieb:
MyFlags flags = MyFlags(f1 | f2);
Edit:
(ios::out|ios::app) // funktioniert, da es enum _Openmode { // constants for file opening options _Openmask = 0xff}; static const _Openmode out = (_Openmode)0x02; static const _Openmode app = (_Openmode)0x08;
letzendlich so definiert ist.
nunja, aber dann geht es dennoch nur mit nem cast:
enum MyFlag{ base=0 }; const MyFlag f1 = (MyFlag)0x01; const MyFlag f2 = (MyFlag)0x02; MyFlag f = f1 | f1; //-> compile error MyFlag f = (MyFlag)(f1 | f2);
Und gerade diesen Cast möchte ich mir sparen
-
Hallo zusammen,
um Flags kombinieren zu können (und entsprechend mit den Bitoperationen abzufragen), sollten die Enum-Werte Potenzen von 2 sein, d.h.
enum Flag { a = 1, b = 2, c = 4, d = 8, e = 16, //... };
Bei größeren Zahlen bietet sich dann auch eher die hexadezimale Schreibweise an, d.h.
enum Flag { f = 0x20, g = 0x40, h = 0x80, i = 0x100, j = 0x200, //... };
Nun kann man mittels der Bitoperationen '|' Flags setzen bzw. mittels '&~' löschen sowie mittels '&' abfragen.
Und wie du um den Cast herumkommst, hat Tobias Gerg schon gepostet (mittels Operatorüberladung).
-
das mit den zweier Potenzen ist klar... Letztlich möchte ich für jedes Bit eine Flagoption. Und von einer Operatorüberladung rät Tobias selbst ab
-
muffmolch schrieb:
...Ähnlich wie bei ios_base::openmode?...
Guck Dir doch mal an, wie es bei
openmode
gemacht wird. Dabei wird Dir auch auffallen, dass das nicht ganz so einfach ist.
-
Genau das habe ich getan. daher denke ich, dass ich vermutlich die flag.hpp von Ian zurückgreifen werde. Hatte gehofft, dass das man das mit einem Zweizeiler lösen könnte...
-
muffmolch schrieb:
das mit den zweier Potenzen ist klar... Letztlich möchte ich für jedes Bit eine Flagoption. Und von einer Operatorüberladung rät Tobias selbst ab
Wenn du die von mir genannten Bedingungen sicher stellen kannst (sprich das Ergebnis des binären Oder ist wieder ein Wert des Enum), dann kannst du die Operator Überladung machen.
Aber bedenke, 01 | 10 = 11.
Sprich das Ergebnis von 1 | 2 ist 3 und damit in einem Enum von zweiter Potenzen nicht enthalten.
Du könntest dir so etwas in der Art basteln. (Soll nur den Ansatz verdeutlichen und hat keinen Anspruch auf vollständige Korrektheit
).
class Flag { public: enum PossibleValue { clear = 0, flag1 = 1, flag2 = 2, flag3 = 4 }; Flag(PossibleValue val) : initVal(val), value(val){}; Flag() : initVal(clear), value(0) {}; friend Flag operator| (Flag rhs, PossibleValue lhs) { rhs.value |= (int)lhs; return rhs; } friend Flag operator| (PossibleValue rhs, PossibleValue lhs) { Flag temp; temp.value = (int)rhs | (int)lhs; return temp; } friend Flag operator| (PossibleValue rhs, Flag lhs) { lhs.value |= (int)rhs; return lhs; } Flag operator | (Flag rhs) { value |= rhs.value; return *this; } operator PossibleValue() { return initVal; } operator int() { return value; } private: const PossibleValue initVal; int value; }; int main() { Flag a(Flag::flag1) ; Flag b(Flag::flag2) ; Flag c(Flag::flag3) ; Flag res = a | b | c; Flag res2 = Flag::flag1 | Flag::flag2; return 0; }