/*========================================================================= * * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkVariableLengthVector_h #define itkVariableLengthVector_h #include #include #include #include "itkNumericTraits.h" #include "itkMetaProgrammingLibrary.h" #include "itkIsNumber.h" #include "itkPromoteType.h" #include "itkBinaryOperationConcept.h" namespace itk { template struct VariableLengthVectorExpression; /** \class VariableLengthVector * \brief Represents an array whose length can be defined at run-time. * * This class is templated over the data type. This data-type is meant * to be a scalar, such as float, double etc... * * \note * ITK itself provides several classes that can serve as \c Arrays. * \li FixedArray - Compile time fixed length arrays that's intended to * represent an enumerated collection of \c n entities. * * \li Array - Run time resizeable array that is intended to hold a * collection of \c n entities * * \li Vector - Compile time fixed length array that is intended to hold * a collection of \c n data types. A vector usually has a mathematical meaning. * It should only be used when mathematical operations such as addition, * multiplication by a scalar, product etc make sense. * * \li VariableLengthVector - Run time array that is intended to hold a collection * of scalar data types. Again, it should be used only when mathematical * operations on it are relevant. If not, use an Array. * * \li Point - Represents the spatial coordinates of a spatial location. Operators * on Point reflect geometrical concepts. * * \par For the reasons listed above, you cannot instantiate * \code VariableLengthVector< bool > \endcode. * * \par * Design Considerations: We do not derive from \c vnl_vector to avoid being * limited by the explicit template instantiations of vnl_vector and other * hacks that vnl folks have been forced to use. * * \note * This work is part of the National Alliance for Medical Image Computing * (NAMIC), funded by the National Institutes of Health through the NIH Roadmap * for Medical Research, Grant U54 EB005149. * * \sa CovariantVector * \sa SymmetricSecondRankTensor * \sa RGBPixel * \sa DiffusionTensor3D * \ingroup DataRepresentation * \ingroup ITKCommon * * \sphinx * \sphinxexample{Core/Common/VariableLengthVector,Variable Length Vector} * \endsphinx * * \invariant If \c m_LetArrayManageMemory is true, \c m_Data is deletable * (whether it's null or pointing to something with no elements. i.e. \c * m_NumElements may be 0 and yet \c m_Data may be not null.) */ template class ITK_TEMPLATE_EXPORT VariableLengthVector { public: /**\name Policies * The following Policies will be used by \c itk::VariableLengthVector::SetSize */ //@{ /** \c VariableLengthVector empty base-class for allocation policies. * All Allocation Policies are expected to inherit from this empty base * class. * * \sa \c itk::VariableLengthVector::SetSize * \sa \c NeverReallocate * \sa \c ShrinkToFit * \sa \c DontShrinkToFit * \ingroup ITKCommon * \ingroup DataRepresentation */ struct AllocateRootPolicy {}; /** \c VariableLengthVector Allocation Policy: Always reallocate memory. * This policy, when used from \c VariableLengthVector::SetSize(), always * implies that the previous internal buffer will be reallocated. Even if * enough memory was available. * \return true (always) * * \sa \c itk::VariableLengthVector::SetSize * \sa \c NeverReallocate * \sa \c ShrinkToFit * \sa \c DontShrinkToFit * \ingroup ITKCommon * \ingroup DataRepresentation */ struct AlwaysReallocate : AllocateRootPolicy { bool operator()(unsigned int itkNotUsed(newSize), unsigned int itkNotUsed(oldSize)) const { return true; } }; /** \c VariableLengthVector Allocation Policy: Never reallocate memory. * This policy, when used from \c VariableLengthVector::SetSize(), always * implies that the previous internal buffer will be kept. Even if not enough * memory was available. * * The typical use case of this policy is to make sure a \c * VariableLengthVector is not a proxy object. * \return false (always) * * \pre oldSize == newSize, checked by assertion * * \sa \c itk::VariableLengthVector::SetSize * \sa \c AlwaysReallocate * \sa \c ShrinkToFit * \sa \c DontShrinkToFit * \ingroup ITKCommon * \ingroup DataRepresentation */ struct NeverReallocate : AllocateRootPolicy { bool operator()(unsigned int newSize, unsigned int oldSize) const { (void)newSize; (void)oldSize; itkAssertInDebugAndIgnoreInReleaseMacro(newSize == oldSize && "SetSize is expected to never change the VariableLengthVector size..."); return true; } }; /** \c VariableLengthVector Allocation Policy: reallocate memory only when * size changes. * This policy, when used from \c VariableLengthVector::SetSize(), will * reallocate the internal buffer only if the size of the \c * VariableLengthVector changes. * \return whether \c newSize differs from \c oldSize * * \note The name is related to \c DontShrinkToFit reallocation policy that * will avoid reallocating when enough memory has already been allocated. * * \sa \c itk::VariableLengthVector::SetSize * \sa \c AlwaysReallocate * \sa \c NeverReallocate * \sa \c DontShrinkToFit * \ingroup ITKCommon * \ingroup DataRepresentation */ struct ShrinkToFit : AllocateRootPolicy { bool operator()(unsigned int newSize, unsigned int oldSize) const { return newSize != oldSize; } }; /** \c VariableLengthVector Allocation Policy: reallocate memory only when * size increases. * This policy, when used from \c VariableLengthVector::SetSize(), will * reallocate the internal buffer only if the new size requested for the \c * VariableLengthVector increases. * \return whether \c newSize is bigger than \c oldSize * * \warning Unlike classes like \c std::vector<>, \c VariableLengthVector has * no capacity concept: the size of the \c VariableLengthVector is its * capacity. However, this will help a class without capacity to emulate one. * The consequence is that reallocations will occur with scenarios such as * the following: \code VariableLengthVector<...> v; v.SetSize(42); v.SetSize(12); // no reallocation v.SetSize(42); // pointless reallocation (given this policy) \endcode * * \sa \c itk::VariableLengthVector::SetSize * \sa \c AlwaysReallocate * \sa \c NeverReallocate * \sa \c ShrinkToFit * \ingroup ITKCommon * \ingroup DataRepresentation */ struct DontShrinkToFit : AllocateRootPolicy { bool operator()(unsigned int newSize, unsigned int oldSize) const { return newSize > oldSize; } }; /** \c VariableLengthVector empty base-class for values Keeping policies. * All Values Keeping Policies are expected to inherit from this empty base * class. * * The preconditions common to all sub classes are: * \pre This policy is only meant to be executed in case of reallocation, * i.e. \c oldBuffer and \c newBuffer are expected to differ (unchecked). * \pre This presumes \c TValue assignment is a \c noexcept operation. * \pre \c newBuffer is not null (pre-conditions imposed by some * implementations of \c std::copy()) * \pre `[oldBuffer, oldBuffer+oldSize)` is a valid range * * \sa \c itk::VariableLengthVector::SetSize * \sa \c KeepOldValues * \sa \c DumpOldValues * \ingroup ITKCommon * \ingroup DataRepresentation */ struct KeepValuesRootPolicy {}; /** \c VariableLengthVector Invariability Policy: Always keep old values. * This policy, when used from \c VariableLengthVector::SetSize(), always * copies min(newSize,oldSize) previous values from the previous * internal buffer to the new one * * \pre This policy is only meant to be executed in case of reallocation, * i.e. \c oldBuffer and \c newBuffer are expected to differ (unchecked). * \pre This presumes \c TValue assignment is a \c noexcept operation. * \pre \c newBuffer is not null (pre-conditions imposed by some * implementations of \c std::copy()) * \pre `[oldBuffer, oldBuffer+oldSize)` is a valid range * * This behaviour mimics \c std::vector<>::resize() behaviour. However, it * makes to sense from \c VariableLengthVector::operator=() * * \sa \c itk::VariableLengthVector::SetSize * \sa \c KeepValuesRootPolicy * \sa \c DumpOldValues * \ingroup ITKCommon * \ingroup DataRepresentation */ struct KeepOldValues : KeepValuesRootPolicy { template void operator()(unsigned int newSize, unsigned int oldSize, TValue2 * oldBuffer, TValue2 * newBuffer) const { itkAssertInDebugAndIgnoreInReleaseMacro(newBuffer); const size_t nb = std::min(newSize, oldSize); itkAssertInDebugAndIgnoreInReleaseMacro(nb == 0 || (nb > 0 && oldBuffer != nullptr)); std::copy_n(oldBuffer, nb, newBuffer); } }; /** \c VariableLengthVector Invariability Policy: Never keep old values. * This policy, when used from \c VariableLengthVector::SetSize(), is a no-op. * It won't try to copy previous values from the previous internal buffer to * the new one. * * \pre This policy is only meant to be executed in case of reallocation, * i.e. \c oldBuffer and \c newBuffer are expected to differ (unchecked). * * This behaviour particularly fits \c VariableLengthVector::operator=() * * \sa \c itk::VariableLengthVector::SetSize * \sa \c KeepValuesRootPolicy * \sa \c DumpOldValues * \ingroup ITKCommon * \ingroup DataRepresentation */ struct DumpOldValues : KeepValuesRootPolicy { template void operator()(unsigned int itkNotUsed(newSize), unsigned int itkNotUsed(oldSize), TValue2 * itkNotUsed(oldBuffer), TValue2 * itkNotUsed(newBuffer)) const {} }; //@} /** The element type stored at each location in the Array. */ using ValueType = TValue; using ComponentType = TValue; using RealValueType = typename NumericTraits::RealType; using Self = VariableLengthVector; /** Typedef used to indicate the number of elements in the vector */ using ElementIdentifier = unsigned int; /** Default constructor. It is created with an empty array * it has to be allocated later by assignment, \c SetSize() or \c Reserve(). * \post \c m_Data is null * \post \c m_NumElements is 0 * \post \c m_LetArrayManageMemory is true */ VariableLengthVector() = default; /** Constructor with size. * Size can only be changed by assignment, \c SetSize() or \c Reserve(). * \post \c m_Data is not null and points to an array of \c m_NumElements, * even if \c m_NumElements is 0 * \post values are left uninitialized. * \post \c m_NumElements is \c dimension * \post \c m_LetArrayManageMemory is true */ explicit VariableLengthVector(unsigned int length); /** Constructor that initializes array with contents from a user supplied * buffer. * The pointer to the buffer and the length is specified. By default, the * array does not manage the memory of the buffer. It merely points to that * location and it is the user's responsibility to delete it. * If \c LetArrayManageMemory is true, then this class will free the * memory when this object is destroyed. * * \post `m_Data == data` * \post values are left unmodified * \post `m_NumElements == sz` * \post `m_LetArrayManageMemory == LetArrayManageMemory` */ VariableLengthVector(ValueType * datain, unsigned int sz, bool LetArrayManageMemory = false); /** Constructor that initializes array with contents from a user supplied * buffer. * The pointer to the buffer and the length is specified. By default, the * array does not manage the memory of the buffer. It merely points to that * location and it is the user's responsibility to delete it. * If \c LetArrayManageMemory is true, then this class will free the * memory when this object is destroyed. * * \warning This overload receives a non-modifiable array, and yet it will let * the end-user try to modify it through \c VariableLengthVector interface. * Use this constructor with care as this may lead to undefined behaviour. * Prefer using `VariableLengthVector` instead of * `VariableLengthVector` in case we which to use this constructor. * * \post `m_Data == data` * \post values are left unmodified * \post `m_NumElements == sz` * \post `m_LetArrayManageMemory == LetArrayManageMemory` */ VariableLengthVector(const ValueType * datain, unsigned int sz, bool LetArrayManageMemory = false); /** Copy constructor. The reason why the copy constructor and the assignment * operator are templated is that it will allow implicit casts to be * performed. For instance: \code VariableLengthVector< int > vI; VariableLengthVector< float > vF( vI ); or for instance vF = static_cast< VariableLengthVector< float > >( vI ); \endcode * \note However that static casting in this way will imply the allocation of * a temporary \c VariableLengthVector. Prefer to directly use the assignment * converting operator in code where uses of \c static_cast<> would be * required. * * \post \c m_Data is not null and points to an array of \c m_NumElements, * if \c m_NumElements is 0, otherwise it's null. * \post values are left uninitialized. * \post \c m_NumElements is \c v.GetSize() * \post \c m_LetArrayManageMemory is true */ template VariableLengthVector(const VariableLengthVector & v) { m_NumElements = v.Size(); m_LetArrayManageMemory = true; if (m_NumElements != 0) { m_Data = this->AllocateElements(m_NumElements); itkAssertInDebugAndIgnoreInReleaseMacro(m_Data != nullptr); for (ElementIdentifier i = 0; i < m_NumElements; ++i) { this->m_Data[i] = static_cast(v[i]); } } else { m_Data = nullptr; } } /** Copy constructor. Overrides the default non-templated copy constructor * that the compiler provides. * \post \c m_Data is not null and points to an array of \c m_NumElements, * if \c m_NumElements is 0, otherwise it's null. * \post values are left uninitialized. * \post \c m_NumElements is \c v.GetSize() * \post \c m_LetArrayManageMemory is true */ VariableLengthVector(const VariableLengthVector & v); /** Swaps two \c VariableLengthVector 's. * \pre Expects either none of the \c VariableLengthVector to act as a proxy, * or both, checked with an assertion. * \post \c *this and \c old contents are swapped. * \param[in,out] v other \c VariableLengthVector to be swapped with. * \throw None * \sa \c itk::swap() */ void Swap(Self & v) noexcept { itkAssertInDebugAndIgnoreInReleaseMacro(m_LetArrayManageMemory == v.m_LetArrayManageMemory); using std::swap; swap(v.m_Data, m_Data); swap(v.m_NumElements, m_NumElements); } /** C++11 Move Constructor. * \post \c v is destructible and assignable. * \post `m_NumElements == 0` * \post `m_LetArrayManageMemory == true` * \post `m_Data == nullptr` * \post Built object contains old \c v data. */ VariableLengthVector(Self && v) noexcept; /** C++11 Move assignment operator. * \pre \c v shall not be the same as the current object * \post \c v is destructible and assignable. * \post `m_NumElements == 0` * \post `m_LetArrayManageMemory == true` * \post `m_Data == nullptr` * \post Current object contains old \c v data. */ Self & operator=(Self && v) noexcept; /** Constructor from an Expression Template vector. * \tparam TExpr1 Type of the left sub-expression * \tparam TExpr2 Type of the right sub-expression * \tparam TBinaryOp Binary Operation to apply to both sub-expressions. * \param[in] rhs Non evaluated Expression Template. * * Builds the new \c VariableLengthVector with an expression template. The * code loops over all components from the template expression, and evaluates * them on the fly to fill the content of the new vector. * * \post \c m_Data is not null and points to an array of \c m_NumElements, * even if \c m_NumElements is 0 * \post `*this == rhs` * \post \c m_NumElements is \c rhs.GetSize() * \post \c m_LetArrayManageMemory is true */ template VariableLengthVector(VariableLengthVectorExpression const & rhs); /** Assignment from an Expression Template vector. * \tparam TExpr1 Type of the left sub-expression * \tparam TExpr2 Type of the right sub-expression * \tparam TBinaryOp Binary Operation to apply to both sub-expressions. * \param[in] rhs Non evaluated Expression Template. * * Resets the new \c VariableLengthVector with an expression template. The * code loops over all components from the template expression, and evaluates * them on the fly to fill the content of the current vector. * * \post if called on a \c VariableLengthVector proxy, the referenced values * are left unchanged. * \post \c m_Data is not null and points to an array of \c m_NumElements, * if \c m_NumElements is not 0. \c m_Data may be null otherwise (an empty * vector is assigned into another empty vector) * \post \c m_LetArrayManageMemory is true * \post `GetSize() == rhs.GetSize()` * \post `*this == rhs` */ template Self & operator=(VariableLengthVectorExpression const & rhs); /** Set all the elements of the array to the specified value. * \pre This function may be called on empty vectors, it's a no-op. */ void Fill(TValue const & v); /** Converting assignment operator. * \note Ensures a String Exception Guarantee: resists to * self-assignment, and no changes are made if memory cannot be allocated to * hold the new elements. This presumes \c TValue assignment is a \c * noexcept operation. * * \post if called on a \c VariableLengthVector proxy, the referenced values * are left unchanged. * \post \c m_LetArrayManageMemory is true * \post GetSize() == v.GetSize(), modulo precision * \post *this == v */ template Self & operator=(const VariableLengthVector & v) { // No self assignment test is done. Indeed: // - the operator already resists self assignment through a strong exception // guarantee // - the test becomes a pessimization as we never write // VLV vcref(v.GetDataPointer(), v.GetSize()); // ...; // v = vcref; ElementIdentifier const N = v.Size(); this->SetSize(N, DontShrinkToFit(), DumpOldValues()); for (ElementIdentifier i = 0; i < N; ++i) { this->m_Data[i] = static_cast(v[i]); } return *this; } /** Copy-Assignment operator. * \note Ensures a String Exception Guarantee: resists to * self-assignment, and no changes are made if memory cannot be allocated to * hold the new elements. This is expecting \c TValue assignment is a \c * noexcept operation. * * \post if called on a \c VariableLengthVector proxy, the referenced values * are left unchanged. * \post \c m_Data is not null and points to an array of \c m_NumElements, * if \c m_NumElements is not 0. \c m_Data may be null otherwise (an empty * vector is assigned into another empty vector) * \post \c m_LetArrayManageMemory is true * \post GetSize() == v.GetSize() * \post *this == v */ Self & operator=(const Self & v); /** Fast Assignment. * \pre \c m_LetArrayManageMemory is true: the \c VariableLengthVector is not * a proxy, checked with an assertion. Call SetSize(GetSize(), NeverReallocate(), * DumpOldValues()) to ensure a vector is not a proxy anymore. * \pre current size is identical to the one from the right hand side * operand, checked with an assertion. * \pre Doesn't not support empty vectors. */ Self & FastAssign(const Self & v); /** Assignment operator from a numeric value. * \pre This assumes \c m_LetArrayManageMemory is true, but it is unchecked. * If this operator is called on a \c VariableLengthVector proxy, referenced * values will be overwritten. * \post Elements in `[m_Data, m_Data+GetSize())` will be equal to \c v, modulo * precision */ Self & operator=(TValue const & v); /** Return the number of elements in the Array */ unsigned int Size() const { return m_NumElements; } unsigned int GetSize() const { return m_NumElements; } unsigned int GetNumberOfElements() const { return m_NumElements; } /** Return reference to the element at specified index. No range checking. */ TValue & operator[](unsigned int i) { return this->m_Data[i]; } /** Return reference to the element at specified index. No range checking. */ TValue const & operator[](unsigned int i) const { return this->m_Data[i]; } /** Get one element */ const TValue & GetElement(unsigned int i) const { return m_Data[i]; } /** Set one element */ void SetElement(unsigned int i, const TValue & value) { m_Data[i] = value; } /** Resizes the vector. * \tparam TReallocatePolicy Policy that determines precisely the conditions * under which the internal buffer shall be reallocated. It shall inherit * from \c AllocateRootPolicy. * \tparam TKeepValuesPolicy Policy that determines whether old elements * shall be kept. It shall inherit from \c KeepValuesRootPolicy. * * \internal * The purpose of this overload is to fine tune what \c SetSize() does. Some * users seem to need to always reallocate, or to maintain old elements. * However, some usages require fast resizing. In the assignment operators * cases, we don't need to reallocate anything if we have enough memory, and * we certainly do not need to maintain previous values as they'll get * overridden with new ones. * \internal * If we could assert that \c VariableLengthVector proxies would (shall!) * never be assigned anything, we could benefit from a version that won't * check \c m_LetArrayManageMemory. * * \pre `m_NumElements == sz` if \c TReallocatePolicy is \c NeverReallocate * \post `m_NumElements == sz` * \post \c m_LetArrayManageMemory is true * \post In case of reallocation, old \c m_Data buffer is deleted. * \post If \c TKeepValuesPolicy is \c KeepOldValues, old values are * guaranteed to be kept, otherwise, it'll depend on the reallocation policy * and the old and new vector size. * \sa \c AlwaysReallocate * \sa \c NeverReallocate * \sa \c ShrinkToFit * \sa \c DontShrinkToFit * \sa \c KeepOldValues * \sa \c DumpOldValues */ template void SetSize(unsigned int sz, TReallocatePolicy reallocatePolicy, TKeepValuesPolicy keepValues); /** Set the size to that given. * * If \c destroyExistingData is \c false: * If the array already contains data, the existing data is copied over and * new space is allocated, if necessary. If the length to reserve is less * than the current number of elements, then an appropriate number of elements * are discarded. * If \c true, the size is set destructively to the length given. If the * length is different from the current length, existing data will be lost. * The default is \c true. */ void SetSize(unsigned int sz, bool destroyExistingData = true) { // Stays compatible with previous code version // And works around the fact C++03 template functions can't have default // arguments on template types. if (destroyExistingData) { SetSize(sz, AlwaysReallocate(), KeepOldValues()); } else { SetSize(sz, ShrinkToFit(), KeepOldValues()); } } /** Destroy data that is allocated internally, if \c LetArrayManageMemory is * true. */ void DestroyExistingData(); /** Set the pointer from which the data is imported. * If "LetArrayManageMemory" is false, then the application retains * the responsibility of freeing the memory for this data. If * "LetArrayManageMemory" is true, then this class will free the * memory when this object is destroyed. * \warning The size of the new \c data shall match vector current size. * Prefer the other overload. * \post old \c m_Data is deleted iff \c m_LetArrayManageMemory is true * \post `m_Data == data` * \post `m_LetArrayManageMemory ==LetArrayManageMemory` * \post \c Size() is left unmodified. */ void SetData(TValue * datain, bool LetArrayManageMemory = false); /** Similar to the previous method. In the above method, the size must be * separately set prior to using user-supplied data. This introduces an * unnecessary allocation step to be performed. This method avoids it * and should be used to import data wherever possible to avoid this. * Set the pointer from which the data is imported. * If "LetArrayManageMemory" is false, then the application retains * the responsibility of freeing the memory for this data. If * "LetArrayManageMemory" is true, then this class will free the * memory when this object is destroyed. * \post old \c m_Data is deleted iff \c m_LetArrayManageMemory is true * \post `m_Data == data` * \post `m_LetArrayManageMemory ==LetArrayManageMemory` * \post `m_NumElements == sz` */ void SetData(TValue * datain, unsigned int sz, bool LetArrayManageMemory = false); /** This destructor is not virtual for performance reasons. However, this * means that subclasses cannot allocate memory. * * \internal * More precisely, this class has value semantics (copyable, assignable, * comparable). It's hardly compatible with public inheritance: slicing would * always be there somewhere to annoy us if we try to inherit publicly from * such a class. * As a consequence, having the destructor virtual makes hardly any sense. */ ~VariableLengthVector(); /** Reserves memory of a certain size of data. * * If the array already contains data, the existing data is copied over and * new space is allocated, if necessary. If the length to reserve is less * than the current number of elements, then an appropriate number of elements * are discarded. * \post \c m_Data is not null and can hold \c size elements. * \post \c m_LetArrayManageMemory may be left unchanged if there already are * enough elements. * * \note You may prefer instead * `SetSize(N, DontShrinkToFit(), KeepOldValues());` that ensures that the * array is not a proxy at the end of the operation. */ void Reserve(ElementIdentifier size); /** Allocate memory of certain size and return it. * \return a non-null pointer to an array of \c size elements (0 is a valid * parameter). */ TValue * AllocateElements(ElementIdentifier size) const; const TValue * GetDataPointer() const { return m_Data; } /** Prefix operator that subtracts 1 from each element of the * vector. */ Self & operator--() { for (ElementIdentifier i = 0; i < m_NumElements; ++i) { this->m_Data[i] -= static_cast(1.0); } return *this; } /** Prefix operator that adds 1 to each element of the vector. */ Self & operator++() // prefix operator ++v; { for (ElementIdentifier i = 0; i < m_NumElements; ++i) { this->m_Data[i] += static_cast(1.0); } return *this; } /** Postfix operator that subtracts 1 from each element of the * vector. */ Self operator--(int) // postfix operator v--; { Self tmp(*this); --tmp; return tmp; } /** Postfix operator that adds 1 to each element of the vector. */ Self operator++(int) // postfix operator v++; { Self tmp(*this); ++tmp; return tmp; } /** Element-wise subtraction of vector 'v' from the current * vector. The vectors do not have to have the same element * type. The input vector elements are cast to the current vector * element type before the subtraction is performed. * * \throw None * \note For efficiency, the length of the vectors is not checked; * they are assumed to have the same length. */ template Self & operator-=(const VariableLengthVector & v) { itkAssertInDebugAndIgnoreInReleaseMacro(m_NumElements == v.GetSize()); for (ElementIdentifier i = 0; i < m_NumElements; ++i) { m_Data[i] -= static_cast(v[i]); } return *this; } /** Subtract scalar 's' from each element of the current vector. */ Self & operator-=(TValue s) { for (ElementIdentifier i = 0; i < m_NumElements; ++i) { m_Data[i] -= s; } return *this; } /** Element-wise addition of vector 'v' to the current vector. The * vectors do not have to have the same element type. The input * vector elements are cast to the current vector element type * before the addition is performed. * * \throw None * \note For efficiency, the length of the vectors is not checked; * they are assumed to have the same length. */ template Self & operator+=(const VariableLengthVector & v) { itkAssertInDebugAndIgnoreInReleaseMacro(m_NumElements == v.GetSize()); for (ElementIdentifier i = 0; i < m_NumElements; ++i) { m_Data[i] += static_cast(v[i]); } return *this; } /** Add scalar 's' to each element of the vector. */ Self & operator+=(TValue s) { for (ElementIdentifier i = 0; i < m_NumElements; ++i) { m_Data[i] += s; } return *this; } /** Compound addition operator with a expression template vector. * \tparam TExpr1 Type of the left sub-expression * \tparam TExpr2 Type of the right sub-expression * \tparam TBinaryOp Binary Operation to apply to both sub-expressions. * \param[in] rhs Non evaluated Expression Template. * * \pre `Size() == rhs.Size()`, checked with an assertion * \note The elements of the expression template are evaluated one by one. */ template Self & operator+=(VariableLengthVectorExpression const & rhs) { itkAssertInDebugAndIgnoreInReleaseMacro(rhs.Size() == Size()); for (ElementIdentifier i = 0; i < m_NumElements; ++i) { m_Data[i] += static_cast(rhs[i]); } return *this; } /** Compound subtraction operator with a expression template vector. * \tparam TExpr1 Type of the left sub-expression * \tparam TExpr2 Type of the right sub-expression * \tparam TBinaryOp Binary Operation to apply to both sub-expressions. * \param[in] rhs Non evaluated Expression Template. * * \pre `Size() == rhs.Size()`, checked with an assertion * \note The elements of the expression template are evaluated one by one. */ template Self & operator-=(VariableLengthVectorExpression const & rhs) { itkAssertInDebugAndIgnoreInReleaseMacro(rhs.Size() == Size()); for (ElementIdentifier i = 0; i < m_NumElements; ++i) { m_Data[i] -= static_cast(rhs[i]); } return *this; } /** Multiply each element of the vector by a scalar 's'. The scalar * value is cast to the current vector element type prior to * multiplication. * \throw None */ template Self & operator*=(T s) { const ValueType & sc = static_cast(s); for (ElementIdentifier i = 0; i < m_NumElements; ++i) { m_Data[i] *= sc; } return *this; } /** Multiply each element of the vector by a scalar 's'. * \throw None */ Self & operator*=(TValue s) { for (ElementIdentifier i = 0; i < m_NumElements; ++i) { m_Data[i] *= s; } return *this; } /** Divide vector elements by a scalar 's'. The vector does not * have to have the same element type as the scalar type. Both the * scalar and vector elements are cast to the RealValueType prior to * division, and the result is cast to the ValueType. * \throw None */ template Self & operator/=(T s) { const RealValueType sc = s; for (ElementIdentifier i = 0; i < m_NumElements; ++i) { m_Data[i] = static_cast(static_cast(m_Data[i]) / sc); } return *this; } /** Negates each vector element. * \warning This operator has a non standard semantics. Instead of returning * a new \c VariableLengthVector, it modifies the current object. */ Self & operator-(); // negation operator bool operator==(const Self & v) const; ITK_UNEQUAL_OPERATOR_MEMBER_FUNCTION(Self); /** Returns vector's Euclidean norm. */ RealValueType GetNorm() const; /** Returns vector's squared Euclidean norm. */ RealValueType GetSquaredNorm() const; /** letArrayManageMemory getter. */ bool IsAProxy() const { return !m_LetArrayManageMemory; } private: bool m_LetArrayManageMemory{ true }; // if true, the array is responsible // for memory of data TValue * m_Data{}; // Array to hold data ElementIdentifier m_NumElements{ 0 }; }; /// \cond HIDE_META_PROGRAMMING namespace mpl { /** Tells whether a type is an array type for which the support of arithmetic * operations is done with Expression Template. * \note For the moment, only \c itk::VariableLengthVector<> is supported. It * could be extended to other types of ITK arrays. * \ingroup MetaProgrammingLibrary * \ingroup ITKCommon * \sa \c VariableLengthVector * \sa \c VariableLengthVectorExpression */ template struct IsArray : FalseType {}; /// \cond SPECIALIZATION_IMPLEMENTATION template struct IsArray> : TrueType {}; template struct IsArray> : TrueType {}; /// \endcond } // namespace mpl /// \endcond namespace Details { /// \cond HIDE_META_PROGRAMMING /** Helper Trait for VLV expression template: returns the value type. * \tparam TExpr Expression type * \return \c Type The value type behind \c TExpr (\c TExpr in case of a * numerical type, \c TExpr::ValueType in case of the \c VariableLengthVector, * etc.) * * Also defines \c Load() that permits to fetch the i-th element in case of an * array, array expression, or just the number in case of a number. * \ingroup ITKCommon * \sa \c VariableLengthVector * \sa \c VariableLengthVectorExpression */ template struct GetType { using Type = TExpr; /** Fetches the i-th element from an array (expression). * \note the default unspecialized behaviour returns the input number \c v. */ static Type Load(Type const & v, unsigned int idx) { (void)idx; return v; } }; /** Helper function for VLV expression templates: returns the common size. * \param[in] lhs left hand side expression * \param[in] rhs right hand side expression * \note The default overload assumes both operands are \c VariableLengthVector * (or expression) arrays * \pre asserts both arrays have the same size. * \ingroup ITKCommon * \sa \c VariableLengthVector * \sa \c VariableLengthVectorExpression */ template inline std::enable_if_t, mpl::IsArray>::Value, unsigned int> GetSize(TExpr1 const & lhs, TExpr2 const & rhs) { (void)rhs; itkAssertInDebugAndIgnoreInReleaseMacro(lhs.Size() == rhs.Size()); return lhs.Size(); } /// \cond SPECIALIZATION_IMPLEMENTATION /** Helper function for VLV expression templates: returns the common size. * \param[in] lhs left hand side expression * \param[in] rhs right hand side expression * \note This overload assumes that only the first operand is a \c * VariableLengthVector (or expression) array. * \ingroup ITKCommon * \sa \c VariableLengthVector * \sa \c VariableLengthVectorExpression */ template inline std::enable_if_t, mpl::Not>>::Value, unsigned int> GetSize(TExpr1 const & lhs, TExpr2 const & itkNotUsed(rhs)) { return lhs.Size(); } /** Helper function for VLV expression templates: returns the common size. * \param[in] lhs left hand side expression * \param[in] rhs right hand side expression * \note This overload assumes that only the second operand is a \c * VariableLengthVector (or expression) array. * \ingroup ITKCommon * \sa \c VariableLengthVector * \sa \c VariableLengthVectorExpression */ template inline std::enable_if_t, mpl::Not>>::Value, unsigned int> GetSize(TExpr1 const & itkNotUsed(lhs), TExpr2 const & rhs) { return rhs.Size(); } template struct GetType> { using Type = T; static Type Load(VariableLengthVector const & v, unsigned int idx) { return v[idx]; } }; template struct GetType> { using Type = typename VariableLengthVectorExpression::ResType; static Type Load(VariableLengthVectorExpression const & v, unsigned int idx) { return v[idx]; } }; /// \endcond namespace op { /** Tells whether objects from two types can be added or subtracted. * The operation is authorized if and only if: * - both are arrays, * - or one operand is an array while the second is a number. * \note As this traits is dedicated to help overload binary operators, it * shall not be used to help overload `operator+()` between floats for instance. * Hence, the case where both operands are numbers is rejected. * * \sa \c mpl::IsArray<> to know the exact array types recognized as \em array by this traits * \ingroup MetaProgrammingLibrary * \ingroup ITKCommon */ template struct CanBeAddedOrSubtracted : mpl::Or, mpl::IsArray>, mpl::And, mpl::IsNumber>, mpl::And, mpl::IsArray>> {}; /** Tells whether objects from two types can be multiplied. * The operation is authorized if and only if: * - one operand is an array while the second is a number. * \note As this traits is dedicated to help overload `operator*()`, it * shall not be used to help overload the operator between floats for instance. * Hence, the case where both operands are numbers is rejected. * * \sa \c mpl::IsArray<> to know the exact array types recognized as \em array by this traits * \ingroup MetaProgrammingLibrary * \ingroup ITKCommon */ template struct CanBeMultiplied : mpl::Or, mpl::IsNumber>, mpl::And, mpl::IsArray>> {}; /** Tells whether objects from two types can be divided. * The operation is authorized if and only if: * - the first operand is an array while the second is a number. * \note As this traits is dedicated to help overload `operator/()`, it * shall not be used to help overload the operator between floats for instance. * Hence, the case where both operands are numbers is rejected. * * \sa \c mpl::IsArray<> to know the exact array types recognized as \em array by this traits * \ingroup MetaProgrammingLibrary * \ingroup ITKCommon */ template struct CanBeDivided : mpl::And, mpl::IsNumber> {}; } // namespace op } // namespace Details /// \endcond /** Expression Template for \c VariableLengthVector. * Contains an expression template that models a binary operation between two * sub expressions (of type \c VariableLengthVector, or \c VariableLengthVectorExpression) * \tparam TExpr1 Type of the left sub-expression * \tparam TExpr2 Type of the right sub-expression * \tparam TBinaryOp Binary Operation to apply to both sub-expressions. * * \note We permit to add a `VariableLengthVector` with a * `VariableLengthVector`, the result will be of type * `VariableLengthVector`. * * \warning Explicitly static casting an expression to a * \c VariableLengthVector<> will defeat the purpose of the optimization * implemented here. It's thus best to let the expression automatically adjust * to the type with the most precision. * Eventually, when assigning to the final destination (a * \c VariableLengthVector<>), a casting on-the-fly could be realized by the * assignment operator, or by the copy constructor. * * \todo Add support for unary operations like `operator-()`. * * \ingroup DataRepresentation * \ingroup ITKCommon */ template struct VariableLengthVectorExpression { VariableLengthVectorExpression(TExpr1 const & lhs, TExpr2 const & rhs) : m_lhs(lhs) , m_rhs(rhs) { // Not necessary actually as end-user/developer is not expected to // provide new BinaryOperations static_assert(std::is_base_of_v, "The Binary Operation shall inherit from BinaryOperationConcept"); } /// Returns the size of the vector expression. unsigned int Size() const { return Details::GetSize(m_lhs, m_rhs); } /// Vector type of the Result Expression using ResType = typename mpl::PromoteType::Type, typename Details::GetType::Type>::Type; /// Real type of the elements using RealValueType = typename NumericTraits::RealType; /** Element access operator. * \pre `idx < Size()` * \internal * This is where the magic happens. Instead of building a new vector based on * the two input vectors, we compute each element on-the-fly when * requested(by a \c VariableLengthVector constructor or an assignment * operator). * * \c Load() is in charge of fetching the i-th element of the sub-expressions */ ResType operator[](unsigned int idx) const { itkAssertInDebugAndIgnoreInReleaseMacro(idx < Size()); return TBinaryOp::Apply(Details::GetType::Load(m_lhs, idx), Details::GetType::Load(m_rhs, idx)); } /** Returns vector's Euclidean Norm */ RealValueType GetNorm() const; /** Returns vector's squared Euclidean Norm */ RealValueType GetSquaredNorm() const; private: TExpr1 const & m_lhs; TExpr2 const & m_rhs; }; /** Addition involving a \c VariableLengthVector. * This operation is generic and takes: * - two arrays, * - or one array and one number (on either side) * \return an expression template proxy object. * \throw None As no allocation will be performed. * \relates itk::VariableLengthVector * \sa \c mpl::IsArray<> to know the exact array types recognized as \em array by this traits */ template inline std::enable_if_t::Value, VariableLengthVectorExpression> operator+(TExpr1 const & lhs, TExpr2 const & rhs) { return VariableLengthVectorExpression(lhs, rhs); } /** Subtraction involving a \c VariableLengthVector. * This operation is generic and takes: * - two arrays, * - or one array and one number (on either side) * \return an expression template proxy object. * \throw None As no allocation will be performed. * \relates itk::VariableLengthVector * \sa \c mpl::IsArray<> to know the exact array types recognized as \em array by this traits */ template inline std::enable_if_t::Value, VariableLengthVectorExpression> operator-(TExpr1 const & lhs, TExpr2 const & rhs) { return VariableLengthVectorExpression(lhs, rhs); } /** Multiplication between a \c VariableLengthVector and a scalar. * This operation is generic and takes one array and one number (on either * side). * \return an expression template proxy object. * \throw None As no allocation will be performed. * \relates itk::VariableLengthVector * \sa \c mpl::IsArray<> to know the exact array types recognized as \em array by this traits */ template inline std::enable_if_t::Value, VariableLengthVectorExpression> operator*(TExpr1 const & lhs, TExpr2 const & rhs) { return VariableLengthVectorExpression(lhs, rhs); } /** Division of a \c VariableLengthVector by a scalar. * This operation is generic and takes one array and one number. * \return an expression template proxy object. * \throw None As no allocation will be performed. * \relates itk::VariableLengthVector * \sa \c mpl::IsArray<> to know the exact array types recognized as \em array by this traits */ template inline std::enable_if_t::Value, VariableLengthVectorExpression> operator/(TExpr1 const & lhs, TExpr2 const & rhs) { return VariableLengthVectorExpression(lhs, rhs); } /** Serialization of \c VariableLengthVectorExpression * \relates itk::VariableLengthVectorExpression */ template std::ostream & operator<<(std::ostream & os, VariableLengthVectorExpression const & v) { os << '['; if (v.Size() != 0) { os << v[0]; for (unsigned int i = 1, N = v.Size(); i != N; ++i) { os << ", " << v[i]; } } return os << ']'; } /** Returns vector's Euclidean Norm. * \tparam TExpr must be an array * \sa \c mpl::IsArray<> to know the exact array types recognized as \em array by this traits * \relates itk::VariableLengthVectorExpression */ template inline std::enable_if_t::Value, typename TExpr::RealValueType> GetNorm(TExpr const & v) { return static_cast(std::sqrt(static_cast(GetSquaredNorm(v)))); } /** Returns vector's squared Euclidean Norm. * \tparam TExpr must be an array * \sa \c mpl::IsArray<> to know the exact array types recognized as \em array by this traits * \relates itk::VariableLengthVectorExpression */ template inline std::enable_if_t::Value, typename TExpr::RealValueType> GetSquaredNorm(TExpr const & v) { using RealValueType = typename TExpr::RealValueType; RealValueType sum = 0.0; for (unsigned int i = 0, N = v.Size(); i < N; ++i) { const RealValueType value = v[i]; sum += value * value; } return sum; } /**\name Serialization */ //@{ /** Serialization of \c VariableLengthVector * \relates itk::VariableLengthVector */ template std::ostream & operator<<(std::ostream & os, const VariableLengthVector & arr) { const unsigned int length = arr.Size(); const int last = static_cast(length) - 1; os << '['; for (int i = 0; i < last; ++i) { os << arr[i] << ", "; } if (length >= 1) { os << arr[last]; } os << ']'; return os; } //@} /**\name Standard compliance functions */ //@{ /** \c swap() overload for \c VariableLengthVector * \throw None * \relates itk::VariableLengthVector * \internal * This overload follows C++ standard naming convention. This is required to * permit \c VariableLengthVector to be exchanged by standard algorithms that * take advantage of Koening Namespace Lookup (a.k.a. Argument Dependant * Lookup). e.g. \code template f(T & l, T & r) { using std::swap; swap(l,r); ... } * \endcode */ template inline void swap(VariableLengthVector & l_, VariableLengthVector & r_) noexcept { l_.Swap(r_); } //@} } // namespace itk #include "itkNumericTraitsVariableLengthVectorPixel.h" #ifndef ITK_MANUAL_INSTANTIATION # include "itkVariableLengthVector.hxx" #endif #endif