diff --git a/Source/Engine/Core/Types/Nullable.h b/Source/Engine/Core/Types/Nullable.h
index fe6db7e4a..655233b85 100644
--- a/Source/Engine/Core/Types/Nullable.h
+++ b/Source/Engine/Core/Types/Nullable.h
@@ -5,20 +5,20 @@
#include "Engine/Platform/Platform.h"
///
-/// 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.
+/// Wrapper for a value type that can be assigned null, controlling the lifetime of the wrapped value.
///
template
struct Nullable
{
private:
- union // Prevents default construction of T
+ union
{
T _value;
};
bool _hasValue;
///
- /// Ensures that the lifetime of the wrapped value ends correctly. This method is called when the state of the wrapper is no more needed.
+ /// Ends the lifetime of the wrapped value by calling its destructor, if the lifetime has not ended yet. Otherwise, does nothing.
///
FORCE_INLINE void KillOld()
{
@@ -30,7 +30,7 @@ private:
public:
///
- /// Initializes a new instance of the struct with a null value.
+ /// Initializes by setting the wrapped value to null.
///
Nullable()
: _hasValue(false)
@@ -44,9 +44,9 @@ public:
}
///
- /// Initializes a new instance of the struct by copying the value.
+ /// Initializes by copying the wrapped value.
///
- /// The initial wrapped value.
+ /// The initial wrapped value to be copied.
Nullable(const T& value)
: _value(value)
, _hasValue(true)
@@ -54,9 +54,9 @@ public:
}
///
- /// Initializes a new instance of the struct by moving the value.
+ /// Initializes by moving the wrapped value.
///
- /// The initial wrapped value.
+ /// The initial wrapped value to be moved.
Nullable(T&& value) noexcept
: _value(MoveTemp(value))
, _hasValue(true)
@@ -64,7 +64,7 @@ public:
}
///
- /// Initializes a new instance of the struct by copying the value from another instance.
+ /// Initializes by copying another .
///
/// The wrapped value to be copied.
Nullable(const Nullable& other)
@@ -74,7 +74,7 @@ public:
}
///
- /// Initializes a new instance of the struct by moving the value from another instance.
+ /// Initializes by moving another .
///
/// The wrapped value to be moved.
Nullable(Nullable&& other) noexcept
@@ -89,6 +89,9 @@ public:
other.Reset();
}
+ ///
+ /// Reassigns the wrapped value by copying.
+ ///
auto operator=(const T& value) -> Nullable&
{
KillOld();
@@ -99,6 +102,9 @@ public:
return *this;
}
+ ///
+ /// Reassigns the wrapped value by moving.
+ ///
auto operator=(T&& value) noexcept -> Nullable&
{
KillOld();
@@ -109,6 +115,9 @@ public:
return *this;
}
+ ///
+ /// Reassigns the wrapped value by copying another .
+ ///
auto operator=(const Nullable& other) -> Nullable&
{
KillOld();
@@ -122,6 +131,9 @@ public:
return *this;
}
+ ///
+ /// Reassigns the wrapped value by moving another .
+ ///
auto operator=(Nullable&& other) noexcept -> Nullable&
{
if (this == &other)
@@ -143,7 +155,7 @@ public:
}
///
- /// Gets a value indicating whether the current NullableBase{T} object has a valid value of its underlying type.
+ /// Checks if wrapped object has a valid value.
///
/// true if this object has a valid value; otherwise, false.
FORCE_INLINE bool HasValue() const
@@ -152,9 +164,9 @@ public:
}
///
- /// Gets the value of the current NullableBase{T} object if it has been assigned a valid underlying value.
+ /// Gets a const reference to the wrapped value. If the value is not valid, the behavior is undefined.
///
- /// The value.
+ /// Reference to the wrapped value.
FORCE_INLINE const T& GetValue() const
{
ASSERT(_hasValue);
@@ -162,29 +174,36 @@ public:
}
///
- /// 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.
+ /// Gets a reference to the wrapped value. If the value is not valid, the behavior is undefined.
+ /// This method can be used to reassign the wrapped value.
///
- /// In the past, this value returned a copy of the stored value. Be careful.
- /// Reference to the value.
+ /// Reference to the wrapped value.
FORCE_INLINE T& GetValue()
{
ASSERT(_hasValue);
return _value;
}
+ ///
+ /// Gets a const reference to the wrapped value or a default value if the value is not valid.
+ ///
+ /// Reference to the wrapped value or the default value.
FORCE_INLINE const T& GetValueOr(const T& defaultValue) const
{
return _hasValue ? _value : defaultValue;
}
+ ///
+ /// Gets an instance of the wrapped value or a default value based on r-value reference, if the wrapped value is not valid.
+ ///
+ /// Copy of the wrapped value or the default value.
FORCE_INLINE T GetValueOr(T&& defaultValue) const noexcept
{
return _hasValue ? _value : defaultValue;
}
///
- /// Sets the wrapped value.
+ /// Sets the wrapped value by copying.
///
/// The value to be copied.
FORCE_INLINE void SetValue(const T& value)
@@ -199,7 +218,7 @@ public:
}
///
- /// Sets the wrapped value.
+ /// Sets the wrapped value by moving.
///
/// The value to be moved.
FORCE_INLINE void SetValue(T&& value) noexcept
@@ -210,18 +229,10 @@ public:
_hasValue = true; // Set the flag AFTER the value is moved.
}
- FORCE_INLINE bool TrySet(T&& value) noexcept
- {
- if (_hasValue)
- {
- return false;
- }
-
- new (&_value) T(MoveTemp(value)); // Placement new (move constructor)
- _hasValue = true; // Set the flag AFTER the value is moved.
- return true;
- }
-
+ ///
+ /// If the wrapped value is not valid, sets it by copying. Otherwise, does nothing.
+ ///
+ /// True if the wrapped value was changed, otherwise false.
FORCE_INLINE bool TrySet(const T& value)
{
if (_hasValue)
@@ -235,7 +246,23 @@ public:
}
///
- /// Resets the value.
+ /// If the wrapped value is not valid, sets it by moving. Otherwise, does nothing.
+ ///
+ /// True if the wrapped value was changed, otherwise false.
+ FORCE_INLINE bool TrySet(T&& value) noexcept
+ {
+ if (_hasValue)
+ {
+ return false;
+ }
+
+ new (&_value) T(MoveTemp(value)); // Placement new (move constructor)
+ _hasValue = true; // Set the flag AFTER the value is moved.
+ return true;
+ }
+
+ ///
+ /// Disposes the wrapped value and sets the wrapped value to null. If the wrapped value is not valid, does nothing.
///
FORCE_INLINE void Reset()
{
@@ -244,8 +271,9 @@ public:
}
///
- /// Moves the value from the current NullableBase{T} object and resets it.
+ /// Moves the wrapped value to the output parameter and sets the wrapped value to null. If the wrapped value is not valid, the behavior is undefined.
///
+ /// The output parameter that will receive the wrapped value.
FORCE_INLINE void GetAndReset(T& value)
{
ASSERT(_hasValue);
@@ -254,10 +282,10 @@ public:
}
///
- /// Indicates whether the current NullableBase{T} object is equal to a specified object.
+ /// Indicates whether this instance is equal to other one.
///
/// The other object.
- /// True if both values are equal.
+ /// true if both values are equal.
FORCE_INLINE bool operator==(const Nullable& other) const
{
if (other._hasValue != _hasValue)
@@ -269,20 +297,19 @@ public:
}
///
- /// Indicates whether the current NullableBase{T} object is not equal to a specified object.
+ /// Indicates whether this instance is NOT equal to other one.
///
/// The other object.
- /// True if both values are not equal.
+ /// true if both values are not equal.
FORCE_INLINE bool operator!=(const Nullable& other) const
{
return !operator==(other);
}
///
- /// Explicit conversion to boolean value.
+ /// Explicit conversion to boolean value. Allows to check if the wrapped value is valid in if-statements without casting.
///
- /// True if this object has a valid value, otherwise false
- /// Hint: If-statements are able to use explicit cast implicitly (sic).
+ /// true if this object has a valid value, otherwise false
FORCE_INLINE explicit operator bool() const
{
return _hasValue;
@@ -290,69 +317,107 @@ public:
};
///
-/// Nullable value container that contains a boolean value or null.
+/// Specialization of for type.
///
template<>
struct Nullable
{
private:
+ ///
+ /// Underlying value of the nullable boolean. Uses only one byte to optimize memory usage.
+ ///
enum class Value : uint8
{
- False = 0,
- True = 1,
- Null = 2,
+ Null,
+ False,
+ True,
};
Value _value = Value::Null;
public:
+ ///
+ /// Initializes nullable boolean by setting the wrapped value to null.
+ ///
Nullable() = default;
~Nullable() = default;
-
+ ///
+ /// Initializes nullable boolean by moving another nullable boolean.
+ ///
Nullable(Nullable&& value) = default;
+ ///
+ /// Initializes nullable boolean by copying another nullable boolean.
+ ///
Nullable(const Nullable& value) = default;
+ ///
+ /// Initializes nullable boolean by implicitly casting a boolean value.
+ ///
Nullable(const bool value) noexcept
{
_value = value ? Value::True : Value::False;
}
+ ///
+ /// Reassigns the wrapped value by implicitly casting a boolean value.
+ ///
auto operator=(const bool value) noexcept -> Nullable&
{
_value = value ? Value::True : Value::False;
return *this;
}
+ ///
+ /// Reassigns the wrapped value by copying another nullable boolean.
+ ///
auto operator=(const Nullable& value) -> Nullable& = default;
+ ///
+ /// Reassigns the wrapped value by moving another nullable boolean.
+ ///
auto operator=(Nullable&& value) -> Nullable& = default;
+ ///
+ /// Checks if wrapped bool has a valid value.
+ ///
FORCE_INLINE bool HasValue() const noexcept
{
return _value != Value::Null;
}
+ ///
+ /// Gets the wrapped boolean value. If the value is not valid, the behavior is undefined.
+ ///
FORCE_INLINE bool GetValue() const
{
ASSERT(_value != Value::Null);
return _value == Value::True;
}
+ ///
+ /// Gets the wrapped boolean value. If the value is not valid, returns the default value.
+ ///
FORCE_INLINE bool GetValueOr(const bool defaultValue) const noexcept
{
return _value == Value::Null ? defaultValue : _value == Value::True;
}
+ ///
+ /// Sets the wrapped value to a valid boolean.
+ ///
FORCE_INLINE void SetValue(const bool value) noexcept
{
_value = value ? Value::True : Value::False;
}
+ ///
+ /// If the wrapped value is not valid, sets it to a valid boolean.
+ ///
FORCE_INLINE bool TrySet(const bool value) noexcept
{
if (_value != Value::Null)
@@ -364,11 +429,17 @@ public:
return true;
}
+ ///
+ /// Sets the wrapped bool to null.
+ ///
FORCE_INLINE void Reset() noexcept
{
_value = Value::Null;
}
+ ///
+ /// Moves the wrapped value to the output parameter and sets the wrapped value to null. If the wrapped value is not valid, the behavior is undefined.
+ ///
FORCE_INLINE void GetAndReset(bool& value) noexcept
{
ASSERT(_value != Value::Null);
@@ -378,27 +449,26 @@ public:
///
- /// Gets a value indicating whether the current Nullable{T} object has a valid value and it's set to true.
+ /// Checks if the current object has a valid value and it's set to true. If the value is false or not valid, the method returns false.
///
- /// true if this object has a valid value set to true; otherwise, false.
FORCE_INLINE bool IsTrue() const
{
return _value == Value::True;
}
///
- /// Gets a value indicating whether the current Nullable{T} object has a valid value and it's set to false.
+ /// Checks if the current object has a valid value and it's set to false. If the value is true or not valid, the method returns false.
///
- /// true if this object has a valid value set to false; otherwise, false.
FORCE_INLINE bool IsFalse() const
{
return _value == Value::False;
}
///
- /// Getting if provoke unacceptably ambiguous code. For template meta-programming use explicit HasValue() instead.
+ /// Deletes implicit conversion to bool to prevent ambiguous code.
///
+ ///
+ /// Implicit cast from nullable bool to a bool produces unacceptably ambiguous code. For template meta-programming use explicit HasValue instead.
+ ///
explicit operator bool() const = delete;
-
- // Note: Even though IsTrue and IsFalse have been added for convenience, but they may be used for performance reasons.
};