Alberto
Alberto

Reputation: 12939

C++ use of enums with bit OR operator

I can't find any resource online on how to use this method, which is quitely widely used in libraries.

What I'm referring to si something that looks like this:

someFun(Class::prop1 | Class::prop2 | Class::prop3, somethingelse);

now, I can undestand that this is based on enums/ints that are power of 2, so something like:

class Class{
public
     enum{prop1 = 1, prop2 = 2, prop3 = 4, prop4 = 8};
};

and then use something like this to check if the n-th bit is 0 or 1, but are there ways to do it in a cleaner way?

If you wan you can use this as example:

class Class{
public:
     enum{prop1 = 1, prop2 = 2, prop3 = 4};
     void f(int i){
         if( ... ) ...
     }
};

I was thinking maybe using the &operator, like if(i & Class::prop1) ... but idk if it's the right way to do it

Upvotes: 1

Views: 590

Answers (1)

Jorge Bellon
Jorge Bellon

Reputation: 3106

Your example is correct, and follows the classic C-style bitset using enums. As an alternative, you can declare the enum members using a left shift, which might improve readability for people unfamiliar:

enum Flags {
  flag0 = 1<<0,
  flag1 = 1<<1,
  flag2 = 1<<2
};

The reason why flags & flag0 works in an if conditional is that the conditional only evaluates to false for an integral number if the number is 0. In a bitwise AND operation, where one of the operands is a power-of-2, this means that the particular bit is not set in the other operand.

An alternative is to declare your enum with implicit values:

enum class MyFlags {
  flag0, flag1, flag2 // implicit values are consecutive, starting with 0
};

And then using std::bitset either directly, or through a new type:

class MyBitset {
public:
   explicit MyBitset(std::initializer_list<MyFlags> flags) {
     for (MyFlags f : flags)
       values.set(static_cast<unsigned>(f));
   }
   
   bool test(MyFlags f) { return values.test(static_cast<unsigned>(f); }
private:
   std::bitset<WIDTH> values; // see below
};

The advantage is that now you can construct a set of flags with a initializer list, which a STL-like:

MyBitset({MyFlags::flag0, MyFlags::flag2})

The problem with this alternative is the maximum value. Ideally we would set the bitset width so that it has enough capacity for the most biggest flag value. We could have a type trait to specify that, and then MyBitset could be generic:

template <class T> struct MyLimits;

template <> struct MyLimits<MyFlags> {
   static constexpr bool isEnum = true;
   static constexpr size_t min = static_cast<size_t>(Flags::flag0);
   static constexpr size_t max = static_cast<size_t>(Flags::flag2);
};

// fails if MyLimits was not specialized for MyFlags
std::bitset<MyLimits<MyFlags>::max> values; 

Upvotes: 1

Related Questions