mirror of
https://github.com/Open-Cascade-SAS/OCCT.git
synced 2026-05-10 09:30:48 +08:00
Foundation Classes - Implement move semantics for math_Matrix and math_Vector (#841)
- 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
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -639,3 +639,65 @@ TEST(MathVectorTest, EdgeCases)
|
||||
EXPECT_EQ(aNegVec.Max(), 1);
|
||||
EXPECT_EQ(aNegVec.Min(), -2);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user