Extended a bit the span implementation with the following changes:

- Documented methods and class.
- Adapted interface to the latest standard: no init list and generic container constructors, new iterator pair ctor.
- Implemented some SFINAE for managing conversions in constructors.
- Re-organised some methods to be grouped by category (modifiers, observers, iterators, ...).
- Added convenience make_span functions.
- Added some more  asserts.
- Adapted test to use some syntax that was modified.
This commit is contained in:
faluco 2020-08-07 21:53:41 +02:00 committed by Francisco Paisana
parent 76a62909c1
commit af8dfcf19c
2 changed files with 152 additions and 41 deletions

View File

@ -26,13 +26,17 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <vector>
namespace srslte {
/// The class template span describes an object that can refer to a contiguous sequence of objects with the first
/// element of the sequence at position zero.
template <typename T>
class span
{
public:
/// Member types.
using element_type = T;
using value_type = typename std::remove_cv<T>::type;
using size_type = std::size_t;
@ -44,62 +48,115 @@ public:
using iterator = pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
/// Constructs an empty span with data() == nullptr and size() == 0.
constexpr span() noexcept = default;
constexpr span(pointer ptr_, size_type N_) noexcept : ptr(ptr_), len(N_) {}
/// Constructs a span that is a view over the range [ptr, ptr + len).
constexpr explicit span(pointer ptr, size_type len) noexcept : ptr(ptr), len(len) {}
/// Constructs a span that is a view over the range [first, last).
constexpr explicit span(pointer first, pointer last) noexcept : ptr(first), len(last - first) {}
/// Constructs a span that is a view over the array arr.
template <std::size_t N>
constexpr span(element_type (&arr)[N]) noexcept : ptr(arr), len(N)
{}
template <std::size_t N>
constexpr span(std::array<value_type, N>& arr) noexcept : ptr(arr.data()), len(N)
{}
template <std::size_t N>
constexpr span(const std::array<value_type, N>& arr) noexcept : ptr(arr.data()), len(N)
{}
constexpr span(const std::initializer_list<T>& lst) :
ptr(lst.begin() == lst.end() ? (T*)nullptr : lst.begin()),
len(lst.size())
{}
constexpr span(byte_buffer_t& buffer) : ptr(buffer.msg), len(buffer.N_bytes) {}
constexpr span(const byte_buffer_t& buffer) : ptr(buffer.msg), len(buffer.N_bytes) {}
constexpr span(unique_byte_buffer_t& buffer) : ptr(buffer->msg), len(buffer->N_bytes) {}
constexpr span(const unique_byte_buffer_t& buffer) : ptr(buffer->msg), len(buffer->N_bytes) {}
template <typename Container>
constexpr span(Container& cont) : ptr(cont.data()), len(cont.size())
/// Constructs a span that is a view over the array arr.
template <typename U,
std::size_t N,
typename std::enable_if<std::is_convertible<U (*)[], element_type (*)[]>::value, int>::type = 0>
constexpr span(std::array<U, N>& arr) noexcept : ptr(arr.data()), len(N)
{}
template <typename OtherElementType>
constexpr span(const span<OtherElementType>& other) noexcept : ptr(other.ptr), len(other.size())
/// Constructs a span that is a view over the array arr.
template <typename U,
std::size_t N,
typename std::enable_if<std::is_convertible<const U (*)[], element_type (*)[]>::value, int>::type = 0>
constexpr span(const std::array<U, N>& arr) noexcept : ptr(arr.data()), len(N)
{}
~span() noexcept = default;
template <typename U, typename std::enable_if<std::is_convertible<U (*)[], element_type (*)[]>::value, int>::type = 0>
constexpr span(const span<U>& other) noexcept : ptr(other.data()), len(other.size())
{}
span& operator=(const span& other) noexcept = default;
~span() noexcept = default;
/// Returns the number of elements in the span.
constexpr size_type size() const noexcept { return len; }
reference operator[](size_type idx) const
/// Returns the size of the sequence in bytes.
constexpr size_type size_bytes() const noexcept { return len * sizeof(element_type); }
/// Checks if the span is empty.
constexpr bool empty() const noexcept { return size() == 0; }
/// Returns a reference to the first element in the span.
/// NOTE: Calling front on an empty span results in undefined behavior.
reference front() const
{
assert(!empty() && "called front with empty span");
return *data();
}
/// Returns a reference to the last element in the span.
/// NOTE: Calling back on an empty span results in undefined behavior.
reference back() const
{
assert(!empty() && "called back with empty span");
return *(data() + size() - 1);
}
/// Returns a reference to the idx-th element of the sequence.
/// NOTE: The behavior is undefined if idx is out of range.
reference operator[](size_type idx) const
{
assert(idx < len && "index out of bounds!");
return ptr[idx];
}
constexpr bool empty() const noexcept { return size() == 0; }
constexpr reference front() const { return *data(); }
constexpr reference back() const { return *(data() + size() - 1); }
/// Returns a pointer to the beginning of the sequence.
constexpr pointer data() const noexcept { return ptr; }
/// Returns an iterator to the first element of the span.
constexpr iterator begin() const noexcept { return data(); }
/// Returns an iterator to the element following the last element of the span.
constexpr iterator end() const noexcept { return data() + size(); }
/// Returns a reverse iterator to the first element of the reversed span.
constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); }
/// Returns a reverse iterator to the element following the last element of the reversed span.
constexpr reverse_iterator rend() const noexcept { return reverse_iterator(begin()); }
bool equals(span rhs) const { return (len == rhs.len) ? std::equal(begin(), end(), rhs.begin()) : false; }
/// Obtains a span that is a view over the first count elements of this span.
/// NOTE: The behavior is undefined if count > size().
span<element_type> first(size_type count) const
{
assert(count <= size() && "count is out of range");
return subspan(0, count);
}
// slicing operations
/// Obtains a span that is a view over the last count elements of this span.
/// NOTE: The behavior is undefined if count > size().
span<element_type> last(size_type count) const
{
assert(count <= size() && "count is out of range");
return subspan(size() - count, count);
}
/// Obtains a span that is a view over the count elements of this span starting at offset offset.
span<element_type> subspan(size_type offset, size_type count) const
{
assert(count <= len && "size out of bounds!");
return {data() + offset, count};
assert(count <= size() - offset && "size out of bounds!");
return span{data() + offset, count};
}
constexpr span<element_type> first(size_type count) const { return subspan(0, count); }
constexpr span<element_type> last(size_type count) const { return subspan(size() - count, count); }
/// Returns true if the input span has the same elements as this.
bool equals(span rhs) const { return (len == rhs.len) ? std::equal(begin(), end(), rhs.begin()) : false; }
private:
pointer ptr = nullptr;
@ -118,8 +175,62 @@ inline bool operator!=(span<T> lhs, span<T> rhs)
return not lhs.equals(rhs);
}
///
/// Helpers to construct span objects from different types of arrays.
///
template <typename T, std::size_t N>
inline span<T> make_span(T (&arr)[N])
{
return span<T>{arr};
}
template <typename T, std::size_t N>
inline span<T> make_span(std::array<T, N>& arr)
{
return span<T>{arr};
}
template <typename T, std::size_t N>
inline span<const T> make_span(const std::array<T, N>& arr)
{
return span<const T>{arr};
}
template <typename T>
inline span<T> make_span(std::vector<T>& v)
{
return span<T>{v.data(), v.size()};
}
template <typename T>
inline span<const T> make_span(const std::vector<T>& v)
{
return span<const T>{v.data(), v.size()};
}
using byte_span = span<uint8_t>;
inline byte_span make_span(byte_buffer_t& b)
{
return byte_span{b.msg, b.N_bytes};
}
inline span<const uint8_t> make_span(const byte_buffer_t& b)
{
return span<const uint8_t>{b.msg, b.N_bytes};
}
inline byte_span make_span(unique_byte_buffer_t& b)
{
return byte_span{b->msg, b->N_bytes};
}
inline span<const uint8_t> make_span(const unique_byte_buffer_t& b)
{
return span<const uint8_t>{b->msg, b->N_bytes};
}
} // namespace srslte
#endif // SRSLTE_SPAN_H

