@ -10,6 +10,14 @@
# include <algorithm>
# include <assert.h>
# ifdef DEBUG
# define CONSTEXPR_IF_NOT_DEBUG
# define ASSERT_IF_DEBUG(x) assert((x))
# else
# define CONSTEXPR_IF_NOT_DEBUG constexpr
# define ASSERT_IF_DEBUG(x)
# endif
/** A Span is an object that can refer to a contiguous sequence of objects.
*
* It implements a subset of C + + 20 ' s std : : span .
@ -18,12 +26,29 @@ template<typename C>
class Span
{
C * m_data ;
std : : ptrdiff _t m_size ;
std : : size _t m_size ;
public :
constexpr Span ( ) noexcept : m_data ( nullptr ) , m_size ( 0 ) { }
constexpr Span ( C * data , std : : ptrdiff_t size ) noexcept : m_data ( data ) , m_size ( size ) { }
constexpr Span ( C * data , C * end ) noexcept : m_data ( data ) , m_size ( end - data ) { }
/** Construct a span from a begin pointer and a size.
*
* This implements a subset of the iterator - based std : : span constructor in C + + 20 ,
* which is hard to implement without std : : address_of .
*/
template < typename T , typename std : : enable_if < std : : is_convertible < T ( * ) [ ] , C ( * ) [ ] > : : value , int > : : type = 0 >
constexpr Span ( T * begin , std : : size_t size ) noexcept : m_data ( begin ) , m_size ( size ) { }
/** Construct a span from a begin and end pointer.
*
* This implements a subset of the iterator - based std : : span constructor in C + + 20 ,
* which is hard to implement without std : : address_of .
*/
template < typename T , typename std : : enable_if < std : : is_convertible < T ( * ) [ ] , C ( * ) [ ] > : : value , int > : : type = 0 >
CONSTEXPR_IF_NOT_DEBUG Span ( T * begin , T * end ) noexcept : m_data ( begin ) , m_size ( end - begin )
{
ASSERT_IF_DEBUG ( end > = begin ) ;
}
/** Implicit conversion of spans between compatible types.
*
@ -42,18 +67,59 @@ public:
/** Default assignment operator. */
Span & operator = ( const Span & other ) noexcept = default ;
/** Construct a Span from an array. This matches the corresponding C++20 std::span constructor. */
template < int N >
constexpr Span ( C ( & a ) [ N ] ) noexcept : m_data ( a ) , m_size ( N ) { }
/** Construct a Span for objects with .data() and .size() (std::string, std::array, std::vector, ...).
*
* This implements a subset of the functionality provided by the C + + 20 std : : span range - based constructor .
*
* To prevent surprises , only Spans for constant value types are supported when passing in temporaries .
* Note that this restriction does not exist when converting arrays or other Spans ( see above ) .
*/
template < typename V , typename std : : enable_if < ( std : : is_const < C > : : value | | std : : is_lvalue_reference < V > : : value ) & & std : : is_convertible < typename std : : remove_pointer < decltype ( std : : declval < V & > ( ) . data ( ) ) > : : type ( * ) [ ] , C ( * ) [ ] > : : value & & std : : is_convertible < decltype ( std : : declval < V & > ( ) . size ( ) ) , std : : size_t > : : value , int > : : type = 0 >
constexpr Span ( V & & v ) noexcept : m_data ( v . data ( ) ) , m_size ( v . size ( ) ) { }
constexpr C * data ( ) const noexcept { return m_data ; }
constexpr C * begin ( ) const noexcept { return m_data ; }
constexpr C * end ( ) const noexcept { return m_data + m_size ; }
constexpr C & front ( ) const noexcept { return m_data [ 0 ] ; }
constexpr C & back ( ) const noexcept { return m_data [ m_size - 1 ] ; }
constexpr std : : ptrdiff_t size ( ) const noexcept { return m_size ; }
constexpr C & operator [ ] ( std : : ptrdiff_t pos ) const noexcept { return m_data [ pos ] ; }
constexpr Span < C > subspan ( std : : ptrdiff_t offset ) const noexcept { return Span < C > ( m_data + offset , m_size - offset ) ; }
constexpr Span < C > subspan ( std : : ptrdiff_t offset , std : : ptrdiff_t count ) const noexcept { return Span < C > ( m_data + offset , count ) ; }
constexpr Span < C > first ( std : : ptrdiff_t count ) const noexcept { return Span < C > ( m_data , count ) ; }
constexpr Span < C > last ( std : : ptrdiff_t count ) const noexcept { return Span < C > ( m_data + m_size - count , count ) ; }
CONSTEXPR_IF_NOT_DEBUG C & front ( ) const noexcept
{
ASSERT_IF_DEBUG ( size ( ) > 0 ) ;
return m_data [ 0 ] ;
}
CONSTEXPR_IF_NOT_DEBUG C & back ( ) const noexcept
{
ASSERT_IF_DEBUG ( size ( ) > 0 ) ;
return m_data [ m_size - 1 ] ;
}
constexpr std : : size_t size ( ) const noexcept { return m_size ; }
CONSTEXPR_IF_NOT_DEBUG C & operator [ ] ( std : : size_t pos ) const noexcept
{
ASSERT_IF_DEBUG ( size ( ) > pos ) ;
return m_data [ pos ] ;
}
CONSTEXPR_IF_NOT_DEBUG Span < C > subspan ( std : : size_t offset ) const noexcept
{
ASSERT_IF_DEBUG ( size ( ) > = offset ) ;
return Span < C > ( m_data + offset , m_size - offset ) ;
}
CONSTEXPR_IF_NOT_DEBUG Span < C > subspan ( std : : size_t offset , std : : size_t count ) const noexcept
{
ASSERT_IF_DEBUG ( size ( ) > = offset + count ) ;
return Span < C > ( m_data + offset , count ) ;
}
CONSTEXPR_IF_NOT_DEBUG Span < C > first ( std : : size_t count ) const noexcept
{
ASSERT_IF_DEBUG ( size ( ) > = count ) ;
return Span < C > ( m_data , count ) ;
}
CONSTEXPR_IF_NOT_DEBUG Span < C > last ( std : : size_t count ) const noexcept
{
ASSERT_IF_DEBUG ( size ( ) > = count ) ;
return Span < C > ( m_data + m_size - count , count ) ;
}
friend constexpr bool operator = = ( const Span & a , const Span & b ) noexcept { return a . size ( ) = = b . size ( ) & & std : : equal ( a . begin ( ) , a . end ( ) , b . begin ( ) ) ; }
friend constexpr bool operator ! = ( const Span & a , const Span & b ) noexcept { return ! ( a = = b ) ; }
@ -65,26 +131,20 @@ public:
template < typename O > friend class Span ;
} ;
/** Create a span to a container exposing data() and size().
*
* This correctly deals with constness : the returned Span ' s element type will be
* whatever data ( ) returns a pointer to . If either the passed container is const ,
* or its element type is const , the resulting span will have a const element type .
*
* std : : span will have a constructor that implements this functionality directly .
*/
template < typename A , int N >
constexpr Span < A > MakeSpan ( A ( & a ) [ N ] ) { return Span < A > ( a , N ) ; }
template < typename V >
constexpr Span < typename std : : remove_pointer < decltype ( std : : declval < V > ( ) . data ( ) ) > : : type > MakeSpan ( V & v ) { return Span < typename std : : remove_pointer < decltype ( std : : declval < V > ( ) . data ( ) ) > : : type > ( v . data ( ) , v . size ( ) ) ; }
// MakeSpan helps constructing a Span of the right type automatically.
/** MakeSpan for arrays: */
template < typename A , int N > Span < A > constexpr MakeSpan ( A ( & a ) [ N ] ) { return Span < A > ( a , N ) ; }
/** MakeSpan for temporaries / rvalue references, only supporting const output. */
template < typename V > constexpr auto MakeSpan ( V & & v ) - > typename std : : enable_if < ! std : : is_lvalue_reference < V > : : value , Span < const typename std : : remove_pointer < decltype ( v . data ( ) ) > : : type > > : : type { return std : : forward < V > ( v ) ; }
/** MakeSpan for (lvalue) references, supporting mutable output. */
template < typename V > constexpr auto MakeSpan ( V & v ) - > Span < typename std : : remove_pointer < decltype ( v . data ( ) ) > : : type > { return v ; }
/** Pop the last element off a span, and return a reference to that element. */
template < typename T >
T & SpanPopBack ( Span < T > & span )
{
size_t size = span . size ( ) ;
assert ( size > 0 ) ;
ASSERT_IF_DEBUG ( size > 0 ) ;
T & back = span [ size - 1 ] ;
span = Span < T > ( span . data ( ) , size - 1 ) ;
return back ;