From e057cdc84d726508f309016b9f43baa02db51aba Mon Sep 17 00:00:00 2001 From: _Redstone_c_ Date: Mon, 2 Jan 2023 21:49:24 +0800 Subject: [PATCH] feat(templates): add TScopeCallback TGuardValue and TScopeCounter --- .../Private/Testing/TemplatesTesting.cpp | 88 ++++++++++++ .../Source/Public/Templates/ScopeHelper.h | 133 ++++++++++++++++++ .../Source/Public/Templates/Templates.h | 1 + .../Source/Public/Testing/TemplatesTesting.h | 1 + 4 files changed, 223 insertions(+) create mode 100644 Redcraft.Utility/Source/Public/Templates/ScopeHelper.h diff --git a/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp b/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp index a64ace5..0f33b03 100644 --- a/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp +++ b/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp @@ -24,6 +24,7 @@ void TestTemplates() TestTuple(); TestFunction(); TestAtomic(); + TestScopeHelper(); TestMiscTemplates(); } @@ -1381,6 +1382,93 @@ void TestAtomic() } } +void TestScopeHelper() +{ + { + int32 CheckNum = 0; + { + TScopeCallback ScopeCallback([&]() { CheckNum = 2; }); + always_check(CheckNum == 0); + CheckNum = 1; + always_check(CheckNum == 1); + } + always_check(CheckNum == 2); + } + + { + int32 CheckNum = 0; + { + TScopeCallback ScopeCallback([&]() { CheckNum = 2; }); + always_check(CheckNum == 0); + CheckNum = 1; + always_check(CheckNum == 1); + ScopeCallback.Release(); + } + always_check(CheckNum == 1); + } + + { + int32 CheckNum = 0; + { + TScopeCallback ScopeCallbackA([&]() { CheckNum = 2; }); + TScopeCallback ScopeCallbackB(MoveTemp(ScopeCallbackA)); + always_check(CheckNum == 0); + CheckNum = 1; + always_check(CheckNum == 1); + } + always_check(CheckNum == 2); + } + + { + int32 CheckNum = 1; + { + TGuardValue GuardValue(CheckNum); + CheckNum = 2; + always_check(CheckNum == 2); + } + always_check(CheckNum == 1); + } + + { + int32 CheckNum = 1; + { + TGuardValue GuardValue(CheckNum, 2); + always_check(CheckNum == 2); + } + always_check(CheckNum == 1); + } + + { + int32 CheckNum = 1; + { + TGuardValue GuardValue(CheckNum, 2); + always_check(CheckNum == 2); + GuardValue.Release(); + } + always_check(CheckNum == 2); + } + + { + int32 CheckNum = 1; + { + TGuardValue GuardValueA(CheckNum, 2); + TGuardValue GuardValueB(MoveTemp(GuardValueA)); + always_check(CheckNum == 2); + } + always_check(CheckNum == 1); + } + + { + int32 CheckNum = 1; + { + TScopeCounter GuardValue(CheckNum); + always_check(CheckNum == 2); + } + always_check(CheckNum == 1); + } + +} + NAMESPACE_UNNAMED_BEGIN template diff --git a/Redcraft.Utility/Source/Public/Templates/ScopeHelper.h b/Redcraft.Utility/Source/Public/Templates/ScopeHelper.h new file mode 100644 index 0000000..83874d8 --- /dev/null +++ b/Redcraft.Utility/Source/Public/Templates/ScopeHelper.h @@ -0,0 +1,133 @@ +#pragma once + +#include "CoreTypes.h" +#include "Templates/Utility.h" +#include "TypeTraits/Invocable.h" +#include "Templates/Noncopyable.h" +#include "TypeTraits/CompositeType.h" +#include "TypeTraits/Miscellaneous.h" +#include "TypeTraits/SupportedOperations.h" + +NAMESPACE_REDCRAFT_BEGIN +NAMESPACE_MODULE_BEGIN(Redcraft) +NAMESPACE_MODULE_BEGIN(Utility) + +/** The class template is a general-purpose scope guard intended to call its callback function when a scope is exited. */ +template requires (CDestructible) +class TScopeCallback final : public FNoncopyable +{ +public: + + /** Initializes the callback function with a function or function object. */ + template requires (!CSameAs, TScopeCallback> && CConstructibleFrom) + FORCEINLINE constexpr explicit TScopeCallback(InF&& Func) : Storage(Forward(Func)), bIsActive(true) { } + + /** Move constructor. Initializes the stored object with the one in other. */ + FORCEINLINE constexpr TScopeCallback(TScopeCallback&& InValue) requires (CMoveConstructible) + : Storage(MoveTemp(InValue.Storage)), bIsActive(InValue.bIsActive) + { + InValue.Release(); + } + + /** Calls the callback function if the TScopeCallback is active, then destroys the stored object. */ + FORCEINLINE constexpr ~TScopeCallback() + { + if (bIsActive) Storage(); + } + + /** Makes the TScopeCallback inactive. */ + FORCEINLINE constexpr void Release() { bIsActive = false; } + + /** @return a const reference to the stored object. */ + FORCEINLINE constexpr const F& Get() const { return Storage; } + +private: + + F Storage; + bool bIsActive; + +}; + +template +TScopeCallback(F) -> TScopeCallback; + +/** The class template is a general-purpose scope guard intended to make sure a value is restored when a scope is exited. */ +template requires (CCopyConstructible && CCopyAssignable && CMoveAssignable && CDestructible) +class TGuardValue final : public FNoncopyable +{ +public: + + /** Initializes the TGuardValue with a reference. */ + FORCEINLINE constexpr TGuardValue(T& InReference) : Reference(InReference), OldValue(InReference), bIsActive(true) { } + + /** Initializes the TGuardValue with a reference and assign a new value to the reference. */ + template requires (CAssignableFrom) + FORCEINLINE constexpr TGuardValue(T& InReference, U&& InValue) : Reference(InReference), OldValue(InReference), bIsActive(true) { InReference = InValue; } + + /** Move constructor. Initializes the referenced value and old value with the one in other. */ + FORCEINLINE constexpr TGuardValue(TGuardValue&& InValue) requires (CMoveConstructible) + : Reference(InValue.Reference), OldValue(MoveTemp(InValue.OldValue)), bIsActive(InValue.bIsActive) + { + InValue.Release(); + } + + /** Restore the referenced value if the TGuardValue is active, then destroys the old value. */ + FORCEINLINE constexpr ~TGuardValue() + { + if (bIsActive) Reference = MoveTemp(OldValue); + } + + /** Makes the TGuardValue inactive. */ + FORCEINLINE constexpr void Release() { bIsActive = false; } + + /** @return a const reference to the old value. */ + FORCEINLINE constexpr const T& Get() const { return OldValue; } + +private: + + T& Reference; + T OldValue; + bool bIsActive; + +}; + +template +TGuardValue(T&) -> TGuardValue; + +template +TGuardValue(T&, U&&) -> TGuardValue; + +/** Commonly used to make sure a value is incremented, and then decremented when a scope is exited. */ +template requires (requires(T& Value) { ++Value; --Value; }) +class TScopeCounter : public FNoncopyable +{ +public: + + /** Initializes the TScopeCounter with a reference and increments it. */ + FORCEINLINE constexpr TScopeCounter(T& InReference) + : Reference(InReference) + { + ++Reference; + } + + /** Decrements the referenced value. */ + FORCEINLINE constexpr ~TScopeCounter() + { + --Reference; + } + + /** @return a const reference to the value. */ + FORCEINLINE constexpr const T& Get() const { return Reference; } + +private: + + T& Reference; + +}; + +template +TScopeCounter(T&) -> TScopeCounter; + +NAMESPACE_MODULE_END(Utility) +NAMESPACE_MODULE_END(Redcraft) +NAMESPACE_REDCRAFT_END diff --git a/Redcraft.Utility/Source/Public/Templates/Templates.h b/Redcraft.Utility/Source/Public/Templates/Templates.h index db3a7d6..7e3c0aa 100644 --- a/Redcraft.Utility/Source/Public/Templates/Templates.h +++ b/Redcraft.Utility/Source/Public/Templates/Templates.h @@ -14,3 +14,4 @@ #include "Templates/TypeHash.h" #include "Templates/Function.h" #include "Templates/Atomic.h" +#include "Templates/ScopeHelper.h" diff --git a/Redcraft.Utility/Source/Public/Testing/TemplatesTesting.h b/Redcraft.Utility/Source/Public/Testing/TemplatesTesting.h index 58d567a..3d92d0f 100644 --- a/Redcraft.Utility/Source/Public/Testing/TemplatesTesting.h +++ b/Redcraft.Utility/Source/Public/Testing/TemplatesTesting.h @@ -17,6 +17,7 @@ REDCRAFTUTILITY_API void TestAny(); REDCRAFTUTILITY_API void TestTuple(); REDCRAFTUTILITY_API void TestFunction(); REDCRAFTUTILITY_API void TestAtomic(); +REDCRAFTUTILITY_API void TestScopeHelper(); REDCRAFTUTILITY_API void TestMiscTemplates(); NAMESPACE_END(Testing)