Search code examples
c++qt5enum-classqdatastream

(De)serializing an enum class


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?


Solution

  • 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.