]> OCCT Git - occt.git/commitdiff
Foundation Classes - Implement move semantics for math_Matrix and math_Vector (... IR master
authorPasukhin Dmitry <dpasukhi@opencascade.com>
Mon, 24 Nov 2025 11:14:19 +0000 (11:14 +0000)
committerGitHub <noreply@github.com>
Mon, 24 Nov 2025 11:14:19 +0000 (11:14 +0000)
- Added move constructors and move assignment operators to `math_VectorBase`, `math_Matrix`, and `math_DoubleTab`
- Optimized move operations to avoid unnecessary copying when dimensions match and both objects use heap allocation
- Added comprehensive test coverage for move semantics with both heap-allocated (large) and buffer-allocated (small) objects

src/FoundationClasses/TKMath/GTests/math_Matrix_Test.cxx
src/FoundationClasses/TKMath/GTests/math_Vector_Test.cxx
src/FoundationClasses/TKMath/math/math_DoubleTab.hxx
src/FoundationClasses/TKMath/math/math_Matrix.hxx
src/FoundationClasses/TKMath/math/math_Matrix.lxx
src/FoundationClasses/TKMath/math/math_VectorBase.hxx
src/FoundationClasses/TKMath/math/math_VectorBase.lxx

index 12ca0728a83df0d036267a4979a75d64d3e0e960..39793242c39454ec8a7898d7d15ea1314b48af36 100644 (file)
@@ -594,3 +594,56 @@ TEST(MathMatrixTest, InPlaceMatrixMultiplication)
   EXPECT_NEAR(aMatrixACopy(2, 1), aExpectedAB(2, 1), Precision::Confusion());
   EXPECT_NEAR(aMatrixACopy(2, 2), aExpectedAB(2, 2), Precision::Confusion());
 }
+
+// Tests for Move Semantics
+TEST(MathMatrixTest, MoveSemantics)
+{
+  // --- Move Constructor ---
+
+  // Large matrix (heap allocated)
+  Standard_Integer aRows = 10;
+  Standard_Integer aCols = 10;
+  math_Matrix      aMat1(1, aRows, 1, aCols);
+  aMat1.Init(1.0);
+  aMat1(1, 1) = 2.0;
+
+  // Move aMat1 to aMat2
+  math_Matrix aMat2(std::move(aMat1));
+
+  EXPECT_EQ(aMat2.RowNumber(), aRows);
+  EXPECT_EQ(aMat2.ColNumber(), aCols);
+  EXPECT_EQ(aMat2(1, 1), 2.0);
+
+  // Verify source state (should be empty after move)
+  EXPECT_EQ(aMat1.RowNumber(), 0);
+
+  // Small matrix (buffer allocated)
+  Standard_Integer aSmallRows = 4;
+  Standard_Integer aSmallCols = 4;
+  math_Matrix      aSmallMat1(1, aSmallRows, 1, aSmallCols);
+  aSmallMat1.Init(1.0);
+
+  // Move aSmallMat1 to aSmallMat2 (should copy because of buffer)
+  math_Matrix aSmallMat2(std::move(aSmallMat1));
+
+  EXPECT_EQ(aSmallMat2.RowNumber(), aSmallRows);
+  EXPECT_EQ(aSmallMat2(1, 1), 1.0);
+
+  // Source remains valid for buffer-based matrix
+  EXPECT_EQ(aSmallMat1.RowNumber(), aSmallRows);
+  EXPECT_EQ(aSmallMat1(1, 1), 1.0);
+
+  // --- Move Assignment ---
+
+  // Large matrix move assignment
+  math_Matrix aMatAssign1(1, aRows, 1, aCols);
+  aMatAssign1.Init(5.0);
+
+  math_Matrix aMatAssign2(1, aRows, 1, aCols);
+  aMatAssign2.Init(0.0);
+
+  aMatAssign2 = std::move(aMatAssign1);
+
+  EXPECT_EQ(aMatAssign2(1, 1), 5.0);
+  EXPECT_EQ(aMatAssign1.RowNumber(), 0);
+}
index 6bdee08fcb025f56260a30463a8043053612ab8e..0881c96847cb223dc1b2ec7f3cb510ebea3a4e6d 100644 (file)
@@ -638,4 +638,66 @@ TEST(MathVectorTest, EdgeCases)
   EXPECT_EQ(aNegVec.Upper(), 1);
   EXPECT_EQ(aNegVec.Max(), 1);
   EXPECT_EQ(aNegVec.Min(), -2);
