From 218862282b468af43559b118b08c9b130e93ae06 Mon Sep 17 00:00:00 2001 From: Pasukhin Dmitry Date: Wed, 28 Jan 2026 16:42:43 +0000 Subject: [PATCH] Foundation Classes - Add Emplace methods to NCollection containers (#1035) Add in-place construction support to sequential and array containers, following the pattern already established in map containers. New methods added: - NCollection_List: EmplaceAppend, EmplacePrepend, EmplaceBefore, EmplaceAfter - NCollection_Sequence: EmplaceAppend, EmplacePrepend, EmplaceAfter, EmplaceBefore - NCollection_DynamicArray: EmplaceAppend, EmplaceValue - NCollection_Array1: EmplaceValue - NCollection_Array2: EmplaceValue NCollection_Sequence::Node class extended with in-place constructor to support the new Emplace methods. All methods use perfect forwarding to construct elements in-place, avoiding unnecessary copies or moves. This is particularly useful for: - Types with expensive copy/move operations - Types with multiple constructor arguments - Move-only types (non-copyable) Added corresponding unit tests for all new methods. --- .../GTests/NCollection_Array1_Test.cxx | 62 ++++++ .../GTests/NCollection_Array2_Test.cxx | 63 +++++++ .../TKernel/GTests/NCollection_List_Test.cxx | 145 ++++++++++++++ .../GTests/NCollection_Sequence_Test.cxx | 153 +++++++++++++++ .../GTests/NCollection_Vector_Test.cxx | 178 ++++++++++++++++++ .../NCollection/NCollection_Array1.hxx | 15 ++ .../NCollection/NCollection_Array2.hxx | 16 ++ .../NCollection/NCollection_DynamicArray.hxx | 62 +++++- .../TKernel/NCollection/NCollection_List.hxx | 50 +++++ .../NCollection/NCollection_Sequence.hxx | 66 +++++++ 10 files changed, 808 insertions(+), 2 deletions(-) diff --git a/src/FoundationClasses/TKernel/GTests/NCollection_Array1_Test.cxx b/src/FoundationClasses/TKernel/GTests/NCollection_Array1_Test.cxx index 1bb2fec6ad..3339ad9f70 100644 --- a/src/FoundationClasses/TKernel/GTests/NCollection_Array1_Test.cxx +++ b/src/FoundationClasses/TKernel/GTests/NCollection_Array1_Test.cxx @@ -413,3 +413,65 @@ TEST(NCollection_Array1Test, STLAlgorithmCompatibility_Sort) EXPECT_TRUE(std::equal(anArray.begin(), anArray.end(), aVector.begin())); } + +// Helper struct for testing in-place construction with multiple arguments +struct Array1MultiArgType +{ + int myA; + double myB; + + Array1MultiArgType() + : myA(0), + myB(0.0) + { + } + + Array1MultiArgType(int theA, double theB) + : myA(theA), + myB(theB) + { + } +}; + +TEST(NCollection_Array1Test, EmplaceValue) +{ + NCollection_Array1 anArray(1, 5); + + // Test EmplaceValue with multiple constructor arguments + Array1MultiArgType& aRef1 = anArray.EmplaceValue(1, 42, 3.14); + EXPECT_EQ(42, aRef1.myA); + EXPECT_NEAR(3.14, aRef1.myB, 1e-10); + + Array1MultiArgType& aRef2 = anArray.EmplaceValue(3, 100, 2.71); + EXPECT_EQ(100, aRef2.myA); + EXPECT_NEAR(2.71, aRef2.myB, 1e-10); + + // Verify the values are in the array + EXPECT_EQ(42, anArray(1).myA); + EXPECT_EQ(100, anArray(3).myA); + + // Verify other elements are default-constructed + EXPECT_EQ(0, anArray(2).myA); + EXPECT_EQ(0, anArray(4).myA); + EXPECT_EQ(0, anArray(5).myA); +} + +TEST(NCollection_Array1Test, EmplaceValue_ReplacesExisting) +{ + NCollection_Array1 anArray(1, 3); + + // Set initial values + anArray.EmplaceValue(1, 10, 1.0); + anArray.EmplaceValue(2, 20, 2.0); + anArray.EmplaceValue(3, 30, 3.0); + + // Replace value at index 2 + Array1MultiArgType& aRef = anArray.EmplaceValue(2, 200, 20.0); + EXPECT_EQ(200, aRef.myA); + EXPECT_NEAR(20.0, aRef.myB, 1e-10); + + // Verify other values unchanged + EXPECT_EQ(10, anArray(1).myA); + EXPECT_EQ(200, anArray(2).myA); + EXPECT_EQ(30, anArray(3).myA); +} diff --git a/src/FoundationClasses/TKernel/GTests/NCollection_Array2_Test.cxx b/src/FoundationClasses/TKernel/GTests/NCollection_Array2_Test.cxx index 1d01bb0fe4..7bb484adf1 100644 --- a/src/FoundationClasses/TKernel/GTests/NCollection_Array2_Test.cxx +++ b/src/FoundationClasses/TKernel/GTests/NCollection_Array2_Test.cxx @@ -318,4 +318,67 @@ TEST(NCollection_Array2Test, ReIndex_Interference) // The total number of rows should never change during these operations. EXPECT_EQ(anInitialNbRows, anArray.NbRows()); +} + +// Helper struct for testing in-place construction with multiple arguments +struct Array2MultiArgType +{ + int myA; + double myB; + + Array2MultiArgType() + : myA(0), + myB(0.0) + { + } + + Array2MultiArgType(int theA, double theB) + : myA(theA), + myB(theB) + { + } +}; + +TEST(NCollection_Array2Test, EmplaceValue) +{ + NCollection_Array2 anArray(1, 3, 1, 4); + + // Test EmplaceValue with multiple constructor arguments + Array2MultiArgType& aRef1 = anArray.EmplaceValue(1, 1, 42, 3.14); + EXPECT_EQ(42, aRef1.myA); + EXPECT_NEAR(3.14, aRef1.myB, 1e-10); + + Array2MultiArgType& aRef2 = anArray.EmplaceValue(2, 3, 100, 2.71); + EXPECT_EQ(100, aRef2.myA); + EXPECT_NEAR(2.71, aRef2.myB, 1e-10); + + // Verify the values are in the array + EXPECT_EQ(42, anArray(1, 1).myA); + EXPECT_EQ(100, anArray(2, 3).myA); + + // Verify other elements are default-constructed + EXPECT_EQ(0, anArray(1, 2).myA); + EXPECT_EQ(0, anArray(3, 4).myA); +} + +TEST(NCollection_Array2Test, EmplaceValue_ReplacesExisting) +{ + NCollection_Array2 anArray(1, 2, 1, 2); + + // Set initial values + anArray.EmplaceValue(1, 1, 11, 1.1); + anArray.EmplaceValue(1, 2, 12, 1.2); + anArray.EmplaceValue(2, 1, 21, 2.1); + anArray.EmplaceValue(2, 2, 22, 2.2); + + // Replace value at (1, 2) + Array2MultiArgType& aRef = anArray.EmplaceValue(1, 2, 120, 12.0); + EXPECT_EQ(120, aRef.myA); + EXPECT_NEAR(12.0, aRef.myB, 1e-10); + + // Verify other values unchanged + EXPECT_EQ(11, anArray(1, 1).myA); + EXPECT_EQ(120, anArray(1, 2).myA); + EXPECT_EQ(21, anArray(2, 1).myA); + EXPECT_EQ(22, anArray(2, 2).myA); } \ No newline at end of file diff --git a/src/FoundationClasses/TKernel/GTests/NCollection_List_Test.cxx b/src/FoundationClasses/TKernel/GTests/NCollection_List_Test.cxx index 8aa82a4b42..9548aebde3 100644 --- a/src/FoundationClasses/TKernel/GTests/NCollection_List_Test.cxx +++ b/src/FoundationClasses/TKernel/GTests/NCollection_List_Test.cxx @@ -484,4 +484,149 @@ TEST_F(NCollection_ListTest, OCC25348_AssignDoesNotChangeAllocator) EXPECT_EQ(1, aList1.Size()); EXPECT_EQ(i, aList1.First()); } +} + +// Helper struct for testing in-place construction with multiple arguments +struct MultiArgType +{ + int myA; + double myB; + + MultiArgType(int theA, double theB) + : myA(theA), + myB(theB) + { + } +}; + +// Helper struct for testing move-only types +struct MoveOnlyType +{ + int myValue; + + explicit MoveOnlyType(int theValue) + : myValue(theValue) + { + } + + MoveOnlyType(MoveOnlyType&& theOther) noexcept + : myValue(theOther.myValue) + { + theOther.myValue = 0; + } + + MoveOnlyType& operator=(MoveOnlyType&& theOther) noexcept + { + myValue = theOther.myValue; + theOther.myValue = 0; + return *this; + } + + MoveOnlyType(const MoveOnlyType&) = delete; + MoveOnlyType& operator=(const MoveOnlyType&) = delete; +}; + +TEST_F(NCollection_ListTest, EmplaceAppend) +{ + NCollection_List aList; + + // Test EmplaceAppend with multiple constructor arguments + MultiArgType& aRef1 = aList.EmplaceAppend(42, 3.14); + EXPECT_EQ(42, aRef1.myA); + EXPECT_NEAR(3.14, aRef1.myB, 1e-10); + EXPECT_EQ(1, aList.Size()); + + MultiArgType& aRef2 = aList.EmplaceAppend(100, 2.71); + EXPECT_EQ(100, aRef2.myA); + EXPECT_NEAR(2.71, aRef2.myB, 1e-10); + EXPECT_EQ(2, aList.Size()); + + // Verify the order + EXPECT_EQ(42, aList.First().myA); + EXPECT_EQ(100, aList.Last().myA); +} + +TEST_F(NCollection_ListTest, EmplacePrepend) +{ + NCollection_List aList; + + // Test EmplacePrepend with multiple constructor arguments + MultiArgType& aRef1 = aList.EmplacePrepend(42, 3.14); + EXPECT_EQ(42, aRef1.myA); + EXPECT_NEAR(3.14, aRef1.myB, 1e-10); + EXPECT_EQ(1, aList.Size()); + + MultiArgType& aRef2 = aList.EmplacePrepend(100, 2.71); + EXPECT_EQ(100, aRef2.myA); + EXPECT_NEAR(2.71, aRef2.myB, 1e-10); + EXPECT_EQ(2, aList.Size()); + + // Verify the order (prepended items should be at the front) + EXPECT_EQ(100, aList.First().myA); + EXPECT_EQ(42, aList.Last().myA); +} + +TEST_F(NCollection_ListTest, EmplaceBefore) +{ + NCollection_List aList; + aList.EmplaceAppend(10, 1.0); + aList.EmplaceAppend(30, 3.0); + + // Get iterator to second element + NCollection_List::Iterator anIter(aList); + anIter.Next(); + + // Emplace before the second element + MultiArgType& aRef = aList.EmplaceBefore(anIter, 20, 2.0); + EXPECT_EQ(20, aRef.myA); + EXPECT_NEAR(2.0, aRef.myB, 1e-10); + + // Verify the order + EXPECT_EQ(3, aList.Size()); + NCollection_List::Iterator aCheckIter(aList); + EXPECT_EQ(10, aCheckIter.Value().myA); + aCheckIter.Next(); + EXPECT_EQ(20, aCheckIter.Value().myA); + aCheckIter.Next(); + EXPECT_EQ(30, aCheckIter.Value().myA); +} + +TEST_F(NCollection_ListTest, EmplaceAfter) +{ + NCollection_List aList; + aList.EmplaceAppend(10, 1.0); + aList.EmplaceAppend(30, 3.0); + + // Get iterator to first element + NCollection_List::Iterator anIter(aList); + + // Emplace after the first element + MultiArgType& aRef = aList.EmplaceAfter(anIter, 20, 2.0); + EXPECT_EQ(20, aRef.myA); + EXPECT_NEAR(2.0, aRef.myB, 1e-10); + + // Verify the order + EXPECT_EQ(3, aList.Size()); + NCollection_List::Iterator aCheckIter(aList); + EXPECT_EQ(10, aCheckIter.Value().myA); + aCheckIter.Next(); + EXPECT_EQ(20, aCheckIter.Value().myA); + aCheckIter.Next(); + EXPECT_EQ(30, aCheckIter.Value().myA); +} + +TEST_F(NCollection_ListTest, EmplaceWithMoveOnlyType) +{ + NCollection_List aList; + + // Test EmplaceAppend with move-only type + MoveOnlyType& aRef1 = aList.EmplaceAppend(42); + EXPECT_EQ(42, aRef1.myValue); + + MoveOnlyType& aRef2 = aList.EmplacePrepend(100); + EXPECT_EQ(100, aRef2.myValue); + + EXPECT_EQ(2, aList.Size()); + EXPECT_EQ(100, aList.First().myValue); + EXPECT_EQ(42, aList.Last().myValue); } \ No newline at end of file diff --git a/src/FoundationClasses/TKernel/GTests/NCollection_Sequence_Test.cxx b/src/FoundationClasses/TKernel/GTests/NCollection_Sequence_Test.cxx index fb89b74f14..0db9cbc653 100644 --- a/src/FoundationClasses/TKernel/GTests/NCollection_Sequence_Test.cxx +++ b/src/FoundationClasses/TKernel/GTests/NCollection_Sequence_Test.cxx @@ -459,4 +459,157 @@ TEST(NCollection_SequenceTest, OCC26448_PrependEmptySequence) aTSeq1.Prepend(aTSeq2); // Prepend empty sequence EXPECT_EQ(aTSeq1.Size(), 1); EXPECT_DOUBLE_EQ(aTSeq1.First(), 11.0); +} + +// Helper struct for testing in-place construction with multiple arguments +struct SeqMultiArgType +{ + int myA; + double myB; + + SeqMultiArgType(int theA, double theB) + : myA(theA), + myB(theB) + { + } +}; + +// Helper struct for testing move-only types +struct SeqMoveOnlyType +{ + int myValue; + + explicit SeqMoveOnlyType(int theValue) + : myValue(theValue) + { + } + + SeqMoveOnlyType(SeqMoveOnlyType&& theOther) noexcept + : myValue(theOther.myValue) + { + theOther.myValue = 0; + } + + SeqMoveOnlyType& operator=(SeqMoveOnlyType&& theOther) noexcept + { + myValue = theOther.myValue; + theOther.myValue = 0; + return *this; + } + + SeqMoveOnlyType(const SeqMoveOnlyType&) = delete; + SeqMoveOnlyType& operator=(const SeqMoveOnlyType&) = delete; +}; + +TEST(NCollection_SequenceTest, EmplaceAppend) +{ + NCollection_Sequence aSeq; + + // Test EmplaceAppend with multiple constructor arguments + SeqMultiArgType& aRef1 = aSeq.EmplaceAppend(42, 3.14); + EXPECT_EQ(42, aRef1.myA); + EXPECT_NEAR(3.14, aRef1.myB, 1e-10); + EXPECT_EQ(1, aSeq.Size()); + + SeqMultiArgType& aRef2 = aSeq.EmplaceAppend(100, 2.71); + EXPECT_EQ(100, aRef2.myA); + EXPECT_NEAR(2.71, aRef2.myB, 1e-10); + EXPECT_EQ(2, aSeq.Size()); + + // Verify the order (1-based indexing) + EXPECT_EQ(42, aSeq(1).myA); + EXPECT_EQ(100, aSeq(2).myA); +} + +TEST(NCollection_SequenceTest, EmplacePrepend) +{ + NCollection_Sequence aSeq; + + // Test EmplacePrepend with multiple constructor arguments + SeqMultiArgType& aRef1 = aSeq.EmplacePrepend(42, 3.14); + EXPECT_EQ(42, aRef1.myA); + EXPECT_NEAR(3.14, aRef1.myB, 1e-10); + EXPECT_EQ(1, aSeq.Size()); + + SeqMultiArgType& aRef2 = aSeq.EmplacePrepend(100, 2.71); + EXPECT_EQ(100, aRef2.myA); + EXPECT_NEAR(2.71, aRef2.myB, 1e-10); + EXPECT_EQ(2, aSeq.Size()); + + // Verify the order (prepended items should be at the front) + EXPECT_EQ(100, aSeq.First().myA); + EXPECT_EQ(42, aSeq.Last().myA); +} + +TEST(NCollection_SequenceTest, EmplaceAfterIterator) +{ + NCollection_Sequence aSeq; + aSeq.EmplaceAppend(10, 1.0); + aSeq.EmplaceAppend(30, 3.0); + + // Get iterator to first element + NCollection_Sequence::Iterator anIter(aSeq); + + // Emplace after the first element + SeqMultiArgType& aRef = aSeq.EmplaceAfter(anIter, 20, 2.0); + EXPECT_EQ(20, aRef.myA); + EXPECT_NEAR(2.0, aRef.myB, 1e-10); + + // Verify the order + EXPECT_EQ(3, aSeq.Size()); + EXPECT_EQ(10, aSeq(1).myA); + EXPECT_EQ(20, aSeq(2).myA); + EXPECT_EQ(30, aSeq(3).myA); +} + +TEST(NCollection_SequenceTest, EmplaceAfterIndex) +{ + NCollection_Sequence aSeq; + aSeq.EmplaceAppend(10, 1.0); + aSeq.EmplaceAppend(30, 3.0); + + // Emplace after index 1 + SeqMultiArgType& aRef = aSeq.EmplaceAfter(1, 20, 2.0); + EXPECT_EQ(20, aRef.myA); + EXPECT_NEAR(2.0, aRef.myB, 1e-10); + + // Verify the order + EXPECT_EQ(3, aSeq.Size()); + EXPECT_EQ(10, aSeq(1).myA); + EXPECT_EQ(20, aSeq(2).myA); + EXPECT_EQ(30, aSeq(3).myA); +} + +TEST(NCollection_SequenceTest, EmplaceBeforeIndex) +{ + NCollection_Sequence aSeq; + aSeq.EmplaceAppend(10, 1.0); + aSeq.EmplaceAppend(30, 3.0); + + // Emplace before index 2 + SeqMultiArgType& aRef = aSeq.EmplaceBefore(2, 20, 2.0); + EXPECT_EQ(20, aRef.myA); + EXPECT_NEAR(2.0, aRef.myB, 1e-10); + + // Verify the order + EXPECT_EQ(3, aSeq.Size()); + EXPECT_EQ(10, aSeq(1).myA); + EXPECT_EQ(20, aSeq(2).myA); + EXPECT_EQ(30, aSeq(3).myA); +} + +TEST(NCollection_SequenceTest, EmplaceWithMoveOnlyType) +{ + NCollection_Sequence aSeq; + + // Test EmplaceAppend with move-only type + SeqMoveOnlyType& aRef1 = aSeq.EmplaceAppend(42); + EXPECT_EQ(42, aRef1.myValue); + + SeqMoveOnlyType& aRef2 = aSeq.EmplacePrepend(100); + EXPECT_EQ(100, aRef2.myValue); + + EXPECT_EQ(2, aSeq.Size()); + EXPECT_EQ(100, aSeq.First().myValue); + EXPECT_EQ(42, aSeq.Last().myValue); } \ No newline at end of file diff --git a/src/FoundationClasses/TKernel/GTests/NCollection_Vector_Test.cxx b/src/FoundationClasses/TKernel/GTests/NCollection_Vector_Test.cxx index ec4463539d..3a2dd250d4 100644 --- a/src/FoundationClasses/TKernel/GTests/NCollection_Vector_Test.cxx +++ b/src/FoundationClasses/TKernel/GTests/NCollection_Vector_Test.cxx @@ -466,4 +466,182 @@ TEST(NCollection_VectorTest, STLAlgorithmCompatibility_Sort) std::sort(aStdVector.begin(), aStdVector.end()); EXPECT_TRUE(std::equal(aVector.begin(), aVector.end(), aStdVector.begin())); +} + +// Helper struct for testing in-place construction with multiple arguments +struct VecMultiArgType +{ + int myA; + double myB; + + VecMultiArgType() + : myA(0), + myB(0.0) + { + } + + VecMultiArgType(int theA, double theB) + : myA(theA), + myB(theB) + { + } +}; + +// Helper struct for testing move-only types +struct VecMoveOnlyType +{ + int myValue; + + VecMoveOnlyType() + : myValue(0) + { + } + + explicit VecMoveOnlyType(int theValue) + : myValue(theValue) + { + } + + VecMoveOnlyType(VecMoveOnlyType&& theOther) noexcept + : myValue(theOther.myValue) + { + theOther.myValue = 0; + } + + VecMoveOnlyType& operator=(VecMoveOnlyType&& theOther) noexcept + { + myValue = theOther.myValue; + theOther.myValue = 0; + return *this; + } + + VecMoveOnlyType(const VecMoveOnlyType&) = delete; + VecMoveOnlyType& operator=(const VecMoveOnlyType&) = delete; +}; + +TEST(NCollection_VectorTest, EmplaceAppend) +{ + NCollection_Vector aVector; + + // Test EmplaceAppend with multiple constructor arguments + VecMultiArgType& aRef1 = aVector.EmplaceAppend(42, 3.14); + EXPECT_EQ(42, aRef1.myA); + EXPECT_NEAR(3.14, aRef1.myB, 1e-10); + EXPECT_EQ(1, aVector.Length()); + + VecMultiArgType& aRef2 = aVector.EmplaceAppend(100, 2.71); + EXPECT_EQ(100, aRef2.myA); + EXPECT_NEAR(2.71, aRef2.myB, 1e-10); + EXPECT_EQ(2, aVector.Length()); + + // Verify the order (0-based indexing) + EXPECT_EQ(42, aVector(0).myA); + EXPECT_EQ(100, aVector(1).myA); +} + +TEST(NCollection_VectorTest, EmplaceValue) +{ + NCollection_Vector aVector; + + // Test EmplaceValue at index 0 + VecMultiArgType& aRef1 = aVector.EmplaceValue(0, 10, 1.0); + EXPECT_EQ(10, aRef1.myA); + EXPECT_NEAR(1.0, aRef1.myB, 1e-10); + EXPECT_EQ(1, aVector.Length()); + + // Test EmplaceValue at index beyond current size (should fill with default values) + VecMultiArgType& aRef2 = aVector.EmplaceValue(3, 40, 4.0); + EXPECT_EQ(40, aRef2.myA); + EXPECT_NEAR(4.0, aRef2.myB, 1e-10); + EXPECT_EQ(4, aVector.Length()); + + // Check that intermediate elements were default-constructed + EXPECT_EQ(0, aVector(1).myA); + EXPECT_NEAR(0.0, aVector(1).myB, 1e-10); + EXPECT_EQ(0, aVector(2).myA); + EXPECT_NEAR(0.0, aVector(2).myB, 1e-10); +} + +TEST(NCollection_VectorTest, EmplaceValue_ReplacesExisting) +{ + NCollection_Vector aVector; + + // Set initial values + aVector.EmplaceValue(0, 10, 1.0); + aVector.EmplaceValue(1, 20, 2.0); + aVector.EmplaceValue(2, 30, 3.0); + + EXPECT_EQ(3, aVector.Length()); + + // Replace value at index 1 (existing element) + VecMultiArgType& aRef = aVector.EmplaceValue(1, 200, 20.0); + EXPECT_EQ(200, aRef.myA); + EXPECT_NEAR(20.0, aRef.myB, 1e-10); + + // Verify other values unchanged and size unchanged + EXPECT_EQ(3, aVector.Length()); + EXPECT_EQ(10, aVector(0).myA); + EXPECT_EQ(200, aVector(1).myA); + EXPECT_EQ(30, aVector(2).myA); +} + +TEST(NCollection_VectorTest, EmplaceWithMoveOnlyType) +{ + NCollection_Vector aVector; + + // Test EmplaceAppend with move-only type + VecMoveOnlyType& aRef1 = aVector.EmplaceAppend(42); + EXPECT_EQ(42, aRef1.myValue); + + VecMoveOnlyType& aRef2 = aVector.EmplaceAppend(100); + EXPECT_EQ(100, aRef2.myValue); + + EXPECT_EQ(2, aVector.Length()); + EXPECT_EQ(42, aVector(0).myValue); + EXPECT_EQ(100, aVector(1).myValue); +} + +TEST(NCollection_VectorTest, EmplaceAppendMany) +{ + NCollection_Vector aVector; + + // Test EmplaceAppend with many elements to trigger internal array expansion + for (int i = 0; i < 1000; i++) + { + VecMultiArgType& aRef = aVector.EmplaceAppend(i, static_cast(i) * 0.1); + EXPECT_EQ(i, aRef.myA); + EXPECT_NEAR(static_cast(i) * 0.1, aRef.myB, 1e-10); + } + + EXPECT_EQ(1000, aVector.Length()); + + // Verify all values + for (int i = 0; i < 1000; i++) + { + EXPECT_EQ(i, aVector(i).myA); + EXPECT_NEAR(static_cast(i) * 0.1, aVector(i).myB, 1e-10); + } +} + +TEST(NCollection_VectorTest, SetValue_ReplacesExisting) +{ + NCollection_Vector aVector; + + // Set initial values + aVector.SetValue(0, VecMultiArgType(10, 1.0)); + aVector.SetValue(1, VecMultiArgType(20, 2.0)); + aVector.SetValue(2, VecMultiArgType(30, 3.0)); + + EXPECT_EQ(3, aVector.Length()); + + // Replace value at index 1 (existing element) + VecMultiArgType& aRef = aVector.SetValue(1, VecMultiArgType(200, 20.0)); + EXPECT_EQ(200, aRef.myA); + EXPECT_NEAR(20.0, aRef.myB, 1e-10); + + // Verify other values unchanged and size unchanged + EXPECT_EQ(3, aVector.Length()); + EXPECT_EQ(10, aVector(0).myA); + EXPECT_EQ(200, aVector(1).myA); + EXPECT_EQ(30, aVector(2).myA); } \ No newline at end of file diff --git a/src/FoundationClasses/TKernel/NCollection/NCollection_Array1.hxx b/src/FoundationClasses/TKernel/NCollection/NCollection_Array1.hxx index 81ea423dc9..142a3a7361 100644 --- a/src/FoundationClasses/TKernel/NCollection/NCollection_Array1.hxx +++ b/src/FoundationClasses/TKernel/NCollection/NCollection_Array1.hxx @@ -325,6 +325,21 @@ public: myPointer[aPos] = std::forward(theItem); } + //! Emplace value at the specified index, constructing it in-place + //! @param theIndex index at which to emplace the value + //! @param theArgs arguments forwarded to TheItemType constructor + //! @return reference to the newly constructed item + template + reference EmplaceValue(const int theIndex, Args&&... theArgs) + { + const size_t aPos = theIndex - myLowerBound; + Standard_OutOfRange_Raise_if(aPos >= mySize, "NCollection_Array1::EmplaceValue"); + pointer aPnt = myPointer + aPos; + myAllocator.destroy(aPnt); + myAllocator.construct(aPnt, std::forward(theArgs)...); + return *aPnt; + } + //! Changes the lowest bound. Do not move data void UpdateLowerBound(const int theLower) noexcept { myLowerBound = theLower; } diff --git a/src/FoundationClasses/TKernel/NCollection/NCollection_Array2.hxx b/src/FoundationClasses/TKernel/NCollection/NCollection_Array2.hxx index e174b92be7..5a1faac751 100644 --- a/src/FoundationClasses/TKernel/NCollection/NCollection_Array2.hxx +++ b/src/FoundationClasses/TKernel/NCollection/NCollection_Array2.hxx @@ -305,6 +305,22 @@ public: NCollection_Array1::at(aPos) = std::forward(theItem); } + //! Emplace value at the specified row and column, constructing it in-place + //! @param theRow row index at which to emplace the value + //! @param theCol column index at which to emplace the value + //! @param theArgs arguments forwarded to TheItemType constructor + //! @return reference to the newly constructed item + template + reference EmplaceValue(const int theRow, const int theCol, Args&&... theArgs) + { + const size_t aPos = (theRow - myLowerRow) * mySizeCol + (theCol - myLowerCol); + Standard_OutOfRange_Raise_if(aPos >= this->mySize, "NCollection_Array2::EmplaceValue"); + pointer aPnt = this->myPointer + aPos; + this->myAllocator.destroy(aPnt); + this->myAllocator.construct(aPnt, std::forward(theArgs)...); + return *aPnt; + } + //! Resizes the array to specified bounds. //! No re-allocation will be done if length of array does not change, //! but existing values will not be discarded if theToCopyData set to FALSE. diff --git a/src/FoundationClasses/TKernel/NCollection/NCollection_DynamicArray.hxx b/src/FoundationClasses/TKernel/NCollection/NCollection_DynamicArray.hxx index 52c924fe52..b6453f90bb 100644 --- a/src/FoundationClasses/TKernel/NCollection/NCollection_DynamicArray.hxx +++ b/src/FoundationClasses/TKernel/NCollection/NCollection_DynamicArray.hxx @@ -257,6 +257,54 @@ public: //! @name public methods return *aPnt; } + //! Emplace one item at the end, constructing it in-place + //! @param theArgs arguments forwarded to TheItemType constructor + //! @return reference to the newly constructed item + template + reference EmplaceAppend(Args&&... theArgs) + { + if (myUsedSize >= availableSize()) + { + expandArray(); + } + pointer aPnt = &at(myUsedSize++); + myAlloc.construct(aPnt, std::forward(theArgs)...); + return *aPnt; + } + + //! Emplace value at the specified index, constructing it in-place + //! If the index is beyond current size, default-constructs intermediate elements + //! @param theIndex index at which to emplace the value + //! @param theArgs arguments forwarded to TheItemType constructor + //! @return reference to the newly constructed item + template + reference EmplaceValue(const int theIndex, Args&&... theArgs) + { + const size_t aBlockInd = static_cast(theIndex / myInternalSize); + const size_t anIndex = static_cast(theIndex); + for (size_t aInd = myContainer.Size(); aInd <= aBlockInd; aInd++) + { + expandArray(); + } + const bool isExisting = anIndex < myUsedSize; + if (!isExisting) + { + for (; myUsedSize < anIndex; myUsedSize++) + { + pointer aPnt = &at(myUsedSize); + myAlloc.construct(aPnt); + } + myUsedSize++; + } + pointer aPnt = &at(anIndex); + if (isExisting) + { + myAlloc.destroy(aPnt); + } + myAlloc.construct(aPnt, std::forward(theArgs)...); + return *aPnt; + } + //! Operator() - query the const value const_reference operator()(const int theIndex) const noexcept { return Value(theIndex); } @@ -303,7 +351,8 @@ public: //! @name public methods { expandArray(); } - if (myUsedSize <= anIndex) + const bool isExisting = anIndex < myUsedSize; + if (!isExisting) { for (; myUsedSize < anIndex; myUsedSize++) { @@ -313,6 +362,10 @@ public: //! @name public methods myUsedSize++; } pointer aPnt = &at(anIndex); + if (isExisting) + { + myAlloc.destroy(aPnt); + } myAlloc.construct(aPnt, theValue); return *aPnt; } @@ -326,7 +379,8 @@ public: //! @name public methods { expandArray(); } - if (myUsedSize <= anIndex) + const bool isExisting = anIndex < myUsedSize; + if (!isExisting) { for (; myUsedSize < anIndex; myUsedSize++) { @@ -336,6 +390,10 @@ public: //! @name public methods myUsedSize++; } pointer aPnt = &at(anIndex); + if (isExisting) + { + myAlloc.destroy(aPnt); + } myAlloc.construct(aPnt, std::forward(theValue)); return *aPnt; } diff --git a/src/FoundationClasses/TKernel/NCollection/NCollection_List.hxx b/src/FoundationClasses/TKernel/NCollection/NCollection_List.hxx index 9b68e831a2..286a3c519b 100644 --- a/src/FoundationClasses/TKernel/NCollection/NCollection_List.hxx +++ b/src/FoundationClasses/TKernel/NCollection/NCollection_List.hxx @@ -343,6 +343,56 @@ public: } } + //! Emplace one item at the end, constructing it in-place + //! @param theArgs arguments forwarded to TheItemType constructor + //! @return reference to the newly constructed item + template + TheItemType& EmplaceAppend(Args&&... theArgs) + { + ListNode* pNew = + new (this->myAllocator) ListNode(std::in_place, nullptr, std::forward(theArgs)...); + PAppend(pNew); + return ((ListNode*)PLast())->ChangeValue(); + } + + //! Emplace one item at the beginning, constructing it in-place + //! @param theArgs arguments forwarded to TheItemType constructor + //! @return reference to the newly constructed item + template + TheItemType& EmplacePrepend(Args&&... theArgs) + { + ListNode* pNew = + new (this->myAllocator) ListNode(std::in_place, nullptr, std::forward(theArgs)...); + PPrepend(pNew); + return ((ListNode*)PFirst())->ChangeValue(); + } + + //! Emplace one item before the iterator position, constructing it in-place + //! @param theIter iterator pointing to the position before which to insert + //! @param theArgs arguments forwarded to TheItemType constructor + //! @return reference to the newly constructed item + template + TheItemType& EmplaceBefore(Iterator& theIter, Args&&... theArgs) + { + ListNode* pNew = + new (this->myAllocator) ListNode(std::in_place, nullptr, std::forward(theArgs)...); + PInsertBefore(pNew, theIter); + return pNew->ChangeValue(); + } + + //! Emplace one item after the iterator position, constructing it in-place + //! @param theIter iterator pointing to the position after which to insert + //! @param theArgs arguments forwarded to TheItemType constructor + //! @return reference to the newly constructed item + template + TheItemType& EmplaceAfter(Iterator& theIter, Args&&... theArgs) + { + ListNode* pNew = + new (this->myAllocator) ListNode(std::in_place, nullptr, std::forward(theArgs)...); + PInsertAfter(pNew, theIter); + return pNew->ChangeValue(); + } + //! Reverse the list void Reverse() { PReverse(); } diff --git a/src/FoundationClasses/TKernel/NCollection/NCollection_Sequence.hxx b/src/FoundationClasses/TKernel/NCollection/NCollection_Sequence.hxx index 64c1f6a647..8c6c1f951a 100644 --- a/src/FoundationClasses/TKernel/NCollection/NCollection_Sequence.hxx +++ b/src/FoundationClasses/TKernel/NCollection/NCollection_Sequence.hxx @@ -53,6 +53,14 @@ public: myValue = std::forward(theItem); } + //! Constructor with in-place value construction + template + Node(std::in_place_t, Args&&... theArgs) + : NCollection_SeqNode(), + myValue(std::forward(theArgs)...) + { + } + //! Constant value access const TheItemType& Value() const noexcept { return myValue; } @@ -370,6 +378,64 @@ public: PInsertAfter(theIndex, new (this->myAllocator) Node(theItem)); } + //! Emplace one item at the end, constructing it in-place + //! @param theArgs arguments forwarded to TheItemType constructor + //! @return reference to the newly constructed item + template + TheItemType& EmplaceAppend(Args&&... theArgs) + { + Node* pNew = new (this->myAllocator) Node(std::in_place, std::forward(theArgs)...); + PAppend(pNew); + return ((Node*)myLastItem)->ChangeValue(); + } + + //! Emplace one item at the beginning, constructing it in-place + //! @param theArgs arguments forwarded to TheItemType constructor + //! @return reference to the newly constructed item + template + TheItemType& EmplacePrepend(Args&&... theArgs) + { + Node* pNew = new (this->myAllocator) Node(std::in_place, std::forward(theArgs)...); + PPrepend(pNew); + return ((Node*)myFirstItem)->ChangeValue(); + } + + //! Emplace one item after the position of iterator, constructing it in-place + //! @param thePosition iterator pointing to the position after which to insert + //! @param theArgs arguments forwarded to TheItemType constructor + //! @return reference to the newly constructed item + template + TheItemType& EmplaceAfter(Iterator& thePosition, Args&&... theArgs) + { + Node* pNew = new (this->myAllocator) Node(std::in_place, std::forward(theArgs)...); + PInsertAfter(thePosition, pNew); + return pNew->ChangeValue(); + } + + //! Emplace one item after the specified index, constructing it in-place + //! @param theIndex index after which to insert (0 means insert at beginning) + //! @param theArgs arguments forwarded to TheItemType constructor + //! @return reference to the newly constructed item + template + TheItemType& EmplaceAfter(const int theIndex, Args&&... theArgs) + { + Standard_OutOfRange_Raise_if(theIndex < 0 || theIndex > mySize, + "NCollection_Sequence::EmplaceAfter"); + Node* pNew = new (this->myAllocator) Node(std::in_place, std::forward(theArgs)...); + PInsertAfter(theIndex, pNew); + return pNew->ChangeValue(); + } + + //! Emplace one item before the specified index, constructing it in-place + //! @param theIndex index before which to insert + //! @param theArgs arguments forwarded to TheItemType constructor + //! @return reference to the newly constructed item + template + TheItemType& EmplaceBefore(const int theIndex, Args&&... theArgs) + { + return EmplaceAfter(theIndex - 1, std::forward(theArgs)...); + } + //! Split in two sequences void Split(const int theIndex, NCollection_Sequence& theSeq) {