How should C bitflag enumerations be translated into C++?


Answers

It sounds like an ideal application for a cast - it's up to you to tell the compiler that yes, you DO mean to instantiate a Foo with a random integer.

Of course, technically speaking, Foo_First | Foo_Second isn't a valid value for a Foo.

Question

C++ is mostly a superset of C, but not always. In particular, while enumeration values in both C and C++ implicitly convert into int, the reverse isn't true: only in C do ints convert back into enumeration values. Thus, bitflags defined via enumeration declarations don't work correctly. Hence, this is OK in C, but not in C++:

typedef enum Foo
{
    Foo_First = 1<<0,
    Foo_Second = 1<<1,
} Foo;

int main(void)
{
    Foo x = Foo_First | Foo_Second; // error in C++
    return 0;
}

How should this problem be handled efficiently and correctly, ideally without harming the debugger-friendly nature of using Foo as the variable type (it decomposes into the component bitflags in watches etc.)?

Consider also that there may be hundreds of such flag enumerations, and many thousands of use-points. Ideally some kind of efficient operator overloading would do the trick, but it really ought to be efficient; the application I have in mind is compute-bound and has a reputation of being fast.

Clarification: I'm translating a large (>300K) C program into C++, so I'm looking for an efficient translation in both run-time and developer-time. Simply inserting casts in all the appropriate locations could take weeks.




It potentially does three copies and a function call, barring RVO, registers, and/or inlining optimizations.

Naked bitwise OR operations themselves usually decompose to a single processor instruction.




Type safe(r) bitflags in C++?

You can overload operators for enumeration types that return the proper typed result.

inline FooFlags operator|(FooFlags a, FooFlags b) {
  return static_cast<FooFlags>(+a | +b);
}

It should be noted that to be theoretically safe, you should declare manually the highest possible value so the enumeration type's range is guaranteed to catch all the combinations.

  • Actually that is not needed: An enumeration's range will always be able to catch all combination, because the highest positive value of an enumeration's range is always (2^N)-1 for the first N being able to represent the highest enumerator. That value has all bits 1.



Thought I might add a c++11 version for enum class

FooFlags operator|(FooFlags a, FooFlags b)
{
  typedef std::underlying_type<FooFlags>::type enum_type;
  return static_cast<FooFlags>(static_cast<enum_type>(a) | static_cast<enum_type>(b));
}

If you c++11 version supports it I guess this would be a prime candidate for constexpr





Tags