Skip to content

Commit 84f2d31

Browse files
committed
Add bitmask template
1 parent 0c5580b commit 84f2d31

2 files changed

Lines changed: 357 additions & 0 deletions

File tree

lib/public/StormByte/bitmask.hxx

Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
#include <StormByte/type_traits.hxx>
2+
3+
using namespace StormByte;
4+
5+
/**
6+
* @brief Bitwise OR operator for unsigned enum classes.
7+
* @tparam E The enumeration type.
8+
* @param a First enum value.
9+
* @param b Second enum value.
10+
* @return The result of the bitwise OR operation.
11+
*
12+
* This operator allows combining flags of an unsigned enum class using the bitwise OR operator.
13+
*/
14+
template <Type::UnsignedEnum E>
15+
constexpr E operator|(E a, E b) noexcept {
16+
return static_cast<E>(Type::ToUnderlying(a) | Type::ToUnderlying(b));
17+
}
18+
19+
/**
20+
* @brief Bitwise AND operator for unsigned enum classes.
21+
* @tparam E The enumeration type.
22+
* @param a First enum value.
23+
* @param b Second enum value.
24+
* @return The result of the bitwise AND operation.
25+
*
26+
* This operator allows checking common flags of an unsigned enum class using the bitwise AND operator.
27+
*/
28+
template <Type::UnsignedEnum E>
29+
constexpr E operator&(E a, E b) noexcept {
30+
return static_cast<E>(Type::ToUnderlying(a) & Type::ToUnderlying(b));
31+
}
32+
33+
/**
34+
* @brief Bitwise XOR operator for unsigned enum classes.
35+
* @tparam E The enumeration type.
36+
* @param a First enum value.
37+
* @param b Second enum value.
38+
* @return The result of the bitwise XOR operation.
39+
*
40+
* This operator allows toggling flags of an unsigned enum class using the bitwise XOR operator.
41+
*/
42+
template <Type::UnsignedEnum E>
43+
constexpr E operator^(E a, E b) noexcept {
44+
return static_cast<E>(Type::ToUnderlying(a) ^ Type::ToUnderlying(b));
45+
}
46+
47+
/**
48+
* @brief Bitwise NOT operator for unsigned enum classes.
49+
* @tparam E The enumeration type.
50+
* @param a Enum value to invert.
51+
* @return The result of the bitwise NOT operation.
52+
*
53+
* This operator allows inverting flags of an unsigned enum class using the bitwise NOT operator.
54+
*/
55+
template <Type::UnsignedEnum E>
56+
constexpr E operator~(E a) noexcept {
57+
return static_cast<E>(~Type::ToUnderlying(a));
58+
}
59+
60+
/**
61+
* @namespace StormByte
62+
* @brief Main namespace for the StormByte library.
63+
*
64+
* The `StormByte` namespace serves as the root for all components and utilities in the StormByte library.
65+
* It provides foundational classes and tools for building robust, thread-safe, and efficient applications.
66+
*/
67+
namespace StormByte {
68+
/**
69+
* @brief Bitmask class template for managing enum class flags.
70+
* @tparam Derived The derived class type.
71+
* @tparam E The enumeration type representing the flags.
72+
*
73+
* This class provides bitwise operations for enum class types,
74+
* allowing easy manipulation of flag combinations.
75+
*
76+
* Example usage:
77+
* @code
78+
* enum class MyFlags : uint8_t {
79+
* FlagA = 0x01,
80+
* FlagB = 0x02,
81+
* FlagC = 0x04
82+
* };
83+
*
84+
* class MyBitmask : public Bitmask<MyBitmask, MyFlags> {
85+
* public:
86+
* using Bitmask<MyBitmask, MyFlags>::Bitmask;
87+
* };
88+
*
89+
* MyBitmask mask(MyFlags::FlagA);
90+
* mask |= MyBitmask(MyFlags::FlagB);
91+
* if (mask.Has(MyFlags::FlagA)) { ... }
92+
* @endcode
93+
*/
94+
template<typename Derived, Type::UnsignedEnum E>
95+
class Bitmask {
96+
public:
97+
/**
98+
* @brief Default constructor initializes the bitmask to zero.
99+
*/
100+
constexpr Bitmask() noexcept: m_value(static_cast<E>(0)) {}
101+
102+
/**
103+
* @brief Constructor initializes the bitmask with a specific enum value.
104+
* @param value The initial enum value for the bitmask.
105+
*/
106+
constexpr Bitmask(E value) noexcept: m_value(value) {}
107+
108+
/**
109+
* @brief Copy constructor.
110+
* @param other The other Bitmask to copy from.
111+
*/
112+
constexpr Bitmask(const Bitmask& other) noexcept = default;
113+
114+
/**
115+
* @brief Move constructor.
116+
* @param other The other Bitmask to move from.
117+
*/
118+
constexpr Bitmask(Bitmask&& other) noexcept = default;
119+
120+
/**
121+
* @brief Destructor.
122+
*/
123+
constexpr virtual ~Bitmask() noexcept = default;
124+
125+
/**
126+
* @brief Copy assignment operator.
127+
* @param other The other Bitmask to copy from.
128+
* @return Reference to this Bitmask.
129+
*/
130+
constexpr Bitmask& operator=(const Bitmask& other) noexcept = default;
131+
132+
/**
133+
* @brief Move assignment operator.
134+
* @param other The other Bitmask to move from.
135+
* @return Reference to this Bitmask.
136+
*/
137+
constexpr Bitmask& operator=(Bitmask&& other) noexcept = default;
138+
139+
/**
140+
* @brief Equality operator.
141+
* @param other The other Bitmask to compare with.
142+
* @return True if the bitmasks are equal, false otherwise.
143+
*/
144+
constexpr bool operator==(const Bitmask& other) const noexcept {
145+
return m_value == other.m_value;
146+
}
147+
148+
/**
149+
* @brief Inequality operator.
150+
* @param other The other Bitmask to compare with.
151+
* @return True if the bitmasks are not equal, false otherwise.
152+
*/
153+
constexpr bool operator!=(const Bitmask& other) const noexcept {
154+
return m_value != other.m_value;
155+
}
156+
157+
/**
158+
* @brief Bitwise OR operator.
159+
* @param other The other Bitmask to combine with.
160+
* @return A new Bitmask representing the result of the bitwise OR operation.
161+
*/
162+
constexpr Derived operator|(const Bitmask& other) const noexcept {
163+
return Derived(m_value | other.m_value);
164+
}
165+
166+
/**
167+
* @brief Bitwise AND operator.
168+
* @param other The other Bitmask to combine with.
169+
* @return A new Bitmask representing the result of the bitwise AND operation.
170+
*/
171+
constexpr Derived operator&(const Bitmask& other) const noexcept {
172+
return Derived(m_value & other.m_value);
173+
}
174+
175+
/**
176+
* @brief Bitwise XOR operator.
177+
* @param other The other Bitmask to combine with.
178+
* @return A new Bitmask representing the result of the bitwise XOR operation.
179+
*/
180+
constexpr Derived operator^(const Bitmask& other) const noexcept {
181+
return Derived(m_value ^ other.m_value);
182+
}
183+
184+
/**
185+
* @brief Bitwise NOT operator.
186+
* @return A new Bitmask representing the result of the bitwise NOT operation.
187+
*/
188+
constexpr Derived operator~() const noexcept {
189+
return Derived(~m_value);
190+
}
191+
192+
/**
193+
* @brief Bitwise OR assignment operator.
194+
* @param other The other Bitmask to combine with.
195+
* @return Reference to this Bitmask after the operation.
196+
*/
197+
constexpr Bitmask& operator|=(const Bitmask& other) noexcept {
198+
m_value = m_value | other.m_value;
199+
return *this;
200+
}
201+
202+
/**
203+
* @brief Bitwise AND assignment operator.
204+
* @param other The other Bitmask to combine with.
205+
* @return Reference to this Bitmask after the operation.
206+
*/
207+
constexpr Bitmask& operator&=(const Bitmask& other) noexcept {
208+
m_value = m_value & other.m_value;
209+
return *this;
210+
}
211+
212+
/**
213+
* @brief Bitwise XOR assignment operator.
214+
* @param other The other Bitmask to combine with.
215+
* @return Reference to this Bitmask after the operation.
216+
*/
217+
constexpr Bitmask& operator^=(const Bitmask& other) noexcept {
218+
m_value = m_value ^ other.m_value;
219+
return *this;
220+
}
221+
222+
/**
223+
* @brief Add a flag to the bitmask.
224+
* @param value The enum value to add.
225+
*/
226+
constexpr void Add(E value) noexcept {
227+
m_value = m_value | value;
228+
}
229+
230+
/**
231+
* @brief Remove a flag from the bitmask.
232+
* @param value The enum value to remove.
233+
*/
234+
constexpr void Remove(E value) noexcept {
235+
m_value = m_value & ~value;
236+
}
237+
238+
/**
239+
* @brief Get the current value of the bitmask.
240+
* @return The current enum value of the bitmask.
241+
*/
242+
constexpr E Value() const noexcept {
243+
return m_value;
244+
}
245+
246+
/**
247+
* @brief Check if the bitmask has a specific flag set.
248+
* @param value The enum value to check.
249+
* @return True if the flag is set, false otherwise.
250+
*/
251+
constexpr bool Has(E value) const noexcept {
252+
return (m_value & value) == value;
253+
}
254+
255+
/**
256+
* @brief Check if the bitmask has all flags set from another bitmask.
257+
* @param other The other Bitmask to check against.
258+
* @return True if all flags are set, false otherwise.
259+
*/
260+
constexpr bool Has(const Bitmask& other) const noexcept {
261+
return Has(other.m_value);
262+
}
263+
264+
/**
265+
* @brief Check if any of the specified flags are set in the bitmask.
266+
* @param value The enum value(s) to check.
267+
* @return True if any of the flags are set, false otherwise.
268+
*/
269+
constexpr bool HasAny(E value) const noexcept {
270+
return (m_value & value) != static_cast<E>(0);
271+
}
272+
273+
/**
274+
* @brief Check if any of the flags from another bitmask are set in this bitmask.
275+
* @param other The other Bitmask to check against.
276+
* @return True if any of the flags are set, false otherwise.
277+
*/
278+
constexpr bool HasAny(const Bitmask& other) const noexcept {
279+
return HasAny(other.m_value);
280+
}
281+
282+
/**
283+
* @brief Check if none of the specified flags are set in the bitmask.
284+
* @param value The enum value(s) to check.
285+
* @return True if none of the flags are set, false otherwise.
286+
*/
287+
constexpr bool HasNone(E value) const noexcept {
288+
return (m_value & value) == static_cast<E>(0);
289+
}
290+
291+
/**
292+
* @brief Check if none of the flags from another bitmask are set in this bitmask.
293+
* @param other The other Bitmask to check against.
294+
* @return True if none of the flags are set, false otherwise.
295+
*/
296+
constexpr bool HasNone(const Bitmask& other) const noexcept {
297+
return HasNone(other.m_value);
298+
}
299+
300+
protected:
301+
E m_value;
302+
};
303+
}

