2016-11-19 17:24:52 +00:00
/*
2018-09-02 00:57:29 +00:00
* This file is part of the DXX - Rebirth project < https : //www.dxx-rebirth.com/>.
2016-11-19 17:24:52 +00:00
* It is copyright by its individual contributors , as recorded in the
* project ' s Git history . See COPYING . txt at the top level for license
* terms and a link to the Git history .
*/
# pragma once
2019-06-27 03:26:20 +00:00
# include <iterator>
2016-11-19 17:24:52 +00:00
# include "dxxsconf.h"
# include "partial_range.h"
2019-05-04 18:27:36 +00:00
# include "ephemeral_range.h"
2020-05-02 21:18:43 +00:00
# include <tuple>
# include <type_traits>
2016-11-19 17:24:52 +00:00
2020-05-02 21:18:43 +00:00
namespace d_enumerate {
namespace detail {
2021-09-04 12:17:14 +00:00
/* In the general case, `value_type` is a tuple of the index and the
* result of the underlying sequence .
*
* In the special case that ` result_type ` is a tuple , construct a
* modified tuple instead of using a tuple of ( index , inner tuple ) .
* This allows simpler structured bindings .
*/
2021-06-28 03:37:50 +00:00
template < typename index_type , typename result_type >
2020-05-02 21:18:43 +00:00
struct adjust_iterator_dereference_type : std : : false_type
{
2021-06-28 03:37:50 +00:00
using value_type = std : : tuple < index_type , result_type > ;
2020-05-02 21:18:43 +00:00
} ;
2021-06-28 03:37:50 +00:00
template < typename index_type , typename . . . T >
struct adjust_iterator_dereference_type < index_type , std : : tuple < T . . . > > : std : : true_type
2020-05-02 21:18:43 +00:00
{
using value_type = std : : tuple < index_type , T . . . > ;
} ;
2021-09-04 12:17:14 +00:00
/* Retrieve the member typedef `index_type` if one exists. Otherwise,
2022-10-09 23:15:20 +00:00
* use ` std : : size_t ` as a fallback .
2021-09-04 12:17:14 +00:00
*/
2022-10-09 23:15:20 +00:00
std : : size_t array_index_type ( . . . ) ;
2021-09-04 12:17:14 +00:00
/* Add, then remove, a reference to the `index_type`. If `index_type`
* is an integer or enum type , this produces ` index_type ` again . If
* ` index_type ` is ` void ` , then this overload refers to
* ` std : : remove_reference < void & > : : type ` , which is ill - formed , and the
* overload is removed by SFINAE . This allows target types to use
* ` using index_type = void ` to decline to specify an ` index_type ` ,
* which may be convenient in templates that conditionally define an
* ` index_type ` based on their template parameters .
*/
template < typename T >
2022-10-09 23:15:20 +00:00
typename std : : remove_reference < typename T : : index_type & > : : type array_index_type ( T * ) ;
2021-09-04 12:17:14 +00:00
2020-05-02 21:18:43 +00:00
}
}
2022-10-09 23:15:20 +00:00
template < typename sentinel_type >
class enumerated_sentinel
{
public :
const sentinel_type m_sentinel ;
constexpr enumerated_sentinel ( sentinel_type & & iter ) :
m_sentinel ( std : : move ( iter ) )
{
}
} ;
template < typename range_index_type , typename range_iterator_type , typename sentinel_type , typename adjust_iterator_dereference_type >
2020-05-02 21:18:43 +00:00
class enumerated_iterator
2016-11-19 17:24:52 +00:00
{
range_iterator_type m_iter ;
2022-02-13 19:13:38 +00:00
range_index_type m_idx ;
2016-11-19 17:24:52 +00:00
public :
2022-02-13 19:13:38 +00:00
using index_type = range_index_type ;
2020-05-02 21:18:43 +00:00
using iterator_category = std : : forward_iterator_tag ;
2020-05-02 21:18:43 +00:00
using value_type = typename adjust_iterator_dereference_type : : value_type ;
2020-05-02 21:18:43 +00:00
using difference_type = std : : ptrdiff_t ;
2020-05-02 21:18:43 +00:00
using pointer = value_type * ;
using reference = value_type & ;
2022-10-09 23:15:20 +00:00
constexpr enumerated_iterator ( range_iterator_type & & iter , const index_type idx ) :
2021-06-28 03:37:50 +00:00
m_iter ( std : : move ( iter ) ) , m_idx ( idx )
2016-11-19 17:24:52 +00:00
{
}
2020-05-02 21:18:43 +00:00
value_type operator * ( ) const
2016-11-19 17:24:52 +00:00
{
2020-05-02 21:18:43 +00:00
if constexpr ( adjust_iterator_dereference_type : : value )
return std : : tuple_cat ( std : : tuple < index_type > ( m_idx ) , * m_iter ) ;
else
2021-06-28 03:37:50 +00:00
return { m_idx , * m_iter } ;
2016-11-19 17:24:52 +00:00
}
enumerated_iterator & operator + + ( )
{
+ + m_iter ;
2021-09-04 12:17:14 +00:00
if constexpr ( std : : is_enum < index_type > : : value )
/* An enum type is not required to have operator++()
* defined , and may deliberately omit it to avoid allowing
* the value to be incremented in the typical use case . For
* this situation , an increment is desired , so emulate
* operator + + ( ) by casting to the underlying integer
* type , adding 1 , and casting back .
*/
m_idx = static_cast < index_type > ( 1u + static_cast < typename std : : underlying_type < index_type > : : type > ( m_idx ) ) ;
else
+ + m_idx ;
2016-11-19 17:24:52 +00:00
return * this ;
}
2022-10-09 23:15:20 +00:00
constexpr bool operator = = ( const enumerated_sentinel < sentinel_type > & i ) const
2019-08-24 18:14:16 +00:00
{
2022-10-09 23:15:20 +00:00
return m_iter = = i . m_sentinel ;
2019-08-24 18:14:16 +00:00
}
2016-11-19 17:24:52 +00:00
} ;
2022-02-13 19:13:38 +00:00
template < typename range_iterator_type , typename range_index_type >
class enumerate : partial_range_t < range_iterator_type , range_index_type >
2016-11-19 17:24:52 +00:00
{
2022-02-13 19:13:38 +00:00
using base_type = partial_range_t < range_iterator_type , range_index_type > ;
2022-10-09 23:15:20 +00:00
using range_sentinel_type = decltype ( std : : declval < base_type & > ( ) . end ( ) ) ;
2019-05-04 18:27:36 +00:00
using iterator_dereference_type = decltype ( * std : : declval < range_iterator_type > ( ) ) ;
2020-05-02 21:18:43 +00:00
using enumerated_iterator_type = enumerated_iterator <
2022-02-13 19:13:38 +00:00
range_index_type ,
2021-06-28 03:37:50 +00:00
range_iterator_type ,
2022-10-09 23:15:20 +00:00
range_sentinel_type ,
2022-02-19 14:52:17 +00:00
d_enumerate : : detail : : adjust_iterator_dereference_type < range_index_type , typename std : : remove_cv < iterator_dereference_type > : : type > > ;
2022-02-13 19:13:38 +00:00
const range_index_type m_idx ;
2016-11-19 17:24:52 +00:00
public :
2019-05-04 18:27:36 +00:00
using range_owns_iterated_storage = std : : false_type ;
2022-02-13 19:13:38 +00:00
using typename base_type : : index_type ;
2020-07-16 02:31:04 +00:00
enumerate ( const range_iterator_type b , const range_iterator_type e , const index_type i ) :
2016-11-19 17:24:52 +00:00
base_type ( b , e ) , m_idx ( i )
{
}
template < typename range_type >
2021-06-28 03:37:50 +00:00
enumerate ( range_type & & t , const index_type i = index_type { } ) :
base_type ( std : : forward < range_type > ( t ) ) , m_idx ( i )
2016-11-19 17:24:52 +00:00
{
2022-02-19 14:52:17 +00:00
/* Block using `enumerate` on an ephemeral range, since the storage
* owned by the range must exist until the ` enumerate ` object is
* fully consumed . If ` range_type & & ` is an ephemeral range , then its
* storage may cease to exist after this constructor returns .
*/
2020-07-16 02:31:04 +00:00
static_assert ( ! any_ephemeral_range < range_type & & > : : value , " cannot enumerate storage of ephemeral ranges " ) ;
2022-02-19 14:52:17 +00:00
static_assert ( std : : is_rvalue_reference < range_type & & > : : value | | ! std : : is_rvalue_reference < iterator_dereference_type > : : value , " lvalue range must not produce rvalue reference enumerated_value " ) ;
2016-11-19 17:24:52 +00:00
}
enumerated_iterator_type begin ( ) const
{
2021-06-28 03:37:50 +00:00
return { this - > base_type : : begin ( ) , m_idx } ;
2016-11-19 17:24:52 +00:00
}
2022-10-09 23:15:20 +00:00
enumerated_sentinel < range_sentinel_type > end ( ) const
2016-11-19 17:24:52 +00:00
{
2022-10-09 23:15:20 +00:00
return { this - > base_type : : end ( ) } ;
2016-11-19 17:24:52 +00:00
}
} ;
2022-10-09 23:15:20 +00:00
template < typename range_type , typename index_type = decltype ( d_enumerate : : detail : : array_index_type ( static_cast < typename std : : remove_reference < range_type > : : type * > ( nullptr ) ) ) >
2022-10-09 23:15:20 +00:00
enumerate ( range_type & & r , index_type start = { /* value ignored for deduction guide */ } ) - > enumerate < /*range_iterator_type = */ decltype ( std : : ranges : : begin ( std : : declval < range_type & > ( ) ) ) , index_type > ;