Add C++20 iterators

Reason: Requested on the mailing list.
This commit is contained in:
Gennaro Prota
2025-09-09 14:42:38 +02:00
parent 1f6e471132
commit b0b94ec560
5 changed files with 645 additions and 16 deletions

View File

@@ -46,22 +46,12 @@ an unsigned long `n`, the bit at position `i` of the bitset has the same value
as `(n >> i) & 1`.
== Rationale
`dynamic_bitset` is not a <a
Because of the proxy reference type, `dynamic_bitset` is not a <a
href="https://en.cppreference.com/w/cpp/named_req/Container.html">Container</a>
and does not provide iterators for the following reason:
A container with a proxy reference type can not fulfill the container
requirements as specified in the C++ standard (unless one resorts to strange
iterator semantics). `std::vector<bool>` has a proxy reference type and does not
fulfill the container requirements and as a result has caused many problems. One
common problem is when people try to use iterators from `std::vector<bool>` with
a standard algorithm such as `std::search()`. The `std::search()` requirements
say that the iterator must be a LegacyForwardIterator, but
`std::vector<bool>::iterator` does not meet this requirement because of the
proxy reference. Depending on the implementation, they may or not be a compile
error or even a run-time error due to this misuse. For further discussion of the
problem see "Effective STL" by Scott Meyers. So `dynamic_bitset` tries to avoid
these problems by not pretending to be a container.
and its iterators do not satisfy the requirements for a LegacyForwardIterator.
This means that its iterators are not usable with many standard algorithms.
However, `dynamic_bitset` provides C++20 iterators which can be used with
ranges.
Some people prefer the name "toggle" to "flip". The name "flip" was chosen
because that is the name used in `std::bitset`. In fact, most of the function

View File

