2

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.

mariia_kornieva
  • 143
  • 1
  • 4
  • 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 Dec 19 '20 at 12:14
  • 3
    You could move your boilerplate code into an overloaded function template (while using SFINAE with [std::is_enum](https://en.cppreference.com/w/cpp/types/is_enum) to ensure that it's restricted to `enum`s): [**Live Demo on coliru**](http://coliru.stacked-crooked.com/a/09b3c929da36a529) – Scheff's Cat Dec 19 '20 at 12:31
  • 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 Dec 19 '20 at 12:47

1 Answers1

0

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);
}
Alex Shirley
  • 385
  • 4
  • 11
  • 1
    Using `enable_if` or similar tricks, one can generalize the code so that it works for all `enum` or for those having `uint8_t` underlying type. If `getBytes` is already overloadded for many types, then it would make sense to have an overloaded `getBytes` that works with enums.. – Phil1970 Dec 19 '20 at 13:46