samedi 19 décembre 2020

Convert enum class to lvalue reference to its underlying type in C++

I have the following code in C++11 (drastically simplified to leave out the inessentials):

#include <cstdint>
#include <type_traits>

enum class State : std::uint8_t
{
  Shutdown,
  Loading,
  Active,
  Idle,
  Off
};

int main()
{
  State state = State::Shutdown;
  getBytes(state);
}

getBytes() is a library function which is used to deserialize data from stream:

void getBytes(std::uint8_t& buf) {
    // in real codebase it would read from stream and modify buf
    // here for simplicity I just modify the argument passed by reference
    buf = 1;  
}

The problem I face is that the scoped enum is not implicitly convertible to the uint8_t so I get a compiler error saying it. When I add the usual static_cast<std::underlying_type<State>::type>() I still get an error saying:

error: no matching function for call to 'getBytes'
  getBytes(static_cast<uint8_t>(state));
  ^~~~~~~~
note: candidate function not viable: expects an l-value for 1st argument
  void getBytes(std::uint8_t& buf) {
       ^

According to the answer here Is it safe to reinterpret_cast an enum class variable to a reference of the underlying type? it's not recommended to use reinterpret_cast in this situation.

I found a solution in the form of creating a temporary variable of underlying type, then passing it into the function and after that modifiying the enum value like this:

int main()
{
  State state = State::Shutdown;
  using UnderlyingType = std::underlying_type<State>::type;
  UnderlyingType temp = static_cast<UnderlyingType>(state);
  getBytes(temp);
  state = static_cast<State>(temp);
}

However, since there are so many places in the codebase with this same problem I do not see it as a great solution. So my question is whether there is a way to call the function getBytes without creating this temporary?

As a side note, since getBytes is a library function I cannot modify it.

Aucun commentaire:

Enregistrer un commentaire