Common/Bitfield: store value as unsigned type
Storing signed type causes the following behaviour: extractValue can do overflow/negative left shift. Now it only relies on two implementation-defined behaviours (which are almost always defined as we want): unsigned->signed conversion and signed right shift
This commit is contained in:
parent
a59920ed35
commit
3d73b8d694
|
@ -111,15 +111,15 @@
|
||||||
template <std::size_t Position, std::size_t Bits, typename T>
|
template <std::size_t Position, std::size_t Bits, typename T>
|
||||||
struct BitField {
|
struct BitField {
|
||||||
private:
|
private:
|
||||||
// StorageType is T for non-enum types and the underlying type of T if
|
// UnderlyingType is T for non-enum types and the underlying type of T if
|
||||||
// T is an enumeration. Note that T is wrapped within an enable_if in the
|
// T is an enumeration. Note that T is wrapped within an enable_if in the
|
||||||
// former case to workaround compile errors which arise when using
|
// former case to workaround compile errors which arise when using
|
||||||
// std::underlying_type<T>::type directly.
|
// std::underlying_type<T>::type directly.
|
||||||
using StorageType = typename std::conditional_t<std::is_enum<T>::value, std::underlying_type<T>,
|
using UnderlyingType = typename std::conditional_t<std::is_enum_v<T>, std::underlying_type<T>,
|
||||||
std::enable_if<true, T>>::type;
|
std::enable_if<true, T>>::type;
|
||||||
|
|
||||||
// Unsigned version of StorageType
|
// We store the value as the unsigned type to avoid undefined behaviour on value shifting
|
||||||
using StorageTypeU = std::make_unsigned_t<StorageType>;
|
using StorageType = std::make_unsigned_t<UnderlyingType>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BitField& operator=(const BitField&) = default;
|
BitField& operator=(const BitField&) = default;
|
||||||
|
@ -127,7 +127,7 @@ public:
|
||||||
/// Constants to allow limited introspection of fields if needed
|
/// Constants to allow limited introspection of fields if needed
|
||||||
static constexpr std::size_t position = Position;
|
static constexpr std::size_t position = Position;
|
||||||
static constexpr std::size_t bits = Bits;
|
static constexpr std::size_t bits = Bits;
|
||||||
static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
|
static constexpr StorageType mask = (((StorageType)~0) >> (8 * sizeof(T) - bits)) << position;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a value by masking and shifting it according to the field parameters. A value
|
* Formats a value by masking and shifting it according to the field parameters. A value
|
||||||
|
@ -144,11 +144,12 @@ public:
|
||||||
* union in a constexpr context.
|
* union in a constexpr context.
|
||||||
*/
|
*/
|
||||||
static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) {
|
static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) {
|
||||||
if (std::numeric_limits<T>::is_signed) {
|
if constexpr (std::numeric_limits<UnderlyingType>::is_signed) {
|
||||||
std::size_t shift = 8 * sizeof(T) - bits;
|
std::size_t shift = 8 * sizeof(T) - bits;
|
||||||
return (T)((storage << (shift - position)) >> shift);
|
return static_cast<T>(static_cast<UnderlyingType>(storage << (shift - position)) >>
|
||||||
|
shift);
|
||||||
} else {
|
} else {
|
||||||
return (T)((storage & mask) >> position);
|
return static_cast<T>((storage & mask) >> position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in New Issue