-}
\ No newline at end of file
+}
+
+// Tests for Move Semantics
+TEST(MathVectorTest, MoveSemantics)
+{
+  // --- Move Constructor ---
+
+  // Large vector (heap allocated)
+  Standard_Integer aLen = 100;
+  math_Vector      aVec1(1, aLen);
+  for (Standard_Integer i = 1; i <= aLen; ++i)
+  {
+    aVec1(i) = static_cast<Standard_Real>(i);
+  }
+
+  // Move aVec1 to aVec2
+  math_Vector aVec2(std::move(aVec1));
+
+  EXPECT_EQ(aVec2.Length(), aLen);
+  EXPECT_EQ(aVec2(1), 1.0);
+  EXPECT_EQ(aVec2(aLen), static_cast<Standard_Real>(aLen));
+
+  // Verify source state (length should be 0 after move for NCollection_Array1)
+  // Note: calling Length() is safe as it just returns size.
+  EXPECT_EQ(aVec1.Length(), 0);
+
+  // Small vector (buffer allocated)
+  Standard_Integer aSmallLen = 10;
+  math_Vector      aSmallVec1(1, aSmallLen);
+  for (Standard_Integer i = 1; i <= aSmallLen; ++i)
+  {
+    aSmallVec1(i) = static_cast<Standard_Real>(i);
+  }
+
+  // Move aSmallVec1 to aSmallVec2 (should copy because of buffer)
+  math_Vector aSmallVec2(std::move(aSmallVec1));
+
+  EXPECT_EQ(aSmallVec2.Length(), aSmallLen);
+  EXPECT_EQ(aSmallVec2(1), 1.0);
+
+  // Source remains valid for buffer-based vector
+  EXPECT_EQ(aSmallVec1.Length(), aSmallLen);
+  EXPECT_EQ(aSmallVec1(1), 1.0);
+
+  // --- Move Assignment ---
+
+  // Large vector move assignment
+  math_Vector aVecAssign1(1, aLen);
+  for (Standard_Integer i = 1; i <= aLen; ++i)
+  {
+    aVecAssign1(i) = static_cast<Standard_Real>(i);
+  }
+
+  math_Vector aVecAssign2(1, aLen);
+  aVecAssign2.Init(0.0);
+
+  aVecAssign2 = std::move(aVecAssign1);
+
+  EXPECT_EQ(aVecAssign2.Length(), aLen);
+  EXPECT_EQ(aVecAssign2(1), 1.0);
+
+  EXPECT_EQ(aVecAssign1.Length(), 0);
+}
index c386b4d4e107812da41ac977ee2129830ec5351e..572f5d704f3cbc07ade96fc5d278c12beb93a0d0 100644 (file)
@@ -26,6 +26,7 @@
 #include <Standard_Boolean.hxx>
 
 #include <array>
+#include <utility>
 
 class math_DoubleTab
 {
@@ -77,9 +78,34 @@ public:
   {
   }
 
+  //! Move constructor
+  math_DoubleTab(math_DoubleTab&& theOther) noexcept
+      : myBuffer{},
+        myArray(theOther.myArray.IsDeletable()
+                  ? std::move(theOther.myArray)
+                  : (theOther.NbRows() * theOther.NbColumns() <= THE_BUFFER_SIZE
+                       ? NCollection_Array2<Standard_Real>(*myBuffer.data(),
+                                                           theOther.LowerRow(),
+                                                           theOther.UpperRow(),
+                                                           theOther.LowerCol(),
+                                                           theOther.UpperCol())
+                       : NCollection_Array2<Standard_Real>(theOther.LowerRow(),
+                                                           theOther.UpperRow(),
+                                                           theOther.LowerCol(),
+                                                           theOther.UpperCol())))
+  {
+    if (!theOther.myArray.IsEmpty())
+    {
+      myArray.Assign(theOther.myArray);
+    }
+  }
+
   //! Copy data to theOther
   void Copy(math_DoubleTab& theOther) const { theOther.myArray.Assign(myArray); }
 
+  //! Returns true if the internal array is deletable (heap-allocated)
+  Standard_Boolean IsDeletable() const { return myArray.IsDeletable(); }
+
   //! Set lower row index
   void SetLowerRow(const Standard_Integer theLowerRow) { myArray.UpdateLowerRow(theLowerRow); }
 
@@ -130,6 +156,39 @@ public:
     return Value(theRowIndex, theColIndex);
   }
 
+  //! Assignment operator
+  math_DoubleTab& operator=(const math_DoubleTab& theOther)
+  {
+    if (this != &theOther)
+    {
+      myArray = theOther.myArray;
+    }
+    return *this;
+  }
+
+  //! Move assignment operator
+  math_DoubleTab& operator=(math_DoubleTab&& theOther) noexcept
+  {
+    if (this == &theOther)
+    {
+      return *this;
+    }
+
+    if (myArray.IsDeletable() && theOther.myArray.IsDeletable()
+        && myArray.NbRows() == theOther.myArray.NbRows()
+        && myArray.NbColumns() == theOther.myArray.NbColumns()
+        && myArray.LowerRow() == theOther.myArray.LowerRow()
+        && myArray.LowerCol() == theOther.myArray.LowerCol())
+    {
+      myArray.Move(theOther.myArray);
+    }
+    else
+    {
+      myArray = theOther.myArray;
+    }
+    return *this;
+  }
+
   //! Destructor
   ~math_DoubleTab() = default;
 
