ENH: PtrList iterate over non-null entries (#2702)

- the iterator/const_iterator now skip any nullptr entries,
  which enables the following code to work even if the PtrList
  contains nullptr:

  for (const auto& intf : interfaces)
  {
      // Do something
      ...
  }

- this is a change in behaviour compared to OpenFOAM-v2212 and earlier,
  but is non-breaking:
  * Lists without null entries will traverse exactly as before.
  * Lists with null entries will now traverse correctly without
    provoking a FatalError.
This commit is contained in:
Mark Olesen 2023-02-15 11:12:13 +01:00
parent afee861af9
commit 9729617ae3
3 changed files with 326 additions and 158 deletions

View File

@ -268,10 +268,7 @@ Ostream& report
int main(int argc, char *argv[])
{
PtrList<Scalar> list1(10);
PtrList<Scalar> list2(15);
PtrList<Scalar> listApp;
#if 0
{
DLPtrList<Scalar> llist1;
llist1.push_front(new Scalar(100));
@ -301,8 +298,10 @@ int main(int argc, char *argv[])
<< "for-: " << it << endl;
}
}
#endif
// Same but as SLPtrList
#if 0
{
SLPtrList<Scalar> llist1;
llist1.push_front(new Scalar(100));
@ -318,18 +317,23 @@ int main(int argc, char *argv[])
PtrList<Scalar> list1b(llist1);
Info<< list1b << endl;
}
#endif
PtrList<Scalar> list1(10);
forAll(list1, i)
{
list1.set(i, new Scalar(1.3*i));
}
PtrList<Scalar> list2(15);
Info<< "Emplace set " << list2.size() << " values" << nl;
forAll(list2, i)
{
list2.emplace(i, (10 + 1.3*i));
}
PtrList<Scalar> listApp;
for (label i = 0; i < 5; ++i)
{
listApp.emplace_back(1.3*i);
@ -375,6 +379,24 @@ int main(int argc, char *argv[])
list1.set(i, nullptr);
}
{
Info<< "range-for of list (" << list1.count() << '/'
<< list1.size() << ") non-null entries" << nl
<< "(" << nl;
for (const auto& item : list1)
{
Info<< " " << item << nl;
}
Info<< ")" << nl;
}
{
Info<< "iterate on non-null:" << endl;
forAllConstIters(list1, iter)
{
Info<< " " << iter.key() << " : " << iter.val() << nl;
}
}
Info<< "release some items:" << endl;
for (label i = -2; i < 5; i++)

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2018-2022 OpenCFD Ltd.
Copyright (C) 2018-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -30,7 +30,41 @@ Class
Description
A list of pointers to objects of type \<T\>, without allocation/deallocation
management of the pointers - this is to be done elsewhere.
The operator[] returns a reference to the object, not the pointer.
The operator[] returns a reference to the object (not the pointer).
The iterators are similar to bitSet in that they skip nullptr entries,
and also return a value (like the list operator[] does).
When traversing lists, it possible to test the validity directly:
\code
forAll(interfaces, i)
{
if (interfaces.test(i))
{
// Interface is set, do something
const auto& intf = interfaces[i];
...
}
}
\endcode
The lists can also be traversed with a for-range
(in OpenFOAM-v2212 and earlier this would have failed on nullptr entries):
\code
for (const auto& intf : interfaces)
{
// Do something
...
}
\endcode
It is also possible to traverse with non-null entries
and use key/val access (like HashTable naming):
\code
forAllConstIters(interfaces, iter)
{
Info<< "entry " << iter.key() << " : " << iter.val() << nl;
}
\endcode
Note
The class definition is such that it contains a list of pointers, but
@ -221,7 +255,7 @@ public:
inline label size() const noexcept;
//- The number of non-null entries in the list
inline label size_nonNull() const noexcept;
inline label count() const noexcept;
//- Reference to the first element of the list
inline T& front();
@ -344,81 +378,209 @@ public:
friend Ostream& operator<< <T>(Ostream& os, const UPtrList<T>& list);
// Iterators
protected:
//- Forward iterator with non-const access
class iterator
// Iterators and helpers
//- Internally used base for iterator and const_iterator
template<bool Const> class Iterator;
//- Allow iterator access to internals
friend class Iterator<true>;
//- Allow iterator access to internals
friend class Iterator<false>;
//- The iterator base for UPtrList (internal use only).
// Iterates non-nullptr entries.
template<bool Const>
class Iterator
{
// Pointer to parent
T** ptr_;
public:
// Typedefs
//- The list container type
using list_type = typename std::conditional
<
Const,
const UPtrList<T>,
UPtrList<T>
>::type;
protected:
// Protected Data
//- The parent being iterated
// Uses pointer for default copy/assignment
list_type* list_;
//- The position within the list
label pos_;
// Friendship with UPtrList, for begin constructor
friend class UPtrList;
// Protected Constructors
//- Default construct. Also the same as the end iterator
inline constexpr Iterator() noexcept;
//- Construct begin iterator
inline explicit Iterator(list_type* list);
// Protected Member Functions
//- Increment to the next non-null position
inline void increment();
//- Permit explicit cast to the other (const/non-const) iterator
template<bool Any>
explicit operator const Iterator<Any>&() const
{
return *reinterpret_cast<const Iterator<Any>*>(this);
}
public:
using iterator_category = std::forward_iterator_tag;
using value_type = T;
using difference_type = label;
// Member Functions/Operators
//- True if iterator points to a non-null entry
bool good() const noexcept { return (list_ && pos_ >= 0); }
//- The iterator position/index within the list
label key() const noexcept { return pos_; }
//- Compare hash-entry element pointers.
// Independent of const/non-const access
template<bool Any>
bool operator==(const Iterator<Any>& iter) const noexcept
{
return (pos_ == iter.pos_);
}
template<bool Any>
bool operator!=(const Iterator<Any>& iter) const noexcept
{
return (pos_ != iter.pos_);
}
};
public:
// Iterators
//- Forward iterator with non-const access
class iterator : public Iterator<false>
{
public:
// using iterator_category = std::forward_iterator_tag;
// using difference_type = label;
using pointer = T*;
using reference = T&;
friend class const_iterator;
//- Construct for a given entry
inline iterator(T** ptr) noexcept;
// Member Functions
// Constructors
//- Return pointer, can be nullptr.
pointer get() const { return *ptr_; }
//- Default construct - an end iterator
constexpr iterator() noexcept = default;
// Member Operators
//- Copy construct from similar access type
explicit iterator(const Iterator<false>& iter)
:
Iterator<false>(iter)
{}
inline pointer operator->() const;
inline reference operator*() const;
// Forward Iteration
inline iterator& operator++() noexcept;
inline iterator operator++(int) noexcept;
// Member Functions/Operators
inline bool operator==(const iterator& iter) const noexcept;
inline bool operator!=(const iterator& iter) const noexcept;
//- Pointer to the referenced object (failsafe)
inline pointer get() const;
//- Reference to the object
inline reference val() const;
//- Pointer to the referenced object
pointer operator->() const { return this->get(); }
//- Reference to the object
reference operator*() const { return this->val(); }
//- Legacy call operator: reference to the object
reference operator()() const { return this->val(); }
//- Move to the next non-nullptr entry
inline iterator& operator++();
inline iterator operator++(int);
};
//- Forward iterator with const access
class const_iterator
class const_iterator : public Iterator<true>
{
// Pointer to parent
const T* const* ptr_;
public:
using iterator_category = std::forward_iterator_tag;
using value_type = const T;
using difference_type = label;
// using iterator_category = std::forward_iterator_tag;
// using difference_type = label;
using pointer = const T*;
using reference = const T&;
//- Construct for a given entry
inline const_iterator(const T* const* ptr) noexcept;
//- Copy construct from non-const iterator
inline const_iterator(const iterator& iter) noexcept;
// Generated Methods
// Member Functions
//- Default construct (end iterator)
const_iterator() = default;
//- Return pointer, can be nullptr.
pointer get() const { return *ptr_; }
//- Copy construct
const_iterator(const const_iterator&) = default;
// Member Operators
//- Copy assignment
const_iterator& operator=(const const_iterator&) = default;
inline pointer operator->() const;
inline reference operator*() const;
// Forward Iteration
inline const_iterator& operator++() noexcept;
inline const_iterator operator++(int) noexcept;
// Constructors
inline bool operator==(const const_iterator& iter) const noexcept;
inline bool operator!=(const const_iterator& iter) const noexcept;
//- Copy construct from any access type
template<bool Any>
const_iterator(const Iterator<Any>& iter)
:
Iterator<true>(static_cast<const Iterator<Any>&>(iter))
{}
//- Implicit conversion from dissimilar access type
const_iterator(const iterator& iter)
:
const_iterator(reinterpret_cast<const const_iterator&>(iter))
{}
// Member Functions/Operators
//- Pointer to the referenced object (failsafe)
inline pointer get() const;
//- Reference to the object
inline reference val() const;
//- Pointer to the referenced object
pointer operator->() const { return this->get(); }
//- Reference to the object
reference operator*() const { return this->val(); }
//- Legacy call operator: reference to the object
reference operator()() const { return this->val(); }
//- Move to the next non-nullptr entry
inline const_iterator& operator++();
inline const_iterator operator++(int);
};
@ -429,20 +591,20 @@ public:
T** end_ptr() noexcept { return ptrs_.end(); }
//- Return an iterator to begin of UPtrList traversal
inline iterator begin() noexcept;
//- Return iterator to begin traversal of non-nullptr entries.
inline iterator begin();
//- Return iterator beyond end of UPtrList traversal
inline iterator end() noexcept;
//- Return const_iterator to begin of UPtrList traversal
inline const_iterator cbegin() const noexcept;
//- Return const_iterator to begin traversal of non-nullptr entries.
inline const_iterator cbegin() const;
//- Return const_iterator beyond end of UPtrList traversal
inline const_iterator cend() const noexcept;
//- Return const_iterator to begin of UPtrList traversal
inline const_iterator begin() const noexcept;
//- Return const_iterator to begin traversal of non-nullptr entries.
inline const_iterator begin() const;
//- Return const_iterator beyond end of UPtrList traversal
inline const_iterator end() const noexcept;

View File

@ -124,7 +124,7 @@ inline Foam::label Foam::UPtrList<T>::size() const noexcept
template<class T>
inline Foam::label Foam::UPtrList<T>::size_nonNull() const noexcept
inline Foam::label Foam::UPtrList<T>::count() const noexcept
{
return ptrs_.count();
}
@ -292,139 +292,139 @@ inline T& Foam::UPtrList<T>::operator[](const label i)
}
// * * * * * * * * * * * * * * * * iterator * * * * * * * * * * * * * * * * //
// * * * * * * * * * * * * * * * iterator base * * * * * * * * * * * * * * * //
template<class T>
inline Foam::UPtrList<T>::iterator::
iterator(T** ptr) noexcept
template<bool Const>
inline constexpr
Foam::UPtrList<T>::Iterator<Const>::Iterator() noexcept
:
ptr_(ptr)
list_(nullptr),
pos_(-1)
{}
template<class T>
inline T* Foam::UPtrList<T>::iterator::operator->() const
template<bool Const>
inline Foam::UPtrList<T>::Iterator<Const>::Iterator
(
list_type* list
)
:
list_(list),
pos_(-1)
{
return *ptr_;
if (list_)
{
pos_ = list_->find_next(-1);
}
}
template<class T>
inline T& Foam::UPtrList<T>::iterator::operator*() const
template<bool Const>
inline void Foam::UPtrList<T>::Iterator<Const>::increment()
{
return **ptr_;
//TDB: extra safety? if (iter.good())
{
pos_ = list_->find_next(pos_);
}
}
// * * * * * * * * * * * * * * * * STL iterator * * * * * * * * * * * * * * //
template<class T>
inline T* Foam::UPtrList<T>::iterator::get() const
{
return this->good() ? this->list_->get(this->pos_) : nullptr;
}
template<class T>
inline T& Foam::UPtrList<T>::iterator::val() const
{
return this->list_->at(this->pos_);
}
template<class T>
inline typename Foam::UPtrList<T>::iterator&
Foam::UPtrList<T>::iterator::operator++() noexcept
Foam::UPtrList<T>::iterator::operator++()
{
++ptr_;
this->increment();
return *this;
}
template<class T>
inline typename Foam::UPtrList<T>::iterator
Foam::UPtrList<T>::iterator::operator++(int) noexcept
Foam::UPtrList<T>::iterator::operator++(int)
{
iterator iter(*this);
++ptr_;
this->increment();
return iter;
}
// * * * * * * * * * * * * * * * STL const_iterator * * * * * * * * * * * * //
template<class T>
inline bool Foam::UPtrList<T>::iterator::
operator==(const iterator& iter) const noexcept
inline const T* Foam::UPtrList<T>::const_iterator::get() const
{
return ptr_ == iter.ptr_;
return this->good() ? this->list_->get(this->pos_) : nullptr;
}
template<class T>
inline bool Foam::UPtrList<T>::iterator::
operator!=(const iterator& iter) const noexcept
inline const T& Foam::UPtrList<T>::const_iterator::val() const
{
return ptr_ != iter.ptr_;
}
// * * * * * * * * * * * * * * * const_iterator * * * * * * * * * * * * * * //
template<class T>
inline Foam::UPtrList<T>::const_iterator::
const_iterator(const T* const* ptr) noexcept
:
ptr_(ptr)
{}
template<class T>
inline Foam::UPtrList<T>::const_iterator::
const_iterator(const iterator& iter) noexcept
:
ptr_(iter.ptr_)
{}
template<class T>
inline const T* Foam::UPtrList<T>::const_iterator::operator->() const
{
return *ptr_;
}
template<class T>
inline const T& Foam::UPtrList<T>::const_iterator::operator*() const
{
return **ptr_;
return this->list_->at(this->pos_);
}
template<class T>
inline typename Foam::UPtrList<T>::const_iterator&
Foam::UPtrList<T>::const_iterator::operator++() noexcept
Foam::UPtrList<T>::const_iterator::operator++()
{
++ptr_;
this->increment();
return *this;
}
template<class T>
inline typename Foam::UPtrList<T>::const_iterator
Foam::UPtrList<T>::const_iterator::operator++(int) noexcept
Foam::UPtrList<T>::const_iterator::operator++(int)
{
const_iterator iter(*this);
++ptr_;
this->increment();
return iter;
}
template<class T>
inline bool Foam::UPtrList<T>::const_iterator::
operator==(const const_iterator& iter) const noexcept
{
return ptr_ == iter.ptr_;
}
template<class T>
inline bool Foam::UPtrList<T>::const_iterator::
operator!=(const const_iterator& iter) const noexcept
{
return ptr_ != iter.ptr_;
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
template<class T>
inline typename Foam::UPtrList<T>::iterator
Foam::UPtrList<T>::begin() noexcept
Foam::UPtrList<T>::begin()
{
return ptrs_.begin();
return iterator(Iterator<false>(this));
}
template<class T>
inline typename Foam::UPtrList<T>::const_iterator
Foam::UPtrList<T>::begin() const
{
return const_iterator(Iterator<true>(this));
}
template<class T>
inline typename Foam::UPtrList<T>::const_iterator
Foam::UPtrList<T>::cbegin() const
{
return const_iterator(Iterator<true>(this));
}
@ -432,31 +432,7 @@ template<class T>
inline typename Foam::UPtrList<T>::iterator
Foam::UPtrList<T>::end() noexcept
{
return ptrs_.end();
}
template<class T>
inline typename Foam::UPtrList<T>::const_iterator
Foam::UPtrList<T>::cbegin() const noexcept
{
return ptrs_.cbegin();
}
template<class T>
inline typename Foam::UPtrList<T>::const_iterator
Foam::UPtrList<T>::cend() const noexcept
{
return ptrs_.cend();
}
template<class T>
inline typename Foam::UPtrList<T>::const_iterator
Foam::UPtrList<T>::begin() const noexcept
{
return ptrs_.begin();
return iterator();
}
@ -464,7 +440,15 @@ template<class T>
inline typename Foam::UPtrList<T>::const_iterator
Foam::UPtrList<T>::end() const noexcept
{
return ptrs_.end();
return UPtrList<T>::const_iterator();
}
template<class T>
inline typename Foam::UPtrList<T>::const_iterator
Foam::UPtrList<T>::cend() const noexcept
{
return UPtrList<T>::const_iterator();
}