span.h 7.02 KB
//===--- span- The span class -----------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef ACXXEL_SPAN_H
#define ACXXEL_SPAN_H

#include <array>
#include <cstddef>
#include <exception>
#include <iterator>
#include <type_traits>

namespace acxxel {

/// Value used to indicate slicing to the end of the span.
static constexpr std::ptrdiff_t dynamic_extent = -1; // NOLINT

class SpanBase {};

/// Implementation of the proposed C++17 std::span class.
///
/// Based on the paper:
/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0122r1.pdf
template <typename ElementType> class Span : public SpanBase {
public:
  /// \name constants and types
  /// \{

  using element_type = ElementType;
  using index_type = std::ptrdiff_t;
  using pointer = element_type *;
  using reference = element_type &;
  using iterator = element_type *;
  using const_iterator = const element_type *;
  using value_type = typename std::remove_const<element_type>::type;

  /// \}

  /// \name constructors, copy, assignment, and destructor.
  /// \{

  /// Constructs an empty span with null pointer data.
  Span() : Data(nullptr), Size(0) {}

  /// Constructs an empty span with null pointer data.
  // Intentionally implicit.
  Span(std::nullptr_t) : Data(nullptr), Size(0) {}

  /// Constructs a span from a pointer and element count.
  Span(pointer Ptr, index_type Count) : Data(Ptr), Size(Count) {
    if (Count < 0 || (!Ptr && Count))
      std::terminate();
  }

  /// Constructs a span from a pointer to the fist element in the range and a
  /// pointer to one past the last element in the range.
  Span(pointer FirstElem, pointer LastElem)
      : Data(FirstElem), Size(std::distance(FirstElem, LastElem)) {
    if (Size < 0)
      std::terminate();
  }

  /// Constructs a span from an array.
  // Intentionally implicit.
  template <typename T, size_t N> Span(T (&Arr)[N]) : Data(Arr), Size(N) {}

  /// Constructs a span from a std::array.
  // Intentionally implicit.
  template <size_t N>
  Span(const std::array<typename std::remove_const<element_type>::type, N> &Arr)
      : Data(Arr.data()), Size(N) {}

  /// Constructs a span from a container such as a std::vector.
  // TODO(jhen): Put in a check to make sure this constructor does not
  // participate in overload resolution unless Container meets the following
  // requirements:
  //  * Container is a contiguous container and a sequence container.
  // Intentionally implicit.
  template <typename Container>
  Span(Container &Cont,
       typename std::enable_if<
           std::is_same<
               typename std::remove_const<typename Container::value_type>::type,
               typename std::remove_const<element_type>::type>::value &&
           !std::is_array<Container>::value &&
           !std::is_base_of<SpanBase, Container>::value &&
           std::is_convertible<decltype(&Cont[0]), pointer>::value>::type * =
           nullptr)
      : Data(Cont.data()), Size(Cont.size()) {}

  /// Avoids creating spans from expiring temporary objects.
  // TODO(jhen): Put in a check to make sure this constructor does not
  // participate in overload resolution unless Container meets the following
  // requirements:
  //  * Container is a contiguous container and a sequence container.
  template <typename Container>
  Span(Container &&Cont,
       typename std::enable_if<
           std::is_same<
               typename std::remove_const<typename Container::value_type>::type,
               typename std::remove_const<element_type>::type>::value &&
           !std::is_array<Container>::value &&
           !std::is_base_of<SpanBase, Container>::value &&
           std::is_convertible<decltype(&Cont[0]), pointer>::value>::type * =
           nullptr) = delete;

  Span(const Span &) noexcept = default;
  Span(Span &&) noexcept;

  /// Constructs a span from copying a span of another type that can be
  /// implicitly converted to the type stored by the constructed span.
  // Intentionally implicit.
  template <typename OtherElementType>
  Span(const Span<OtherElementType> &Other)
      : Data(Other.Data), Size(Other.Size) {}

  /// Constructs a span from moving a span of another type that can be
  /// implicitly converted to the type stored by the constructed span.
  // Intentionally implicit.
  template <typename OtherElementType>
  Span(Span<OtherElementType> &&Other) : Data(Other.Data), Size(Other.Size) {}

  ~Span() = default;

  Span &operator=(const Span &) noexcept = default;
  Span &operator=(Span &&) noexcept;

  /// \}

  /// \name subviews
  /// \{

  /// Creates a span out of the first Count elements of this span.
  Span<element_type> first(index_type Count) const {
    bool Valid = Count >= 0 && Count <= size();
    if (!Valid)
      std::terminate();
    return Span<element_type>(data(), Count);
  }

  /// Creates a span out of the last Count elements of this span.
  Span<element_type> last(index_type Count) const {
    bool Valid = Count >= 0 && Count <= size();
    if (!Valid)
      std::terminate();
    return Span<element_type>(Count == 0 ? data() : data() + (size() - Count),
                              Count);
  }

  /// Creates a span out of the Count elements of this span beginning at Offset.
  ///
  /// If no arguments is provided for Count, the new span will extend to the end
  /// of the current span.
  Span<element_type> subspan(index_type Offset,
                             index_type Count = dynamic_extent) const {
    bool Valid =
        (Offset == 0 || (Offset > 0 && Offset <= size())) &&
        (Count == dynamic_extent || (Count >= 0 && Offset + Count <= size()));
    if (!Valid)
      std::terminate();
    return Span<element_type>(
        data() + Offset, Count == dynamic_extent ? size() - Offset : Count);
  }

  /// \}

  /// \name observers
  /// \{

  index_type length() const { return Size; }
  index_type size() const { return Size; }
  bool empty() const { return size() == 0; }

  /// \}

  /// \name element access
  /// \{

  reference operator[](index_type Idx) const {
    bool Valid = Idx >= 0 && Idx < size();
    if (!Valid)
      std::terminate();
    return Data[Idx];
  }

  reference operator()(index_type Idx) const { return operator[](Idx); }

  pointer data() const noexcept { return Data; }

  /// \}

  /// \name iterator support
  /// \{

  iterator begin() const noexcept { return Data; }
  iterator end() const noexcept { return Data + Size; }
  const_iterator cbegin() const noexcept { return Data; }
  const_iterator cend() const noexcept { return Data + Size; }

  /// \}

private:
  template <typename OtherElementType> friend class Span;

  pointer Data;
  index_type Size;
};

template <typename ElementType>
Span<ElementType>::Span(Span &&) noexcept = default;
template <typename ElementType>
Span<ElementType> &Span<ElementType>::operator=(Span &&) noexcept = default;

} // namespace acxxel

#endif // ACXXEL_SPAN_H