@@ -24,6 +24,9 @@
#include "boost/dynamic_bitset_fwd.hpp"
#include "boost/limits.hpp"
#include "boost/static_assert.hpp"
#if __cpp_lib_three_way_comparison
#include <compare>
#endif
#include <iosfwd>
#include <string>
#include <vector>
@@ -45,6 +48,15 @@ struct hash< boost::dynamic_bitset< Block, AllocatorOrContainer > >;
namespace boost {
template< typename Iterator >
class bit_iterator_base;
template< typename DynamicBitset >
class bit_iterator;
template< typename DynamicBitset >
class const_bit_iterator;
//! The `dynamic_bitset` template represents a set of bits.
//!
//! \par Template parameters
@@ -65,7 +77,8 @@ namespace boost {
//! `Block` is a cv-unqualified unsigned integer type other than
//! `bool`. `AllocatorOrContainer` satisfies the standard requirements for an
//! <a href="https://en.cppreference.com/w/cpp/named_req/Allocator.html">allocator</a>
//! or is a container-like type.
//! or is a container-like type which provides at least bidirectional
//! iterators.
// ---------------------------------------------------------------------------
template< typename Block, typename AllocatorOrContainer >
class dynamic_bitset
@@ -251,6 +264,25 @@ public:
// -----------------------------------------------------------------------
typedef bool const_reference;
friend class bit_iterator< dynamic_bitset >;
friend class const_bit_iterator< dynamic_bitset >;
//! A read/write iterator into the bitset.
// -----------------------------------------------------------------------
typedef bit_iterator< dynamic_bitset > iterator;
//! A read-only iterator into the bitset.
// -----------------------------------------------------------------------
typedef const_bit_iterator< dynamic_bitset > const_iterator;
//! A reverse read/write reverse iterator into the bitset.
// -----------------------------------------------------------------------
typedef std::reverse_iterator< iterator > reverse_iterator;
//! A reverse read-only iterator into the bitset.
// -----------------------------------------------------------------------
typedef std::reverse_iterator< const_iterator > const_reverse_iterator;
//! Constructs a bitset of size zero.
//!
//! \par Postconditions
@@ -446,6 +478,66 @@ public:
// -----------------------------------------------------------------------
~dynamic_bitset();
//! Returns a read/write iterator that refers to the least
//! significant bit in the bitset.
// -----------------------------------------------------------------------
iterator begin();
//! Returns a read-only iterator that refers to the least
//! significant bit in the bitset.
// -----------------------------------------------------------------------
const_iterator begin() const;
//! Returns a read/write iterator that refers one past the most
//! significant bit in the bitset.
// -----------------------------------------------------------------------
iterator end();
//! Returns a read-only iterator that refers one past the most
//! significant bit in the bitset.
// -----------------------------------------------------------------------
const_iterator end() const;
//! Returns a read/write reverse iterator that refers to the
//! most significant bit in the bitset.
// -----------------------------------------------------------------------
reverse_iterator rbegin();
//! Returns a read-only reverse iterator that refers to the most
//! significant bit in the bitset.
// -----------------------------------------------------------------------
const_reverse_iterator rbegin() const;
//! Returns a read/write reverse iterator that refers to one
//! before the least significant bit in the bitset.
// -----------------------------------------------------------------------
reverse_iterator rend();
//! Returns a read-only reverse iterator that refers to one
//! before the least significant bit in the bitset.
// -----------------------------------------------------------------------
const_reverse_iterator rend() const;
//! Returns a read-only iterator that refers to the least
//! significant bit in the bitset.
// -----------------------------------------------------------------------
const_iterator cbegin() const;
//! Returns a read-only iterator that refers to one past the
//! most significant bit in the bitset.
// -----------------------------------------------------------------------
const_iterator cend() const;
//! Returns a read-only reverse iterator that refers to the most
//! significant bit in the bitset.
// -----------------------------------------------------------------------
const_reverse_iterator crbegin() const;
//! Returns a read-only reverse iterator that refers to one
//! before the least significant bit in the bitset.
// -----------------------------------------------------------------------
const_reverse_iterator crend() const;
//! Swaps the contents of this bitset and bitset `b`.
//!
//! \param b The bitset to be swapped with `*this`.
@@ -1266,6 +1358,72 @@ private:
};
};
template< typename Iterator >
class bit_iterator_base
{
public:
typedef typename Iterator::iterator_category iterator_category;
typedef bool value_type;
typedef std::ptrdiff_t difference_type;
typedef value_type * pointer;
typedef value_type & reference;
bit_iterator_base( Iterator block_iterator, int bit_index );
void increment();
void decrement();
void add( typename Iterator::difference_type n );
BOOST_STATIC_CONSTANT( int, bits_per_block = std::numeric_limits< typename Iterator::value_type >::digits );
Iterator m_block_iterator;
int m_bit_index = 0;
};
template< typename DynamicBitset >
class bit_iterator
: public bit_iterator_base< typename DynamicBitset::buffer_type::iterator >
{
public:
typedef typename DynamicBitset::reference reference;
typedef reference * pointer;
typedef typename bit_iterator_base< typename DynamicBitset::buffer_type::iterator >::difference_type difference_type;
bit_iterator();
bit_iterator( typename DynamicBitset::buffer_type::iterator block_iterator, int bit_index );
reference operator*() const;
bit_iterator & operator++();
bit_iterator operator++( int );
bit_iterator & operator--();
bit_iterator operator--( int );
bit_iterator & operator+=( difference_type n );
bit_iterator & operator-=( difference_type n );
reference operator[]( difference_type n ) const;
};
template< typename DynamicBitset >
class const_bit_iterator
: public bit_iterator_base< typename DynamicBitset::buffer_type::const_iterator >
{
public:
typedef bool reference;
typedef bool const_reference;
typedef const bool * pointer;
typedef typename bit_iterator_base< typename DynamicBitset::buffer_type::const_iterator >::difference_type difference_type;
const_bit_iterator( typename DynamicBitset::buffer_type::const_iterator block_iterator, int bit_index );
const_bit_iterator( const bit_iterator< DynamicBitset > & it );
const_reference operator*() const;
const_bit_iterator & operator++();
const_bit_iterator operator++( int );
const_bit_iterator & operator--();
const_bit_iterator operator--( int );
const_bit_iterator & operator+=( difference_type n );
const_bit_iterator & operator-=( difference_type n );
const_reference operator[]( difference_type n ) const;
};
#if ! defined BOOST_NO_INCLASS_MEMBER_INITIALIZATION
template< typename Block, typename AllocatorOrContainer >
@@ -1280,6 +1438,9 @@ template< typename Block, typename AllocatorOrContainer >
const int
dynamic_bitset< Block, AllocatorOrContainer >::ulong_width;
template< typename Container >
const int
bit_iterator_base< Container >::bits_per_block;
#endif
//! Compares two bitsets.

