Skip to content

Commit

Permalink
Merge pull request #2635 from timvdm/modern-cpp-iterators
Browse files Browse the repository at this point in the history
Add support for range-based for loops
  • Loading branch information
ghutchis committed Oct 26, 2023
2 parents 2e11d9b + 8fa5c36 commit d62e512
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 4 deletions.
60 changes: 60 additions & 0 deletions include/openbabel/atom.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,62 @@ namespace OpenBabel
if (value) SetFlag(X); \
else UnsetFlag(X);

class OBAPI OBAtomAtomIterAdaptor
{
public:
using Iter = std::vector<OBBond*>::const_iterator;

using value_type = OBAtom*;
using difference_type = std::ptrdiff_t;
using pointer = OBAtom**;
using reference = OBAtom*&;
using iterator_category = std::forward_iterator_tag;

OBAtomAtomIterAdaptor() = default;

OBAtomAtomIterAdaptor(Iter iter, const OBAtom *atom = nullptr)
: m_iter(iter), m_atom(atom)
{
}

OBAtom* operator*() const;

OBAtomAtomIterAdaptor& operator++()
{
++m_iter;
return *this;
}

OBAtomAtomIterAdaptor operator++(int)
{
auto tmp = *this;
++m_iter;
return tmp;
}

bool operator==(const OBAtomAtomIterAdaptor &other) const
{
return m_iter == other.m_iter;
}

bool operator!=(const OBAtomAtomIterAdaptor &other) const
{
return m_iter != other.m_iter;
}

private:
Iter m_iter;
const OBAtom *m_atom = nullptr;
};

using OBAtomBondRange = OBRange<OBBond*>;
using OBAtomAtomRange = OBRange<OBAtom*, OBAtomAtomIterAdaptor>;

#if __cplusplus >= 202002L
static_assert(std::forward_iterator<OBAtomAtomIterAdaptor>);
static_assert(std::ranges::range<OBAtomAtomRange>);
#endif

// Class OBAtom
// class introduction in atom.cpp
#define OBATOM_TYPE_LEN 6
Expand Down Expand Up @@ -269,12 +325,16 @@ namespace OpenBabel
//! \return An iterator to the end of the bonds to this atom
OBBondIterator EndBonds()
{ return(_vbond.end()); }
//! \return A range over the bonds to this atom. This range can be used in a range-based for loop.
OBAtomBondRange GetBonds() const { return {_vbond.begin(), _vbond.end()}; }
//! Set the iterator @p i to the beginning of the bonds
//! \return The first bond to this atom (or NULL if none exist)
OBBond *BeginBond(OBBondIterator &i);
//! Increment the iterator @p i
//! \return The next bond to this atom (or NULL if none exist)
OBBond *NextBond(OBBondIterator &i);
//! \return A range over the neighbors of this atom. This range can be used in a range-based for loop.
OBAtomAtomRange GetNbrs() const { return { {_vbond.begin(), this}, {_vbond.end()} }; }
//! Set the iterator @p i to the beginning of the bonds
//! \return The first neighboring atom (or NULL if none exist)
OBAtom *BeginNbrAtom(OBBondIterator &i);
Expand Down
16 changes: 16 additions & 0 deletions include/openbabel/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,22 @@ class OBConversion; //used only as pointer

};

template<typename T, typename Iter = typename std::vector<T>::const_iterator>
class OBAPI OBRange
{
public:
OBRange(Iter begin, Iter end) : m_begin{begin}, m_end{end}
{
}

Iter begin() const { return m_begin; }
Iter end() const { return m_end; }

private:
Iter m_begin;
Iter m_end;
};

} //namespace OpenBabel

#endif // OB_BASE_H
Expand Down
6 changes: 6 additions & 0 deletions include/openbabel/bond.h
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,12 @@ namespace OpenBabel

}; // class OBBond

inline OBAtom* OBAtomAtomIterAdaptor::operator*() const
{
auto bond = *m_iter;
return m_atom != bond->GetBeginAtom() ? bond->GetBeginAtom() : bond->GetEndAtom();
}

//! A standard iterator over a vector of bonds
typedef std::vector<OBBond*>::iterator OBBondIterator;

Expand Down
12 changes: 12 additions & 0 deletions include/openbabel/mol.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,14 @@ namespace OpenBabel

enum HydrogenType { AllHydrogen, PolarHydrogen, NonPolarHydrogen };

using OBMolAtomRange = OBRange<OBAtom*>;
using OBMolBondRange = OBRange<OBBond*>;

