The /include directory now contains the correct STL-based implementation.

This commit is contained in:
Tim Ambrogi
2022-03-04 16:06:29 -05:00
parent cb814bd7dd
commit 675cf43acc
10 changed files with 2474 additions and 490 deletions

View File

@@ -13,7 +13,7 @@ class LinkBase
{
public:
virtual ~LinkBase() = default;
virtual TOptional<TransitionEvent> EvaluateLink(const tOnStateTransitionFn& in_onTransitionFn) const = 0;
virtual std::optional<TransitionEvent> EvaluateLink(const tOnStateTransitionFn& in_onTransitionFn) const = 0;
};
// Type-safe link handle
@@ -42,19 +42,19 @@ protected:
// Constructors (friend-only)
LinkHandle() = delete;
LinkHandle(TSharedPtr<LinkBase> in_link, eType in_linkType, bool in_isConditional)
: m_link(MoveTemp(in_link))
LinkHandle(std::shared_ptr<LinkBase> in_link, eType in_linkType, bool in_isConditional)
: m_link(std::move(in_link))
, m_linkType(in_linkType)
, m_isConditionalLink(in_isConditional)
{
}
TOptional<TransitionEvent> EvaluateLink(const tOnStateTransitionFn& in_onTransitionFn) const
std::optional<TransitionEvent> EvaluateLink(const tOnStateTransitionFn& in_onTransitionFn) const
{
return m_link->EvaluateLink(in_onTransitionFn);
}
private:
TSharedPtr<LinkBase> m_link; // The underlying link
std::shared_ptr<LinkBase> m_link; // The underlying link
eType m_linkType; // Whether the link is normal or OnComplete
bool m_isConditionalLink; // Whether the link has an associated condition predicate
};
@@ -63,7 +63,7 @@ private:
template<class tStateInput, class tStateConstructorFn>
struct State
{
State(tStateConstructorFn in_stateCtorFn, StateId in_stateId, FString in_debugName)
State(tStateConstructorFn in_stateCtorFn, StateId in_stateId, std::string in_debugName)
: stateCtorFn(in_stateCtorFn)
, stateId(in_stateId)
, debugName(in_debugName)
@@ -72,21 +72,21 @@ struct State
tStateConstructorFn stateCtorFn;
StateId stateId;
FString debugName;
std::string debugName;
};
// Internal FSM state object (exit state specialization)
template<>
struct State<void, void>
{
State(StateId in_stateId, FString in_debugName)
State(StateId in_stateId, std::string in_debugName)
: stateId(in_stateId)
, debugName(in_debugName)
{
}
StateId stateId;
FString debugName;
std::string debugName;
};
// Internal link definition object
@@ -94,28 +94,28 @@ template<class ReturnT, class tStateConstructorFn, class tPredicateFn>
class Link : public LinkBase
{
public:
Link(TSharedPtr<State<ReturnT, tStateConstructorFn>> in_targetState, tPredicateFn in_predicate)
: m_targetState(MoveTemp(in_targetState))
Link(std::shared_ptr<State<ReturnT, tStateConstructorFn>> in_targetState, tPredicateFn in_predicate)
: m_targetState(std::move(in_targetState))
, m_predicate(in_predicate)
{
}
private:
virtual TOptional<TransitionEvent> EvaluateLink(const tOnStateTransitionFn& in_onTransitionFn) const final
virtual std::optional<TransitionEvent> EvaluateLink(const tOnStateTransitionFn& in_onTransitionFn) const final
{
TOptional<TransitionEvent> result;
if(TOptional<ReturnT> payload = m_predicate())
std::optional<TransitionEvent> result;
if(std::optional<ReturnT> payload = m_predicate())
{
if(in_onTransitionFn)
{
in_onTransitionFn();
}
result = TransitionEvent{ m_targetState->stateCtorFn(payload.GetValue()), m_targetState->stateId };
result = TransitionEvent{ m_targetState->stateCtorFn(payload.value()), m_targetState->stateId };
}
return result;
}
TSharedPtr<State<ReturnT, tStateConstructorFn>> m_targetState;
std::shared_ptr<State<ReturnT, tStateConstructorFn>> m_targetState;
tPredicateFn m_predicate;
};
@@ -124,16 +124,16 @@ template<class tStateConstructorFn, class tPredicateFn>
class Link<void, tStateConstructorFn, tPredicateFn> : public LinkBase
{
public:
Link(TSharedPtr<State<void, tStateConstructorFn>> in_targetState, tPredicateFn in_predicate)
: m_targetState(MoveTemp(in_targetState))
Link(std::shared_ptr<State<void, tStateConstructorFn>> in_targetState, tPredicateFn in_predicate)
: m_targetState(std::move(in_targetState))
, m_predicate(in_predicate)
{
}
private:
virtual TOptional<TransitionEvent> EvaluateLink(const tOnStateTransitionFn& in_onTransitionFn) const final
virtual std::optional<TransitionEvent> EvaluateLink(const tOnStateTransitionFn& in_onTransitionFn) const final
{
TOptional<TransitionEvent> result;
std::optional<TransitionEvent> result;
if(m_predicate())
{
if(in_onTransitionFn)
@@ -145,7 +145,7 @@ private:
return result;
}
TSharedPtr<State<void, tStateConstructorFn>> m_targetState;
std::shared_ptr<State<void, tStateConstructorFn>> m_targetState;
tPredicateFn m_predicate;
};
@@ -154,16 +154,16 @@ template<class tPredicateFn>
class Link<void, void, tPredicateFn> : public LinkBase
{
public:
Link(TSharedPtr<State<void, void>> in_targetState, tPredicateFn in_predicate)
: m_targetState(MoveTemp(in_targetState))
Link(std::shared_ptr<State<void, void>> in_targetState, tPredicateFn in_predicate)
: m_targetState(std::move(in_targetState))
, m_predicate(in_predicate)
{
}
private:
virtual TOptional<TransitionEvent> EvaluateLink(const tOnStateTransitionFn& in_onTransitionFn) const final
virtual std::optional<TransitionEvent> EvaluateLink(const tOnStateTransitionFn& in_onTransitionFn) const final
{
TOptional<TransitionEvent> result;
std::optional<TransitionEvent> result;
if(m_predicate())
{
if(in_onTransitionFn)
@@ -175,16 +175,16 @@ private:
return result;
}
TSharedPtr<State<void, void>> m_targetState;
std::shared_ptr<State<void, void>> m_targetState;
tPredicateFn m_predicate;
};
// Specialized type traits that deduce the first argument type of an arbitrary callable type
template <typename tRet, typename tArg>
static tArg get_first_arg_type(TFunction<tRet(tArg)> f); // Return type is first argument type
static tArg get_first_arg_type(std::function<tRet(tArg)> f); // Return type is first argument type
template <typename tRet>
static void get_first_arg_type(TFunction<tRet()> f); // Return type is void (function has no arguments)
static void get_first_arg_type(std::function<tRet()> f); // Return type is void (function has no arguments)
template <typename T>
struct function_traits : public function_traits<decltype(&T::operator())> // Generic callable objects (use operator())
@@ -194,27 +194,27 @@ struct function_traits : public function_traits<decltype(&T::operator())> // Gen
template <typename tRet, typename... tArgs> // Function
struct function_traits<tRet(tArgs...)>
{
using tFunction = TFunction<tRet(tArgs...)>;
using tFunction = std::function<tRet(tArgs...)>;
using tArg = decltype(get_first_arg_type(tFunction()));
};
template <typename tRet, typename... tArgs> // Function ptr
struct function_traits<tRet(*)(tArgs...)>
{
using tFunction = TFunction<tRet(tArgs...)>;
using tFunction = std::function<tRet(tArgs...)>;
using tArg = decltype(get_first_arg_type(tFunction()));
};
template <typename tClass, typename tRet, typename... tArgs> // Member function ptr (const)
struct function_traits<tRet(tClass::*)(tArgs...) const>
{
using tFunction = TFunction<tRet(tArgs...)>;
using tFunction = std::function<tRet(tArgs...)>;
using tArg = decltype(get_first_arg_type(tFunction()));
};
template <typename tClass, typename tRet, typename... tArgs> // Member function ptr
struct function_traits<tRet(tClass::*)(tArgs...)>
{
using tFunction = TFunction<tRet(tArgs...)>;
using tFunction = std::function<tRet(tArgs...)>;
using tArg = decltype(get_first_arg_type(tFunction()));
};

View File

@@ -7,7 +7,7 @@ class TaskInternalBase;
template <typename tRet> class TaskInternal;
//--- tTaskReadyFn ---//
using tTaskReadyFn = TFunction<bool()>;
using tTaskReadyFn = std::function<bool()>;
template <typename tRet, eTaskRef RefType, eTaskResumable Resumable>
auto CancelTaskIf(Task<tRet, RefType, Resumable>&& in_task, tTaskCancelFn in_cancelFn);
@@ -38,16 +38,16 @@ private:
struct TaskDebugStackFormatter
{
// Format function (formats a debug output string) [virtual]
virtual FString Format(const FString& in_str) const
virtual std::string Format(const std::string& in_str)
{
FString result = Indent(0);
std::string result = Indent(0);
int32_t indent = 0;
int32_t start = 0;
int32_t found = 0;
while((found = in_str.FindChar('\n', start)) != INDEX_NONE)
size_t start = 0;
size_t found = 0;
while((found = in_str.find('\n', start)) != std::string::npos)
{
int32_t end = found + 1;
if((found < in_str.Len() - 1) && (in_str[found + 1] == '`')) // indent
size_t end = found + 1;
if((found < in_str.size() - 1) && (in_str[found + 1] == '`')) // indent
{
++indent;
++end;
@@ -57,22 +57,21 @@ struct TaskDebugStackFormatter
--indent;
--found;
}
result += in_str.Mid(start, found - start) + '\n' + Indent(indent);
result += in_str.substr(start, found - start) + '\n' + Indent(indent);
start = end;
}
result += in_str.Mid(start);
result += in_str.substr(start);
return result;
}
virtual FString Indent(int32_t in_indent) const
virtual std::string Indent(int32_t in_indent)
{
return FString::ChrN(in_indent * 2, ' ');
return std::string((long long)in_indent * 2, ' ');
}
};
static FString FormatDebugString(FString in_str)
static std::string FormatDebugString(std::string in_str)
{
in_str.ReplaceCharInline('\n', ' ');
in_str.LeftChopInline(32, false);
return in_str;
std::replace(in_str.begin(), in_str.end(), '\n', ' ');
return in_str.substr(0, 32);
}
//--- SetDebugName Awaiter ---//
@@ -84,7 +83,7 @@ struct SetDebugName
: m_name(in_name)
{
}
SetDebugName(const char* in_name, TFunction<FString()> in_dataFn)
SetDebugName(const char* in_name, std::function<std::string()> in_dataFn)
: m_name(in_name)
, m_dataFn(in_dataFn)
{
@@ -93,7 +92,7 @@ struct SetDebugName
private:
template <typename tRet> friend class TaskPromiseBase;
const char* m_name = nullptr;
TFunction<FString()> m_dataFn;
std::function<std::string()> m_dataFn;
};
#endif //SQUID_ENABLE_TASK_DEBUG
@@ -146,13 +145,13 @@ struct TaskAwaiterBase
// This constructor exists to minimize downstream compile-error spam when co_awaiting a non-copyable Task by copy
}
TaskAwaiterBase(Task<tRet, RefType, Resumable>&& in_task)
: m_task(MoveTemp(in_task))
: m_task(std::move(in_task))
{
SQUID_RUNTIME_CHECK(m_task.IsValid(), "Tried to await an invalid task");
}
TaskAwaiterBase(TaskAwaiterBase&& in_taskAwaiter) noexcept
{
m_task = MoveTemp(in_taskAwaiter.m_task);
m_task = std::move(in_taskAwaiter.m_task);
}
bool await_ready() noexcept
{
@@ -173,7 +172,7 @@ struct TaskAwaiterBase
{
subTaskInternal->RequestStop(); // Propagate any stop request to new sub-tasks
}
taskInternal->SetSubTask(StaticCastSharedPtr<TaskInternalBase>(subTaskInternal));
taskInternal->SetSubTask(std::static_pointer_cast<TaskInternalBase>(subTaskInternal));
// Resume the task
if(m_task.Resume() == eTaskStatus::Done)
@@ -213,8 +212,8 @@ struct TaskAwaiter : public TaskAwaiterBase<tRet, RefType, Resumable, promise_ty
{
this->m_task.RethrowUnhandledException(); // Re-throw any exceptions
auto retVal = this->m_task.TakeReturnValue();
SQUID_RUNTIME_CHECK(retVal, "Awaited task return value is unset");
return MoveTemp(retVal.GetValue());
SQUID_RUNTIME_CHECK(retVal.has_value(), "Awaited task return value is unset");
return std::move(retVal.value());
}
template <typename U = tRet, typename std::enable_if_t<std::is_void<U>::value>* = nullptr>
@@ -228,8 +227,8 @@ struct TaskAwaiter : public TaskAwaiterBase<tRet, RefType, Resumable, promise_ty
template <typename tRet, typename promise_type>
struct FutureAwaiter
{
FutureAwaiter(TFuture<tRet>&& in_future)
: m_future(MoveTemp(in_future))
FutureAwaiter(std::future<tRet>&& in_future)
: m_future(std::move(in_future))
{
}
~FutureAwaiter()
@@ -237,89 +236,95 @@ struct FutureAwaiter
}
FutureAwaiter(FutureAwaiter&& in_futureAwaiter) noexcept
{
m_future = MoveTemp(in_futureAwaiter.m_future);
m_future = std::move(in_futureAwaiter.m_future);
}
bool await_ready() noexcept
{
bool isReady = m_future.IsReady();
bool isReady = m_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
return isReady;
}
bool await_suspend(std::coroutine_handle<promise_type> in_coroHandle) noexcept
{
// Set the ready function
auto& promise = in_coroHandle.promise();
auto IsFutureReady = [this] {
return m_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
};
// Suspend if future is not ready
bool shouldSuspend = !m_future.IsReady();
if(shouldSuspend)
bool isReady = m_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
if(!isReady)
{
promise.SetReadyFunction([this] { return m_future.IsReady(); });
promise.SetReadyFunction(IsFutureReady);
}
return shouldSuspend;
return !isReady;
}
template <typename U = tRet, typename std::enable_if_t<!std::is_void<U>::value>* = nullptr>
auto await_resume()
{
return m_future.Get();
return m_future.get(); // Re-throws any exceptions
}
template <typename U = tRet, typename std::enable_if_t<std::is_void<U>::value>* = nullptr>
void await_resume()
{
m_future.Get();
m_future.get(); // Re-throws any exceptions
}
private:
TFuture<tRet> m_future;
std::future<tRet> m_future;
};
//--- Shared Future Awaiter ---//
template <typename tRet, typename promise_type>
struct SharedFutureAwaiter
{
SharedFutureAwaiter(const TSharedFuture<tRet>& in_sharedFuture)
SharedFutureAwaiter(const std::shared_future<tRet>& in_sharedFuture)
: m_sharedFuture(in_sharedFuture)
{
}
bool await_ready() noexcept
{
bool isReady = m_sharedFuture.IsReady();
bool isReady = m_sharedFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
return isReady;
}
bool await_suspend(std::coroutine_handle<promise_type> in_coroHandle) noexcept
{
// Set the ready function
auto& promise = in_coroHandle.promise();
auto IsFutureReady = [this] {
return m_sharedFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
};
// Suspend if future is not ready
bool shouldSuspend = !m_sharedFuture.IsReady();
if(shouldSuspend)
bool isReady = m_sharedFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
if(!isReady)
{
promise.SetReadyFunction([this] { return m_sharedFuture.IsReady(); });
promise.SetReadyFunction(IsFutureReady);
}
return shouldSuspend;
return !isReady;
}
template <typename U = tRet, typename std::enable_if_t<!std::is_void<U>::value>* = nullptr>
auto await_resume()
{
return m_sharedFuture.Get();
return m_sharedFuture.get(); // Re-throws any exceptions
}
template <typename U = tRet, typename std::enable_if_t<std::is_void<U>::value>* = nullptr>
void await_resume()
{
m_sharedFuture.Get(); // Trigger any pending errors
m_sharedFuture.get(); // Re-throws any exceptions
}
private:
TSharedFuture<tRet> m_sharedFuture;
std::shared_future<tRet> m_sharedFuture;
};
//--- TaskPromiseBase ---//
template <typename tRet>
class alignas(16) TaskPromiseBase
class TaskPromiseBase
{
public:
// Type aliases
@@ -346,30 +351,11 @@ public:
{
return std::coroutine_handle<promise_type>::from_promise(*static_cast<promise_type*>(this));
}
static TSharedPtr<tTaskInternal> get_return_object_on_allocation_failure()
static std::shared_ptr<tTaskInternal> get_return_object_on_allocation_failure()
{
SQUID_THROW(std::bad_alloc(), "Failed to allocate memory for Task");
return {};
}
//----------------------------------------------------------------------------
// HACK: Coroutines in UE5 under MSVC is currently causing a memory underrun
// These allocators are a workaround for the issue (as is alignas(16))
void* operator new(size_t Size) noexcept
{
const size_t WorkaroundAlign = std::alignment_of<TaskPromiseBase>();
Size += WorkaroundAlign;
return (void*)((uint8_t*)FMemory::Malloc(Size, WorkaroundAlign) + WorkaroundAlign);
}
void operator delete(void* Ptr) noexcept
{
const size_t WorkaroundAlign = std::alignment_of<TaskPromiseBase>();
auto OffsetPtr = (uint8_t*)Ptr - WorkaroundAlign;
FMemory::Free(OffsetPtr);
}
//----------------------------------------------------------------------------
#if SQUID_NEEDS_UNHANDLED_EXCEPTION
void unhandled_exception() noexcept
{
#if SQUID_USE_EXCEPTIONS
@@ -377,7 +363,6 @@ public:
m_taskInternal->SetUnhandledException(std::current_exception());
#endif //SQUID_USE_EXCEPTIONS
}
#endif // SQUID_NEEDS_UNHANDLED_EXCEPTION
// Internal Task
void SetInternalTask(tTaskInternal* in_taskInternal)
@@ -461,13 +446,13 @@ public:
}
template <typename tFutureRet>
auto await_transform(TFuture<tFutureRet>&& in_future)
auto await_transform(std::future<tFutureRet>&& in_future)
{
return FutureAwaiter<tFutureRet, promise_type>(MoveTemp(in_future));
return FutureAwaiter<tFutureRet, promise_type>(std::move(in_future));
}
template <typename tFutureRet>
auto await_transform(const TSharedFuture<tFutureRet>& in_sharedFuture)
auto await_transform(const std::shared_future<tFutureRet>& in_sharedFuture)
{
return SharedFutureAwaiter<tFutureRet, promise_type>(in_sharedFuture);
}
@@ -477,22 +462,22 @@ public:
typename std::enable_if_t<Resumable == eTaskResumable::Yes>* = nullptr>
auto await_transform(Task<tTaskRet, RefType, Resumable>&& in_task) // Move version
{
return TaskAwaiter<tTaskRet, RefType, Resumable, promise_type>(MoveTemp(in_task));
return TaskAwaiter<tTaskRet, RefType, Resumable, promise_type>(std::move(in_task));
}
template <typename tTaskRet, eTaskRef RefType, eTaskResumable Resumable,
typename std::enable_if_t<Resumable == eTaskResumable::No>* = nullptr>
auto await_transform(Task<tTaskRet, RefType, Resumable> in_task) // Copy version (Non-Resumable)
{
return TaskAwaiter<tTaskRet, RefType, Resumable, promise_type>(MoveTemp(in_task));
return TaskAwaiter<tTaskRet, RefType, Resumable, promise_type>(std::move(in_task));
}
template <typename tTaskRet, eTaskRef RefType, eTaskResumable Resumable,
typename std::enable_if_t<Resumable == eTaskResumable::Yes>* = nullptr>
auto await_transform(const Task<tTaskRet, RefType, Resumable>& in_task) // Invalid copy version (Resumable)
{
static_assert(static_false<tTaskRet>::value, "Cannot await a non-copyable (resumable) Task by copy (try co_await MoveTemp(task), co_await WeakTaskHandle(task), or co_await task.WaitUntilDone()");
return TaskAwaiter<tTaskRet, RefType, Resumable, promise_type>(MoveTemp(in_task));
static_assert(static_false<tTaskRet>::value, "Cannot await a non-copyable (resumable) Task by copy (try co_await std::move(task), co_await WeakTaskHandle(task), or co_await task.WaitUntilDone()");
return TaskAwaiter<tTaskRet, RefType, Resumable, promise_type>(std::move(in_task));
}
protected:
@@ -511,7 +496,7 @@ public:
}
void return_value(tRet&& in_retVal) // Move return value
{
this->m_taskInternal->SetReturnValue(MoveTemp(in_retVal));
this->m_taskInternal->SetReturnValue(std::move(in_retVal));
}
};
@@ -550,12 +535,12 @@ public:
m_isStopRequested = true;
for(auto& stopTask : m_stopTasks)
{
if(auto locked = stopTask.Pin())
if(auto locked = stopTask.lock())
{
locked->RequestStop();
}
}
m_stopTasks.SetNum(0);
m_stopTasks.clear();
}
template <typename tRet, eTaskRef RefType, eTaskResumable Resumable>
void AddStopTask(Task<tRet, RefType, Resumable>& in_taskToStop) // Adds a task to the list of tasks to which we propagate stop requests
@@ -566,7 +551,7 @@ public:
}
else if(in_taskToStop.IsValid())
{
m_stopTasks.Add(in_taskToStop.GetInternalTask());
m_stopTasks.push_back(in_taskToStop.GetInternalTask());
}
}
template <typename tRet, eTaskRef RefType, eTaskResumable Resumable>
@@ -574,12 +559,12 @@ public:
{
if(in_taskToStop.IsValid())
{
for(int32_t i = 0; i < m_stopTasks.Num(); ++i)
for(size_t i = 0; i < m_stopTasks.size(); ++i)
{
if(m_stopTasks[i].Pin() == in_taskToStop.GetInternalTask())
if(m_stopTasks[i].lock() == in_taskToStop.GetInternalTask())
{
m_stopTasks[i] = m_stopTasks.Last();
m_stopTasks.Pop();
m_stopTasks[i] = m_stopTasks.back();
m_stopTasks.pop_back();
return;
}
}
@@ -637,20 +622,20 @@ public:
}
// Sub-task
void SetSubTask(TSharedPtr<TaskInternalBase> in_subTaskInternal)
void SetSubTask(std::shared_ptr<TaskInternalBase> in_subTaskInternal)
{
m_subTaskInternal = in_subTaskInternal;
}
#if SQUID_ENABLE_TASK_DEBUG
// Debug task name + stack
FString GetDebugName() const
std::string GetDebugName() const
{
return (!IsDone() && m_debugDataFn) ? (FString(m_debugName) + " [" + m_debugDataFn() + "]") : m_debugName;
return (!IsDone() && m_debugDataFn) ? (std::string(m_debugName) + " [" + m_debugDataFn() + "]") : m_debugName;
}
FString GetDebugStack() const
std::string GetDebugStack() const
{
FString result = m_subTaskInternal ? (GetDebugName() + " -> " + m_subTaskInternal->GetDebugStack()) : GetDebugName();
std::string result = m_subTaskInternal ? (GetDebugName() + " -> " + m_subTaskInternal->GetDebugStack()) : GetDebugName();
return result;
}
void SetDebugName(const char* in_debugName)
@@ -660,7 +645,7 @@ public:
m_debugName = in_debugName;
}
}
void SetDebugDataFn(TFunction<FString()> in_debugDataFn)
void SetDebugDataFn(std::function<std::string()> in_debugDataFn)
{
m_debugDataFn = in_debugDataFn;
}
@@ -755,7 +740,7 @@ private:
};
eInternalState m_internalState = eInternalState::Idle;
// Task ready condition (when awaiting a TFunction<bool>)
// Task ready condition (when awaiting a std::function<bool>)
tTaskReadyFn m_taskReadyFn;
#if SQUID_USE_EXCEPTIONS
@@ -765,7 +750,7 @@ private:
#endif //SQUID_USE_EXCEPTIONS
// Sub-task
TSharedPtr<TaskInternalBase> m_subTaskInternal;
std::shared_ptr<TaskInternalBase> m_subTaskInternal;
// Reference-counting (determines underlying std::coroutine_handle lifetime, not lifetime of this internal task)
void AddLogicalRef()
@@ -787,12 +772,12 @@ private:
// Stop request
bool m_isStopRequested = false;
TArray<TWeakPtr<TaskInternalBase>> m_stopTasks;
std::vector<std::weak_ptr<TaskInternalBase>> m_stopTasks;
#if SQUID_ENABLE_TASK_DEBUG
// Debug Data
const char* m_debugName = "[unnamed task]";
TFunction<FString()> m_debugDataFn;
std::function<std::string()> m_debugDataFn;
#endif //SQUID_ENABLE_TASK_DEBUG
};
@@ -819,13 +804,13 @@ public:
void SetReturnValue(const tRet& in_retVal)
{
tRet retVal = in_retVal;
SetReturnValue(MoveTemp(retVal));
SetReturnValue(std::move(retVal));
}
void SetReturnValue(tRet&& in_retVal)
{
if(m_retValState == eTaskRetValState::Unset)
{
m_retVal = MoveTemp(in_retVal);
m_retVal = std::move(in_retVal);
m_retValState = eTaskRetValState::Set;
return;
}
@@ -835,13 +820,13 @@ public:
SQUID_RUNTIME_CHECK(m_retValState != eTaskRetValState::Taken, "Attempted to set a task's return value after it was already taken");
SQUID_RUNTIME_CHECK(m_retValState != eTaskRetValState::Orphaned, "Attempted to set a task's return value after it was orphaned");
}
TOptional<tRet> TakeReturnValue()
std::optional<tRet> TakeReturnValue()
{
// If the value has been set, mark it as taken and move-return the value
if(m_retValState == eTaskRetValState::Set)
{
m_retValState = eTaskRetValState::Taken;
return MoveTemp(m_retVal);
return std::move(m_retVal);
}
// If the value was not set, return an unset optional (checking that it was neither taken nor orphaned)
@@ -866,7 +851,7 @@ private:
};
eTaskRetValState m_retValState = eTaskRetValState::Unset; // Initially unset
TOptional<tRet> m_retVal;
std::optional<tRet> m_retVal;
};
template <>