View File

@@ -155,6 +155,319 @@ dynamic_bitset< Block, AllocatorOrContainer >::reference::do_assign( bool x )
}
}
template< typename Iterator >
bit_iterator_base< Iterator >::bit_iterator_base( Iterator block_iterator, int bit_index )
: m_block_iterator( block_iterator )
, m_bit_index( bit_index )
{
BOOST_ASSERT( 0 <= bit_index && bit_index < bits_per_block );
}
template< typename Iterator >
void
bit_iterator_base< Iterator >::increment()
{
++m_bit_index;
if ( m_bit_index == bits_per_block ) {
m_bit_index = 0;
++m_block_iterator;
}
}
template< typename Iterator >
void
bit_iterator_base< Iterator >::decrement()
{
--m_bit_index;
if ( m_bit_index < 0 ) {
m_bit_index = bits_per_block - 1;
--m_block_iterator;
}
}
template< typename Iterator >
void
bit_iterator_base< Iterator >::add( typename Iterator::difference_type n )
{
typename Iterator::difference_type d = m_bit_index + n;
m_block_iterator += d / bits_per_block;
d %= bits_per_block;
if ( d < 0 ) {
d += bits_per_block;
--m_block_iterator;
}
m_bit_index = static_cast< int >( d );
}
template< typename Iterator >
bool
operator==( const bit_iterator_base< Iterator > & lhs, const bit_iterator_base< Iterator > & rhs )
{
return lhs.m_block_iterator == rhs.m_block_iterator && lhs.m_bit_index == rhs.m_bit_index;
}
#if __cpp_lib_three_way_comparison
template< typename Iterator >
std::strong_ordering
operator<=>( const bit_iterator_base< Iterator > & lhs, const bit_iterator_base< Iterator > & rhs )
{
if ( const auto cmp = lhs.m_block_iterator <=> rhs.m_block_iterator; cmp != 0 ) {
return cmp;
} else {
return lhs.m_bit_index <=> rhs.m_bit_index;
}
}
#else
template< typename Iterator >
bool
operator!=( const bit_iterator_base< Iterator > & lhs, const bit_iterator_base< Iterator > & rhs )
{
return !( lhs == rhs );
}
template< typename Iterator >
bool
operator<( const bit_iterator_base< Iterator > & lhs, const bit_iterator_base< Iterator > & rhs )
{
return lhs.m_block_iterator < rhs.m_block_iterator
|| ( lhs.m_block_iterator == rhs.m_block_iterator && lhs.m_bit_index < rhs.m_bit_index );
}
template< typename Iterator >
bool
operator<=( const bit_iterator_base< Iterator > & lhs, const bit_iterator_base< Iterator > & rhs )
{
return !( rhs < lhs );
}
template< typename Iterator >
bool
operator>( const bit_iterator_base< Iterator > & lhs, const bit_iterator_base< Iterator > & rhs )
{
return rhs < lhs;
}
template< typename Iterator >
bool
operator>=( const bit_iterator_base< Iterator > & lhs, const bit_iterator_base< Iterator > & rhs )
{
return !( lhs < rhs );
}
#endif // __cpp_lib_three_way_comparison
template< typename Iterator >
std::ptrdiff_t
operator-( const bit_iterator_base< Iterator > & lhs, const bit_iterator_base< Iterator > & rhs )
{
return ( lhs.m_block_iterator - rhs.m_block_iterator ) * bit_iterator_base< Iterator >::bits_per_block
+ lhs.m_bit_index - rhs.m_bit_index;
}
template< typename DynamicBitset>
bit_iterator< DynamicBitset >::bit_iterator()
: bit_iterator_base< typename DynamicBitset::buffer_type::iterator >()
{
}
template< typename DynamicBitset >
bit_iterator< DynamicBitset >::bit_iterator( typename DynamicBitset::buffer_type::iterator block_iterator, int bit_index )
: bit_iterator_base< typename DynamicBitset::buffer_type::iterator >( block_iterator, bit_index )
{
}
template< typename DynamicBitset >
typename DynamicBitset::reference
bit_iterator< DynamicBitset >::operator*() const
{
return reference( *(this->m_block_iterator), this->m_bit_index );
}
template< typename DynamicBitset >
bit_iterator< DynamicBitset > &
bit_iterator< DynamicBitset >::operator++()
{
this->increment();
return *this;
}
template< typename DynamicBitset >
bit_iterator< DynamicBitset >
bit_iterator< DynamicBitset >::operator++( int )
{
bit_iterator temp = *this;
this->increment();
return temp;
}
template< typename DynamicBitset >
bit_iterator< DynamicBitset > &
bit_iterator< DynamicBitset >::operator--()
{
this->decrement();
return *this;
}
template< typename DynamicBitset >
bit_iterator< DynamicBitset >
bit_iterator< DynamicBitset >::operator--( int )
{
bit_iterator temp = *this;
this->decrement();
return temp;
}
template< typename DynamicBitset >
bit_iterator< DynamicBitset > &
bit_iterator< DynamicBitset >::operator+=( difference_type n )
{
this->add( n );
return *this;
}
template< typename DynamicBitset >
bit_iterator< DynamicBitset > &
bit_iterator< DynamicBitset >::operator-=( difference_type n )
{
this->add( -n );
return *this;
}
template< typename DynamicBitset >
bit_iterator< DynamicBitset >
operator+( const bit_iterator< DynamicBitset > & it, typename bit_iterator< DynamicBitset >::difference_type n )
{
bit_iterator< DynamicBitset > temp = it;
temp += n;
return temp;
}
template< typename DynamicBitset >
bit_iterator< DynamicBitset >
operator+( typename bit_iterator< DynamicBitset >::difference_type n, const bit_iterator< DynamicBitset > & it )
{
return it + n;
}
template< typename DynamicBitset >
bit_iterator< DynamicBitset >
operator-( const bit_iterator< DynamicBitset > & it, typename bit_iterator< DynamicBitset >::difference_type n )
{
bit_iterator< DynamicBitset > temp = it;
temp -= n;
return temp;
}
template< typename DynamicBitset >
typename DynamicBitset::reference
bit_iterator< DynamicBitset >::operator[]( difference_type n ) const
{
return *( *this + n );
}
template< typename DynamicBitset >
const_bit_iterator< DynamicBitset >::const_bit_iterator( typename DynamicBitset::buffer_type::const_iterator block_iterator, int bit_index )
: bit_iterator_base< typename DynamicBitset::buffer_type::const_iterator >( block_iterator, bit_index )
{
}
template< typename DynamicBitset >
const_bit_iterator< DynamicBitset >::const_bit_iterator( const bit_iterator< DynamicBitset > & it )
: bit_iterator_base< typename DynamicBitset::buffer_type::const_iterator >( it.m_block_iterator, it.m_bit_index )
{
}
template< typename DynamicBitset >
typename const_bit_iterator< DynamicBitset >::const_reference
const_bit_iterator< DynamicBitset >::operator*() const
{
return ( *( this->m_block_iterator ) & ( typename DynamicBitset::block_type( 1 ) << this->m_bit_index ) ) != 0;
}
template< typename DynamicBitset >
const_bit_iterator< DynamicBitset > &
const_bit_iterator< DynamicBitset >::const_bit_iterator::operator++()
{
this->increment();
return *this;
}
template< typename DynamicBitset >
const_bit_iterator< DynamicBitset >
const_bit_iterator< DynamicBitset >::operator++( int )
{
const_bit_iterator temp = *this;
this->increment();
return temp;
}
template< typename DynamicBitset >
const_bit_iterator< DynamicBitset > &
const_bit_iterator< DynamicBitset >::const_bit_iterator::operator--()
{
this->decrement();
return *this;
}
template< typename DynamicBitset >
const_bit_iterator< DynamicBitset >
const_bit_iterator< DynamicBitset >::operator--( int )
{
const_bit_iterator temp = *this;
this->decrement();
return temp;
}
template< typename DynamicBitset >
const_bit_iterator< DynamicBitset > &
const_bit_iterator< DynamicBitset >::operator+=( difference_type n )
{
this->add( n );
return *this;
}
template< typename DynamicBitset >
const_bit_iterator< DynamicBitset > &
const_bit_iterator< DynamicBitset >::operator-=( difference_type n )
{
this->add( -n );
return *this;
}
template< typename DynamicBitset >
const_bit_iterator< DynamicBitset >
operator+( const const_bit_iterator< DynamicBitset > & it,
typename const_bit_iterator< DynamicBitset >::difference_type n )
{
const_bit_iterator< DynamicBitset > temp = it;
temp += n;
return temp;
}
template< typename DynamicBitset >
const_bit_iterator< DynamicBitset >
operator+( typename const_bit_iterator< DynamicBitset >::difference_type n,
const const_bit_iterator< DynamicBitset > & it )
{
return it + n;
}
template< typename DynamicBitset >
const_bit_iterator< DynamicBitset >
operator-( const const_bit_iterator< DynamicBitset > & it,
typename const_bit_iterator< DynamicBitset >::difference_type n )
{
const_bit_iterator< DynamicBitset > temp = it;
temp -= n;
return temp;
}
template< typename DynamicBitset >
typename const_bit_iterator< DynamicBitset >::const_reference
const_bit_iterator< DynamicBitset >::operator[]( difference_type n ) const
{
return *( *this + n );
}
template< typename BlockIterator, typename B, typename A >
void
from_block_range( BlockIterator first, BlockIterator last, dynamic_bitset< B, A > & result )
@@ -228,6 +541,98 @@ dynamic_bitset< Block, AllocatorOrContainer >::~dynamic_bitset()
BOOST_ASSERT( m_check_invariants() );
}
template< typename Block, typename AllocatorOrContainer >
typename dynamic_bitset< Block, AllocatorOrContainer >::iterator
dynamic_bitset< Block, AllocatorOrContainer >::begin()
{
return iterator( m_bits.begin(), 0 );
}
template< typename Block, typename AllocatorOrContainer >
typename dynamic_bitset< Block, AllocatorOrContainer >::const_iterator
dynamic_bitset< Block, AllocatorOrContainer >::begin() const
{
return const_iterator( m_bits.cbegin(), 0 );
}
template< typename Block, typename AllocatorOrContainer >
typename dynamic_bitset< Block, AllocatorOrContainer >::iterator
dynamic_bitset< Block, AllocatorOrContainer >::end()
{
if ( count_extra_bits() == 0 ) {
return iterator( m_bits.end(), 0 );
} else {
return iterator( std::prev( m_bits.end() ), size() % bits_per_block );
}
}
template< typename Block, typename AllocatorOrContainer >
typename dynamic_bitset< Block, AllocatorOrContainer >::const_iterator
dynamic_bitset< Block, AllocatorOrContainer >::end() const
{
if ( count_extra_bits() == 0 ) {
return const_iterator( m_bits.cend(), 0 );
} else {
return const_iterator( std::prev( m_bits.cend() ), size() % bits_per_block );
}
}
template< typename Block, typename AllocatorOrContainer >
typename dynamic_bitset< Block, AllocatorOrContainer >::reverse_iterator
dynamic_bitset< Block, AllocatorOrContainer >::rbegin()
{
return reverse_iterator( end() );
}
template< typename Block, typename AllocatorOrContainer >
typename dynamic_bitset< Block, AllocatorOrContainer >::const_reverse_iterator
dynamic_bitset< Block, AllocatorOrContainer >::rbegin() const
{
return const_reverse_iterator( end() );
}
template< typename Block, typename AllocatorOrContainer >
typename dynamic_bitset< Block, AllocatorOrContainer >::reverse_iterator
dynamic_bitset< Block, AllocatorOrContainer >::rend()
{
return reverse_iterator( begin() );
}
template< typename Block, typename AllocatorOrContainer >
typename dynamic_bitset< Block, AllocatorOrContainer >::const_reverse_iterator
dynamic_bitset< Block, AllocatorOrContainer >::rend() const
{
return const_reverse_iterator( begin() );
}
template< typename Block, typename AllocatorOrContainer >
typename dynamic_bitset< Block, AllocatorOrContainer >::const_iterator
dynamic_bitset< Block, AllocatorOrContainer >::cbegin() const
{
return const_iterator( begin() );
}
template< typename Block, typename AllocatorOrContainer >
typename dynamic_bitset< Block, AllocatorOrContainer >::const_iterator
dynamic_bitset< Block, AllocatorOrContainer >::cend() const
{
return const_iterator( end() );
}
template< typename Block, typename AllocatorOrContainer >
typename dynamic_bitset< Block, AllocatorOrContainer >::const_reverse_iterator
dynamic_bitset< Block, AllocatorOrContainer >::crbegin() const
{
return const_reverse_iterator( end() );
}
template< typename Block, typename AllocatorOrContainer >
typename dynamic_bitset< Block, AllocatorOrContainer >::const_reverse_iterator
dynamic_bitset< Block, AllocatorOrContainer >::crend() const
{
return const_reverse_iterator( begin() );
}
template< typename Block, typename AllocatorOrContainer >
void
dynamic_bitset< Block, AllocatorOrContainer >::

