Make BitField and ResultCode constexpr-initializable

pull/8/head
Yuri Kunde Schlesner 9 years ago
parent cc566dadd8
commit a75145a2c6

@ -108,7 +108,7 @@
* symptoms. * symptoms.
*/ */
#pragma pack(1) #pragma pack(1)
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:
// We hide the copy assigment operator here, because the default copy // We hide the copy assigment operator here, because the default copy
@ -117,7 +117,45 @@ private:
// We don't delete it because we want BitField to be trivially copyable. // We don't delete it because we want BitField to be trivially copyable.
BitField& operator=(const BitField&) = default; BitField& operator=(const BitField&) = default;
// StorageType 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
// former case to workaround compile errors which arise when using
// std::underlying_type<T>::type directly.
typedef typename std::conditional<std::is_enum<T>::value, std::underlying_type<T>,
std::enable_if<true, T>>::type::type StorageType;
// Unsigned version of StorageType
typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
public: public:
/// Constants to allow limited introspection of fields if needed
static constexpr size_t position = Position;
static constexpr size_t bits = Bits;
static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
/**
* Formats a value by masking and shifting it according to the field parameters. A value
* containing several bitfields can be assembled by formatting each of their values and ORing
* the results together.
*/
static constexpr FORCE_INLINE StorageType FormatValue(const T& value) {
return ((StorageType)value << position) & mask;
}
/**
* Extracts a value from the passed storage. In most situations prefer use the member functions
* (such as Value() or operator T), but this can be used to extract a value from a bitfield
* union in a constexpr context.
*/
static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) {
if (std::numeric_limits<T>::is_signed) {
std::size_t shift = 8 * sizeof(T) - bits;
return (T)((storage << (shift - position)) >> shift);
} else {
return (T)((storage & mask) >> position);
}
}
// This constructor and assignment operator might be considered ambiguous: // This constructor and assignment operator might be considered ambiguous:
// Would they initialize the storage or just the bitfield? // Would they initialize the storage or just the bitfield?
// Hence, delete them. Use the Assign method to set bitfield values! // Hence, delete them. Use the Assign method to set bitfield values!
@ -126,23 +164,18 @@ public:
// Force default constructor to be created // Force default constructor to be created
// so that we can use this within unions // so that we can use this within unions
BitField() = default; constexpr BitField() = default;
FORCE_INLINE operator T() const { FORCE_INLINE operator T() const {
return Value(); return Value();
} }
FORCE_INLINE void Assign(const T& value) { FORCE_INLINE void Assign(const T& value) {
storage = (storage & ~GetMask()) | (((StorageType)value << position) & GetMask()); storage = (storage & ~mask) | FormatValue(value);
} }
FORCE_INLINE T Value() const { FORCE_INLINE T Value() const {
if (std::numeric_limits<T>::is_signed) { return ExtractValue(storage);
std::size_t shift = 8 * sizeof(T) - bits;
return (T)((storage << (shift - position)) >> shift);
} else {
return (T)((storage & GetMask()) >> position);
}
} }
// TODO: we may want to change this to explicit operator bool() if it's bug-free in VS2015 // TODO: we may want to change this to explicit operator bool() if it's bug-free in VS2015
@ -151,20 +184,6 @@ public:
} }
private: private:
// StorageType 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
// former case to workaround compile errors which arise when using
// std::underlying_type<T>::type directly.
typedef typename std::conditional<std::is_enum<T>::value, std::underlying_type<T>,
std::enable_if<true, T>>::type::type StorageType;
// Unsigned version of StorageType
typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
FORCE_INLINE StorageType GetMask() const {
return (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
}
StorageType storage; StorageType storage;
static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range"); static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range");

@ -228,45 +228,42 @@ union ResultCode {
// error // error
BitField<31, 1, u32> is_error; BitField<31, 1, u32> is_error;
explicit ResultCode(u32 raw) : raw(raw) {} constexpr explicit ResultCode(u32 raw) : raw(raw) {}
ResultCode(ErrorDescription description_, ErrorModule module_, ErrorSummary summary_,
constexpr ResultCode(ErrorDescription description_, ErrorModule module_, ErrorSummary summary_,
ErrorLevel level_) ErrorLevel level_)
: raw(0) { : raw(description.FormatValue(description_) | module.FormatValue(module_) |
description.Assign(description_); summary.FormatValue(summary_) | level.FormatValue(level_)) {}
module.Assign(module_);
summary.Assign(summary_);
level.Assign(level_);
}
ResultCode& operator=(const ResultCode& o) { constexpr ResultCode& operator=(const ResultCode& o) {
raw = o.raw; raw = o.raw;
return *this; return *this;
} }
bool IsSuccess() const { constexpr bool IsSuccess() const {
return is_error == 0; return is_error.ExtractValue(raw) == 0;
} }
bool IsError() const { constexpr bool IsError() const {
return is_error == 1; return is_error.ExtractValue(raw) == 1;
} }
}; };
inline bool operator==(const ResultCode& a, const ResultCode& b) { constexpr bool operator==(const ResultCode& a, const ResultCode& b) {
return a.raw == b.raw; return a.raw == b.raw;
} }
inline bool operator!=(const ResultCode& a, const ResultCode& b) { constexpr bool operator!=(const ResultCode& a, const ResultCode& b) {
return a.raw != b.raw; return a.raw != b.raw;
} }
// Convenience functions for creating some common kinds of errors: // Convenience functions for creating some common kinds of errors:
/// The default success `ResultCode`. /// The default success `ResultCode`.
const ResultCode RESULT_SUCCESS(0); constexpr ResultCode RESULT_SUCCESS(0);
/// Might be returned instead of a dummy success for unimplemented APIs. /// Might be returned instead of a dummy success for unimplemented APIs.
inline ResultCode UnimplementedFunction(ErrorModule module) { constexpr ResultCode UnimplementedFunction(ErrorModule module) {
return ResultCode(ErrorDescription::NotImplemented, module, ErrorSummary::NotSupported, return ResultCode(ErrorDescription::NotImplemented, module, ErrorSummary::NotSupported,
ErrorLevel::Permanent); ErrorLevel::Permanent);
} }

Loading…
Cancel
Save