2

I am trying to serialize and deserialize (using QDataStream but that is irrelevant here) an enum class variable:

enum class Type : char
{
    Trivial,
    Complex
};

The serialization is easy:

QDataStream &operator<<(QDataStream &stream, Type type)
{
    return stream << static_cast<char>(type);
}

But the deserialization is not:

QDataStream &operator>>(QDataStream &stream, Type &type)
{    
    return stream >> static_cast<char &>(type);
}

Apparently the static_cast of reference to enum class to a reference to its underlying type is not allowed. Furthermore, the "obvious" solution:

QDataStream &operator>>(QDataStream &stream, Type &type)
{    
    return stream >> reinterpret_cast<char &>(type);
}

might actually be illegal and not defined by the standard according to the answer to this question because the equivalent expression return stream >> (*static_cast<char *>(static_cast<void *>(&type))); is declared as illegal there (or rather not defined by the standard). If that was the case I would need to do this:

QDataStream &operator>>(QDataStream &stream, Type &type)
{    
    char c = 0;
    stream >> c;
    type = static_cast<Type>(c);
    return stream;
}

which is NOT pretty, is 4 lines instead of 1 etc. etc. And seems pretty unnecesasry to me for such a (seemingly) simple thing.

My question: Is the reinterpret_cast or equivalent static_cast via void* really illegal (undefined by standard) when casting a reference to enum class variable to a reference of its underlying type?

Mohammad Kanan
  • 4,452
  • 10
  • 23
  • 47
Resurrection
  • 3,916
  • 2
  • 34
  • 56
  • 2
    In what way is what you are asking different from the question you linked? (I'm probably missing something...) Asking "Is the answer to the linked question really correct?" will most likely get you flagged as duplicate. –  Jan 05 '19 at 19:25
  • 1
    Generally I would avoid clever tricks like the plague and consider reinterpret_cast to be a code smell at best. What is wrong with the 4 line version? It is easy to understand and the compiler will optimize it away anyway... –  Jan 05 '19 at 19:32
  • @jakub_d The linked question (and its answer) is concerned with manipulating the value (incrementing) after the cast. My question is purely about the cast. Even though the answer to the linked question seems to answer that question as well it was a bit too lawerly for me hence this question. I also try to avoid `reinterpret_cast` but for example you cannot avoid it with SIMD and other stuff. – Resurrection Jan 05 '19 at 19:37
  • I think invoking operator >> on the cast value could also be called manipulation. The lawyery stuff applies to your case as well. So it is not guaranted to work as far as the standard is concerned. –  Jan 05 '19 at 19:52

2 Answers2

3

You could write a template function that will allow you to write 1 line for each operator>> that you define.

template <class UT, class S, class E> S& DeserializeEnumClassValue(S &s, E &e)
{
    UT temp;
    s >> temp;
    e = static_cast<E>(temp);
    return s;
}

And use it like that:

QDataStream &operator>>(QDataStream &stream, Type &type)
{    
    return DeserializeEnumClassValue<char>(stream, value);
}

But this can be improved using std::underlying_type (https://en.cppreference.com/w/cpp/types/underlying_type) as it is possible to get it at compile time.

If you take that approach, then you should also do something similar for operator<< to make maintenance easier.

Mohammad Kanan
  • 4,452
  • 10
  • 23
  • 47
Phil1970
  • 2,605
  • 2
  • 14
  • 15
1

I get the following solution:

template <typename T>
typename std::enable_if<std::is_enum<T>::value, QDataStream &>::type&
operator<<(QDataStream &s, const T &t)
{ return s << static_cast<typename std::underlying_type<T>::type>(t); }

template <typename T>
typename std::enable_if<std::is_enum<T>::value, QDataStream &>::type&
operator>>(QDataStream &s, T &t)
{ return s >> reinterpret_cast<typename std::underlying_type<T>::type &>(t); }

Which from github, and get verified in the src.

I feel that people often fall into the trap of language and cannot extricate themselves.If you use your hand to fill in the machine code, there is no problem, if you are not wrong.I mean if you have a hard time to come up with a counter-example,hope for some convenience and can tolerate possible mistakes,just do it.Otherwise, using the most secure method.

Crawl.W
  • 403
  • 5
  • 17