C++ enum class Tip: Bitwise operator support

August 7, 2017

no comments

One of the nice features of C++ 11 is scoped enums ("enum class"). This solves a few issues with the classic C++ enums:

  1. Scoped enums don’t "leak" into the enclosing scope as classic C++ enums do.
  2. Scoped enums don’t automatically convert to integers, helping with type safety.
  3. Scoped enums can be declared with the size of the underlying integer.

However, there is one feature that I believe was overlooked, or at least deemed unimportant to get into the standard: the automatic support for bitwise operations.

For example, suppose I’m writing a class called Process that wraps a Windows process handle and provides convenient access to a process’ properties and operations. One way to create an instance of the Process class is to provide it with an existing process ID (PID) and the requested access mask. For example:

class Process {
public:
   static std::shared_ptr<Process> FromPid(ProcessAccessMask accessMask, int pid) {
     //...
   }

   Process(HANDLE hProcess) : _hProcess(hProcess) { }
   ~Process() { ::CloseHandle(_hProcess); }

private:
   HANDLE _hProcess;
};

ProcessAccessMask is a type-safe enumeration that holds the possible values for a process access mask. This is far better than just a DWORD as required by the OpenProcess function, since any value can be passed there. Here is a declaration for ProcessAccessMask:

enum class ProcessAccessMask : uint32_t {
   QueryInformation = 0x400,
   QueryLimitedInformation = 0x1000,
   VmRead = 0x10,
   VmWrite = 0x20,
   VmOperation = 0x8,
   Terminate = 0x1,
   Synchronize = 0x100000,
};

(not all possible values for a process’ access mask are listed here)

A common usage is to combine several access mask values with the bitwise or operator to select several values. Sometimes it’s useful to add such values within the enum itself, like so:

enum class ProcessAccessMask : uint32_t {
  QueryInformation = 0x400,
  QueryLimitedInformation = 0x1000,
  VmRead = 0x10,
  VmWrite = 0x20,
  VmOperation = 0x8,
  Terminate = 0x1,
  Synchronize = 0x100000,
  VmAll = VmRead | VmWrite | VmOperation
};

Notice the VmAll value above. With these definitions, the code fails to compile, complaining that there is no operator | that takes arguments of type ProcessAccessMask – technically correct, because scoped enums don’t convert to integers automatically (which was mentioned earlier as a strength!).

.NET solves this elegantly with the [Flags] attribute that can be placed over a declared enum (techically in .NET these combinations are allowed even without that attribute; the attribute adds better support to Enum.Parse and Enum.ToString). I would have expected C++ to provide a custom attribute (now that such a thing is part of the standard) so that the above declarations would work. Alas, there is no such attribute in C++.

The (annoying) solution is to generate the bitwise operators manually (not just |, but others that are useful, such as &, ^, ~ and |=, &=, etc.). Clearly, quite a lot of work for just one enum!

Luckily, the Windows SDK header <winnt.h> already has the solution inside it with a macro. Just add the following after the enum declaration:

DEFINE_ENUM_FLAG_OPERATORS(ProcessAccessMask);

If you navigate to the macro, you’ll see all the operators implemented with static_cast<> back and forth to satisfy the compiler. With this, we can complete our implementation of the Process::FromPid factory method:

static std::shared_ptr<Process> FromPid(ProcessAccessMask accessMask, int pid) {
   auto hProces = ::OpenProcess(static_cast<DWORD>(accessMask), FALSE, pid);
   if (!hProces)
      return nullptr;
   return std::make_shared<Process>(hProces);
}

Now, client code can use the enum with bitwise operators as needed:

auto process = Process::FromPid(
   ProcessAccessMask::QueryInformation | ProcessAccessMask::Terminate | ProcessAccessMask::Synchronize, 
   1234);

This creates a Process object based on PID 1234 (which, by the way, can never exist in reality!).

I do hope this functionality makes its way to a future C++ standard.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*