index ba1763ee481320a4005e8af6381f9e2df9bd9567..713522da41720955719330e5a9248b17d624f3d6 100644 (file)
@@ -379,7 +379,7 @@ public:
   math_Matrix& operator=(const math_Matrix& Other) { return Initialized(Other); }
 
   //! Move assignment operator
-  inline math_Matrix& operator=(math_Matrix&& Other) noexcept;
+  inline math_Matrix& operator=(math_Matrix&& Other);
 
   //! Returns the product of 2 matrices.
   //! An exception is raised if the dimensions are different.
index 68e1638259d5fd5ea3519c62c9f163c1cb700839..224d0ec0d75957a27bec6d4875d990364c05a174 100644 (file)
@@ -701,12 +701,23 @@ inline math_Matrix& math_Matrix::Initialized(const math_Matrix& Other)
 
 //==================================================================================================
 
-inline math_Matrix& math_Matrix::operator=(math_Matrix&& Other) noexcept
+inline math_Matrix& math_Matrix::operator=(math_Matrix&& Other)
 {
-  if (this != &Other)
+  if (this == &Other)
+  {
+    return *this;
+  }
+
+  if (Array.IsDeletable() && Other.Array.IsDeletable() && RowNumber() == Other.RowNumber()
+      && ColNumber() == Other.ColNumber() && LowerRow() == Other.LowerRow()
+      && LowerCol() == Other.LowerCol())
   {
     Array = std::move(Other.Array);
   }
+  else
+  {
+    Initialized(Other);
+  }
   return *this;
 }
 
index 7445b95c95ecbbed80636d8314d0a7615d727adf..a38afd1a739f5782bbfd92a48a6fbb7f510d7404 100644 (file)
@@ -27,6 +27,7 @@
 #include <math_Matrix.hxx>
 
 #include <array>
+#include <utility>
 
 //! This class implements the real vector abstract data type.
 //! Vectors can have an arbitrary range which must be defined at
@@ -94,9 +95,11 @@ public:
   void Init(const TheItemType theInitialValue);
 
   //! Constructs a copy for initialization.
-  //! An exception is raised if the lengths of the vectors are different.
   inline math_VectorBase(const math_VectorBase& theOther);
 
+  //! Move constructor
+  inline math_VectorBase(math_VectorBase&& theOther) noexcept;
+
   //! Returns the length of a vector
   inline Standard_Integer Length() const { return Array.Length(); }
 
@@ -255,6 +258,9 @@ public:
 
   math_VectorBase& operator=(const math_VectorBase& theOther) { return Initialized(theOther); }
 
+  //! Move assignment operator
+  inline math_VectorBase& operator=(math_VectorBase&& theOther);
+
   //! returns the inner product of 2 vectors.
   //! An exception is raised if the lengths are not equal.
   Standard_NODISCARD inline TheItemType Multiplied(const math_VectorBase& theRight) const;
index fbd83f37bf8e9efbbe1e7e97e75ac21a3127fce0..6e0314c0100be926c6b6c4b587700af2345f4f54 100644 (file)
@@ -76,6 +76,23 @@ math_VectorBase<TheItemType>::math_VectorBase(const math_VectorBase<TheItemType>
 {
 }
 
+template <typename TheItemType>
+math_VectorBase<TheItemType>::math_VectorBase(math_VectorBase<TheItemType>&& theOther) noexcept
+    : myBuffer{},
+      Array(theOther.Array.IsDeletable()
+              ? std::move(theOther.Array)
+              : (theOther.Length() <= math_VectorBase::THE_BUFFER_SIZE
+                   ? NCollection_Array1<TheItemType>(*myBuffer.data(),
+                                                     theOther.Lower(),
+                                                     theOther.Upper())
+                   : NCollection_Array1<TheItemType>(theOther.Lower(), theOther.Upper())))
+{
+  if (!theOther.Array.IsEmpty())
+  {
+    Array.Assign(theOther.Array);
+  }
+}
+
 template <typename TheItemType>
 void math_VectorBase<TheItemType>::SetLower(const Standard_Integer theLower)
 {
@@ -543,6 +560,27 @@ math_VectorBase<TheItemType>& math_VectorBase<TheItemType>::Initialized(
   return *this;
 }
 
+template <typename TheItemType>
+math_VectorBase<TheItemType>& math_VectorBase<TheItemType>::operator=(
+  math_VectorBase<TheItemType>&& theOther)
+{
+  if (this == &theOther)
+  {
+    return *this;
+  }
+
+  if (Array.IsDeletable() && theOther.Array.IsDeletable() && Lower() == theOther.Lower()
+      && Length() == theOther.Length())
+  {
+    Array.Move(theOther.Array);
+  }
+  else
+  {
+    Initialized(theOther);
+  }
+  return *this;
+}
+
 template <typename TheItemType>
 void math_VectorBase<TheItemType>::Dump(Standard_OStream& theO) const
 {