C++11 introduced two different additions to how we can handle enums: an option to make them scoped, and an option to make them typed. So now we have four different enum subtypes:
enum Old {};
enum Typed : int8_t {};
enum class Scoped {};
enum class TypedScoped : int8_t {};
This question asks how to determine whether the enumeration is scoped. I want to know how to determine whether the enumeration is typed.
Additional information
I use the Qt framework, which provides the QDataStream
class for serializing/deserializing data in a portable cross-platform way.
Obviously, in order for the resulting data stream to be portable, you must store all integers in fixed-length form. That includes enums, too.
Back in the day, I made a couple of helper macros to define serialization/deserialization of enums by casting them to an integer with fixed (user-specified) length:
#define SC_DECLARE_DATASTREAM_WRITE_OPERATOR(_TYPE) \
QDataStream &operator<<(QDataStream &stream, _TYPE v);
#define SC_DECLARE_DATASTREAM_READ_OPERATOR(_TYPE) \
QDataStream &operator>>(QDataStream &stream, _TYPE &v);
#define SC_DECLARE_DATASTREAM_OPERATORS(_TYPE) \
SC_DECLARE_DATASTREAM_WRITE_OPERATOR(_TYPE) \
SC_DECLARE_DATASTREAM_READ_OPERATOR(_TYPE)
#define SC_DEFINE_DATASTREAM_ENUM_WRITE_OPERATOR(_TYPE, _LEN) \
QDataStream &operator<<(QDataStream &stream, _TYPE v) \
{ \
qint ## _LEN t = v; \
static_assert(sizeof(t) >= sizeof(v), "Increase length"); \
stream << t; \
return stream; \
}
#define SC_DEFINE_DATASTREAM_ENUM_READ_OPERATOR(_TYPE, _LEN) \
QDataStream &operator>>(QDataStream &stream, _TYPE &v) \
{ \
qint ## _LEN t {0}; \
static_assert(sizeof(t) >= sizeof(v), "Increase length"); \
stream >> t; \
if(stream.status() == QDataStream::Ok) \
v = static_cast<_TYPE>(t); \
return stream; \
}
#define SC_DEFINE_DATASTREAM_ENUM_OPERATORS(_TYPE, _LEN) \
SC_DEFINE_DATASTREAM_ENUM_WRITE_OPERATOR(_TYPE, _LEN) \
SC_DEFINE_DATASTREAM_ENUM_READ_OPERATOR(_TYPE, _LEN)
Now that C++11 allows specifying the underlying enum type, I can simplify the above mentioned macros:
#define SC_DEFINE_DATASTREAM_TYPED_ENUM_WRITE_OPERATOR(_TYPE) \
QDataStream &operator<<(QDataStream &stream, _TYPE v) \
{ \
const std::underlying_type<_TYPE>::type t {static_cast<std::underlying_type<_TYPE>::type>(v)}; \
stream << t; \
return stream; \
}
#define SC_DEFINE_DATASTREAM_TYPED_ENUM_READ_OPERATOR(_TYPE) \
QDataStream &operator>>(QDataStream &stream, _TYPE &v) \
{ \
std::underlying_type<_TYPE>::type t {0}; \
stream >> t; \
if(stream.status() == QDataStream::Ok) \
v = static_cast<_TYPE>(t); \
return stream; \
}
However, if the user accidentally uses the new (*_TYPED_*
) macros for enums that do not have their underlying type specified, that will break the guarantee of portability, because compiling the same code on different platform may yield different underlying type and hence different integer length in serialization/deserialization code.
What I need is to add a static_assert
to the code, which will break the compilation process if the enum was not strongly typed at the point of its declaration.