From 977df63d24717836129935b0af60ddc9b781b39d Mon Sep 17 00:00:00 2001 From: _Redstone_c_ Date: Sun, 2 Apr 2023 22:49:15 +0800 Subject: [PATCH] feat(containers): add TList and the corresponding testing --- .../Private/Testing/ContainersTesting.cpp | 94 +++ .../Source/Public/Containers/Containers.h | 1 + .../Source/Public/Containers/List.h | 652 ++++++++++++++++++ .../Source/Public/Testing/ContainersTesting.h | 1 + 4 files changed, 748 insertions(+) create mode 100644 Redcraft.Utility/Source/Public/Containers/List.h diff --git a/Redcraft.Utility/Source/Private/Testing/ContainersTesting.cpp b/Redcraft.Utility/Source/Private/Testing/ContainersTesting.cpp index da74cd7..ad21371 100644 --- a/Redcraft.Utility/Source/Private/Testing/ContainersTesting.cpp +++ b/Redcraft.Utility/Source/Private/Testing/ContainersTesting.cpp @@ -16,6 +16,7 @@ void TestContainers() TestArrayView(); TestBitset(); TestStaticBitset(); + TestList(); } NAMESPACE_UNNAMED_BEGIN @@ -507,6 +508,99 @@ void TestStaticBitset() } } +void TestList() +{ + { + TList ListA; + TList ListB(4); + TList ListC(4, 4); + TList ListD(ListC); + TList ListE(MoveTemp(ListB)); + TList ListF({ 0, 1, 2, 3 }); + + TList ListG; + TList ListH; + TList ListI; + + ListG = ListD; + ListH = MoveTemp(ListE); + ListI = { 0, 1, 2, 3 }; + + always_check((ListC == TList({ 4, 4, 4, 4 }))); + always_check((ListD == TList({ 4, 4, 4, 4 }))); + always_check((ListG == TList({ 4, 4, 4, 4 }))); + always_check((ListF == TList({ 0, 1, 2, 3 }))); + always_check((ListI == TList({ 0, 1, 2, 3 }))); + } + + { + TList ListA = { 1, 2, 3 }; + TList ListB = { 7, 8, 9, 10 }; + TList ListC = { 1, 2, 3 }; + + always_check((!(ListA == ListB))); + always_check(( (ListA != ListB))); + always_check(( (ListA < ListB))); + always_check(( (ListA <= ListB))); + always_check((!(ListA > ListB))); + always_check((!(ListA >= ListB))); + + always_check(( (ListA == ListC))); + always_check((!(ListA != ListC))); + always_check((!(ListA < ListC))); + always_check(( (ListA <= ListC))); + always_check((!(ListA > ListC))); + always_check(( (ListA >= ListC))); + } + + { + TList List = { 1, 2, 3 }; + + List.Insert(++List.Begin(), 2); + always_check((List == TList({ 1, 2, 2, 3 }))); + + List.Insert(List.End(), 2, 4); + always_check((List == TList({ 1, 2, 2, 3, 4, 4 }))); + + List.Insert(List.Begin(), { 1, 1, 4, 5, 1, 4 }); + always_check((List == TList({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3, 4, 4 }))); + + List.Emplace(List.End(), 5); + always_check((List == TList({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3, 4, 4, 5 }))); + + List.Erase(--List.End()); + always_check((List == TList({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3, 4, 4 }))); + + List.Erase(----List.End(), List.End()); + always_check((List == TList({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3 }))); + } + + { + TList List = { 1, 2, 3 }; + + List.PushBack(4); + always_check((List == TList({ 1, 2, 3, 4 }))); + + List.EmplaceBack(5); + always_check((List == TList({ 1, 2, 3, 4, 5 }))); + + List.EmplaceBack(5) = 6; + always_check((List == TList({ 1, 2, 3, 4, 5, 6 }))); + + List.PopBack(); + always_check((List == TList({ 1, 2, 3, 4, 5 }))); + + List.EmplaceFront(1) = 0; + always_check((List == TList({ 0, 1, 2, 3, 4, 5 }))); + + List.PopFront(); + always_check((List == TList({ 1, 2, 3, 4, 5 }))); + + List.SetNum(4); + always_check((List == TList({ 1, 2, 3, 4 }))); + } +} + NAMESPACE_END(Testing) NAMESPACE_MODULE_END(Utility) diff --git a/Redcraft.Utility/Source/Public/Containers/Containers.h b/Redcraft.Utility/Source/Public/Containers/Containers.h index f8426ac..9e3e5b6 100644 --- a/Redcraft.Utility/Source/Public/Containers/Containers.h +++ b/Redcraft.Utility/Source/Public/Containers/Containers.h @@ -7,3 +7,4 @@ #include "Containers/ArrayView.h" #include "Containers/Bitset.h" #include "Containers/StaticBitset.h" +#include "Containers/List.h" diff --git a/Redcraft.Utility/Source/Public/Containers/List.h b/Redcraft.Utility/Source/Public/Containers/List.h new file mode 100644 index 0000000..aeb7077 --- /dev/null +++ b/Redcraft.Utility/Source/Public/Containers/List.h @@ -0,0 +1,652 @@ +#pragma once + +#include "CoreTypes.h" +#include "Memory/Allocator.h" +#include "Templates/Utility.h" +#include "Templates/TypeHash.h" +#include "Templates/Container.h" +#include "Containers/Iterator.h" +#include "TypeTraits/TypeTraits.h" +#include "Miscellaneous/Compare.h" +#include "Memory/MemoryOperator.h" +#include "Memory/ObserverPointer.h" +#include "Miscellaneous/AssertionMacros.h" + +NAMESPACE_REDCRAFT_BEGIN +NAMESPACE_MODULE_BEGIN(Redcraft) +NAMESPACE_MODULE_BEGIN(Utility) + +template Allocator = FHeapAllocator> +class TList final +{ +private: + + struct FNode; + + template + class IteratorImpl; + +public: + + using ElementType = T; + using AllocatorType = Allocator; + + using Reference = T&; + using ConstReference = const T&; + + using Iterator = IteratorImpl; + using ConstIterator = IteratorImpl; + + using ReverseIterator = TReverseIterator< Iterator>; + using ConstReverseIterator = TReverseIterator; + + static_assert(CBidirectionalIterator< Iterator>); + static_assert(CBidirectionalIterator); + + /** Default constructor. Constructs an empty container with a default-constructed allocator. */ + TList() + { + Impl.HeadNode = Impl->Allocate(1); + Impl.HeadNode->PrevNode = Impl.HeadNode; + Impl.HeadNode->NextNode = Impl.HeadNode; + + Impl.ListNum = 0; + } + + /** Constructs the container with 'Count' default instances of T. */ + explicit TList(size_t Count) requires (CDefaultConstructible) : TList() + { + FNode* EndNode = Impl.HeadNode->PrevNode; + + while (Count > Impl.ListNum) + { + FNode* Node = new (Impl->Allocate(1)) FNode; + + EndNode->NextNode = Node; + Node->PrevNode = EndNode; + + ++Impl.ListNum; + + EndNode = Node; + } + + EndNode->NextNode = Impl.HeadNode; + Impl.HeadNode->PrevNode = EndNode; + } + + /** Constructs the container with 'Count' copies of elements with 'InValue'. */ + TList(size_t Count, const ElementType& InValue) requires (CCopyable) : TList() + { + FNode* EndNode = Impl.HeadNode->PrevNode; + + while (Count > Impl.ListNum) + { + FNode* Node = new (Impl->Allocate(1)) FNode(InPlace, InValue); + + EndNode->NextNode = Node; + Node->PrevNode = EndNode; + + ++Impl.ListNum; + + EndNode = Node; + } + + EndNode->NextNode = Impl.HeadNode; + Impl.HeadNode->PrevNode = EndNode; + } + + /** Constructs the container with the contents of the range ['First', 'Last'). */ + template S> requires (CConstructibleFrom>) + TList(I First, S Last) : TList() + { + FNode* EndNode = Impl.HeadNode->PrevNode; + + for (; First != Last; ++First) + { + FNode* Node = new (Impl->Allocate(1)) FNode(InPlace, *First); + + EndNode->NextNode = Node; + Node->PrevNode = EndNode; + + ++Impl.ListNum; + + EndNode = Node; + } + + EndNode->NextNode = Impl.HeadNode; + Impl.HeadNode->PrevNode = EndNode; + } + + /** Copy constructor. Constructs the container with the copy of the contents of 'InValue'. */ + FORCEINLINE TList(const TList& InValue) requires (CCopyConstructible) : TList(InValue.Begin(), InValue.End()) { } + + /** Move constructor. After the move, 'InValue' is guaranteed to be empty. */ + FORCEINLINE TList(TList&& InValue) : TList() { Swap(*this, InValue); } + + /** Constructs the container with the contents of the initializer list. */ + FORCEINLINE TList(initializer_list IL) requires (CCopyConstructible) : TList(Iteration::Begin(IL), Iteration::End(IL)) { } + + /** Destructs the list. The destructors of the elements are called and the used storage is deallocated. */ + ~TList() + { + FNode* NodeToDeallocate = Impl.HeadNode->NextNode; + + Impl->Deallocate(NodeToDeallocate->PrevNode); + + for (size_t Index = 0; Index != Impl.ListNum; ++Index) + { + FNode* NextNode = NodeToDeallocate->NextNode; + + Memory::Destruct(NodeToDeallocate); + Impl->Deallocate(NodeToDeallocate); + + NodeToDeallocate = NextNode; + } + } + + /** Copy assignment operator. Replaces the contents with a copy of the contents of 'InValue'. */ + TList& operator=(const TList& InValue) requires (CCopyable) + { + if (&InValue == this) UNLIKELY return *this; + + Iterator ThisIter = Begin(); + ConstIterator OtherIter = InValue.Begin(); + + while (ThisIter != End() && OtherIter != InValue.End()) + { + *ThisIter = *OtherIter; + + ++ThisIter; + ++OtherIter; + } + + if (ThisIter == End()) + { + while (OtherIter != InValue.End()) + { + EmplaceBack(*OtherIter); + ++OtherIter; + } + } + else if (OtherIter == InValue.End()) + { + Erase(ThisIter, End()); + } + + Impl.ListNum = InValue.Num(); + + return *this; + } + + /** Move assignment operator. After the move, 'InValue' is guaranteed to be empty. */ + FORCEINLINE TList& operator=(TList&& InValue) { Swap(*this, InValue); InValue.Reset(); return *this; } + + /** Replaces the contents with those identified by initializer list. */ + TList& operator=(initializer_list IL) requires (CCopyable) + { + Iterator ThisIter = Begin(); + const ElementType* OtherIter = Iteration::Begin(IL); + + while (ThisIter != End() && OtherIter != Iteration::End(IL)) + { + *ThisIter = *OtherIter; + + ++ThisIter; + ++OtherIter; + } + + if (ThisIter == End()) + { + while (OtherIter != Iteration::End(IL)) + { + EmplaceBack(*OtherIter); + ++OtherIter; + } + } + else if (OtherIter == Iteration::End(IL)) + { + Erase(ThisIter, End()); + } + + Impl.ListNum = GetNum(IL); + + return *this; + } + + /** Compares the contents of two lists. */ + NODISCARD friend bool operator==(const TList& LHS, const TList& RHS) requires (CWeaklyEqualityComparable) + { + if (LHS.Num() != RHS.Num()) return false; + + ConstIterator LHSIter = LHS.Begin(); + ConstIterator RHSIter = RHS.Begin(); + + while (LHSIter != LHS.End()) + { + if (*LHSIter != *RHSIter) return false; + + ++LHSIter; + ++RHSIter; + } + + check(RHSIter == RHS.End()); + + return true; + } + + /** Compares the contents of 'LHS' and 'RHS' lexicographically. */ + NODISCARD friend auto operator<=>(const TList& LHS, const TList& RHS) requires (CSynthThreeWayComparable) + { + ConstIterator LHSIter = LHS.Begin(); + ConstIterator RHSIter = RHS.Begin(); + + while (LHSIter != LHS.End() && RHSIter != RHS.End()) + { + if (const auto Result = SynthThreeWayCompare(*LHSIter, *RHSIter); Result != 0) return Result; + + ++LHSIter; + ++RHSIter; + } + + return LHS.Num() <=> RHS.Num(); + } + + /** Inserts 'InValue' before 'Iter' in the container. */ + FORCEINLINE Iterator Insert(ConstIterator Iter, const ElementType& InValue) requires (CCopyConstructible) { return Emplace(Iter, InValue); } + + /** Inserts 'InValue' before 'Iter' in the container. */ + FORCEINLINE Iterator Insert(ConstIterator Iter, ElementType&& InValue) requires (CMoveConstructible) { return Emplace(Iter, MoveTemp(InValue)); } + + /** Inserts 'Count' copies of the 'InValue' before 'Iter' in the container. */ + Iterator Insert(ConstIterator Iter, size_t Count, const ElementType& InValue) requires (CCopyConstructible) + { + if (Count == 0) return Iterator(Iter.Pointer); + + FNode* InsertNode = Iter.Pointer->PrevNode; + + const auto InsertOnce = [&]() -> FNode* + { + FNode* Node = new (Impl->Allocate(1)) FNode(InPlace, InValue); + + InsertNode->NextNode = Node; + Node->PrevNode = InsertNode; + + ++Impl.ListNum; + + InsertNode = Node; + + return Node; + }; + + FNode* FirstNode = InsertOnce(); + + for (size_t Index = 0; Index != Count - 1; ++Index) + { + InsertOnce(); + } + + InsertNode->NextNode = Iter.Pointer; + Iter.Pointer->PrevNode = InsertNode; + + return Iterator(FirstNode); + } + + /** Inserts elements from range ['First', 'Last') before 'Iter'. */ + template S> requires (CConstructibleFrom>) + Iterator Insert(ConstIterator Iter, I First, S Last) + { + if (First == Last) return Iterator(Iter.Pointer); + + FNode* InsertNode = Iter.Pointer->PrevNode; + + const auto InsertOnce = [&]() -> FNode* + { + FNode* Node = new (Impl->Allocate(1)) FNode(InPlace, *First); + + InsertNode->NextNode = Node; + Node->PrevNode = InsertNode; + + ++Impl.ListNum; + + InsertNode = Node; + + return Node; + }; + + FNode* FirstNode = InsertOnce(); + + for (++First; First != Last; ++First) + { + InsertOnce(); + } + + InsertNode->NextNode = Iter.Pointer; + Iter.Pointer->PrevNode = InsertNode; + + return Iterator(FirstNode); + } + + /** Inserts elements from initializer list before 'Iter' in the container. */ + FORCEINLINE Iterator Insert(ConstIterator Iter, initializer_list IL) requires (CCopyConstructible) { return Insert(Iter, Iteration::Begin(IL), Iteration::End(IL)); } + + /** Inserts a new element into the container directly before 'Iter'. */ + template requires (CConstructibleFrom) + Iterator Emplace(ConstIterator Iter, Ts&&... Args) + { + FNode* Node = new (Impl->Allocate(1)) FNode(InPlace, Forward(Args)...); + + ++Impl.ListNum; + + Node->PrevNode = Iter.Pointer->PrevNode; + Node->NextNode = Iter.Pointer; + + Node->PrevNode->NextNode = Node; + Node->NextNode->PrevNode = Node; + + return Iterator(Node); + } + + /** Removes the element at 'Iter' in the container. */ + Iterator Erase(ConstIterator Iter) + { + FNode* NodeToErase = Iter.Pointer; + + checkf(NodeToErase->NextNode != NodeToErase, TEXT("Read access violation. Please check Iter != End().")); + + NodeToErase->PrevNode->NextNode = NodeToErase->NextNode; + NodeToErase->NextNode->PrevNode = NodeToErase->PrevNode; + + FNode* NextNode = NodeToErase->NextNode; + + Memory::Destruct(NodeToErase); + Impl->Deallocate(NodeToErase); + + --Impl.ListNum; + + return Iterator(NextNode); + } + + /** Removes the elements in the range ['First', 'Last') in the container. */ + Iterator Erase(ConstIterator First, ConstIterator Last) + { + FNode* FirstToErase = First.Pointer; + FNode* LastToErase = Last.Pointer; + + FirstToErase->PrevNode->NextNode = LastToErase; + LastToErase->PrevNode = FirstToErase->PrevNode; + + while (FirstToErase != LastToErase) + { + FNode* NextNode = FirstToErase->NextNode; + + Memory::Destruct(FirstToErase); + Impl->Deallocate(FirstToErase); + + --Impl.ListNum; + + FirstToErase = NextNode; + } + + return Iterator(LastToErase); + } + + /** Appends the given element value to the end of the container. */ + FORCEINLINE void PushBack(const ElementType& InValue) requires (CCopyConstructible) { EmplaceBack(InValue); } + + /** Appends the given element value to the end of the container. */ + FORCEINLINE void PushBack(ElementType&& InValue) requires (CMoveConstructible) { EmplaceBack(MoveTemp(InValue)); } + + /** Appends a new element to the end of the container. */ + template requires (CConstructibleFrom) + FORCEINLINE Reference EmplaceBack(Ts&&... Args) { return *Emplace(End(), Forward(Args)...); } + + /** Removes the last element of the container. The list cannot be empty. */ + FORCEINLINE void PopBack() { Erase(--End()); } + + /** Prepends the given element value to the beginning of the container. */ + FORCEINLINE void PushFront(const ElementType& InValue) requires (CCopyConstructible) { EmplaceFront(InValue); } + + /** Prepends the given element value to the beginning of the container. */ + FORCEINLINE void PushFront(ElementType&& InValue) requires (CMoveConstructible) { EmplaceFront(MoveTemp(InValue)); } + + /** Prepends a new element to the beginning of the container. */ + template requires (CConstructibleFrom) + FORCEINLINE Reference EmplaceFront(Ts&&... Args) { return *Emplace(Begin(), Forward(Args)...); } + + /** Removes the first element of the container. The list cannot be empty. */ + FORCEINLINE void PopFront() { Erase(Begin()); } + + /** Resizes the container to contain 'Count' elements. Additional default elements are appended. */ + void SetNum(size_t Count) requires (CDefaultConstructible) + { + if (Count == Impl.ListNum) return; + + if (Count < Impl.ListNum) + { + Iterator First = End(); + + Iteration::Advance(First, Count - Impl.ListNum); + + Erase(First, End()); + + Impl.ListNum = Count; + + return; + } + + FNode* EndNode = Impl.HeadNode->PrevNode; + + while (Count > Impl.ListNum) + { + FNode* Node = new (Impl->Allocate(1)) FNode; + + EndNode->NextNode = Node; + Node->PrevNode = EndNode; + + ++Impl.ListNum; + + EndNode = Node; + } + + EndNode->NextNode = Impl.HeadNode; + Impl.HeadNode->PrevNode = EndNode; + } + + /** Resizes the container to contain 'Count' elements. Additional copies of 'InValue' are appended. */ + void SetNum(size_t Count, const ElementType& InValue) requires (CCopyConstructible) + { + if (Count == Impl.ListNum) return; + + if (Count < Impl.ListNum) + { + Iterator First = End(); + + Iteration::Advance(First, Count - Impl.ListNum); + + Erase(First, End()); + + Impl.ListNum = Count; + + return; + } + + FNode* EndNode = Impl.HeadNode->PrevNode; + + while (Count > Impl.ListNum) + { + FNode* Node = new (Impl->Allocate(1)) FNode(InPlace, InValue); + + EndNode->NextNode = Node; + Node->PrevNode = EndNode; + + ++Impl.ListNum; + + EndNode = Node; + } + + EndNode->NextNode = Impl.HeadNode; + Impl.HeadNode->PrevNode = EndNode; + } + + /** @return The iterator to the first or end element. */ + NODISCARD FORCEINLINE Iterator Begin() { return Iterator(Impl.HeadNode->NextNode); } + NODISCARD FORCEINLINE ConstIterator Begin() const { return ConstIterator(Impl.HeadNode->NextNode); } + NODISCARD FORCEINLINE Iterator End() { return Iterator(Impl.HeadNode); } + NODISCARD FORCEINLINE ConstIterator End() const { return ConstIterator(Impl.HeadNode); } + + /** @return The reverse iterator to the first or end element. */ + NODISCARD FORCEINLINE ReverseIterator RBegin() { return ReverseIterator(End()); } + NODISCARD FORCEINLINE ConstReverseIterator RBegin() const { return ConstReverseIterator(End()); } + NODISCARD FORCEINLINE ReverseIterator REnd() { return ReverseIterator(Begin()); } + NODISCARD FORCEINLINE ConstReverseIterator REnd() const { return ConstReverseIterator(Begin()); } + + /** @return The number of elements in the container. */ + NODISCARD FORCEINLINE size_t Num() const { return Impl.ListNum; } + + /** @return true if the container is empty, false otherwise. */ + NODISCARD FORCEINLINE bool IsEmpty() const { return Num() == 0; } + + /** @return true if the iterator is valid, false otherwise. */ + NODISCARD FORCEINLINE bool IsValidIterator(ConstIterator Iter) const + { + FNode* Current = Impl.HeadNode; + + for (size_t Index = 0; Index != Impl.ListNum + 1; ++Index) + { + if (Current == Iter.Pointer) + { + return true; + } + + Current = Current->NextNode; + } + + return false; + } + + /** @return The reference to the first or last element. */ + NODISCARD FORCEINLINE ElementType& Front() { return *Begin(); } + NODISCARD FORCEINLINE const ElementType& Front() const { return *Begin(); } + NODISCARD FORCEINLINE ElementType& Back() { return *(End() - 1); } + NODISCARD FORCEINLINE const ElementType& Back() const { return *(End() - 1); } + + /** Erases all elements from the container. After this call, Num() returns zero. */ + void Reset() + { + FNode* NodeToDeallocate = Impl.HeadNode->NextNode; + + Impl.HeadNode->PrevNode = Impl.HeadNode; + Impl.HeadNode->NextNode = Impl.HeadNode; + + for (size_t Index = 0; Index != Impl.ListNum; ++Index) + { + FNode* NextNode = NodeToDeallocate->NextNode; + + Memory::Destruct(NodeToDeallocate); + Impl->Deallocate(NodeToDeallocate); + + NodeToDeallocate = NextNode; + } + + Impl.ListNum = 0; + } + + /** Overloads the GetTypeHash algorithm for TList. */ + NODISCARD friend FORCEINLINE size_t GetTypeHash(const TList& A) requires (CHashable) + { + size_t Result = 0; + + for (const ElementType& Element : A) + { + Result = HashCombine(Result, GetTypeHash(Element)); + } + + return Result; + } + + /** Overloads the Swap algorithm for TList. */ + friend FORCEINLINE void Swap(TList& A, TList& B) + { + Swap(A.Impl.HeadNode, B.Impl.HeadNode); + Swap(A.Impl.ListNum, B.Impl.ListNum); + } + + ENABLE_RANGE_BASED_FOR_LOOP_SUPPORT + +private: + + struct FNode + { + FNode* PrevNode; + FNode* NextNode; + ElementType Value; + + FORCEINLINE FNode() = default; + + template + FORCEINLINE FNode(FInPlace, Ts&&... Args) : Value(Forward(Args)...) { } + }; + + static_assert(CMultipleAllocator); + + ALLOCATOR_WRAPPER_BEGIN(AllocatorType, FNode, Impl) + { + FNode* HeadNode; + size_t ListNum; + } + ALLOCATOR_WRAPPER_END(AllocatorType, FNode, Impl) + + template + class IteratorImpl + { + public: + + using ElementType = TConditional; + + FORCEINLINE IteratorImpl() = default; + + FORCEINLINE IteratorImpl(const IteratorImpl& InValue) requires (bConst) + : Pointer(InValue.Pointer) + { } + + FORCEINLINE IteratorImpl(const IteratorImpl&) = default; + FORCEINLINE IteratorImpl(IteratorImpl&&) = default; + FORCEINLINE IteratorImpl& operator=(const IteratorImpl&) = default; + FORCEINLINE IteratorImpl& operator=(IteratorImpl&&) = default; + + NODISCARD friend FORCEINLINE bool operator==(const IteratorImpl& LHS, const IteratorImpl& RHS) { return LHS.Pointer == RHS.Pointer; } + + NODISCARD FORCEINLINE ElementType& operator*() const { return Pointer->Value; } + NODISCARD FORCEINLINE ElementType* operator->() const { return &Pointer->Value; } + + FORCEINLINE IteratorImpl& operator++() { Pointer = Pointer->NextNode; return *this; } + FORCEINLINE IteratorImpl& operator--() { Pointer = Pointer->PrevNode; return *this; } + + FORCEINLINE IteratorImpl operator++(int) { IteratorImpl Temp = *this; ++*this; return Temp; } + FORCEINLINE IteratorImpl operator--(int) { IteratorImpl Temp = *this; --*this; return Temp; } + + private: + + FNode* Pointer = nullptr; + + FORCEINLINE IteratorImpl(FNode* InPointer) + : Pointer(InPointer) + { } + + template friend class IteratorImpl; + + friend TList; + + }; + +}; + +template +TList(I, S) -> TList>; + +template +TList(initializer_list) -> TList; + +NAMESPACE_MODULE_END(Utility) +NAMESPACE_MODULE_END(Redcraft) +NAMESPACE_REDCRAFT_END diff --git a/Redcraft.Utility/Source/Public/Testing/ContainersTesting.h b/Redcraft.Utility/Source/Public/Testing/ContainersTesting.h index 41cd20c..afdc249 100644 --- a/Redcraft.Utility/Source/Public/Testing/ContainersTesting.h +++ b/Redcraft.Utility/Source/Public/Testing/ContainersTesting.h @@ -14,6 +14,7 @@ REDCRAFTUTILITY_API void TestStaticArray(); REDCRAFTUTILITY_API void TestArrayView(); REDCRAFTUTILITY_API void TestBitset(); REDCRAFTUTILITY_API void TestStaticBitset(); +REDCRAFTUTILITY_API void TestList(); NAMESPACE_END(Testing)