1
votes

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.

1
Im fairly certain that changing it to an enum instead of enum class would solve this. Yes, enum class is better, but if it's a trade-off between using an oldschool enum and copy-pasting the same 5 lines, I think just using an enum isn't a crime either.lionkor
You could move your boilerplate code into an overloaded function template (while using SFINAE with std::is_enum to ensure that it's restricted to enums): Live Demo on coliruScheff's Cat
The enum class is only advised due to type safety. But if you're working with legacy library functions and so, you should just use what works. You could build a wrapper function (class) that internally uses an enum or uint8_t and then casts it to your enum class type. But in every case, try to use reinterpret cast as little as possible, if at all.JHBonarius

1 Answers

0
votes

The issue is not the enum so much as it's the what happens with static_cast. getBytes can't insert a value into a static_cast value.

However, you could create two helper functions that do this for you

template <typename T>
constexpr auto to_integral(T e){
    return static_cast<std::underlying_type_t<T>>(e);
}

State wrapped_getBytes(State in){
    auto i = to_integral(in);
    getBytes(i);
    return State(i);
}