BitSet.h
Go to the documentation of this file.
1 /****
2  * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development.
3  * Created 2015 by Skurydin Alexey
4  * http://github.com/SmingHub/Sming
5  * All files of the Sming Core are provided under the LGPL v3 license.
6  *
7  * BitSet.h - Strongly typed sets of values
8  *
9  * @author: 2020 - Mikee47 <mike@sillyhouse.net>
10  *
11  ****/
12 
13 #pragma once
14 
15 #include <cstddef>
16 #include <cstdint>
17 #include <limits>
18 #include <type_traits>
19 #include <WString.h>
20 #include <Print.h>
21 
44 template <typename S, typename E, size_t size_ = sizeof(S) * 8> class BitSet
45 {
46 public:
47  static_assert(std::is_integral<S>::value && std::is_unsigned<S>::value,
48  "BitSet requires an unsigned integral storage type");
49  static_assert(std::is_enum<E>::value || (std::is_integral<E>::value && std::is_unsigned<E>::value),
50  "BitSets may use only enum or unsigned integral type elements");
51  static_assert(size_ > 0, "BitSet size cannot be zero!");
52  static_assert(size_ <= (sizeof(S) * 8), "BitSet has too many elements for storage type");
53 
54  class BitRef
55  {
56  public:
57  operator bool() const
58  {
59  return bitset.test(e);
60  }
61 
62  BitRef& operator=(bool b)
63  {
64  bitset.set(e, b);
65  return *this;
66  }
67 
68  private:
69  friend BitSet;
70  BitRef(BitSet& bitset, E e) : bitset(bitset), e(e)
71  {
72  }
73 
74  BitSet& bitset;
75  E e;
76  };
77 
81  constexpr BitSet() = default;
82 
87  template <typename S2> constexpr BitSet(const BitSet<S2, E>& bitset) : bitSetValue(bitset.value())
88  {
89  }
90 
95  constexpr BitSet(S value) : bitSetValue(value)
96  {
97  }
98 
103  constexpr BitSet(E e) : bitSetValue{bitVal(e)}
104  {
105  }
106 
110  bool operator==(const BitSet& other) const
111  {
112  return bitSetValue == other.bitSetValue;
113  }
114 
118  bool operator!=(const BitSet& other) const
119  {
120  return bitSetValue != other.bitSetValue;
121  }
122 
126  constexpr BitSet operator~() const
127  {
128  return BitSet(~bitSetValue & domain().bitSetValue);
129  }
130 
134  static constexpr size_t size()
135  {
136  return size_;
137  }
138 
142  static constexpr BitSet domain()
143  {
144  return std::numeric_limits<S>::max() >> ((sizeof(S) * 8) - size_);
145  }
146 
150  static constexpr S bitVal(E e)
151  {
152  return S{1U} << unsigned(e);
153  }
154 
159  {
160  bitSetValue = ~bitSetValue & domain().bitSetValue;
161  return *this;
162  }
163 
167  BitSet& flip(E e)
168  {
169  bitSetValue ^= bitVal(e);
170  return *this;
171  }
172 
176  size_t count() const
177  {
178  return __builtin_popcount(bitSetValue);
179  }
180 
184  BitSet& operator+=(const BitSet& rhs)
185  {
186  bitSetValue |= rhs.bitSetValue;
187  return *this;
188  }
189 
193  BitSet& operator-=(const BitSet& rhs)
194  {
195  bitSetValue &= ~rhs.bitSetValue;
196  return *this;
197  }
198 
202  BitSet& operator&=(const BitSet& rhs)
203  {
204  bitSetValue &= rhs.bitSetValue;
205  return *this;
206  }
207 
211  BitSet& operator|=(const BitSet& rhs)
212  {
213  bitSetValue |= rhs.bitSetValue;
214  return *this;
215  }
216 
220  BitSet& operator^=(const BitSet& rhs)
221  {
222  bitSetValue ^= rhs.bitSetValue;
223  return *this;
224  }
225 
229  bool test(E e) const
230  {
231  return (bitSetValue & bitVal(e)) != 0;
232  }
233 
239  bool operator[](E e) const
240  {
241  return test(e);
242  }
243 
252  {
253  return BitRef{*this, e};
254  }
255 
259  bool any() const
260  {
261  return bitSetValue != 0;
262  }
263 
268  bool any(const BitSet& other) const
269  {
270  return (bitSetValue & other.bitSetValue) != 0;
271  }
272 
276  bool all() const
277  {
278  return bitSetValue == domain().bitSetValue;
279  }
280 
284  bool none() const
285  {
286  return bitSetValue == S{0};
287  }
288 
293  {
294  bitSetValue = domain().bitSetValue;
295  return *this;
296  }
297 
303  BitSet& set(E e, bool state = true)
304  {
305  if(state) {
306  bitSetValue |= bitVal(e);
307  } else {
308  bitSetValue &= ~bitVal(e);
309  }
310  return *this;
311  }
312 
317  {
318  bitSetValue = S{0};
319  return *this;
320  }
321 
325  BitSet& reset(E e)
326  {
327  return set(e, false);
328  }
329 
333  bool operator==(E e) const
334  {
335  return bitSetValue == bitVal(e);
336  }
337 
341  explicit constexpr operator S() const
342  {
343  return bitSetValue;
344  }
345 
349  constexpr S value() const
350  {
351  return bitSetValue;
352  }
353 
354  /*
355  * use a function pointer to allow for "if (s)" without the
356  * complications of an operator bool(). for more information,
357  * see: http://www.artima.com/cppsource/safebool.html
358  */
359  using IfHelperType = void (BitSet::*)() const;
360 
361  /*
362  * Provides safe bool() operator
363  * Evaluates as false if set is empty
364  */
365  operator IfHelperType() const
366  {
367  return any() ? &BitSet::IfHelper : 0;
368  }
369 
374  size_t printTo(Print& p, const String& separator = ", ") const
375  {
376  extern String toString(E e);
377 
378  size_t n{0};
379 
380  for(unsigned i = 0; i < size(); ++i) {
381  auto e = E(i);
382  if(!test(e)) {
383  continue;
384  }
385  if(n != 0) {
386  n += p.print(separator);
387  }
388  n += p.print(e);
389  }
390 
391  return n;
392  }
393 
394 private:
395  void IfHelper() const
396  {
397  }
398 
399  S bitSetValue{0};
400 };
401 
402 template <typename S, typename E, size_t size_>
404 {
405  return BitSet<S, E, size_>(S(x) & S(y));
406 }
407 
408 template <typename S, typename E, size_t size_>
410 {
411  return BitSet<S, E, size_>(S(x) | S(y));
412 }
413 
414 template <typename S, typename E, size_t size_>
416 {
417  return x | BitSet<S, E, size_>(b);
418 }
419 
420 template <typename S, typename E, size_t size_>
422 {
423  return x | y;
424 }
425 
426 template <typename S, typename E, size_t size_>
428 {
429  return BitSet<S, E, size_>(S(x) & ~S(y));
430 }
431 
432 template <typename S, typename E, size_t size_>
433 inline constexpr BitSet<S, E, size_> operator+(const BitSet<S, E, size_>& x, E b)
434 {
435  return x | b;
436 }
437 
438 template <typename S, typename E, size_t size_>
439 inline constexpr BitSet<S, E, size_> operator-(const BitSet<S, E, size_>& x, E b)
440 {
442 }
443 
444 template <typename S, typename E, size_t size_>
446 {
447  return BitSet<S, E, size_>(S(x) ^ S(y));
448 }
449 
450 template <typename S, typename E, size_t size_>
452 {
453  return x ^ BitSet<S, E, size_>(b);
454 }
455 
456 /*
457  * These allow construction of a maximally-sized BitSet in an expression,
458  * which is then copy-constructed to the actual value. For example:
459  *
460  * constexpr BitSet<uint8_t, Fruit> fixedBasket = Fruit::apple | Fruit::orange;
461  *
462  * `is_convertible` prevents match to regular enums or integral values, but allows enum class.
463  *
464  */
465 template <typename E>
466 constexpr
467  typename std::enable_if<std::is_enum<E>::value && !std::is_convertible<E, int>::value, BitSet<uint32_t, E>>::type
468  operator|(E a, E b)
469 {
471 }
472 
473 template <typename E>
474 constexpr
475  typename std::enable_if<std::is_enum<E>::value && !std::is_convertible<E, int>::value, BitSet<uint32_t, E>>::type
476  operator+(E a, E b)
477 {
478  return a | b;
479 }
480 
481 template <typename T> typename std::enable_if<std::is_integral<T>::value, String>::type toString(T value)
482 {
483  return String(value);
484 }
485 
490 template <typename S, typename E, size_t size_>
491 String toString(const BitSet<S, E, size_>& bitset, const String& separator = ", ")
492 {
493  extern String toString(E e);
494 
495  String s = String::empty;
496 
497  for(unsigned i = 0; i < bitset.size(); ++i) {
498  if(!bitset[E(i)]) {
499  continue;
500  }
501 
502  if(s.length() != 0) {
503  s += separator;
504  }
505  s += toString(E(i));
506  }
507 
508  return s;
509 }
510 
std::enable_if< std::is_integral< T >::value, String >::type toString(T value)
Definition: BitSet.h:481
constexpr BitSet< S, E, size_ > operator+(const BitSet< S, E, size_ > &x, const BitSet< S, E, size_ > &y)
Definition: BitSet.h:421
constexpr BitSet< S, E, size_ > operator-(const BitSet< S, E, size_ > &x, const BitSet< S, E, size_ > &y)
Definition: BitSet.h:427
constexpr BitSet< S, E, size_ > operator|(BitSet< S, E, size_ > x, BitSet< S, E, size_ > y)
Definition: BitSet.h:409
constexpr BitSet< S, E, size_ > operator&(const BitSet< S, E, size_ > &x, const BitSet< S, E, size_ > &y)
Definition: BitSet.h:403
constexpr BitSet< S, E, size_ > operator^(BitSet< S, E, size_ > x, BitSet< S, E, size_ > y)
Definition: BitSet.h:445
Definition: BitSet.h:55
BitRef & operator=(bool b)
Definition: BitSet.h:62
Manage a set of bit values using enumeration.
Definition: BitSet.h:45
BitSet & reset()
Remove all values from the set.
Definition: BitSet.h:316
void(BitSet::*)() const IfHelperType
Definition: BitSet.h:359
BitSet & reset(E e)
Clear the state of the given bit (i.e. remove it from the set)
Definition: BitSet.h:325
BitSet & set()
Add all possible values to the bit set.
Definition: BitSet.h:292
bool operator[](E e) const
Read-only [] operator.
Definition: BitSet.h:239
bool test(E e) const
Test to see if given element is in the set.
Definition: BitSet.h:229
bool all() const
Test if set contains all possible values.
Definition: BitSet.h:276
BitSet & operator|=(const BitSet &rhs)
Union: Add elements to set.
Definition: BitSet.h:211
BitSet & operator&=(const BitSet &rhs)
Intersection: Leave only elements common to both sets.
Definition: BitSet.h:202
bool operator==(E e) const
Determine if set consists of only the one given element.
Definition: BitSet.h:333
constexpr S value() const
Get stored bits for this bitset.
Definition: BitSet.h:349
constexpr BitSet(const BitSet< S2, E > &bitset)
Copy constructor.
Definition: BitSet.h:87
constexpr BitSet(S value)
Construct from a raw set of bits.
Definition: BitSet.h:95
bool any() const
Determine if set contains any values.
Definition: BitSet.h:259
BitSet & operator^=(const BitSet &rhs)
XOR - toggle state of bits using another set.
Definition: BitSet.h:220
BitSet & flip(E e)
Flip state of the given bit.
Definition: BitSet.h:167
size_t printTo(Print &p, const String &separator=", ") const
Class template to print the contents of a BitSet to a String.
Definition: BitSet.h:374
static constexpr S bitVal(E e)
Get the bitmask corresponding to a given value.
Definition: BitSet.h:150
static constexpr size_t size()
Get the number of possible elements in the set.
Definition: BitSet.h:134
constexpr BitSet()=default
Construct empty set.
bool any(const BitSet &other) const
Determine if set contains any values from another set i.e. intersection != [].
Definition: BitSet.h:268
size_t count() const
Get the number of elements in the set, i.e. bits set to 1.
Definition: BitSet.h:176
BitSet & operator+=(const BitSet &rhs)
Union: Add elements to set.
Definition: BitSet.h:184
bool none() const
Test if set is empty.
Definition: BitSet.h:284
constexpr BitSet operator~() const
Obtain a set containing all elements not in this one.
Definition: BitSet.h:126
bool operator==(const BitSet &other) const
Compare this set with another for equality.
Definition: BitSet.h:110
static constexpr BitSet domain()
Get the set of all possible values.
Definition: BitSet.h:142
BitRef operator[](E e)
Read/write [] operator.
Definition: BitSet.h:251
BitSet & operator-=(const BitSet &rhs)
Remove elements from set.
Definition: BitSet.h:193
constexpr BitSet(E e)
Construct set containing a single value.
Definition: BitSet.h:103
BitSet & set(E e, bool state=true)
Set the state of the given bit (i.e. add to or remove from the set)
Definition: BitSet.h:303
bool operator!=(const BitSet &other) const
Compare this set with another for inequality.
Definition: BitSet.h:118
BitSet & flip()
Flip all bits in the set.
Definition: BitSet.h:158
Provides formatted output to stream.
Definition: Print.h:37
size_t print(char c)
Prints a single character to output stream.
Definition: Print.h:97
The String class.
Definition: WString.h:137
size_t length(void) const
Obtain the String length in characters, excluding NUL terminator.
Definition: WString.h:243
static const String empty
An empty string evaluates to true.
Definition: WString.h:149