(Де) сериализация класса 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.
Я чувствую, что люди часто попадают в ловушку языка и не могут освободиться. Если вы используете свою руку, чтобы заполнить машинный код, нет проблем, если вы не ошибаетесь. Я имею в виду, если вам трудно подойти с контрпримером, надеетесь на удобство и можете терпеть возможные ошибки, просто сделайте это. В противном случае, используя наиболее безопасный метод.