test/bitmask_test.cxx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#include <StormByte/bitmask.hxx>
2+
#include <StormByte/test_handlers.h>
3+
4+
using namespace StormByte;
5+
6+
enum class MyFlags : uint8_t {
7+
FlagA = 0x01,
8+
FlagB = 0x02,
9+
FlagC = 0x04,
10+
FlagD = 0x08
11+
};
12+
13+
class MyBitmask: public Bitmask<MyBitmask, MyFlags> {
14+
public:
15+
using Bitmask<MyBitmask, MyFlags>::Bitmask;
16+
};
17+
18+
int test_bitmask_operations() {
19+
int result = 0;
20+
21+
MyBitmask a(MyFlags::FlagA);
22+
MyBitmask b(MyFlags::FlagB);
23+
MyBitmask c = a | b;
24+
25+
ASSERT_TRUE("test_bitmask_operations", (c.Value() == (MyFlags::FlagA | MyFlags::FlagB)));
26+
27+
c |= MyBitmask(MyFlags::FlagC);
28+
ASSERT_TRUE("test_bitmask_operations", (c.Value() == (MyFlags::FlagA | MyFlags::FlagB | MyFlags::FlagC)));
29+
30+
c &= MyBitmask(MyFlags::FlagA | MyFlags::FlagC);
31+
ASSERT_TRUE("test_bitmask_operations", (c.Value() == (MyFlags::FlagA | MyFlags::FlagC)));
32+
33+
c ^= MyBitmask(MyFlags::FlagC);
34+
ASSERT_TRUE("test_bitmask_operations", (c.Value() == MyFlags::FlagA));
35+
36+
MyBitmask d = ~a;
37+
ASSERT_TRUE("test_bitmask_operations", ((d.Value() & MyFlags::FlagA) == static_cast<MyFlags>(0)));
38+
39+
RETURN_TEST("test_bitmask_operations", result);
40+
}
41+
42+
int main() {
43+
int result = 0;
44+
45+
result += test_bitmask_operations();
46+
47+
if (result == 0) {
48+
std::cout << "All tests passed!" << std::endl;
49+
} else {
50+
std::cout << result << " tests failed." << std::endl;
51+
}
52+
return result;
53+
}
54+

0 commit comments

Comments
 (0)