Hallo Leute,
ich frage mich, wie man folgendes Problem "richtig löst" (bzw. wie ihr es lösen würdet):
Ich möchte irgendwie Bit-Flags in C++ verwenden mit folgenden Eigenschaften haben:
- Typensicher (keine implizite Konvertierung zu integer Typen)
- Es wird er globale namespace nicht mit häufigen symbolen "verschmutzt" (Bsp.: Bei Richtungen soll es zwar Direction::noth geben, aber north alleine trotzdem noch verwendbar)
- simple bitweise Operatoren (|=, &=, ^=, ~, !, &, |)
- am Ende soll es auf einem Mikroprozessor laufen (Arm-Cortex-M3), für den es aber eine fast vollständig geportete C++ Standard Library gibt.
Es gibt mehrere Möglichkeiten, von denen ich bis jetzt gehört habe (manche simpel, manche aufwendig, fast keine ideal):
- Einfach in verschiedenen Namespaces mit constexpr den Werten die Namen geben:
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
|
namespace Direction {
constexpr uint8_t north = 1 <<0;
constexpr uint8_t east = 1 << 1;
}
int main()
{
uint8_t WhereCanIGo = Direction::north | Direction::east;
}
|
- Möglichkeiten enum class mit einem underlying type verwenden:
* Alle nötigen Operatoren per Makro für jedes Enum erzeugen
* Per templates, type-traits und SFINAE die nötigen operatoren überladen (Nicht möglich, da für Enums in einer Klasse keine traits erzeugt werden können / Oder wie auch immer man das nennt)
* Eine Template Klasse, die alle Operatoren hat. Siehe Beispiel:
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
template<typename T = typename std::enable_if<std::is_enum<T>, T>>
class EnumAsFlag {
private:
std::underlying_type<T>::type _val
public:
// Alle Operatoren und Konstruktoren
};
namespace x {
enum class Direction : uint8_t {
north = 1 << 0,
east = 1 << 1
};
}
int main() {
EnumAsFlag<x::Direction> WherCanIGo = x::Direction::north;
}
|
Das ist mein aktueller Favourite, weil dadurch auch klar wird, wann das enum nur eine Eigenschaft beinhält (x::Direction) und wann ich es als Flag als Kombination von mehreren Eigenschaften verwende (EnumAsFlag<x::Direction>)
- Ich hab auch irgendwas von bit fields / bit sets gelesen, hab mich aber noch nicht darüber schlau gemacht, und bin mir nicht sicher, ob das nicht zu viel Overhead wird, da alle anderen Methoden eigentlich vom Compiler zu einer sehr primitiven Variante optimiert werden können (glaub ich halt).
Also, wie würdet ihr das machen?
Mit freundlichen Grüßen,
Patrick Ziegler