Hard Nullable<T> refactor *
This commit is contained in:
@@ -8,39 +8,132 @@
|
||||
/// Represents a value type that can be assigned null. A nullable type can represent the correct range of values for its underlying value type, plus an additional null value.
|
||||
/// </summary>
|
||||
template<typename T>
|
||||
struct NullableBase
|
||||
struct Nullable
|
||||
{
|
||||
protected:
|
||||
private:
|
||||
union // Prevents default construction of T
|
||||
{
|
||||
T _value;
|
||||
};
|
||||
bool _hasValue;
|
||||
T _value;
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the lifetime of the wrapped value ends correctly. This method is called when the state of the wrapper is no more needed.
|
||||
/// </summary>
|
||||
FORCE_INLINE void KillOld()
|
||||
{
|
||||
if (_hasValue)
|
||||
{
|
||||
_value.~T();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NullableBase{T}"/> struct.
|
||||
/// Initializes a new instance of the <see cref="NullableBase{T}"/> struct with a null value.
|
||||
/// </summary>
|
||||
NullableBase()
|
||||
Nullable()
|
||||
: _hasValue(false)
|
||||
{
|
||||
_hasValue = false;
|
||||
// Value is not initialized.
|
||||
}
|
||||
|
||||
~Nullable()
|
||||
{
|
||||
KillOld();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NullableBase{T}"/> struct.
|
||||
/// Initializes a new instance of the <see cref="NullableBase{T}"/> struct by copying the value.
|
||||
/// </summary>
|
||||
/// <NullableBase name="value">The initial value.</param>
|
||||
NullableBase<T>(const T& value)
|
||||
/// <NullableBase name="value">The initial wrapped value.</param>
|
||||
Nullable(const T& value)
|
||||
: _value(value)
|
||||
, _hasValue(true)
|
||||
{
|
||||
_value = value;
|
||||
_hasValue = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NullableBase{T}"/> struct.
|
||||
/// Initializes a new instance of the <see cref="NullableBase{T}"/> struct by moving the value.
|
||||
/// </summary>
|
||||
/// <param name="other">The other.</param>
|
||||
NullableBase(const NullableBase& other)
|
||||
/// <NullableBase name="value">The initial wrapped value.</param>
|
||||
Nullable(T&& value) noexcept
|
||||
: _value(MoveTemp(value))
|
||||
, _hasValue(true)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NullableBase{T}"/> struct by copying the value from another instance.
|
||||
/// </summary>
|
||||
/// <param name="other">The wrapped value to be copied.</param>
|
||||
Nullable(const Nullable& other)
|
||||
: _value(other._value)
|
||||
, _hasValue(other._hasValue)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NullableBase{T}"/> struct by moving the value from another instance.
|
||||
/// </summary>
|
||||
/// <param name="other">The wrapped value to be moved.</param>
|
||||
Nullable(Nullable&& other) noexcept
|
||||
{
|
||||
_value = other._value;
|
||||
_hasValue = other._hasValue;
|
||||
if (_hasValue)
|
||||
{
|
||||
new (&_value) T(MoveTemp(other._value)); // Placement new (move constructor)
|
||||
}
|
||||
|
||||
other.Reset();
|
||||
}
|
||||
|
||||
auto operator=(const T& value) -> Nullable&
|
||||
{
|
||||
KillOld();
|
||||
|
||||
new (&_value) T(value); // Placement new (copy constructor)
|
||||
_hasValue = true;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto operator=(T&& value) noexcept -> Nullable&
|
||||
{
|
||||
KillOld();
|
||||
|
||||
new (&_value) T(MoveTemp(value)); // Placement new (move constructor)
|
||||
_hasValue = true;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto operator=(const Nullable& other) -> Nullable&
|
||||
{
|
||||
KillOld();
|
||||
|
||||
_hasValue = other._hasValue;
|
||||
if (_hasValue)
|
||||
{
|
||||
new (&_value) T(other._value); // Placement new (copy constructor)
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto operator=(Nullable&& other) noexcept -> Nullable&
|
||||
{
|
||||
KillOld();
|
||||
|
||||
_hasValue = other._hasValue;
|
||||
if (_hasValue)
|
||||
{
|
||||
new (&_value) T(MoveTemp(other._value)); // Placement new (move constructor)
|
||||
|
||||
other.Reset();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -64,30 +157,61 @@ public:
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the current NullableBase{T} object if it has been assigned a valid underlying value.
|
||||
/// Gets a reference to the value of the current NullableBase{T} object.
|
||||
/// If is assumed that the value is valid, otherwise the behavior is undefined.
|
||||
/// </summary>
|
||||
/// <returns>The value.</returns>
|
||||
FORCE_INLINE T GetValue()
|
||||
/// <remarks>In the past, this value returned a copy of the stored value. Be careful.</remarks>
|
||||
/// <returns>Reference to the value.</returns>
|
||||
FORCE_INLINE T& GetValue()
|
||||
{
|
||||
ASSERT(_hasValue);
|
||||
return _value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value.
|
||||
/// Sets the wrapped value.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
void SetValue(const T& value)
|
||||
/// <param name="value">The value to be copied.</param>
|
||||
FORCE_INLINE void SetValue(const T& value)
|
||||
{
|
||||
_value = value;
|
||||
if (_hasValue)
|
||||
{
|
||||
_value.~T();
|
||||
}
|
||||
|
||||
new (&_value) T(value); // Placement new (copy constructor)
|
||||
_hasValue = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the wrapped value.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to be moved.</param>
|
||||
FORCE_INLINE void SetValue(T&& value) noexcept
|
||||
{
|
||||
KillOld();
|
||||
|
||||
new (&_value) T(MoveTemp(value)); // Placement new (move constructor)
|
||||
_hasValue = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the value.
|
||||
/// </summary>
|
||||
void Reset()
|
||||
FORCE_INLINE void Reset()
|
||||
{
|
||||
KillOld();
|
||||
_hasValue = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the value from the current NullableBase{T} object and resets it.
|
||||
/// </summary>
|
||||
FORCE_INLINE void GetAndReset(T& value)
|
||||
{
|
||||
ASSERT(_hasValue);
|
||||
value = MoveTemp(_value);
|
||||
_value.~T(); // Move is not destructive.
|
||||
_hasValue = false;
|
||||
}
|
||||
|
||||
@@ -97,14 +221,14 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="other">The other object.</param>
|
||||
/// <returns>True if both values are equal.</returns>
|
||||
bool operator==(const NullableBase& other) const
|
||||
FORCE_INLINE bool operator==(const Nullable& other) const
|
||||
{
|
||||
if (_hasValue)
|
||||
if (other._hasValue != _hasValue)
|
||||
{
|
||||
return other._hasValue && _value == other._value;
|
||||
return false;
|
||||
}
|
||||
|
||||
return !other._hasValue;
|
||||
return _value == other._value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -112,43 +236,19 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="other">The other object.</param>
|
||||
/// <returns>True if both values are not equal.</returns>
|
||||
FORCE_INLINE bool operator!=(const NullableBase& other) const
|
||||
FORCE_INLINE bool operator!=(const Nullable& other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Represents a value type that can be assigned null. A nullable type can represent the correct range of values for its underlying value type, plus an additional null value.
|
||||
/// </summary>
|
||||
template<typename T>
|
||||
struct Nullable : NullableBase<T>
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Nullable{T}"/> struct.
|
||||
/// </summary>
|
||||
Nullable()
|
||||
: NullableBase<T>()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Nullable{T}"/> struct.
|
||||
/// Explicit conversion to boolean value.
|
||||
/// </summary>
|
||||
/// <NullableBase name="value">The initial value.</param>
|
||||
Nullable<T>(const T& value)
|
||||
: NullableBase<T>(value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Nullable{T}"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="other">The other.</param>
|
||||
Nullable(const Nullable& other)
|
||||
: NullableBase<T>(other)
|
||||
/// <returns>True if this object has a valid value, otherwise false</returns>
|
||||
/// <remarks>Hint: If-statements are able to use explicit cast implicitly (sic).</remarks>
|
||||
FORCE_INLINE explicit operator bool() const
|
||||
{
|
||||
return _hasValue;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -156,43 +256,81 @@ public:
|
||||
/// Nullable value container that contains a boolean value or null.
|
||||
/// </summary>
|
||||
template<>
|
||||
struct Nullable<bool> : NullableBase<bool>
|
||||
struct Nullable<bool>
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Nullable{T}"/> struct.
|
||||
/// </summary>
|
||||
Nullable()
|
||||
: NullableBase<bool>()
|
||||
private:
|
||||
enum class Value : uint8
|
||||
{
|
||||
}
|
||||
False = 0,
|
||||
True = 1,
|
||||
Null = 2,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Nullable{T}"/> struct.
|
||||
/// </summary>
|
||||
/// <NullableBase name="value">The initial value.</param>
|
||||
Nullable(bool value)
|
||||
: NullableBase<bool>(value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Nullable{T}"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="other">The other.</param>
|
||||
Nullable(const Nullable& other)
|
||||
: NullableBase<bool>(other)
|
||||
{
|
||||
}
|
||||
Value _value = Value::Null;
|
||||
|
||||
public:
|
||||
Nullable() = default;
|
||||
|
||||
~Nullable() = default;
|
||||
|
||||
|
||||
Nullable(Nullable&& value) = default;
|
||||
|
||||
Nullable(const Nullable& value) = default;
|
||||
|
||||
Nullable(const bool value) noexcept
|
||||
{
|
||||
_value = value ? Value::True : Value::False;
|
||||
}
|
||||
|
||||
|
||||
auto operator=(const bool value) noexcept -> Nullable&
|
||||
{
|
||||
_value = value ? Value::True : Value::False;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto operator=(const Nullable& value) -> Nullable& = default;
|
||||
|
||||
auto operator=(Nullable&& value) -> Nullable& = default;
|
||||
|
||||
|
||||
FORCE_INLINE bool HasValue() const noexcept
|
||||
{
|
||||
return _value != Value::Null;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool GetValue() const
|
||||
{
|
||||
ASSERT(_value != Value::Null);
|
||||
return _value == Value::True;
|
||||
}
|
||||
|
||||
FORCE_INLINE void SetValue(const bool value) noexcept
|
||||
{
|
||||
_value = value ? Value::True : Value::False;
|
||||
}
|
||||
|
||||
FORCE_INLINE void Reset() noexcept
|
||||
{
|
||||
_value = Value::Null;
|
||||
}
|
||||
|
||||
FORCE_INLINE void GetAndReset(bool& value) noexcept
|
||||
{
|
||||
ASSERT(_value != Value::Null);
|
||||
value = _value == Value::True;
|
||||
_value = Value::Null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the current Nullable{T} object has a valid value and it's set to true.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if this object has a valid value set to true; otherwise, <c>false</c>.</returns>
|
||||
FORCE_INLINE bool IsTrue() const
|
||||
{
|
||||
return _hasValue && _value;
|
||||
return _value == Value::True;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -201,15 +339,13 @@ public:
|
||||
/// <returns><c>true</c> if this object has a valid value set to false; otherwise, <c>false</c>.</returns>
|
||||
FORCE_INLINE bool IsFalse() const
|
||||
{
|
||||
return _hasValue && !_value;
|
||||
return _value == Value::False;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit conversion to boolean value.
|
||||
/// Getting if provoke unacceptably ambiguous code. For template meta-programming use explicit HasValue() instead.
|
||||
/// </summary>
|
||||
/// <returns>True if this object has a valid value set to true, otherwise false</returns>
|
||||
FORCE_INLINE operator bool() const
|
||||
{
|
||||
return _hasValue && _value;
|
||||
}
|
||||
explicit operator bool() const = delete;
|
||||
|
||||
// Note: Even though IsTrue and IsFalse have been added for convenience, but they may be used for performance reasons.
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user