View File

@@ -210,6 +210,51 @@ struct bitset_test
BOOST_TEST( b[ j ] == 0 );
}
static void
iterate_forward( const Bitset & b )
{
std::ptrdiff_t i = 0;
for ( auto it = b.begin(); it != b.end(); ++it ) {
BOOST_TEST( *it == b[ i ] );
++i;
}
}
static void
iterate_backward( const Bitset & b )
{
std::ptrdiff_t i = static_cast< std::ptrdiff_t >( b.size() ) - 1;
for ( auto it = b.rbegin(); it != b.rend(); ++it ) {
BOOST_TEST( *it == b[ i ] );
--i;
}
}
static void
iterator_operations( const Bitset & b )
{
if ( b.size() >= 1 ) {
BOOST_TEST( *( b.end() - 1 ) == b[ b.size() - 1 ] );
BOOST_TEST( b.begin() < b.end() );
BOOST_TEST( b.begin() <= b.end() );
BOOST_TEST( b.end() > b.begin() );
BOOST_TEST( b.end() >= b.begin() );
typename Bitset::const_iterator it = b.begin();
it += b.size() / 2;
BOOST_TEST( *it == b[ b.size() / 2 ] );
it -= b.size() / 2;
BOOST_TEST( *it == b[ 0 ] );
} else {
BOOST_TEST( b.begin() == b.end() );
}
if ( b.size() > 1 ) {
BOOST_TEST( *( b.begin() + 1 ) == b[ 1 ] );
BOOST_TEST( *( 1 + b.begin() ) == b[ 1 ] );
BOOST_TEST( *( b.end() - 1 ) == b[ b.size() - 1 ] );
}
}
static void
to_block_range( const Bitset & b /*, BlockOutputIterator result*/ )
{

View File

@@ -233,6 +233,34 @@ run_test_cases( BOOST_EXPLICIT_TEMPLATE_TYPE( Block ) )
blocks[ i ] = static_cast< Block >( i );
Tests::from_block_range( blocks );
}
//=====================================================================
// test iterators
{
bitset_type b;
Tests::iterate_forward( b );
Tests::iterate_backward( b );
Tests::iterator_operations( b );
}
{
bitset_type b( 1, 1ul );
Tests::iterate_forward( b );
Tests::iterate_backward( b );
Tests::iterator_operations( b );
}
{
bitset_type b( bitset_type::bits_per_block, 100ul );
Tests::iterate_forward( b );
Tests::iterate_backward( b );
Tests::iterator_operations( b );
}
{
bitset_type b( long_string );
Tests::iterate_forward( b );
Tests::iterate_backward( b );
Tests::iterator_operations( b );
}
//=====================================================================
// test to_block_range
{