View File

@@ -86,12 +86,9 @@ struct static_false : std::false_type
#undef CPP_LANGUAGE_VERSION
// C++20 Compatibility (std::coroutine)
#if HAS_CXX20 || (defined(_MSVC_LANG) && defined(__cpp_lib_coroutine)) // Standard coroutines
#include <coroutine>
#define SQUID_EXPERIMENTAL_COROUTINES 0
#else // Experimental coroutines
#if defined(__clang__) && defined(_STL_COMPILER_PREPROCESSOR)
// HACK: Some distributions of clang don't have a <experimental/coroutine> header. We only need a few symbols, so just define them ourselves
#if defined(__clang__) && defined(_STL_COMPILER_PREPROCESSOR) // Clang-friendly implementation of <coroutine> below
// HACK: The above condition checks if we're compiling w/ Clang under Visual Studio
#define USING_EXPERIMENTAL_COROUTINES
namespace std {
namespace experimental {
inline namespace coroutines_v1 {
@@ -174,47 +171,23 @@ namespace std {
}
}
}
#else
#elif HAS_CXX20 || defined(SQUID_HAS_AWAIT_STRICT) // Built-in C++20 coroutines implementation
#include <coroutine>
#else // Built-in experimental coroutines implementation
#define USING_EXPERIMENTAL_COROUTINES
#include <experimental/coroutine>
#endif
namespace std // Alias experimental coroutine symbols into std namespace
// Pull experimental coroutines implementation into the main ::std:: namspace
#if defined(USING_EXPERIMENTAL_COROUTINES)
namespace std
{
template <class _Promise = void>
using coroutine_handle = experimental::coroutine_handle<_Promise>;
using suspend_never = experimental::suspend_never;
using suspend_always = experimental::suspend_always;
};
#define SQUID_EXPERIMENTAL_COROUTINES 1
#endif
// Determine whether our tasks need the member function "unhandled_exception()" defined or not
#if defined(_MSC_VER)
// MSVC's rules for exceptions differ between standard + experimental coroutines
#if SQUID_EXPERIMENTAL_COROUTINES
// If exceptions are enabled, we must define unhandled_exception()
#if defined(__cpp_exceptions) && __cpp_exceptions == 199711
#define SQUID_NEEDS_UNHANDLED_EXCEPTION 1
#else
#define SQUID_NEEDS_UNHANDLED_EXCEPTION 0
#endif
#else
// If we're using VS16.11 or newer -- or older than 16.10, we have one set of rules for standard coroutines
#if _MSC_FULL_VER >= 192930133L || _MSC_VER < 1429L
#define SQUID_NEEDS_UNHANDLED_EXCEPTION 1
#else
#if defined(__cpp_exceptions) && __cpp_exceptions == 199711
#define SQUID_NEEDS_UNHANDLED_EXCEPTION 1
#else
// 16.10 has a bug with their standard coroutine implementation that creates a set of contradicting requirements
// https://developercommunity.visualstudio.com/t/coroutine-uses-promise_type::unhandled_e/1374530
#error Visual Studio 16.10 has a compiler bug that prevents all coroutines from compiling when exceptions are disabled and using standard C++20 coroutines or /await:strict. Please either upgrade your version of Visual Studio, or use the experimental /await flag, or enable exceptions.
#endif
#endif
#endif
#else
// Clang always requires unhandled_exception() to be defined
#define SQUID_NEEDS_UNHANDLED_EXCEPTION 1
#endif
#endif //USING_EXPERIMENTAL_COROUTINES
// C++17 Compatibility ([[nodiscard]])
#if !defined(SQUID_NODISCARD) && defined(__has_cpp_attribute)
@@ -226,11 +199,17 @@ namespace std // Alias experimental coroutine symbols into std namespace
#define SQUID_NODISCARD
#endif
// C++17 Compatibility (std::optional)
#if HAS_CXX17 // Built-in C++17 optional implementation
#include <optional>
#else // Public-domain optional implementation (c/o TartanLlama)
#include "tl/optional.hpp"
namespace std
{
template <class T>
using optional = tl::optional<T>;
};
#endif
#undef HAS_CXX17
#undef HAS_CXX20
// Include UE core headers
#include "CoreMinimal.h"
#include "Engine/World.h"
#include "Engine/Engine.h"
#include "Async/Future.h"

File diff suppressed because it is too large Load Diff