(Де) сериализация класса enum

Я пытаюсь сериализовать и десериализовать (используя QDataStream но это не имеет значения здесь) enum class переменная:

enum class Type : char
{
    Trivial,
    Complex
};

Сериализация проста:

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

Но десериализация не

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

Видимо static_cast ссылки на enum class ссылка на его базовый тип не допускается. Кроме того, "очевидное" решение:

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

на самом деле может быть незаконным и не определенным стандартом в соответствии с ответом на этот вопрос, потому что эквивалентное выражение return stream >> (*static_cast<char *>(static_cast<void *>(&type))); там объявлено незаконным (или, скорее, не определено стандартом). Если бы это было так, я должен был бы сделать это:

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

что НЕ симпатично, это 4 строки вместо 1 и т. д. и т. д. И мне кажется, что для такой (на первый взгляд) простой вещи это кажется излишним.

Мой вопрос: reinterpret_cast или эквивалент static_cast с помощью void* действительно незаконно (не определено стандартом) при приведении ссылки на enum class переменная ссылка на его базовый тип?

1 ответ

Решение

Вы можете написать шаблонную функцию, которая позволит вам написать 1 строку для каждого operator>> что вы определяете.

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

И используйте это так:

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

Но это может быть улучшено с помощью std::under_type ( https://en.cppreference.com/w/cpp/types/underlying_type), так как его можно получить во время компиляции.

Если вы берете такой подход, то вы должны также сделать что-то подобное для operator<< сделать обслуживание проще.

Получаю следующее решение:

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); }

Который из github, и проверяется в src.

Я чувствую, что люди часто попадают в ловушку языка и не могут освободиться. Если вы используете свою руку, чтобы заполнить машинный код, нет проблем, если вы не ошибаетесь. Я имею в виду, если вам трудно подойти с контрпримером, надеетесь на удобство и можете терпеть возможные ошибки, просто сделайте это. В противном случае, используя наиболее безопасный метод.

Другие вопросы по тегам