Code: Select all
#pragma once
#include <cassert>
#include <cstdint>
#include <limits>
using offset_t = long long;
class IndexRange
{
public:
class iterator
{
public:
constexpr iterator( std::int32_t _value, std::int32_t _step )noexcept
:
m_value( _value ),
m_step( _step )
{}
constexpr void _validate( offset_t _offset )const noexcept {
constexpr auto iMax =
offset_t( std::numeric_limits<std::int32_t>::max() );
constexpr auto iMin =
offset_t( std::numeric_limits<std::int32_t>::min() );
// If _offset is equal to iMax or iMin then _offset is not valid
assert( _offset < iMax );
assert( _offset > iMin );
// If iMax / _offset is equal to 0, m_value cannot be incremented
if( _offset != 0 )
assert( std::abs( iMax / ( _offset * m_step ) ) > 0 );
// If iMin / _offset is equal to 0, m_value cannot be decremented
if( _offset != 0 )
assert( std::abs( iMin / ( _offset * m_step ) ) > 0 );
const auto steppedOffset = std::int32_t( _offset ) * m_step;
// If m_value is equal to iMax m_value cannot be incremented
if( steppedOffset > 0 ) assert( offset_t( m_value ) != iMax );
// If m_value is equal to iMin m_value cannot be decremented
if( steppedOffset < 0 ) assert( offset_t( m_value ) != iMin );
// If m_value + _offset would be greater than iMax, m_value cannot be incremented
if( m_value > 0 && steppedOffset > 0 )
assert( iMax - offset_t( m_value ) >= steppedOffset );
// If m_value + _offset would be less than iMin, m_value cannot be decremented
if( m_value < 0 && steppedOffset < 0 )
assert( iMin + offset_t( m_value ) < steppedOffset );
}
constexpr auto& operator++()noexcept {
_validate( 1 );
m_value += m_step;
return *this;
}
constexpr auto operator++( int )noexcept {
auto cur = *this;
++( *this );
return cur;
}
constexpr auto& operator--()noexcept {
_validate( -1 );
m_value -= m_step;
return *this;
}
constexpr auto operator--( int )noexcept {
auto cur = *this;
--( *this );
return cur;
}
constexpr auto operator+( offset_t _offset )const noexcept {
_validate( _offset );
return iterator{ m_value + std::int32_t( _offset ) * m_step, m_step };
}
constexpr auto operator-( offset_t _offset )const noexcept {
_validate( _offset );
return iterator{ m_value - std::int32_t( _offset ) * m_step, m_step };
}
constexpr auto& operator+=( offset_t _offset )noexcept {
*this = *this + _offset;
return *this;
}
constexpr auto& operator-=( offset_t _offset )noexcept {
*this = *this - _offset;
return *this;
}
constexpr auto operator*()const noexcept {
return m_value;
}
constexpr auto operator==( iterator const& _other )const noexcept {
return m_value == _other.m_value;
}
constexpr auto operator!=( iterator const& _other )const noexcept {
// Since m_value + m_step may step over the end value so we
// cannot use inequality, this way if m_value is greater or equal
// this returns false
auto const testA = std::abs( offset_t( m_value ) );
auto const testB = std::abs( offset_t( _other.m_value ) );
return testA < testB;
}
private:
std::int32_t m_value = 0, m_step = 1;
};
public:
IndexRange() = default;
constexpr IndexRange( std::int32_t _beg, std::int32_t _end, std::int32_t _step = 1 )noexcept
:
m_beg( _beg ), m_end( _end ), m_step( _step )
{}
constexpr iterator begin()const noexcept { return iterator{ m_beg, m_step }; }
constexpr iterator end()const noexcept { return iterator{ m_end, m_step }; }
private:
std::int32_t m_beg = 0, m_end = 0, m_step;
};
Code: Select all
int main( int argc, char* argv[] )
{
// Loop from 0 to 600 ( 0-599 inclusive ) stepping is 1
for( const int y : IndexRange( 0, 600 ) )
{
// Loop from 0 to 800 ( 0-796 inclusive ) stepping is 4
for( const int x : IndexRange( 0, 800, 4 ) )
{
}
}
return 1;
}
It's works the same as the code below
Code: Select all
for( int y = 0; y < 600; ++y )
{
for( int x = 0; x < 800; x += 4 )
{
}
}
There are probably other "gotchas" in there depending on the values you pass in.
One of the things I like about this is being able to declare the "index" you get back from this as const, which is something you can't do with normal for loops. Also, it keeps the value in a limited scope thus allowing you to reuse the same variable name for another loop later on in a function if needed.
You could use it like so as well:
Code: Select all
std::vector<Entity> entities{ 50 };
for( auto const i : IndexRange( 0, int( entities.size() ) )
{
auto& iEntity = entities[ i ];
for( auto const j = IndexRange( i, int( entities.size() );
{
auto& jEntity = entities[ j ];
if( iEntity.getBoundingBox().overlaps( jEntity.getBoundingBox() ) )
{
iEntity.takeDamage( jEntity.damageValue() );
jEntity.takeDamage( iEntity.damageValue() );
}
}
}