Merge branch 'mtszkarbowiak-fix/nullable' into 1.10
This commit is contained in:
@@ -403,7 +403,7 @@ int32 Editor::LoadProduct()
|
||||
}
|
||||
|
||||
// Create new project option
|
||||
if (CommandLine::Options.NewProject)
|
||||
if (CommandLine::Options.NewProject.IsTrue())
|
||||
{
|
||||
Array<String> projectFiles;
|
||||
FileSystem::DirectoryGetFiles(projectFiles, projectPath, TEXT("*.flaxproj"), DirectorySearchOption::TopDirectoryOnly);
|
||||
@@ -428,7 +428,7 @@ int32 Editor::LoadProduct()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (CommandLine::Options.NewProject)
|
||||
if (CommandLine::Options.NewProject.IsTrue())
|
||||
{
|
||||
if (projectPath.IsEmpty())
|
||||
projectPath = Platform::GetWorkingDirectory();
|
||||
@@ -529,7 +529,7 @@ int32 Editor::LoadProduct()
|
||||
if (projectPath.IsEmpty())
|
||||
{
|
||||
#if PLATFORM_HAS_HEADLESS_MODE
|
||||
if (CommandLine::Options.Headless)
|
||||
if (CommandLine::Options.Headless.IsTrue())
|
||||
{
|
||||
Platform::Fatal(TEXT("Missing project path."));
|
||||
return -1;
|
||||
@@ -657,7 +657,7 @@ Window* Editor::CreateMainWindow()
|
||||
bool Editor::Init()
|
||||
{
|
||||
// Scripts project files generation from command line
|
||||
if (CommandLine::Options.GenProjectFiles)
|
||||
if (CommandLine::Options.GenProjectFiles.IsTrue())
|
||||
{
|
||||
const String customArgs = TEXT("-verbose -log -logfile=\"Cache/Intermediate/ProjectFileLog.txt\"");
|
||||
const bool failed = ScriptsBuilder::GenerateProject(customArgs);
|
||||
|
||||
@@ -147,7 +147,7 @@ SplashScreen::~SplashScreen()
|
||||
void SplashScreen::Show()
|
||||
{
|
||||
// Skip if already shown or in headless mode
|
||||
if (IsVisible() || CommandLine::Options.Headless)
|
||||
if (IsVisible() || CommandLine::Options.Headless.IsTrue())
|
||||
return;
|
||||
|
||||
LOG(Info, "Showing splash screen");
|
||||
|
||||
@@ -119,7 +119,7 @@ void Log::Logger::Write(const StringView& msg)
|
||||
IsDuringLog = true;
|
||||
|
||||
// Send message to standard process output
|
||||
if (CommandLine::Options.Std)
|
||||
if (CommandLine::Options.Std.IsTrue())
|
||||
{
|
||||
#if PLATFORM_TEXT_IS_CHAR16
|
||||
StringAnsi ansi(msg);
|
||||
|
||||
@@ -2,50 +2,161 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Templates.h"
|
||||
#include "Engine/Platform/Platform.h"
|
||||
|
||||
/// <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.
|
||||
/// Wrapper for a value type that can be assigned null, controlling the lifetime of the wrapped value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// The type of the wrapped value. It must be move-constructible but does not have to be copy-constructible. Value is never reassigned.
|
||||
/// </typeparam>
|
||||
template<typename T>
|
||||
struct NullableBase
|
||||
struct Nullable
|
||||
{
|
||||
protected:
|
||||
private:
|
||||
struct Dummy { Dummy() {} };
|
||||
|
||||
union
|
||||
{
|
||||
T _value;
|
||||
Dummy _dummy;
|
||||
};
|
||||
bool _hasValue;
|
||||
T _value;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NullableBase{T}"/> struct.
|
||||
/// Initializes <see cref="Nullable{T}"/> by setting the wrapped value to null.
|
||||
/// </summary>
|
||||
NullableBase()
|
||||
Nullable()
|
||||
: _dummy()
|
||||
, _hasValue(false)
|
||||
{
|
||||
_hasValue = false;
|
||||
}
|
||||
|
||||
~Nullable()
|
||||
{
|
||||
if (_hasValue)
|
||||
_value.~T();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NullableBase{T}"/> struct.
|
||||
/// Initializes <see cref="Nullable{T}"/> by copying the wrapped value.
|
||||
/// </summary>
|
||||
/// <NullableBase name="value">The initial value.</param>
|
||||
NullableBase<T>(const T& value)
|
||||
/// <NullableBase name="value">The initial wrapped value to be copied.</param>
|
||||
template<typename U = T, typename = typename TEnableIf<TIsCopyConstructible<U>::Value>::Type>
|
||||
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 <see cref="Nullable{T}"/> by moving the wrapped value.
|
||||
/// </summary>
|
||||
/// <param name="other">The other.</param>
|
||||
NullableBase(const NullableBase& other)
|
||||
/// <NullableBase name="value">The initial wrapped value to be moved.</param>
|
||||
Nullable(T&& value) noexcept
|
||||
: _value(MoveTemp(value))
|
||||
, _hasValue(true)
|
||||
{
|
||||
_value = other._value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes <see cref="Nullable{T}"/> by copying another <see cref="Nullable{T}"/>.
|
||||
/// </summary>
|
||||
/// <param name="other">The wrapped value to be copied.</param>
|
||||
template<typename U = T, typename = typename TEnableIf<TIsCopyConstructible<U>::Value>::Type>
|
||||
Nullable(const Nullable& other)
|
||||
: _value(other._value)
|
||||
, _hasValue(other._hasValue)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes <see cref="Nullable{T}"/> by moving another <see cref="Nullable{T}"/>.
|
||||
/// </summary>
|
||||
/// <param name="other">The wrapped value to be moved.</param>
|
||||
Nullable(Nullable&& other) noexcept
|
||||
{
|
||||
if (other._hasValue)
|
||||
new (&_value) T(MoveTemp(other._value)); // Placement new (move constructor)
|
||||
_hasValue = other._hasValue;
|
||||
|
||||
other.Reset();
|
||||
}
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the current NullableBase{T} object has a valid value of its underlying type.
|
||||
/// Reassigns the wrapped value by copying.
|
||||
/// </summary>
|
||||
template<typename U = T, typename = typename TEnableIf<TIsCopyConstructible<U>::Value>::Type>
|
||||
Nullable& operator=(const T& value)
|
||||
{
|
||||
if (_hasValue)
|
||||
_value.~T();
|
||||
|
||||
new (&_value) T(value); // Placement new (copy constructor)
|
||||
_hasValue = true;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reassigns the wrapped value by moving.
|
||||
/// </summary>
|
||||
Nullable& operator=(T&& value) noexcept
|
||||
{
|
||||
if (_hasValue)
|
||||
_value.~T();
|
||||
|
||||
new (&_value) T(MoveTemp(value)); // Placement new (move constructor)
|
||||
_hasValue = true;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reassigns the wrapped value by copying another <see cref="Nullable{T}"/>.
|
||||
/// </summary>
|
||||
template<typename U = T, typename = typename TEnableIf<TIsCopyConstructible<U>::Value>::Type>
|
||||
Nullable& operator=(const Nullable& other)
|
||||
{
|
||||
if (_hasValue)
|
||||
_value.~T();
|
||||
|
||||
if (other._hasValue)
|
||||
new (&_value) T(other._value); // Placement new (copy constructor)
|
||||
|
||||
_hasValue = other._hasValue; // Set the flag AFTER the value is copied.
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reassigns the wrapped value by moving another <see cref="Nullable{T}"/>.
|
||||
/// </summary>
|
||||
Nullable& operator=(Nullable&& other) noexcept
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
if (_hasValue)
|
||||
_value.~T();
|
||||
|
||||
if (other._hasValue)
|
||||
{
|
||||
new (&_value) T(MoveTemp(other._value)); // Placement new (move constructor)
|
||||
|
||||
other._value.~T(); // Kill the old value in the source object.
|
||||
other._hasValue = false;
|
||||
}
|
||||
|
||||
_hasValue = other._hasValue; // Set the flag AFTER the value is moved.
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if wrapped object has a valid value.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if this object has a valid value; otherwise, <c>false</c>.</returns>
|
||||
FORCE_INLINE bool HasValue() const
|
||||
@@ -54,9 +165,9 @@ public:
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <returns>The value.</returns>
|
||||
/// <returns>Reference to the wrapped value.</returns>
|
||||
FORCE_INLINE const T& GetValue() const
|
||||
{
|
||||
ASSERT(_hasValue);
|
||||
@@ -64,152 +175,302 @@ 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 wrapped value. If the value is not valid, the behavior is undefined.
|
||||
/// This method can be used to reassign the wrapped value.
|
||||
/// </summary>
|
||||
/// <returns>The value.</returns>
|
||||
FORCE_INLINE T GetValue()
|
||||
/// <returns>Reference to the wrapped value.</returns>
|
||||
FORCE_INLINE T& GetValue()
|
||||
{
|
||||
ASSERT(_hasValue);
|
||||
return _value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value.
|
||||
/// Gets a const reference to the wrapped value or a default value if the value is not valid.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
void SetValue(const T& value)
|
||||
/// <returns>Reference to the wrapped value or the default value.</returns>
|
||||
FORCE_INLINE const T& GetValueOr(const T& defaultValue) const
|
||||
{
|
||||
_value = value;
|
||||
_hasValue = true;
|
||||
return _hasValue ? _value : defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the value.
|
||||
/// Gets a mutable reference to the wrapped value or a default value if the value is not valid.
|
||||
/// </summary>
|
||||
void Reset()
|
||||
/// <returns>Reference to the wrapped value or the default value.</returns>
|
||||
FORCE_INLINE T& GetValueOr(T& defaultValue) const
|
||||
{
|
||||
_hasValue = false;
|
||||
return _hasValue ? _value : defaultValue;
|
||||
}
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Indicates whether the current NullableBase{T} object is equal to a specified object.
|
||||
/// Sets the wrapped value by copying.
|
||||
/// </summary>
|
||||
/// <param name="other">The other object.</param>
|
||||
/// <returns>True if both values are equal.</returns>
|
||||
bool operator==(const NullableBase& other) const
|
||||
/// <param name="value">The value to be copied.</param>
|
||||
template<typename U = T, typename = typename TEnableIf<TIsCopyConstructible<U>::Value>::Type>
|
||||
FORCE_INLINE void SetValue(const T& value)
|
||||
{
|
||||
if (_hasValue)
|
||||
{
|
||||
return other._hasValue && _value == other._value;
|
||||
}
|
||||
|
||||
return !other._hasValue;
|
||||
_value.~T();
|
||||
new (&_value) T(value); // Placement new (copy constructor)
|
||||
_hasValue = true; // Set the flag AFTER the value is copied.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the current NullableBase{T} object is not equal to a specified object.
|
||||
/// Sets the wrapped value by moving.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to be moved.</param>
|
||||
FORCE_INLINE void SetValue(T&& value) noexcept
|
||||
{
|
||||
if (_hasValue)
|
||||
_value.~T();
|
||||
new (&_value) T(MoveTemp(value)); // Placement new (move constructor)
|
||||
_hasValue = true; // Set the flag AFTER the value is moved.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the wrapped value is not valid, sets it by copying. Otherwise, does nothing.
|
||||
/// </summary>
|
||||
/// <returns>True if the wrapped value was changed, otherwise false.</returns>
|
||||
template<typename U = T, typename = typename TEnableIf<TIsCopyConstructible<U>::Value>::Type>
|
||||
FORCE_INLINE bool TrySet(const T& value)
|
||||
{
|
||||
if (_hasValue)
|
||||
return false;
|
||||
new (&_value) T(value); // Placement new (copy constructor)
|
||||
_hasValue = true; // Set the flag AFTER the value is copied.
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the wrapped value is not valid, sets it by moving. Otherwise, does nothing.
|
||||
/// </summary>
|
||||
/// <returns>True if the wrapped value was changed, otherwise false.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the wrapped value and sets the wrapped value to null. If the wrapped value is not valid, does nothing.
|
||||
/// </summary>
|
||||
FORCE_INLINE void Reset()
|
||||
{
|
||||
if (!_hasValue)
|
||||
return;
|
||||
_hasValue = false; // Reset the flag BEFORE the value is (potentially) destructed.
|
||||
_value.~T();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="value">The output parameter that will receive the wrapped value.</param>
|
||||
FORCE_INLINE void GetAndReset(T& value)
|
||||
{
|
||||
ASSERT(_hasValue);
|
||||
value = MoveTemp(_value);
|
||||
Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this instance is equal to other one.
|
||||
/// </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
|
||||
/// <returns><c>true</c> if both values are equal.</returns>
|
||||
FORCE_INLINE bool operator==(const Nullable& other) const
|
||||
{
|
||||
return other._hasValue == _hasValue && _value == other._value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this instance is NOT equal to other one.
|
||||
/// </summary>
|
||||
/// <param name="other">The other object.</param>
|
||||
/// <returns><c>true</c> if both values are not equal.</returns>
|
||||
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.
|
||||
/// Explicit conversion to boolean value. Allows to check if the wrapped value is valid in if-statements without casting.
|
||||
/// </summary>
|
||||
Nullable()
|
||||
: NullableBase<T>()
|
||||
/// <returns><c>true</c> if this object has a valid value, otherwise false</returns>
|
||||
FORCE_INLINE explicit operator bool() const
|
||||
{
|
||||
return _hasValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Nullable{T}"/> struct.
|
||||
/// Matches the wrapped value with a handler for the value or a handler for the null 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)
|
||||
/// <param name="valueHandler">Value visitor handling valid nullable value.</param>
|
||||
/// <param name="nullHandler">Null visitor handling invalid nullable value.</param>
|
||||
/// <returns>Result of the call of one of handlers. Handlers must share the same result type.</returns>
|
||||
template<typename ValueVisitor, typename NullVisitor>
|
||||
FORCE_INLINE auto Match(ValueVisitor valueHandler, NullVisitor nullHandler) const
|
||||
{
|
||||
if (_hasValue)
|
||||
return valueHandler(_value);
|
||||
return nullHandler();
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Nullable value container that contains a boolean value or null.
|
||||
/// Specialization of <see cref="Nullable{T}"/> for <see cref="bool"/> type.
|
||||
/// </summary>
|
||||
template<>
|
||||
struct Nullable<bool> : NullableBase<bool>
|
||||
struct Nullable<bool>
|
||||
{
|
||||
public:
|
||||
private:
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Nullable{T}"/> struct.
|
||||
/// Underlying value of the nullable boolean. Uses only one byte to optimize memory usage.
|
||||
/// </summary>
|
||||
Nullable()
|
||||
: NullableBase<bool>()
|
||||
enum class Value : uint8
|
||||
{
|
||||
}
|
||||
Null,
|
||||
False,
|
||||
True,
|
||||
};
|
||||
|
||||
/// <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:
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the current Nullable{T} object has a valid value and it's set to true.
|
||||
/// Initializes nullable boolean by setting the wrapped value to null.
|
||||
/// </summary>
|
||||
Nullable() = default;
|
||||
|
||||
~Nullable() = default;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes nullable boolean by moving another nullable boolean.
|
||||
/// </summary>
|
||||
Nullable(Nullable&& value) = default;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes nullable boolean by copying another nullable boolean.
|
||||
/// </summary>
|
||||
Nullable(const Nullable& value) = default;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes nullable boolean by implicitly casting a boolean value.
|
||||
/// </summary>
|
||||
Nullable(const bool value) noexcept
|
||||
{
|
||||
_value = value ? Value::True : Value::False;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reassigns the wrapped value by implicitly casting a boolean value.
|
||||
/// </summary>
|
||||
Nullable& operator=(const bool value) noexcept
|
||||
{
|
||||
_value = value ? Value::True : Value::False;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reassigns the wrapped value by copying another nullable boolean.
|
||||
/// </summary>
|
||||
Nullable& operator=(const Nullable& value) = default;
|
||||
|
||||
/// <summary>
|
||||
/// Reassigns the wrapped value by moving another nullable boolean.
|
||||
/// </summary>
|
||||
Nullable& operator=(Nullable&& value) = default;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if wrapped bool has a valid value.
|
||||
/// </summary>
|
||||
FORCE_INLINE bool HasValue() const noexcept
|
||||
{
|
||||
return _value != Value::Null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the wrapped boolean value. If the value is not valid, the behavior is undefined.
|
||||
/// </summary>
|
||||
FORCE_INLINE bool GetValue() const
|
||||
{
|
||||
ASSERT(_value != Value::Null);
|
||||
return _value == Value::True;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the wrapped boolean value. If the value is not valid, returns the default value.
|
||||
/// </summary>
|
||||
FORCE_INLINE bool GetValueOr(const bool defaultValue) const noexcept
|
||||
{
|
||||
return _value == Value::Null ? defaultValue : _value == Value::True;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the wrapped value to a valid boolean.
|
||||
/// </summary>
|
||||
FORCE_INLINE void SetValue(const bool value) noexcept
|
||||
{
|
||||
_value = value ? Value::True : Value::False;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the wrapped value is not valid, sets it to a valid boolean.
|
||||
/// </summary>
|
||||
FORCE_INLINE bool TrySet(const bool value) noexcept
|
||||
{
|
||||
if (_value != Value::Null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_value = value ? Value::True : Value::False;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the wrapped bool to null.
|
||||
/// </summary>
|
||||
FORCE_INLINE void Reset() noexcept
|
||||
{
|
||||
_value = Value::Null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
FORCE_INLINE void GetAndReset(bool& value) noexcept
|
||||
{
|
||||
ASSERT(_value != Value::Null);
|
||||
value = _value == Value::True;
|
||||
_value = Value::Null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the current object has a valid value and it's set to <c>true</c>. If the value is <c>false</c> or not valid, the method returns <c>false</c>.
|
||||
/// </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>
|
||||
/// 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 <c>false</c>. If the value is <c>true</c> or not valid, the method returns <c>false</c>.
|
||||
/// </summary>
|
||||
/// <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.
|
||||
/// Deletes implicit conversion to bool to prevent ambiguous code.
|
||||
/// </summary>
|
||||
/// <returns>True if this object has a valid value set to true, otherwise false</returns>
|
||||
FORCE_INLINE operator bool() const
|
||||
{
|
||||
return _hasValue && _value;
|
||||
}
|
||||
/// <remarks>
|
||||
/// Implicit cast from nullable bool to a bool produces unacceptably ambiguous code. For template meta-programming use explicit <c>HasValue</c> instead.
|
||||
/// </remarks>
|
||||
explicit operator bool() const = delete;
|
||||
};
|
||||
|
||||
@@ -631,9 +631,9 @@ void EngineImpl::InitPaths()
|
||||
FileSystem::CreateDirectory(Globals::ProjectContentFolder);
|
||||
if (!FileSystem::DirectoryExists(Globals::ProjectSourceFolder))
|
||||
FileSystem::CreateDirectory(Globals::ProjectSourceFolder);
|
||||
if (CommandLine::Options.ClearCache)
|
||||
if (CommandLine::Options.ClearCache.IsTrue())
|
||||
FileSystem::DeleteDirectory(Globals::ProjectCacheFolder, true);
|
||||
else if (CommandLine::Options.ClearCookerCache)
|
||||
else if (CommandLine::Options.ClearCookerCache.IsTrue())
|
||||
FileSystem::DeleteDirectory(Globals::ProjectCacheFolder / TEXT("Cooker"), true);
|
||||
if (!FileSystem::DirectoryExists(Globals::ProjectCacheFolder))
|
||||
FileSystem::CreateDirectory(Globals::ProjectCacheFolder);
|
||||
|
||||
@@ -104,7 +104,7 @@ bool GraphicsService::Init()
|
||||
GPUDevice* device = nullptr;
|
||||
|
||||
// Null
|
||||
if (!device && CommandLine::Options.Null)
|
||||
if (!device && CommandLine::Options.Null.IsTrue())
|
||||
{
|
||||
#if GRAPHICS_API_NULL
|
||||
device = CreateGPUDeviceNull();
|
||||
@@ -114,7 +114,7 @@ bool GraphicsService::Init()
|
||||
}
|
||||
|
||||
// Vulkan
|
||||
if (!device && CommandLine::Options.Vulkan)
|
||||
if (!device && CommandLine::Options.Vulkan.IsTrue())
|
||||
{
|
||||
#if GRAPHICS_API_VULKAN
|
||||
device = CreateGPUDeviceVulkan();
|
||||
@@ -124,7 +124,7 @@ bool GraphicsService::Init()
|
||||
}
|
||||
|
||||
// DirectX 12
|
||||
if (!device && CommandLine::Options.D3D12)
|
||||
if (!device && CommandLine::Options.D3D12.IsTrue())
|
||||
{
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
if (Platform::IsWindows10())
|
||||
@@ -137,7 +137,7 @@ bool GraphicsService::Init()
|
||||
}
|
||||
|
||||
// DirectX 11 and DirectX 10
|
||||
if (!device && (CommandLine::Options.D3D11 || CommandLine::Options.D3D10))
|
||||
if (!device && (CommandLine::Options.D3D11.IsTrue() || CommandLine::Options.D3D10.IsTrue()))
|
||||
{
|
||||
#if GRAPHICS_API_DIRECTX11
|
||||
device = CreateGPUDeviceDX11();
|
||||
@@ -193,10 +193,10 @@ bool GraphicsService::Init()
|
||||
// Initialize
|
||||
if (device->IsDebugToolAttached
|
||||
#if USE_EDITOR || !BUILD_RELEASE
|
||||
|| CommandLine::Options.ShaderProfile
|
||||
|| CommandLine::Options.ShaderProfile.IsTrue()
|
||||
#endif
|
||||
#if USE_EDITOR
|
||||
|| CommandLine::Options.ShaderDebug
|
||||
|| CommandLine::Options.ShaderDebug.IsTrue()
|
||||
#endif
|
||||
)
|
||||
{
|
||||
|
||||
@@ -249,12 +249,12 @@ bool ShaderAssetBase::LoadShaderCache(ShaderCacheResult& result)
|
||||
options.SourceLength = sourceLength;
|
||||
options.Profile = shaderProfile;
|
||||
options.Output = &cacheStream;
|
||||
if (CommandLine::Options.ShaderDebug)
|
||||
if (CommandLine::Options.ShaderDebug.IsTrue())
|
||||
{
|
||||
options.GenerateDebugData = true;
|
||||
options.NoOptimize = true;
|
||||
}
|
||||
else if (CommandLine::Options.ShaderProfile)
|
||||
else if (CommandLine::Options.ShaderProfile.IsTrue())
|
||||
{
|
||||
options.GenerateDebugData = true;
|
||||
}
|
||||
|
||||
@@ -193,8 +193,8 @@ bool ShaderCacheManagerService::Init()
|
||||
CacheVersion cacheVersion;
|
||||
const String cacheVerFile = rootDir / TEXT("CacheVersion");
|
||||
#if USE_EDITOR
|
||||
const bool shaderDebug = CommandLine::Options.ShaderDebug;
|
||||
const bool shaderProfile = CommandLine::Options.ShaderProfile;
|
||||
const bool shaderDebug = CommandLine::Options.ShaderDebug.IsTrue();
|
||||
const bool shaderProfile = CommandLine::Options.ShaderProfile.IsTrue();
|
||||
#else
|
||||
const bool shaderDebug = false;
|
||||
#endif
|
||||
|
||||
@@ -106,9 +106,9 @@ GPUDevice* GPUDeviceDX11::Create()
|
||||
#else
|
||||
D3D_FEATURE_LEVEL maxAllowedFeatureLevel = D3D_FEATURE_LEVEL_11_0;
|
||||
#endif
|
||||
if (CommandLine::Options.D3D10)
|
||||
if (CommandLine::Options.D3D10.IsTrue())
|
||||
maxAllowedFeatureLevel = D3D_FEATURE_LEVEL_10_0;
|
||||
else if (CommandLine::Options.D3D11)
|
||||
else if (CommandLine::Options.D3D11.IsTrue())
|
||||
maxAllowedFeatureLevel = D3D_FEATURE_LEVEL_11_0;
|
||||
#if !USE_EDITOR && PLATFORM_WINDOWS
|
||||
auto winSettings = WindowsPlatformSettings::Get();
|
||||
@@ -209,11 +209,11 @@ GPUDevice* GPUDeviceDX11::Create()
|
||||
}
|
||||
GPUAdapterDX selectedAdapter = adapters[selectedAdapterIndex];
|
||||
uint32 vendorId = 0;
|
||||
if (CommandLine::Options.NVIDIA)
|
||||
if (CommandLine::Options.NVIDIA.IsTrue())
|
||||
vendorId = GPU_VENDOR_ID_NVIDIA;
|
||||
else if (CommandLine::Options.AMD)
|
||||
else if (CommandLine::Options.AMD.IsTrue())
|
||||
vendorId = GPU_VENDOR_ID_AMD;
|
||||
else if (CommandLine::Options.Intel)
|
||||
else if (CommandLine::Options.Intel.IsTrue())
|
||||
vendorId = GPU_VENDOR_ID_INTEL;
|
||||
if (vendorId != 0)
|
||||
{
|
||||
|
||||
@@ -161,11 +161,11 @@ GPUDevice* GPUDeviceDX12::Create()
|
||||
}
|
||||
GPUAdapterDX selectedAdapter = adapters[selectedAdapterIndex];
|
||||
uint32 vendorId = 0;
|
||||
if (CommandLine::Options.NVIDIA)
|
||||
if (CommandLine::Options.NVIDIA.IsTrue())
|
||||
vendorId = GPU_VENDOR_ID_NVIDIA;
|
||||
else if (CommandLine::Options.AMD)
|
||||
else if (CommandLine::Options.AMD.IsTrue())
|
||||
vendorId = GPU_VENDOR_ID_AMD;
|
||||
else if (CommandLine::Options.Intel)
|
||||
else if (CommandLine::Options.Intel.IsTrue())
|
||||
vendorId = GPU_VENDOR_ID_INTEL;
|
||||
if (vendorId != 0)
|
||||
{
|
||||
@@ -425,7 +425,7 @@ bool GPUDeviceDX12::Init()
|
||||
|
||||
#if !BUILD_RELEASE
|
||||
// Prevent the GPU from overclocking or under-clocking to get consistent timings
|
||||
if (CommandLine::Options.ShaderProfile)
|
||||
if (CommandLine::Options.ShaderProfile.IsTrue())
|
||||
{
|
||||
_device->SetStablePowerState(TRUE);
|
||||
}
|
||||
|
||||
@@ -1222,11 +1222,11 @@ GPUDevice* GPUDeviceVulkan::Create()
|
||||
return nullptr;
|
||||
}
|
||||
uint32 vendorId = 0;
|
||||
if (CommandLine::Options.NVIDIA)
|
||||
if (CommandLine::Options.NVIDIA.IsTrue())
|
||||
vendorId = GPU_VENDOR_ID_NVIDIA;
|
||||
else if (CommandLine::Options.AMD)
|
||||
else if (CommandLine::Options.AMD.IsTrue())
|
||||
vendorId = GPU_VENDOR_ID_AMD;
|
||||
else if (CommandLine::Options.Intel)
|
||||
else if (CommandLine::Options.Intel.IsTrue())
|
||||
vendorId = GPU_VENDOR_ID_INTEL;
|
||||
if (vendorId != 0)
|
||||
{
|
||||
|
||||
@@ -365,7 +365,7 @@ void PlatformBase::Fatal(const Char* msg, void* context)
|
||||
void PlatformBase::Error(const Char* msg)
|
||||
{
|
||||
#if PLATFORM_HAS_HEADLESS_MODE
|
||||
if (CommandLine::Options.Headless)
|
||||
if (CommandLine::Options.Headless.IsTrue())
|
||||
{
|
||||
#if PLATFORM_TEXT_IS_CHAR16
|
||||
StringAnsi ansi(msg);
|
||||
@@ -385,7 +385,7 @@ void PlatformBase::Error(const Char* msg)
|
||||
void PlatformBase::Warning(const Char* msg)
|
||||
{
|
||||
#if PLATFORM_HAS_HEADLESS_MODE
|
||||
if (CommandLine::Options.Headless)
|
||||
if (CommandLine::Options.Headless.IsTrue())
|
||||
{
|
||||
std::cout << "Warning: " << msg << std::endl;
|
||||
}
|
||||
@@ -399,7 +399,7 @@ void PlatformBase::Warning(const Char* msg)
|
||||
void PlatformBase::Info(const Char* msg)
|
||||
{
|
||||
#if PLATFORM_HAS_HEADLESS_MODE
|
||||
if (CommandLine::Options.Headless)
|
||||
if (CommandLine::Options.Headless.IsTrue())
|
||||
{
|
||||
std::cout << "Info: " << msg << std::endl;
|
||||
}
|
||||
|
||||
@@ -653,7 +653,7 @@ static int X11_MessageBoxLoop(MessageBoxData* data)
|
||||
|
||||
DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
||||
{
|
||||
if (CommandLine::Options.Headless)
|
||||
if (CommandLine::Options.Headless.IsTrue())
|
||||
return DialogResult::None;
|
||||
|
||||
// Setup for simple popup
|
||||
@@ -1369,7 +1369,7 @@ public:
|
||||
|
||||
DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
|
||||
{
|
||||
if (CommandLine::Options.Headless)
|
||||
if (CommandLine::Options.Headless.IsTrue())
|
||||
return DragDropEffect::None;
|
||||
auto cursorWrong = X11::XCreateFontCursor(xDisplay, 54);
|
||||
auto cursorTransient = X11::XCreateFontCursor(xDisplay, 24);
|
||||
@@ -1673,7 +1673,7 @@ void LinuxClipboard::Clear()
|
||||
|
||||
void LinuxClipboard::SetText(const StringView& text)
|
||||
{
|
||||
if (CommandLine::Options.Headless)
|
||||
if (CommandLine::Options.Headless.IsTrue())
|
||||
return;
|
||||
auto mainWindow = (LinuxWindow*)Engine::MainWindow;
|
||||
if (!mainWindow)
|
||||
@@ -1695,7 +1695,7 @@ void LinuxClipboard::SetFiles(const Array<String>& files)
|
||||
|
||||
String LinuxClipboard::GetText()
|
||||
{
|
||||
if (CommandLine::Options.Headless)
|
||||
if (CommandLine::Options.Headless.IsTrue())
|
||||
return String::Empty;
|
||||
String result;
|
||||
auto mainWindow = (LinuxWindow*)Engine::MainWindow;
|
||||
@@ -2118,7 +2118,7 @@ bool LinuxPlatform::Init()
|
||||
Platform::MemoryClear(CursorsImg, sizeof(CursorsImg));
|
||||
|
||||
// Skip setup if running in headless mode (X11 might not be available on servers)
|
||||
if (CommandLine::Options.Headless)
|
||||
if (CommandLine::Options.Headless.IsTrue())
|
||||
return false;
|
||||
|
||||
X11::XInitThreads();
|
||||
|
||||
@@ -54,7 +54,7 @@ String ComputerName;
|
||||
|
||||
DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
||||
{
|
||||
if (CommandLine::Options.Headless)
|
||||
if (CommandLine::Options.Headless.IsTrue())
|
||||
return DialogResult::None;
|
||||
NSAlert* alert = [[NSAlert alloc] init];
|
||||
ASSERT(alert);
|
||||
|
||||
@@ -616,7 +616,7 @@ bool WindowsPlatform::Init()
|
||||
return true;
|
||||
|
||||
// Init console output (engine is linked with /SUBSYSTEM:WINDOWS so it lacks of proper console output on Windows)
|
||||
if (CommandLine::Options.Std)
|
||||
if (CommandLine::Options.Std.IsTrue())
|
||||
{
|
||||
// Attaches output of application to parent console, returns true if running in console-mode
|
||||
// [Reference: https://www.tillett.info/2013/05/13/how-to-create-a-windows-program-that-works-as-both-as-a-gui-and-console-application]
|
||||
|
||||
163
Source/Engine/Tests/TestNullable.cpp
Normal file
163
Source/Engine/Tests/TestNullable.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "Engine/Core/Types/Nullable.h"
|
||||
#include <ThirdParty/catch2/catch.hpp>
|
||||
|
||||
TEST_CASE("Nullable")
|
||||
{
|
||||
SECTION("Trivial Type")
|
||||
{
|
||||
Nullable<int> a;
|
||||
|
||||
REQUIRE(a.HasValue() == false);
|
||||
REQUIRE(a.GetValueOr(2) == 2);
|
||||
|
||||
a = 1;
|
||||
|
||||
REQUIRE(a.HasValue() == true);
|
||||
REQUIRE(a.GetValue() == 1);
|
||||
REQUIRE(a.GetValueOr(2) == 1);
|
||||
|
||||
a.Reset();
|
||||
|
||||
REQUIRE(a.HasValue() == false);
|
||||
}
|
||||
|
||||
SECTION("Move-Only Type")
|
||||
{
|
||||
struct MoveOnly
|
||||
{
|
||||
MoveOnly() = default;
|
||||
~MoveOnly() = default;
|
||||
|
||||
MoveOnly(const MoveOnly&) = delete;
|
||||
MoveOnly(MoveOnly&&) = default;
|
||||
|
||||
MoveOnly& operator=(const MoveOnly&) = delete;
|
||||
MoveOnly& operator=(MoveOnly&&) = default;
|
||||
};
|
||||
|
||||
Nullable<MoveOnly> a;
|
||||
|
||||
REQUIRE(a.HasValue() == false);
|
||||
|
||||
a = MoveOnly();
|
||||
|
||||
REQUIRE(a.HasValue() == true);
|
||||
}
|
||||
|
||||
SECTION("Bool Type")
|
||||
{
|
||||
Nullable<bool> a;
|
||||
|
||||
REQUIRE(a.HasValue() == false);
|
||||
REQUIRE(a.GetValueOr(true) == true);
|
||||
REQUIRE(a.IsTrue() == false);
|
||||
REQUIRE(a.IsFalse() == false);
|
||||
|
||||
a = false;
|
||||
|
||||
REQUIRE(a.HasValue() == true);
|
||||
REQUIRE(a.GetValue() == false);
|
||||
REQUIRE(a.GetValueOr(true) == false);
|
||||
|
||||
REQUIRE(a.IsTrue() == false);
|
||||
REQUIRE(a.IsFalse() == true);
|
||||
|
||||
a = true;
|
||||
|
||||
REQUIRE(a.IsTrue() == true);
|
||||
REQUIRE(a.IsFalse() == false);
|
||||
|
||||
a.Reset();
|
||||
|
||||
REQUIRE(a.HasValue() == false);
|
||||
}
|
||||
|
||||
SECTION("Lifetime (No Construction)")
|
||||
{
|
||||
struct DoNotConstruct
|
||||
{
|
||||
DoNotConstruct() { FAIL("DoNotConstruct must not be constructed."); }
|
||||
};
|
||||
|
||||
Nullable<DoNotConstruct> a;
|
||||
a.Reset();
|
||||
}
|
||||
|
||||
SECTION("Lifetime")
|
||||
{
|
||||
struct Lifetime
|
||||
{
|
||||
int* _constructed;
|
||||
int* _destructed;
|
||||
|
||||
Lifetime(int* constructed, int* destructed)
|
||||
: _constructed(constructed)
|
||||
, _destructed(destructed)
|
||||
{
|
||||
++(*_constructed);
|
||||
}
|
||||
|
||||
Lifetime(Lifetime&& other) noexcept
|
||||
: _constructed(other._constructed)
|
||||
, _destructed(other._destructed)
|
||||
{
|
||||
++(*_constructed);
|
||||
}
|
||||
|
||||
Lifetime() = delete;
|
||||
Lifetime& operator=(const Lifetime&) = delete;
|
||||
Lifetime& operator=(Lifetime&&) = delete;
|
||||
|
||||
~Lifetime()
|
||||
{
|
||||
++(*_destructed);
|
||||
}
|
||||
};
|
||||
|
||||
int constructed = 0, destructed = 0;
|
||||
REQUIRE(constructed == destructed);
|
||||
|
||||
{
|
||||
|
||||
Nullable<Lifetime> a = Lifetime(&constructed, &destructed);
|
||||
REQUIRE(a.HasValue());
|
||||
REQUIRE(constructed == destructed + 1);
|
||||
|
||||
a.Reset();
|
||||
REQUIRE(!a.HasValue());
|
||||
REQUIRE(constructed == destructed);
|
||||
}
|
||||
REQUIRE(constructed == destructed);
|
||||
|
||||
{
|
||||
Nullable<Lifetime> b = Lifetime(&constructed, &destructed);
|
||||
REQUIRE(constructed == destructed + 1);
|
||||
}
|
||||
REQUIRE(constructed == destructed);
|
||||
|
||||
{
|
||||
Nullable<Lifetime> c = Lifetime(&constructed, &destructed);
|
||||
Nullable<Lifetime> d = MoveTemp(c);
|
||||
REQUIRE(constructed == destructed + 1);
|
||||
}
|
||||
REQUIRE(constructed == destructed);
|
||||
}
|
||||
|
||||
SECTION("Matching")
|
||||
{
|
||||
Nullable<int> a;
|
||||
Nullable<int> b = 2;
|
||||
|
||||
a.Match(
|
||||
[](int) { FAIL("Null nullable must not match value handler."); },
|
||||
[]() {}
|
||||
);
|
||||
|
||||
b.Match(
|
||||
[](int) {},
|
||||
[]() { FAIL("Nullable with valid value must not match null handler."); }
|
||||
);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user