#if __cplusplus >= 202002L
static_assert(std::ranges::range<OBMolAtomRange>);
static_assert(std::ranges::range<OBMolBondRange>);
#endif

// class introduction in mol.cpp
class OBAPI OBMol: public OBBase
{
Expand Down Expand Up @@ -652,10 +660,14 @@ enum HydrogenType { AllHydrogen, PolarHydrogen, NonPolarHydrogen };
OBAtomIterator EndAtoms() { return _vatom.begin() + NumAtoms() ; }
//! \return A constant atom iterator pointing to the end of the atom list
OBAtomConstIterator CEndAtoms() const { return _vatom.cbegin() + NumAtoms(); }
//! \return A range over the atoms. This range can be used in a range-based for loop.
OBMolAtomRange GetAtoms() const { return {_vatom.begin(), _vatom.begin() + NumAtoms()}; }
//! \return A bond iterator pointing to the beginning of the bond list
OBBondIterator BeginBonds() { return _vbond.begin(); }
//! \return A bond iterator pointing to the end of the bond list
OBBondIterator EndBonds() { return _vbond.begin() + NumBonds() ; }
//! \return A range over the bonds. This range can be used in a range-based for loop.
OBMolBondRange GetBonds() const { return {_vbond.begin(), _vbond.begin() + NumBonds()}; }
//! \return A residue iterator pointing to the beginning of the residue list
OBResidueIterator BeginResidues() { return _residue.begin(); }
//! \return A residue iterator pointing to the end of the residue list
Expand Down
27 changes: 23 additions & 4 deletions include/openbabel/obiter.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ GNU General Public License for more details.

#include <openbabel/babelconfig.h>
#include <openbabel/bitvec.h>
#include <openbabel/mol.h>
#include <openbabel/atom.h>
#include <openbabel/bond.h>

#include <vector>
#include <stack>
Expand Down Expand Up @@ -395,10 +398,26 @@ namespace OpenBabel
OBRing& operator*() const { return *_ptr;}
};

#define FOR_ATOMS_OF_MOL(a,m) for( OpenBabel::OBMolAtomIter a(m); a; ++a )
#define FOR_BONDS_OF_MOL(b,m) for( OpenBabel::OBMolBondIter b(m); b; ++b )
#define FOR_NBORS_OF_ATOM(a,p) for( OpenBabel::OBAtomAtomIter a(p); a; ++a )
#define FOR_BONDS_OF_ATOM(b,p) for( OpenBabel::OBAtomBondIter b(p); b; ++b )
namespace impl {

inline OBMolAtomRange MolGetAtoms(const OpenBabel::OBMol &mol) { return mol.GetAtoms(); }
inline OBMolAtomRange MolGetAtoms(const OpenBabel::OBMol *mol) { return mol->GetAtoms(); }

inline OBMolBondRange MolGetBonds(const OpenBabel::OBMol &mol) { return mol.GetBonds(); }
inline OBMolBondRange MolGetBonds(const OpenBabel::OBMol *mol) { return mol->GetBonds(); }

inline OBAtomBondRange AtomGetBonds(const OpenBabel::OBAtom &atom) { return atom.GetBonds(); }
inline OBAtomBondRange AtomGetBonds(const OpenBabel::OBAtom *atom) { return atom->GetBonds(); }

inline OBAtomAtomRange AtomGetNbrs(const OpenBabel::OBAtom &atom) { return atom.GetNbrs(); }
inline OBAtomAtomRange AtomGetNbrs(const OpenBabel::OBAtom *atom) { return atom->GetNbrs(); }

}

#define FOR_ATOMS_OF_MOL(a,m) for (auto a : impl::MolGetAtoms(m))
#define FOR_BONDS_OF_MOL(b,m) for (auto b : impl::MolGetBonds(m))
#define FOR_NBORS_OF_ATOM(a,p) for (auto a : impl::AtomGetNbrs(p))
#define FOR_BONDS_OF_ATOM(b,p) for (auto b : impl::AtomGetBonds(p))
#define FOR_RESIDUES_OF_MOL(r,m) for( OpenBabel::OBResidueIter r(m); r; ++r )
#define FOR_ATOMS_OF_RESIDUE(a,r) for( OpenBabel::OBResidueAtomIter a(r); a; ++a )
#define FOR_DFS_OF_MOL(a,m) for( OpenBabel::OBMolAtomDFSIter a(m); a; ++a )
Expand Down

0 comments on commit d62e512

Please sign in to comment.