View File

@ -26,10 +26,10 @@
int test_span_access()
{
std::vector<int> values{1, 2, 3, 4, 5, 6, 7};
std::array<int, 7> values{1, 2, 3, 4, 5, 6, 7};
{
srslte::span<int> view{values};
auto view = srslte::make_span(values);
// access operators
TESTASSERT(view.size() == 7);
@ -58,7 +58,7 @@ int test_span_conversion()
{
// TEST: changing individual values
srslte::span<int> v{values}, v2{values2};
auto v = srslte::make_span(values), v2 = srslte::make_span(values2);
TESTASSERT(v == v2);
v[0] = 3;
@ -70,7 +70,7 @@ int test_span_conversion()
{
// TEST: const context
const srslte::span<int> v{values}, v2{values2};
const auto v = srslte::make_span(values), v2 = srslte::make_span(values2);
TESTASSERT(v != v2);
TESTASSERT(v[0] == 3);
TESTASSERT(v2[0] == 2);
@ -80,8 +80,8 @@ int test_span_conversion()
{
// TEST: raw arrays
int carray[] = {2, 3, 4, 5, 6, 7, 8};
srslte::span<int> v{values}, v2{carray};
int carray[] = {2, 3, 4, 5, 6, 7, 8};
auto v = srslte::make_span(values), v2 = srslte::make_span(carray);
TESTASSERT(v == v2);
TESTASSERT(v2.size() == v.size());
}
@ -109,7 +109,7 @@ int test_byte_buffer_conversion()
pdu.msg[4] = 4;
{
srslte::byte_span v{pdu};
auto v = srslte::make_span(pdu);
TESTASSERT(v.size() == 5);
TESTASSERT(v[0] == 0);
TESTASSERT(v[2] == 2);
@ -118,15 +118,15 @@ int test_byte_buffer_conversion()
const srslte::byte_buffer_t& pdu2 = pdu;
{
const srslte::byte_span v{pdu2};
const auto v = srslte::make_span(pdu2);
TESTASSERT(v.size() == 5);
TESTASSERT(v[0] == 0);
TESTASSERT(v[2] == 2);
TESTASSERT(v[4] == 4);
}
TESTASSERT(foo(pdu));
TESTASSERT(cfoo(pdu));
TESTASSERT(foo(srslte::make_span(pdu)));
TESTASSERT(cfoo(srslte::make_span(pdu)));
return SRSLTE_SUCCESS;
}
@ -138,4 +138,4 @@ int main()
TESTASSERT(test_byte_buffer_conversion() == SRSLTE_SUCCESS);
printf("Success\n");
return SRSLTE_SUCCESS;
}
}