Make BitField and ResultCode constexpr-initializable
This commit is contained in:
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,40 +117,6 @@ 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;
|
||||||
|
|
||||||
public:
|
|
||||||
// This constructor and assignment operator might be considered ambiguous:
|
|
||||||
// Would they initialize the storage or just the bitfield?
|
|
||||||
// Hence, delete them. Use the Assign method to set bitfield values!
|
|
||||||
BitField(T val) = delete;
|
|
||||||
BitField& operator=(T val) = delete;
|
|
||||||
|
|
||||||
// Force default constructor to be created
|
|
||||||
// so that we can use this within unions
|
|
||||||
BitField() = default;
|
|
||||||
|
|
||||||
FORCE_INLINE operator T() const {
|
|
||||||
return Value();
|
|
||||||
}
|
|
||||||
|
|
||||||
FORCE_INLINE void Assign(const T& value) {
|
|
||||||
storage = (storage & ~GetMask()) | (((StorageType)value << position) & GetMask());
|
|
||||||
}
|
|
||||||
|
|
||||||
FORCE_INLINE T Value() const {
|
|
||||||
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 & GetMask()) >> position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: we may want to change this to explicit operator bool() if it's bug-free in VS2015
|
|
||||||
FORCE_INLINE bool ToBool() const {
|
|
||||||
return Value() != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// StorageType is T for non-enum types and the underlying type of T if
|
// 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
|
// 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
|
||||||
|
@ -161,10 +127,63 @@ private:
|
||||||
// Unsigned version of StorageType
|
// Unsigned version of StorageType
|
||||||
typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
|
typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
|
||||||
|
|
||||||
FORCE_INLINE StorageType GetMask() const {
|
public:
|
||||||
return (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
|
/// 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:
|
||||||
|
// Would they initialize the storage or just the bitfield?
|
||||||
|
// Hence, delete them. Use the Assign method to set bitfield values!
|
||||||
|
BitField(T val) = delete;
|
||||||
|
BitField& operator=(T val) = delete;
|
||||||
|
|
||||||
|
// Force default constructor to be created
|
||||||
|
// so that we can use this within unions
|
||||||
|
constexpr BitField() = default;
|
||||||
|
|
||||||
|
FORCE_INLINE operator T() const {
|
||||||
|
return Value();
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCE_INLINE void Assign(const T& value) {
|
||||||
|
storage = (storage & ~mask) | FormatValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCE_INLINE T Value() const {
|
||||||
|
return ExtractValue(storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: we may want to change this to explicit operator bool() if it's bug-free in VS2015
|
||||||
|
FORCE_INLINE bool ToBool() const {
|
||||||
|
return Value() != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
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_,
|
|
||||||
ErrorLevel level_)
|
|
||||||
: raw(0) {
|
|
||||||
description.Assign(description_);
|
|
||||||
module.Assign(module_);
|
|
||||||
summary.Assign(summary_);
|
|
||||||
level.Assign(level_);
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultCode& operator=(const ResultCode& o) {
|
constexpr ResultCode(ErrorDescription description_, ErrorModule module_, ErrorSummary summary_,
|
||||||
|
ErrorLevel level_)
|
||||||
|
: raw(description.FormatValue(description_) | module.FormatValue(module_) |
|
||||||
|
summary.FormatValue(summary_) | level.FormatValue(level_)) {}
|
||||||
|
|
||||||
|
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…
Reference in New Issue