Skip to content

Commit 4cad94f

Browse files
committed
Add idioms notes
1 parent 6a71337 commit 4cad94f

File tree

2 files changed

+137
-0
lines changed

2 files changed

+137
-0
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ This repo reflects some of the best practices for C++ based on my knowledge and
3131

3232
[Misc](notes/Misc.md)
3333

34+
[Idioms](notes/Idioms.md)
35+
3436
## Build
3537
### Linux
3638
```bash

notes/Idioms.md

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# Idioms
2+
3+
## (A possible) Generic implementation for flags
4+
5+
First make sure that the `underlying_type` of the enum is unsigned integer.
6+
7+
```cpp
8+
enum class MyFlags : unsigned int {
9+
None = 0,
10+
Flag1 = 1 << 0, // 0001
11+
Flag2 = 1 << 1, // 0010
12+
Flag3 = 1 << 2, // 0100
13+
Flag4 = 1 << 3 // 1000
14+
};
15+
```
16+
17+
Using `<type_traits>` might provide a solution to achieve a generic implementation for flag enums.
18+
19+
- Solution 1:
20+
21+
```cpp
22+
#include <type_traits>
23+
24+
template<typename T, typename ULT = unsigned int>
25+
struct is_flag_type : std::integral_constant<bool, (std::is_enum_v<T>) && (std::is_same_v<std::underlying_type_t<T>, ULT>)> {};
26+
27+
template<typename T, typename ULT = unsigned int, std::enable_if_t<is_flag_type<T,ULT>::value, bool> = true>
28+
inline T operator|(T lhs, T rhs) {
29+
return static_cast<T>(static_cast<ULT>(lhs) | static_cast<ULT>(rhs));
30+
}
31+
32+
template<typename T, typename ULT = unsigned int, std::enable_if_t<is_flag_type<T,ULT>::value, bool> = true>
33+
inline T operator&(T lhs, T rhs) {
34+
return static_cast<T>(static_cast<ULT>(lhs) & static_cast<ULT>(rhs));
35+
}
36+
37+
template<typename T, typename ULT = unsigned int, std::enable_if_t<is_flag_type<T,ULT>::value, bool> = true>
38+
inline T operator~(T rhs) {
39+
return static_cast<T>(~static_cast<ULT>(rhs));
40+
}
41+
42+
template<typename T, typename ULT = unsigned int, std::enable_if_t<is_flag_type<T,ULT>::value, bool> = true>
43+
inline T& operator|=(T& lhs, T rhs) {
44+
lhs = lhs | rhs;
45+
return lhs;
46+
}
47+
48+
template<typename T, typename ULT = unsigned int, std::enable_if_t<is_flag_type<T,ULT>::value, bool> = true>
49+
inline T& operator&=(T& lhs, T rhs) {
50+
lhs = lhs & rhs;
51+
return lhs;
52+
}
53+
54+
//=====================
55+
template<typename T, typename ULT = unsigned int, std::enable_if_t<is_flag_type<T,ULT>::value, bool> = true>
56+
inline T& SetFlag(T& lhs, T rhs)
57+
{
58+
return lhs |= rhs;
59+
}
60+
61+
template<typename T, typename ULT = unsigned int, std::enable_if_t<is_flag_type<T,ULT>::value, bool> = true>
62+
inline T& UnSetFlag(T& lhs, T rhs)
63+
{
64+
return lhs &= ~rhs;
65+
}
66+
67+
template<typename T, typename ULT = unsigned int, std::enable_if_t<is_flag_type<T,ULT>::value, bool> = true>
68+
inline bool IsFlagSet(T flag, T check)
69+
{
70+
return (flag & check) == check;
71+
}
72+
73+
```
74+
75+
- Solution 2 (A bit cleaner):
76+
77+
```cpp
78+
template<typename T>
79+
struct is_flag_type2 : std::integral_constant<bool, (std::is_enum_v<T>) && (std::is_unsigned_v<std::underlying_type_t<T>>)> {};
80+
81+
82+
template<typename T, std::enable_if_t<is_flag_type2<T>::value, bool> = true>
83+
inline T operator|(T lhs, T rhs) {
84+
return static_cast<T>(static_cast<std::underlying_type_t<T>>(lhs) | static_cast<std::underlying_type_t<T>>(rhs));
85+
}
86+
87+
template<typename T, std::enable_if_t<is_flag_type2<T>::value, bool> = true>
88+
inline T operator&(T lhs, T rhs) {
89+
return static_cast<T>(static_cast<std::underlying_type_t<T>>(lhs) & static_cast<std::underlying_type_t<T>>(rhs));
90+
}
91+
92+
template<typename T, std::enable_if_t<is_flag_type2<T>::value, bool> = true>
93+
inline T operator~(T rhs) {
94+
return static_cast<T>(~static_cast<std::underlying_type_t<T>>(rhs));
95+
}
96+
97+
template<typename T, std::enable_if_t<is_flag_type2<T>::value, bool> = true>
98+
inline T& operator|=(T& lhs, T rhs) {
99+
lhs = lhs | rhs;
100+
return lhs;
101+
}
102+
103+
template<typename T, std::enable_if_t<is_flag_type2<T>::value, bool> = true>
104+
inline T& operator&=(T& lhs, T rhs) {
105+
lhs = lhs & rhs;
106+
return lhs;
107+
}
108+
//=====================
109+
template<typename T, std::enable_if_t<is_flag_type2<T>::value, bool> = true>
110+
inline T& SetFlag(T& lhs, T rhs)
111+
{
112+
return lhs |= rhs;
113+
}
114+
115+
template<typename T, std::enable_if_t<is_flag_type2<T>::value, bool> = true>
116+
inline T& UnSetFlag(T& lhs, T rhs)
117+
{
118+
return lhs &= ~rhs;
119+
}
120+
121+
template<typename T, std::enable_if_t<is_flag_type2<T>::value, bool> = true>
122+
inline bool IsFlagSet(T flag, T check)
123+
{
124+
return (flag & check) == check;
125+
}
126+
```
127+
128+
- Usage:
129+
130+
```cpp
131+
MyFlags flags = MyFlags::None;
132+
SetFlag<MyFlags>(flags, MyFlags::Flag1);
133+
UnSetFlag<MyFlags>(flags, MyFlags::Flag1);
134+
auto res = IsFlagSet<MyFlags>(flags, MyFlags::Flag2);
135+
```

0 commit comments

Comments
 (0)