ENH: improve UPtrList sorting

- adjust nullptr checks to discourage flip-flop when confronted with
  multiple null values.

     Old:   (a && b) ? (*a < *b) : bool(a);
     New:   (a && b) ? (*a < *b) : !b;

  comparing (non-null < null) and (null < non-null) behaves
  identically, but comparing (null < null) now tests as true
  (ie, already sorted) whereas before it would have been false
  (ie, needs a swap)

- add UPtrList trimTrailingNull(), which reduces the effective
  (addressable) list size to ignore any trailing null pointers, but
  without reallocation. This is particularly useful when creating a
  UPtrList list view. For example,

     UPtrList<some_iterator> validValues(container.size());

     ...Loop to add valid entries, by some criteria...

     // Shorten list to hide null entries
     validValues.trimTrailingNull();

   This list view now only needs a single allocation, whereas using
   a resize (as was previously necessary) could invoke a second
   allocation, as well as recopying.
This commit is contained in:
Mark Olesen
2023-02-15 10:10:26 +01:00
parent f215ad15d1
commit 521bdf0f07
4 changed files with 47 additions and 29 deletions

View File

@ -187,7 +187,7 @@ template<class T, int SizeMin>
inline Foam::label Foam::PtrDynList<T, SizeMin>::squeezeNull()
{
const label newLen = UPtrList<T>::squeezeNull();
resize(newLen);
PtrList<T>::setAddressableSize(newLen);
return newLen;
}

View File

@ -65,6 +65,23 @@ Foam::label Foam::UPtrList<T>::squeezeNull()
}
template<class T>
void Foam::UPtrList<T>::trimTrailingNull()
{
label newLen = this->size();
for (label i = newLen-1; i >= 0 && !ptrs_[i]; --i)
{
--newLen;
}
// Or mutable?
// const_cast<Detail::PtrListDetail<T>&>(ptrs_).setAddressableSize(newLen);
ptrs_.setAddressableSize(newLen);
}
template<class T>
void Foam::UPtrList<T>::reorder(const labelUList& oldToNew, const bool check)
{
@ -177,6 +194,22 @@ Foam::Ostream& Foam::operator<<(Ostream& os, const UPtrList<T>& list)
// * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
template<class T>
void Foam::sort(UPtrList<T>& list)
{
std::stable_sort
(
list.begin_ptr(),
list.end_ptr(),
// Compare less, with nullptr protect and sort nullptr to end
[](const T* const a, const T* const b) -> bool
{
return (a && b) ? (*a < *b) : !b;
}
);
}
template<class T, class Compare>
void Foam::sort(UPtrList<T>& list, const Compare& comp)
{
@ -189,12 +222,4 @@ void Foam::sort(UPtrList<T>& list, const Compare& comp)
}
template<class T>
void Foam::sort(UPtrList<T>& list)
{
// ie, lessOp<T>() or std::less<T>()
Foam::sort(list, [](const T& a, const T& b) { return (a < b); });
}
// ************************************************************************* //

View File

@ -129,7 +129,7 @@ public:
//- Compare dereferenced pointers
bool operator()(const T* const a, const T* const b) const
{
return (a && b) ? comp(*a, *b) : bool(a);
return (a && b) ? comp(*a, *b) : !b;
}
};
@ -150,7 +150,7 @@ public:
const T* const a = values.get(ai);
const T* const b = values.get(bi);
return (a && b) ? (*a < *b) : bool(a);
return (a && b) ? (*a < *b) : !b;
}
};
@ -171,7 +171,7 @@ public:
const T* const a = values.get(ai);
const T* const b = values.get(bi);
return (a && b) ? (*b < *a) : bool(b);
return (a && b) ? (*b < *a) : !a;
}
};
@ -262,10 +262,15 @@ public:
//- Alias for resize()
void setSize(const label n) { this->resize(n); }
//- Squeeze out intermediate nullptr entries in the list of pointers
//- Squeeze out nullptr entries in the list of pointers after which
//- any null pointers will be at the end of the list
// \return the number of non-null entries
label squeezeNull();
//- Reduce addressable list size to ignore any trailing null pointers.
// The reduces the effective list length without reallocation
void trimTrailingNull();
//- Append an element to the end of the list
inline void push_back(T* ptr);
@ -343,7 +348,7 @@ public:
// Member Functions
//- Return pointer, can be nullptr.
inline pointer get() const;
pointer get() const { return *ptr_; }
// Member Operators
@ -403,7 +408,7 @@ public:
// Member Functions
//- Return pointer, can be nullptr.
inline pointer get() const;
pointer get() const { return *ptr_; }
// Member Operators
@ -507,6 +512,8 @@ public:
// * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * * //
//- Inplace (stable) sorting of pointer list.
// This sort function includes null pointer guards and will also sort
// any null pointers to the end (eg, rubbish that can be truncated)
template<class T>
void sort(UPtrList<T>& list);

View File

@ -281,13 +281,6 @@ iterator(T** ptr) noexcept
{}
template<class T>
inline T* Foam::UPtrList<T>::iterator::get() const
{
return *ptr_;
}
template<class T>
inline T* Foam::UPtrList<T>::iterator::operator->() const
{
@ -455,13 +448,6 @@ const_iterator(const iterator& iter) noexcept
{}
template<class T>
inline const T* Foam::UPtrList<T>::const_iterator::get() const
{
return *ptr_;
}
template<class T>
inline const T* Foam::UPtrList<T>::const_iterator::operator->() const
{