pw_span: Begin transitioning to std::span
- Provide an implementation of std::span accessible from the <span>
header.
- Add tests to confirm that std::span and pw::span implicitly convert to
one another.
Change-Id: I02e8ffc289975810b51ef32ffbc3b59a6e037caf
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/12760
Commit-Queue: Wyatt Hepler <hepler@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
diff --git a/pw_span/BUILD b/pw_span/BUILD
index 47129f2..528106a 100644
--- a/pw_span/BUILD
+++ b/pw_span/BUILD
@@ -24,7 +24,11 @@
pw_cc_library(
name = "pw_span",
- hdrs = ["public/pw_span/span.h"],
+ srcs = ["public/pw_span/internal/span.h"],
+ hdrs = [
+ "public/pw_span/span.h",
+ "public_overrides/span",
+ ],
includes = ["public"],
deps = ["//pw_polyfill"],
)
@@ -37,3 +41,12 @@
"//pw_unit_test",
],
)
+
+pw_cc_test(
+ name = "pw_span_test",
+ srcs = ["pw_span_test.cc"],
+ deps = [
+ ":pw_span",
+ "//pw_unit_test",
+ ],
+)
diff --git a/pw_span/BUILD.gn b/pw_span/BUILD.gn
index 737f6f5..cd2c881 100644
--- a/pw_span/BUILD.gn
+++ b/pw_span/BUILD.gn
@@ -19,18 +19,27 @@
import("$dir_pw_docgen/docs.gni")
import("$dir_pw_unit_test/test.gni")
config("default_config") {
- include_dirs = [ "public" ]
+ include_dirs = [
+ "public",
+ "public_overrides",
+ ]
}
pw_source_set("pw_span") {
public_configs = [ ":default_config" ]
public_deps = [ "$dir_pw_polyfill" ]
- public = [ "public/pw_span/span.h" ]
- sources = public
+ public = [
+ "public/pw_span/span.h",
+ "public_overrides/span",
+ ]
+ sources = [ "public/pw_span/internal/span.h" ]
}
pw_test_group("tests") {
- tests = [ ":test" ]
+ tests = [
+ ":test",
+ ":pw_span_test",
+ ]
}
pw_test("test") {
@@ -38,6 +47,11 @@
sources = [ "span_test.cc" ]
}
+pw_test("pw_span_test") {
+ deps = [ ":pw_span" ]
+ sources = [ "pw_span_test.cc" ]
+}
+
pw_doc_group("docs") {
sources = [ "docs.rst" ]
}
diff --git a/pw_span/CMakeLists.txt b/pw_span/CMakeLists.txt
index 4b848e2..65644a1 100644
--- a/pw_span/CMakeLists.txt
+++ b/pw_span/CMakeLists.txt
@@ -13,3 +13,4 @@
# the License.
pw_auto_add_simple_module(pw_span PUBLIC_DEPS pw_polyfill)
+target_include_directories(pw_span PUBLIC public_overrides)
diff --git a/pw_span/docs.rst b/pw_span/docs.rst
index 9fff2f3..b44ba1e 100644
--- a/pw_span/docs.rst
+++ b/pw_span/docs.rst
@@ -9,11 +9,30 @@
-------
The ``pw_span`` module provides an implementation of C++20's
`std::span <https://en.cppreference.com/w/cpp/container/span>`_, which is a
-non-owning view of an array of values. The intent is for ``pw::span``'s
-interface to exactly match ``std::span``.
+non-owning view of an array of values. The intent is for this implementation of
+``std::span`` is to exactly match the C++20 standard.
-``pw::span`` is a convenient abstraction that wraps a pointer and a size.
-``pw::span`` is especially useful in APIs. Spans support implicit conversions
+The only header provided by the ``pw_span`` namespace is ``<span>``. It is
+included as if it were coming from the C++ Standard Library. If the C++ library
+provides ``<span>``, the library's version of ``std::span`` is used in place of
+``pw_span``'s.
+
+``pw_span`` requires two include paths -- ``public/`` and ``public_overrides/``.
+The internal implementation header is in ``public/``, and the ``<span>`` header
+that mimics the C++ Standard Library is in ``public_overrides/``.
+
+.. warning::
+
+ Currently, there is a ``pw_span/span.h`` header that provides a ``pw::span``
+ class. ``pw::span`` is deprecated. Do NOT use it in new code. Instead, include
+ ``<span>`` and use ``std::span``. ``pw::span`` will be removed as soon as
+ projects have migrated to ``std::span``.
+
+
+Using std::span
+===============
+``std::span`` is a convenient abstraction that wraps a pointer and a size.
+``std::span`` is especially useful in APIs. Spans support implicit conversions
from C arrays, ``std::array``, or any STL-style container, such as
``std::string_view``.
@@ -30,33 +49,35 @@
ProcessBuffer(data_pointer, data_size);
}
-Pointer and size arguments can be replaced with a ``pw::span``:
+Pointer and size arguments can be replaced with a ``std::span``:
.. code-block:: cpp
- // With pw::span, the buffer is passed as a single argument.
- bool ProcessBuffer(const pw::span<uint8_t>& buffer);
+ #include <span>
+
+ // With std::span, the buffer is passed as a single argument.
+ bool ProcessBuffer(std::span<uint8_t> buffer);
bool DoStuff() {
ProcessBuffer(c_array);
ProcessBuffer(array_object);
- ProcessBuffer(pw::span(data_pointer, data_size));
+ ProcessBuffer(std::span(data_pointer, data_size));
}
.. tip::
- Use ``pw::span<std::byte>`` or ``pw::span<const std::byte>`` to represent
- spans of binary data. Use ``pw::as_bytes`` or ``pw::as_writeable_bytes``
+ Use ``std::span<std::byte>`` or ``std::span<const std::byte>`` to represent
+ spans of binary data. Use ``std::as_bytes`` or ``std::as_writeable_bytes``
to convert any span to a byte span.
.. code-block:: cpp
- void ProcessData(pw::span<const std::byte> data);
+ void ProcessData(std::span<const std::byte> data);
void DoStuff() {
std::array<AnyType, 7> data = { ... };
- ProcessData(pw::as_bytes(pw::span(data)));
+ ProcessData(std::as_bytes(std::span(data)));
}
Compatibility
=============
-C++17
+Works with C++11, but some features require C++17.
diff --git a/pw_span/public/pw_span/internal/span.h b/pw_span/public/pw_span/internal/span.h
new file mode 100644
index 0000000..f3ac9be
--- /dev/null
+++ b/pw_span/public/pw_span/internal/span.h
@@ -0,0 +1,471 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+// std::span is a stand-in for C++20's std::span. Do NOT include this header
+// directly; instead, include it as <span>.
+//
+// A span is a non-owning array view class. It refers to an external array by
+// storing a pointer and length. Unlike std::array, the size does not have to be
+// a template parameter, so this class can be used to without stating its size.
+//
+// This file a modified version of base::span from Chromium:
+// https://chromium.googlesource.com/chromium/src/+/d93ae920e4309682deb9352a4637cfc2941c1d1f/base/containers/span.h
+//
+// In order to minimize changes from the original, this file does NOT fully
+// adhere to Pigweed's style guide.
+//
+// A few changes were made to the Chromium version of span. These include:
+// - Use std::data and std::size instead of base::* versions.
+// - Rename base namespace to std.
+// - Rename internal namespace to pw_span_internal.
+// - Remove uses of checked_iterators.h and CHECK.
+// - Replace make_span functions with C++17 class template deduction guides.
+// - Use std::byte instead of uint8_t for compatibility with std::span.
+//
+#pragma once
+
+#include <algorithm>
+#include <array>
+#include <cstddef>
+#include <iterator>
+#include <limits>
+#include <type_traits>
+#include <utility>
+
+#include "pw_polyfill/language_features.h"
+
+// Pigweed: Disable the asserts from Chromium for now.
+#define _PW_SPAN_ASSERT(arg)
+
+namespace std {
+
+// [views.constants]
+constexpr size_t dynamic_extent = std::numeric_limits<size_t>::max();
+
+template <typename T, size_t Extent = dynamic_extent>
+class span;
+
+namespace pw_span_internal {
+
+template <typename T>
+struct ExtentImpl : std::integral_constant<size_t, dynamic_extent> {};
+
+template <typename T, size_t N>
+struct ExtentImpl<T[N]> : std::integral_constant<size_t, N> {};
+
+template <typename T, size_t N>
+struct ExtentImpl<std::array<T, N>> : std::integral_constant<size_t, N> {};
+
+template <typename T, size_t N>
+struct ExtentImpl<std::span<T, N>> : std::integral_constant<size_t, N> {};
+
+template <typename T>
+using Extent = ExtentImpl<std::remove_cv_t<std::remove_reference_t<T>>>;
+
+template <typename T>
+struct IsSpanImpl : std::false_type {};
+
+template <typename T, size_t Extent>
+struct IsSpanImpl<span<T, Extent>> : std::true_type {};
+
+template <typename T>
+using IsSpan = IsSpanImpl<std::decay_t<T>>;
+
+template <typename T>
+struct IsStdArrayImpl : std::false_type {};
+
+template <typename T, size_t N>
+struct IsStdArrayImpl<std::array<T, N>> : std::true_type {};
+
+template <typename T>
+using IsStdArray = IsStdArrayImpl<std::decay_t<T>>;
+
+template <typename T>
+using IsCArray = std::is_array<std::remove_reference_t<T>>;
+
+template <typename From, typename To>
+using IsLegalDataConversion = std::is_convertible<From (*)[], To (*)[]>;
+
+template <typename Container, typename T>
+using ContainerHasConvertibleData = IsLegalDataConversion<
+ std::remove_pointer_t<decltype(std::data(std::declval<Container>()))>,
+ T>;
+
+template <typename Container>
+using ContainerHasIntegralSize =
+ std::is_integral<decltype(std::size(std::declval<Container>()))>;
+
+template <typename From, size_t FromExtent, typename To, size_t ToExtent>
+using EnableIfLegalSpanConversion =
+ std::enable_if_t<(ToExtent == dynamic_extent || ToExtent == FromExtent) &&
+ IsLegalDataConversion<From, To>::value>;
+
+// SFINAE check if Array can be converted to a span<T>.
+template <typename Array, typename T, size_t Extent>
+using EnableIfSpanCompatibleArray =
+ std::enable_if_t<(Extent == dynamic_extent ||
+ Extent == pw_span_internal::Extent<Array>::value) &&
+ ContainerHasConvertibleData<Array, T>::value>;
+
+// SFINAE check if Container can be converted to a span<T>.
+template <typename Container, typename T>
+using IsSpanCompatibleContainer =
+ std::conditional_t<!IsSpan<Container>::value &&
+ !IsStdArray<Container>::value &&
+ !IsCArray<Container>::value &&
+ ContainerHasConvertibleData<Container, T>::value &&
+ ContainerHasIntegralSize<Container>::value,
+ std::true_type,
+ std::false_type>;
+
+template <typename Container, typename T>
+using EnableIfSpanCompatibleContainer =
+ std::enable_if_t<IsSpanCompatibleContainer<Container, T>::value>;
+
+template <typename Container, typename T, size_t Extent>
+using EnableIfSpanCompatibleContainerAndSpanIsDynamic =
+ std::enable_if_t<IsSpanCompatibleContainer<Container, T>::value &&
+ Extent == dynamic_extent>;
+
+// A helper template for storing the size of a span. Spans with static extents
+// don't require additional storage, since the extent itself is specified in the
+// template parameter.
+template <size_t Extent>
+class ExtentStorage {
+ public:
+ constexpr explicit ExtentStorage(size_t /* size */) noexcept {}
+ constexpr size_t size() const noexcept { return Extent; }
+};
+
+// Specialization of ExtentStorage for dynamic extents, which do require
+// explicit storage for the size.
+template <>
+struct ExtentStorage<dynamic_extent> {
+ constexpr explicit ExtentStorage(size_t size) noexcept : size_(size) {}
+ constexpr size_t size() const noexcept { return size_; }
+
+ private:
+ size_t size_;
+};
+
+} // namespace pw_span_internal
+
+// A span is a value type that represents an array of elements of type T. Since
+// it only consists of a pointer to memory with an associated size, it is very
+// light-weight. It is cheap to construct, copy, move and use spans, so that
+// users are encouraged to use it as a pass-by-value parameter. A span does not
+// own the underlying memory, so care must be taken to ensure that a span does
+// not outlive the backing store.
+//
+// span is somewhat analogous to StringPiece, but with arbitrary element types,
+// allowing mutation if T is non-const.
+//
+// span is implicitly convertible from C++ arrays, as well as most [1]
+// container-like types that provide a data() and size() method (such as
+// std::vector<T>). A mutable span<T> can also be implicitly converted to an
+// immutable span<const T>.
+//
+// Consider using a span for functions that take a data pointer and size
+// parameter: it allows the function to still act on an array-like type, while
+// allowing the caller code to be a bit more concise.
+//
+// For read-only data access pass a span<const T>: the caller can supply either
+// a span<const T> or a span<T>, while the callee will have a read-only view.
+// For read-write access a mutable span<T> is required.
+//
+// Without span:
+// Read-Only:
+// // std::string HexEncode(const uint8_t* data, size_t size);
+// std::vector<uint8_t> data_buffer = GenerateData();
+// std::string r = HexEncode(data_buffer.data(), data_buffer.size());
+//
+// Mutable:
+// // ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args...);
+// char str_buffer[100];
+// SafeSNPrintf(str_buffer, sizeof(str_buffer), "Pi ~= %lf", 3.14);
+//
+// With span:
+// Read-Only:
+// // std::string HexEncode(std::span<const uint8_t> data);
+// std::vector<uint8_t> data_buffer = GenerateData();
+// std::string r = HexEncode(data_buffer);
+//
+// Mutable:
+// // ssize_t SafeSNPrintf(std::span<char>, const char* fmt, Args...);
+// char str_buffer[100];
+// SafeSNPrintf(str_buffer, "Pi ~= %lf", 3.14);
+//
+// Spans with "const" and pointers
+// -------------------------------
+//
+// Const and pointers can get confusing. Here are vectors of pointers and their
+// corresponding spans:
+//
+// const std::vector<int*> => std::span<int* const>
+// std::vector<const int*> => std::span<const int*>
+// const std::vector<const int*> => std::span<const int* const>
+//
+// Differences from the C++20 draft
+// --------------------------------
+//
+// http://eel.is/c++draft/views contains the latest C++20 draft of std::span.
+// Chromium tries to follow the draft as close as possible. Differences between
+// the draft and the implementation are documented in subsections below.
+//
+// Differences from [span.cons]:
+// - Constructing a static span (i.e. Extent != dynamic_extent) from a dynamic
+// sized container (e.g. std::vector) requires an explicit conversion (in the
+// C++20 draft this is simply UB)
+//
+// Furthermore, all constructors and methods are marked noexcept due to the lack
+// of exceptions in Chromium.
+
+// [span], class template span
+template <typename T, size_t Extent>
+class span : public pw_span_internal::ExtentStorage<Extent> {
+ private:
+ using ExtentStorage = pw_span_internal::ExtentStorage<Extent>;
+
+ public:
+ using element_type = T;
+ using value_type = std::remove_cv_t<T>;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
+ using pointer = T*;
+ using reference = T&;
+ using iterator = T*;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ static constexpr size_t extent = Extent;
+
+ // [span.cons], span constructors, copy, assignment, and destructor
+ constexpr span() noexcept : ExtentStorage(0), data_(nullptr) {
+ static_assert(Extent == dynamic_extent || Extent == 0, "Invalid Extent");
+ }
+
+ constexpr span(T* data, size_t size) noexcept
+ : ExtentStorage(size), data_(data) {
+ _PW_SPAN_ASSERT(Extent == dynamic_extent || Extent == size);
+ }
+
+ // Artificially templatized to break ambiguity for span(ptr, 0).
+ template <typename = void>
+ constexpr span(T* begin, T* end) noexcept : span(begin, end - begin) {
+ // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
+ _PW_SPAN_ASSERT(begin <= end);
+ }
+
+ template <
+ size_t N,
+ typename =
+ pw_span_internal::EnableIfSpanCompatibleArray<T (&)[N], T, Extent>>
+ constexpr span(T (&array)[N]) noexcept : span(std::data(array), N) {}
+
+ template <typename U,
+ size_t N,
+ typename = pw_span_internal::
+ EnableIfSpanCompatibleArray<std::array<U, N>&, T, Extent>>
+ constexpr span(std::array<U, N>& array) noexcept
+ : span(std::data(array), N) {}
+
+ template <typename U,
+ size_t N,
+ typename = pw_span_internal::
+ EnableIfSpanCompatibleArray<const std::array<U, N>&, T, Extent>>
+ constexpr span(const std::array<U, N>& array) noexcept
+ : span(std::data(array), N) {}
+
+ // Conversion from a container that has compatible std::data() and integral
+ // std::size().
+ template <typename Container,
+ typename = pw_span_internal::
+ EnableIfSpanCompatibleContainerAndSpanIsDynamic<Container&,
+ T,
+ Extent>>
+ constexpr span(Container& container) noexcept
+ : span(std::data(container), std::size(container)) {}
+
+ template <
+ typename Container,
+ typename = pw_span_internal::
+ EnableIfSpanCompatibleContainerAndSpanIsDynamic<const Container&,
+ T,
+ Extent>>
+ constexpr span(const Container& container) noexcept
+ : span(std::data(container), std::size(container)) {}
+
+ constexpr span(const span& other) noexcept = default;
+
+ // Conversions from spans of compatible types and extents: this allows a
+ // span<T> to be seamlessly used as a span<const T>, but not the other way
+ // around. If extent is not dynamic, OtherExtent has to be equal to Extent.
+ template <typename U,
+ size_t OtherExtent,
+ typename = pw_span_internal::
+ EnableIfLegalSpanConversion<U, OtherExtent, T, Extent>>
+ constexpr span(const span<U, OtherExtent>& other)
+ : span(other.data(), other.size()) {}
+
+ PW_CONSTEXPR_FUNCTION span& operator=(const span& other) noexcept = default;
+ ~span() noexcept = default;
+
+ // [span.sub], span subviews
+ template <size_t Count>
+ constexpr span<T, Count> first() const noexcept {
+ static_assert(Count <= Extent, "Count must not exceed Extent");
+ _PW_SPAN_ASSERT(Extent != dynamic_extent || Count <= size());
+ return {data(), Count};
+ }
+
+ template <size_t Count>
+ constexpr span<T, Count> last() const noexcept {
+ static_assert(Count <= Extent, "Count must not exceed Extent");
+ _PW_SPAN_ASSERT(Extent != dynamic_extent || Count <= size());
+ return {data() + (size() - Count), Count};
+ }
+
+ template <size_t Offset, size_t Count = dynamic_extent>
+ constexpr span<T,
+ (Count != dynamic_extent
+ ? Count
+ : (Extent != dynamic_extent ? Extent - Offset
+ : dynamic_extent))>
+ subspan() const noexcept {
+ static_assert(Offset <= Extent, "Offset must not exceed Extent");
+ static_assert(Count == dynamic_extent || Count <= Extent - Offset,
+ "Count must not exceed Extent - Offset");
+ _PW_SPAN_ASSERT(Extent != dynamic_extent || Offset <= size());
+ _PW_SPAN_ASSERT(Extent != dynamic_extent || Count == dynamic_extent ||
+ Count <= size() - Offset);
+ return {data() + Offset, Count != dynamic_extent ? Count : size() - Offset};
+ }
+
+ constexpr span<T, dynamic_extent> first(size_t count) const noexcept {
+ // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
+ _PW_SPAN_ASSERT(count <= size());
+ return {data(), count};
+ }
+
+ constexpr span<T, dynamic_extent> last(size_t count) const noexcept {
+ // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
+ _PW_SPAN_ASSERT(count <= size());
+ return {data() + (size() - count), count};
+ }
+
+ constexpr span<T, dynamic_extent> subspan(
+ size_t offset, size_t count = dynamic_extent) const noexcept {
+ // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
+ _PW_SPAN_ASSERT(offset <= size());
+ _PW_SPAN_ASSERT(count == dynamic_extent || count <= size() - offset);
+ return {data() + offset, count != dynamic_extent ? count : size() - offset};
+ }
+
+ // [span.obs], span observers
+ constexpr size_t size() const noexcept { return ExtentStorage::size(); }
+ constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); }
+ [[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; }
+
+ // [span.elem], span element access
+ constexpr T& operator[](size_t idx) const noexcept {
+ // Note: CHECK_LT is not constexpr, hence regular CHECK must be used.
+ _PW_SPAN_ASSERT(idx < size());
+ return *(data() + idx);
+ }
+
+ constexpr T& front() const noexcept {
+ static_assert(Extent == dynamic_extent || Extent > 0,
+ "Extent must not be 0");
+ _PW_SPAN_ASSERT(Extent != dynamic_extent || !empty());
+ return *data();
+ }
+
+ constexpr T& back() const noexcept {
+ static_assert(Extent == dynamic_extent || Extent > 0,
+ "Extent must not be 0");
+ _PW_SPAN_ASSERT(Extent != dynamic_extent || !empty());
+ return *(data() + size() - 1);
+ }
+
+ constexpr T* data() const noexcept { return data_; }
+
+ // [span.iter], span iterator support
+ constexpr iterator begin() const noexcept { return data_; }
+ constexpr iterator end() const noexcept { return data_ + size(); }
+
+ constexpr reverse_iterator rbegin() const noexcept {
+ return reverse_iterator(end());
+ }
+ constexpr reverse_iterator rend() const noexcept {
+ return reverse_iterator(begin());
+ }
+
+ private:
+ T* data_;
+};
+
+// span<T, Extent>::extent can not be declared inline prior to C++17, hence this
+// definition is required.
+// template <class T, size_t Extent>
+// constexpr size_t span<T, Extent>::extent;
+
+// [span.objectrep], views of object representation
+template <typename T, size_t X>
+span<const std::byte, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
+as_bytes(span<T, X> s) noexcept {
+ return {reinterpret_cast<const std::byte*>(s.data()), s.size_bytes()};
+}
+
+template <typename T,
+ size_t X,
+ typename = std::enable_if_t<!std::is_const<T>::value>>
+span<std::byte, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
+as_writable_bytes(span<T, X> s) noexcept {
+ return {reinterpret_cast<std::byte*>(s.data()), s.size_bytes()};
+}
+
+// Type-deducing helpers for constructing a span.
+// Pigweed: Instead of a make_span function, provide the deduction guides
+// specified in the C++20 standard.
+#ifdef __cpp_deduction_guides
+
+template <class T, std::size_t N>
+span(T (&)[N]) -> span<T, N>;
+
+template <class T, std::size_t N>
+span(std::array<T, N>&) -> span<T, N>;
+
+template <class T, std::size_t N>
+span(const std::array<T, N>&) -> span<const T, N>;
+
+namespace pw_span_internal {
+
+// Containers can be mutable or const and have mutable or const members. Check
+// the type of the accessed elements to determine which type of span should be
+// created (e.g. span<char> or span<const char>).
+template <typename T>
+using ValueType = std::remove_reference_t<decltype(std::declval<T>()[0])>;
+
+} // namespace pw_span_internal
+
+// This diverges a little from the standard, which uses std::ranges.
+template <class Container>
+span(Container&) -> span<pw_span_internal::ValueType<Container>>;
+
+template <class Container>
+span(const Container&) -> span<pw_span_internal::ValueType<const Container>>;
+
+#endif // __cpp_deduction_guides
+
+} // namespace std
+
+#undef _PW_SPAN_ASSERT
diff --git a/pw_span/public/pw_span/span.h b/pw_span/public/pw_span/span.h
index 8aa270d..98e7acf 100644
--- a/pw_span/public/pw_span/span.h
+++ b/pw_span/public/pw_span/span.h
@@ -12,28 +12,13 @@
// License for the specific language governing permissions and limitations under
// the License.
-// pw::span is a stand-in for C++20's std::span.
+// pw::span is DEPRECATED. Instead of using pw::span from pw_span/span.h, use
+// std::span from <span>. pw_span/span.h and pw::span will be removed once code
+// has been migrated to std::span.
//
-// A span is a non-owning array view class. It refers to an external array by
-// storing a pointer and length. Unlike std::array, the size does not have to be
-// a template parameter, so this class can be used to without stating its size.
-//
-// This file a modified version of base::span from Chromium:
-// https://chromium.googlesource.com/chromium/src/+/d93ae920e4309682deb9352a4637cfc2941c1d1f/base/containers/span.h
-//
-// In order to minimize changes from the original, this file does NOT fully
-// adhere to Pigweed's style guide.
-//
-// A few changes were made to the Chromium version of span. These include:
-// - Use std::data and std::size instead of base::* versions.
-// - Rename base namespace to pw.
-// - Rename internal namespace to span_internal.
-// - Remove uses of checked_iterators.h and CHECK.
-// - Replace make_span functions with C++17 class template deduction guides.
-// - Use std::byte instead of uint8_t for compatibility with std::span.
-//
-// Eventually, when C++20 is widely supported, this class will be replaced with
-// std::span.
+// This code is a copy of the std::span code in pw_span/internal/span.h.
+// pw::span cannot be an alias of std::span because class template argument
+// deduction does not work with aliases.
#pragma once
#include <algorithm>
@@ -234,7 +219,7 @@
// [span], class template span
template <typename T, size_t Extent>
-class span : public span_internal::ExtentStorage<Extent> {
+class /* [[deprecated]] */ span : public span_internal::ExtentStorage<Extent> {
private:
using ExtentStorage = span_internal::ExtentStorage<Extent>;
diff --git a/pw_span/public_overrides/span b/pw_span/public_overrides/span
new file mode 100644
index 0000000..7991e33
--- /dev/null
+++ b/pw_span/public_overrides/span
@@ -0,0 +1,28 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#if __has_include(<version>)
+#include <version>
+#endif // __has_include(<version>)
+
+#ifdef __cpp_lib_span // C++ library feature test macro, provided by <version>.
+
+#include_next <span>
+
+#else
+
+#include "pw_span/internal/span.h"
+
+#endif // __cpp_lib_span
diff --git a/pw_span/pw_span_test.cc b/pw_span/pw_span_test.cc
new file mode 100644
index 0000000..00a1adf
--- /dev/null
+++ b/pw_span/pw_span_test.cc
@@ -0,0 +1,1681 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+// This file is a copy of the tests for std::span. It tests pw::span, which is a
+// temporary copy of std::span for compatibility purposes. The pw::span class,
+// and this test, will be deleted as soon as projects fully migrate to
+// std::span.
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+#include <span>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "pw_span/span.h"
+
+// Pigweed: gMock matchers are not yet supported.
+#if 0
+using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::Pointwise;
+#endif // 0
+
+namespace pw {
+
+namespace {
+
+// constexpr implementation of std::equal's 4 argument overload.
+template <class InputIterator1, class InputIterator2>
+constexpr bool constexpr_equal(InputIterator1 first1,
+ InputIterator1 last1,
+ InputIterator2 first2,
+ InputIterator2 last2) {
+ for (; first1 != last1 && first2 != last2; ++first1, ++first2) {
+ if (*first1 != *first2)
+ return false;
+ }
+
+ return first1 == last1 && first2 == last2;
+}
+
+} // namespace
+
+// TODO(hepler): Remove these tests after eliminating pw::span.
+TEST(StdSpanCompatibility, ConstructFromPwSpanToStdSpan) {
+ char array[] = {'a', 'b', 'c', 'd', '\0'};
+
+ ::pw::span pw_span(array);
+ std::span std_span(pw_span);
+
+ std_span[0] = '!';
+ EXPECT_STREQ(std_span.data(), "!bcd");
+
+ EXPECT_EQ(pw_span.data(), std_span.data());
+ EXPECT_EQ(pw_span.size(), std_span.size());
+}
+
+TEST(StdSpanCompatibility, ConstructFromStdSpanToPwSpan) {
+ char array[] = {'a', 'b', 'c', 'd', '\0'};
+
+ std::span std_span(array);
+ ::pw::span pw_span(std_span);
+
+ pw_span[0] = '!';
+ EXPECT_STREQ(pw_span.data(), "!bcd");
+
+ EXPECT_EQ(pw_span.data(), std_span.data());
+ EXPECT_EQ(pw_span.size(), std_span.size());
+}
+
+TEST(StdSpanCompatibility, AssignFromPwSpanToStdSpan) {
+ char array[] = {'a', 'b', 'c', 'd', '\0'};
+
+ auto pw_span = ::pw::span(array);
+ std::span std_span = pw_span;
+
+ std_span[0] = '!';
+ EXPECT_STREQ(std_span.data(), "!bcd");
+
+ EXPECT_EQ(pw_span.data(), std_span.data());
+ EXPECT_EQ(pw_span.size(), std_span.size());
+}
+
+TEST(StdSpanCompatibility, AssignFromStdSpanToPwSpan) {
+ char array[] = {'a', 'b', 'c', 'd', '\0'};
+
+ auto std_span = std::span(array);
+ ::pw::span pw_span = std_span;
+
+ pw_span[0] = '!';
+ EXPECT_STREQ(pw_span.data(), "!bcd");
+
+ EXPECT_EQ(pw_span.data(), std_span.data());
+ EXPECT_EQ(pw_span.size(), std_span.size());
+}
+
+TEST(SpanTest, DeductionGuides_MutableArray) {
+ char array[] = {'a', 'b', 'c', 'd', '\0'};
+
+ auto the_span = span(array);
+ static_assert(the_span.extent == 5u);
+ static_assert(the_span.size() == 5u);
+
+ the_span[0] = '!';
+ EXPECT_STREQ(the_span.data(), "!bcd");
+}
+
+TEST(SpanTest, DeductionGuides_ConstArray) {
+ static constexpr char array[] = {'a', 'b', 'c', 'd', '\0'};
+
+ constexpr auto the_span = span(array);
+ static_assert(the_span.extent == 5u);
+ static_assert(the_span.size() == 5u);
+
+ EXPECT_STREQ(the_span.data(), "abcd");
+}
+
+TEST(SpanTest, DeductionGuides_MutableStdArray) {
+ std::array<char, 5> array{'a', 'b', 'c', 'd'};
+
+ auto the_span = span(array);
+ static_assert(the_span.extent == 5u);
+ static_assert(the_span.size() == 5u);
+
+ the_span[0] = '?';
+ EXPECT_STREQ(the_span.data(), "?bcd");
+}
+
+TEST(SpanTest, DeductionGuides_ConstStdArray) {
+ static constexpr std::array<char, 5> array{'a', 'b', 'c', 'd'};
+
+ constexpr auto the_span = span(array);
+ static_assert(the_span.extent == 5u);
+ static_assert(the_span.size() == 5u);
+
+ EXPECT_STREQ(the_span.data(), "abcd");
+}
+
+TEST(SpanTest, DeductionGuides_MutableContainerWithConstElements) {
+ std::string_view string("Hello");
+ auto the_span = span(string);
+ static_assert(the_span.extent == dynamic_extent);
+
+ EXPECT_STREQ("Hello", the_span.data());
+ EXPECT_EQ(5u, the_span.size());
+}
+
+TEST(SpanTest, DeductionGuides_MutableContainerWithMutableElements) {
+ std::string string("Hello");
+ auto the_span = span(string);
+ static_assert(the_span.extent == dynamic_extent);
+
+ EXPECT_EQ(5u, the_span.size());
+ the_span[1] = 'a';
+ EXPECT_STREQ(the_span.data(), string.data());
+ EXPECT_STREQ("Hallo", the_span.data());
+}
+
+class MutableStringView {
+ public:
+ using element_type = char;
+ using value_type = char;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
+ using pointer = char*;
+ using reference = char&;
+ using iterator = char*;
+ using const_iterator = const char*;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ MutableStringView(char* str) : data_(str, std::strlen(str)) {}
+
+ char& operator[](size_type index) const { return data_[index]; }
+ pointer data() const { return data_.data(); }
+ size_type size() const { return data_.size(); }
+ iterator begin() const { return data_.begin(); }
+ iterator end() const { return data_.end(); }
+
+ private:
+ span<char> data_;
+};
+
+TEST(SpanTest, DeductionGuides_ConstContainerWithMutableElements) {
+ char data[] = "54321";
+ MutableStringView view(data);
+
+ auto the_span = span(view);
+ static_assert(the_span.extent == dynamic_extent);
+
+ EXPECT_EQ(5u, the_span.size());
+ view[2] = '?';
+ EXPECT_STREQ("54?21", the_span.data());
+ EXPECT_STREQ("54?21", data);
+}
+
+TEST(SpanTest, DeductionGuides_ConstContainerWithMutableValueType) {
+ const std::string string("Hello");
+ auto the_span = span(string);
+ static_assert(the_span.extent == dynamic_extent);
+
+ EXPECT_EQ(5u, the_span.size());
+ EXPECT_STREQ("Hello", the_span.data());
+}
+
+TEST(SpanTest, DeductionGuides_ConstContainerWithConstElements) {
+ const std::string_view string("Hello");
+ auto the_span = span(string);
+ static_assert(the_span.extent == dynamic_extent);
+
+ EXPECT_EQ(5u, the_span.size());
+ EXPECT_STREQ("Hello", the_span.data());
+}
+
+TEST(SpanTest, DeductionGuides_FromTemporary_ContainerWithConstElements) {
+ auto the_span = span(std::string_view("Hello"));
+ static_assert(the_span.extent == dynamic_extent);
+
+ EXPECT_EQ(5u, the_span.size());
+ EXPECT_STREQ("Hello", the_span.data());
+}
+
+TEST(SpanTest, DeductionGuides_FromReference) {
+ std::array<int, 5> array{1, 3, 5, 7, 9};
+ std::array<int, 5>& array_ref = array;
+
+ auto the_span = span(array_ref);
+ static_assert(the_span.extent == 5);
+
+ for (unsigned i = 0; i < array.size(); ++i) {
+ ASSERT_EQ(array[i], the_span[i]);
+ }
+}
+
+TEST(SpanTest, DeductionGuides_FromConstReference) {
+ std::string_view string = "yo!";
+ const std::string_view& string_ref = string;
+
+ auto the_span = span(string_ref);
+ static_assert(the_span.extent == dynamic_extent);
+
+ EXPECT_EQ(string, the_span.data());
+}
+
+TEST(SpanTest, DefaultConstructor) {
+ span<int> dynamic_span;
+ EXPECT_EQ(nullptr, dynamic_span.data());
+ EXPECT_EQ(0u, dynamic_span.size());
+
+ constexpr span<int, 0> static_span;
+ static_assert(nullptr == static_span.data(), "");
+ static_assert(static_span.empty(), "");
+}
+
+TEST(SpanTest, ConstructFromDataAndSize) {
+ constexpr span<int> empty_span(nullptr, 0);
+ EXPECT_TRUE(empty_span.empty());
+ EXPECT_EQ(nullptr, empty_span.data());
+
+ std::vector<int> vector = {1, 1, 2, 3, 5, 8};
+
+ span<int> dynamic_span(vector.data(), vector.size());
+ EXPECT_EQ(vector.data(), dynamic_span.data());
+ EXPECT_EQ(vector.size(), dynamic_span.size());
+
+ for (size_t i = 0; i < dynamic_span.size(); ++i)
+ EXPECT_EQ(vector[i], dynamic_span[i]);
+
+ span<int, 6> static_span(vector.data(), vector.size());
+ EXPECT_EQ(vector.data(), static_span.data());
+ EXPECT_EQ(vector.size(), static_span.size());
+
+ for (size_t i = 0; i < static_span.size(); ++i)
+ EXPECT_EQ(vector[i], static_span[i]);
+}
+
+TEST(SpanTest, ConstructFromPointerPair) {
+ constexpr span<int> empty_span(nullptr, nullptr);
+ EXPECT_TRUE(empty_span.empty());
+ EXPECT_EQ(nullptr, empty_span.data());
+
+ std::vector<int> vector = {1, 1, 2, 3, 5, 8};
+
+ span<int> dynamic_span(vector.data(), vector.data() + vector.size() / 2);
+ EXPECT_EQ(vector.data(), dynamic_span.data());
+ EXPECT_EQ(vector.size() / 2, dynamic_span.size());
+
+ for (size_t i = 0; i < dynamic_span.size(); ++i)
+ EXPECT_EQ(vector[i], dynamic_span[i]);
+
+ span<int, 3> static_span(vector.data(), vector.data() + vector.size() / 2);
+ EXPECT_EQ(vector.data(), static_span.data());
+ EXPECT_EQ(vector.size() / 2, static_span.size());
+
+ for (size_t i = 0; i < static_span.size(); ++i)
+ EXPECT_EQ(vector[i], static_span[i]);
+}
+
+TEST(SpanTest, AllowedConversionsFromStdArray) {
+ // In the following assertions we use std::is_convertible_v<From, To>, which
+ // for non-void types is equivalent to checking whether the following
+ // expression is well-formed:
+ //
+ // T obj = std::declval<From>();
+ //
+ // In particular we are checking whether From is implicitly convertible to To,
+ // which also implies that To is explicitly constructible from From.
+ static_assert(
+ std::is_convertible<std::array<int, 3>&, span<int>>::value,
+ "Error: l-value reference to std::array<int> should be convertible to "
+ "span<int> with dynamic extent.");
+ static_assert(
+ std::is_convertible<std::array<int, 3>&, span<int, 3>>::value,
+ "Error: l-value reference to std::array<int> should be convertible to "
+ "span<int> with the same static extent.");
+ static_assert(
+ std::is_convertible<std::array<int, 3>&, span<const int>>::value,
+ "Error: l-value reference to std::array<int> should be convertible to "
+ "span<const int> with dynamic extent.");
+ static_assert(
+ std::is_convertible<std::array<int, 3>&, span<const int, 3>>::value,
+ "Error: l-value reference to std::array<int> should be convertible to "
+ "span<const int> with the same static extent.");
+ static_assert(
+ std::is_convertible<const std::array<int, 3>&, span<const int>>::value,
+ "Error: const l-value reference to std::array<int> should be "
+ "convertible to span<const int> with dynamic extent.");
+ static_assert(
+ std::is_convertible<const std::array<int, 3>&, span<const int, 3>>::value,
+ "Error: const l-value reference to std::array<int> should be convertible "
+ "to span<const int> with the same static extent.");
+ static_assert(
+ std::is_convertible<std::array<const int, 3>&, span<const int>>::value,
+ "Error: l-value reference to std::array<const int> should be "
+ "convertible to span<const int> with dynamic extent.");
+ static_assert(
+ std::is_convertible<std::array<const int, 3>&, span<const int, 3>>::value,
+ "Error: l-value reference to std::array<const int> should be convertible "
+ "to span<const int> with the same static extent.");
+ static_assert(
+ std::is_convertible<const std::array<const int, 3>&,
+ span<const int>>::value,
+ "Error: const l-value reference to std::array<const int> should be "
+ "convertible to span<const int> with dynamic extent.");
+ static_assert(
+ std::is_convertible<const std::array<const int, 3>&,
+ span<const int, 3>>::value,
+ "Error: const l-value reference to std::array<const int> should be "
+ "convertible to span<const int> with the same static extent.");
+}
+
+TEST(SpanTest, DisallowedConstructionsFromStdArray) {
+ // In the following assertions we use !std::is_constructible_v<T, Args>, which
+ // is equivalent to checking whether the following expression is malformed:
+ //
+ // T obj(std::declval<Args>()...);
+ //
+ // In particular we are checking that T is not explicitly constructible from
+ // Args, which also implies that T is not implicitly constructible from Args
+ // as well.
+ static_assert(
+ !std::is_constructible<span<int>, const std::array<int, 3>&>::value,
+ "Error: span<int> with dynamic extent should not be constructible "
+ "from const l-value reference to std::array<int>");
+
+ static_assert(
+ !std::is_constructible<span<int>, std::array<const int, 3>&>::value,
+ "Error: span<int> with dynamic extent should not be constructible "
+ "from l-value reference to std::array<const int>");
+
+ static_assert(
+ !std::is_constructible<span<int>, const std::array<const int, 3>&>::value,
+ "Error: span<int> with dynamic extent should not be constructible "
+ "const from l-value reference to std::array<const int>");
+
+ static_assert(
+ !std::is_constructible<span<int, 2>, std::array<int, 3>&>::value,
+ "Error: span<int> with static extent should not be constructible "
+ "from l-value reference to std::array<int> with different extent");
+
+ static_assert(
+ !std::is_constructible<span<int, 4>, std::array<int, 3>&>::value,
+ "Error: span<int> with dynamic extent should not be constructible "
+ "from l-value reference to std::array<int> with different extent");
+
+ static_assert(
+ !std::is_constructible<span<int>, std::array<bool, 3>&>::value,
+ "Error: span<int> with dynamic extent should not be constructible "
+ "from l-value reference to std::array<bool>");
+}
+
+TEST(SpanTest, ConstructFromConstexprArray) {
+ static constexpr int kArray[] = {5, 4, 3, 2, 1};
+
+ constexpr span<const int> dynamic_span(kArray);
+ static_assert(kArray == dynamic_span.data(), "");
+ static_assert(std::size(kArray) == dynamic_span.size(), "");
+
+ static_assert(kArray[0] == dynamic_span[0], "");
+ static_assert(kArray[1] == dynamic_span[1], "");
+ static_assert(kArray[2] == dynamic_span[2], "");
+ static_assert(kArray[3] == dynamic_span[3], "");
+ static_assert(kArray[4] == dynamic_span[4], "");
+
+ constexpr span<const int, std::size(kArray)> static_span(kArray);
+ static_assert(kArray == static_span.data(), "");
+ static_assert(std::size(kArray) == static_span.size(), "");
+
+ static_assert(kArray[0] == static_span[0], "");
+ static_assert(kArray[1] == static_span[1], "");
+ static_assert(kArray[2] == static_span[2], "");
+ static_assert(kArray[3] == static_span[3], "");
+ static_assert(kArray[4] == static_span[4], "");
+}
+
+TEST(SpanTest, ConstructFromArray) {
+ int array[] = {5, 4, 3, 2, 1};
+
+ span<const int> const_span(array);
+ EXPECT_EQ(array, const_span.data());
+ EXPECT_EQ(std::size(array), const_span.size());
+ for (size_t i = 0; i < const_span.size(); ++i)
+ EXPECT_EQ(array[i], const_span[i]);
+
+ span<int> dynamic_span(array);
+ EXPECT_EQ(array, dynamic_span.data());
+ EXPECT_EQ(std::size(array), dynamic_span.size());
+ for (size_t i = 0; i < dynamic_span.size(); ++i)
+ EXPECT_EQ(array[i], dynamic_span[i]);
+
+ span<int, std::size(array)> static_span(array);
+ EXPECT_EQ(array, static_span.data());
+ EXPECT_EQ(std::size(array), static_span.size());
+ for (size_t i = 0; i < static_span.size(); ++i)
+ EXPECT_EQ(array[i], static_span[i]);
+}
+
+TEST(SpanTest, ConstructFromStdArray) {
+ // Note: Constructing a constexpr span from a constexpr std::array does not
+ // work prior to C++17 due to non-constexpr std::array::data.
+ std::array<int, 5> array = {{5, 4, 3, 2, 1}};
+
+ span<const int> const_span(array);
+ EXPECT_EQ(array.data(), const_span.data());
+ EXPECT_EQ(array.size(), const_span.size());
+ for (size_t i = 0; i < const_span.size(); ++i)
+ EXPECT_EQ(array[i], const_span[i]);
+
+ span<int> dynamic_span(array);
+ EXPECT_EQ(array.data(), dynamic_span.data());
+ EXPECT_EQ(array.size(), dynamic_span.size());
+ for (size_t i = 0; i < dynamic_span.size(); ++i)
+ EXPECT_EQ(array[i], dynamic_span[i]);
+
+ span<int, std::size(array)> static_span(array);
+ EXPECT_EQ(array.data(), static_span.data());
+ EXPECT_EQ(array.size(), static_span.size());
+ for (size_t i = 0; i < static_span.size(); ++i)
+ EXPECT_EQ(array[i], static_span[i]);
+}
+
+TEST(SpanTest, ConstructFromInitializerList) {
+ std::initializer_list<int> il = {1, 1, 2, 3, 5, 8};
+
+ span<const int> const_span(il);
+ EXPECT_EQ(il.begin(), const_span.data());
+ EXPECT_EQ(il.size(), const_span.size());
+
+ for (size_t i = 0; i < const_span.size(); ++i)
+ EXPECT_EQ(il.begin()[i], const_span[i]);
+
+ span<const int, 6> static_span(il.begin(), il.end());
+ EXPECT_EQ(il.begin(), static_span.data());
+ EXPECT_EQ(il.size(), static_span.size());
+
+ for (size_t i = 0; i < static_span.size(); ++i)
+ EXPECT_EQ(il.begin()[i], static_span[i]);
+}
+
+TEST(SpanTest, ConstructFromStdString) {
+ std::string str = "foobar";
+
+ span<const char> const_span(str);
+ EXPECT_EQ(str.data(), const_span.data());
+ EXPECT_EQ(str.size(), const_span.size());
+
+ for (size_t i = 0; i < const_span.size(); ++i)
+ EXPECT_EQ(str[i], const_span[i]);
+
+ span<char> dynamic_span(str);
+ EXPECT_EQ(str.data(), dynamic_span.data());
+ EXPECT_EQ(str.size(), dynamic_span.size());
+
+ for (size_t i = 0; i < dynamic_span.size(); ++i)
+ EXPECT_EQ(str[i], dynamic_span[i]);
+
+ span<char, 6> static_span(data(str), str.size());
+ EXPECT_EQ(str.data(), static_span.data());
+ EXPECT_EQ(str.size(), static_span.size());
+
+ for (size_t i = 0; i < static_span.size(); ++i)
+ EXPECT_EQ(str[i], static_span[i]);
+}
+
+TEST(SpanTest, ConstructFromConstContainer) {
+ const std::vector<int> vector = {1, 1, 2, 3, 5, 8};
+
+ span<const int> const_span(vector);
+ EXPECT_EQ(vector.data(), const_span.data());
+ EXPECT_EQ(vector.size(), const_span.size());
+
+ for (size_t i = 0; i < const_span.size(); ++i)
+ EXPECT_EQ(vector[i], const_span[i]);
+
+ span<const int, 6> static_span(vector.data(), vector.size());
+ EXPECT_EQ(vector.data(), static_span.data());
+ EXPECT_EQ(vector.size(), static_span.size());
+
+ for (size_t i = 0; i < static_span.size(); ++i)
+ EXPECT_EQ(vector[i], static_span[i]);
+}
+
+TEST(SpanTest, ConstructFromContainer) {
+ std::vector<int> vector = {1, 1, 2, 3, 5, 8};
+
+ span<const int> const_span(vector);
+ EXPECT_EQ(vector.data(), const_span.data());
+ EXPECT_EQ(vector.size(), const_span.size());
+
+ for (size_t i = 0; i < const_span.size(); ++i)
+ EXPECT_EQ(vector[i], const_span[i]);
+
+ span<int> dynamic_span(vector);
+ EXPECT_EQ(vector.data(), dynamic_span.data());
+ EXPECT_EQ(vector.size(), dynamic_span.size());
+
+ for (size_t i = 0; i < dynamic_span.size(); ++i)
+ EXPECT_EQ(vector[i], dynamic_span[i]);
+
+ span<int, 6> static_span(vector.data(), vector.size());
+ EXPECT_EQ(vector.data(), static_span.data());
+ EXPECT_EQ(vector.size(), static_span.size());
+
+ for (size_t i = 0; i < static_span.size(); ++i)
+ EXPECT_EQ(vector[i], static_span[i]);
+}
+
+#if 0
+
+// Pigweed: gMock matchers are not yet supported.
+TEST(SpanTest, ConvertNonConstIntegralToConst) {
+ std::vector<int> vector = {1, 1, 2, 3, 5, 8};
+
+ span<int> int_span(vector.data(), vector.size());
+ span<const int> const_span(int_span);
+ EXPECT_EQ(int_span.size(), const_span.size());
+
+ EXPECT_THAT(const_span, Pointwise(Eq(), int_span));
+
+ span<int, 6> static_int_span(vector.data(), vector.size());
+ span<const int, 6> static_const_span(static_int_span);
+ EXPECT_THAT(static_const_span, Pointwise(Eq(), static_int_span));
+}
+
+// Pigweed: gMock matchers are not yet supported.
+TEST(SpanTest, ConvertNonConstPointerToConst) {
+ auto a = std::make_unique<int>(11);
+ auto b = std::make_unique<int>(22);
+ auto c = std::make_unique<int>(33);
+ std::vector<int*> vector = {a.get(), b.get(), c.get()};
+
+ span<int*> non_const_pointer_span(vector);
+ EXPECT_THAT(non_const_pointer_span, Pointwise(Eq(), vector));
+ span<int* const> const_pointer_span(non_const_pointer_span);
+ EXPECT_THAT(const_pointer_span, Pointwise(Eq(), non_const_pointer_span));
+ // Note: no test for conversion from span<int> to span<const int*>, since that
+ // would imply a conversion from int** to const int**, which is unsafe.
+ //
+ // Note: no test for conversion from span<int*> to span<const int* const>,
+ // due to CWG Defect 330:
+ // http://open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#330
+
+ span<int*, 3> static_non_const_pointer_span(vector.data(), vector.size());
+ EXPECT_THAT(static_non_const_pointer_span, Pointwise(Eq(), vector));
+ span<int* const, 3> static_const_pointer_span(static_non_const_pointer_span);
+ EXPECT_THAT(static_const_pointer_span,
+ Pointwise(Eq(), static_non_const_pointer_span));
+}
+
+// Pigweed: This test does not work on platforms where int32_t is long int.
+TEST(SpanTest, ConvertBetweenEquivalentTypes) {
+ std::vector<int32_t> vector = {2, 4, 8, 16, 32};
+
+ span<int32_t> int32_t_span(vector);
+ span<int> converted_span(int32_t_span);
+ EXPECT_EQ(int32_t_span.data(), converted_span.data());
+ EXPECT_EQ(int32_t_span.size(), converted_span.size());
+
+ span<int32_t, 5> static_int32_t_span(vector.data(), vector.size());
+ span<int, 5> static_converted_span(static_int32_t_span);
+ EXPECT_EQ(static_int32_t_span.data(), static_converted_span.data());
+ EXPECT_EQ(static_int32_t_span.size(), static_converted_span.size());
+}
+
+#endif // 0
+
+TEST(SpanTest, TemplatedFirst) {
+ static constexpr int array[] = {1, 2, 3};
+ constexpr span<const int, 3> span(array);
+
+ {
+ constexpr auto subspan = span.first<0>();
+ static_assert(span.data() == subspan.data(), "");
+ static_assert(subspan.empty(), "");
+ static_assert(0u == decltype(subspan)::extent, "");
+ }
+
+ {
+ constexpr auto subspan = span.first<1>();
+ static_assert(span.data() == subspan.data(), "");
+ static_assert(1u == subspan.size(), "");
+ static_assert(1u == decltype(subspan)::extent, "");
+ static_assert(1 == subspan[0], "");
+ }
+
+ {
+ constexpr auto subspan = span.first<2>();
+ static_assert(span.data() == subspan.data(), "");
+ static_assert(2u == subspan.size(), "");
+ static_assert(2u == decltype(subspan)::extent, "");
+ static_assert(1 == subspan[0], "");
+ static_assert(2 == subspan[1], "");
+ }
+
+ {
+ constexpr auto subspan = span.first<3>();
+ static_assert(span.data() == subspan.data(), "");
+ static_assert(3u == subspan.size(), "");
+ static_assert(3u == decltype(subspan)::extent, "");
+ static_assert(1 == subspan[0], "");
+ static_assert(2 == subspan[1], "");
+ static_assert(3 == subspan[2], "");
+ }
+}
+
+TEST(SpanTest, TemplatedLast) {
+ static constexpr int array[] = {1, 2, 3};
+ constexpr span<const int, 3> span(array);
+
+ {
+ constexpr auto subspan = span.last<0>();
+ static_assert(span.data() + 3 == subspan.data(), "");
+ static_assert(subspan.empty(), "");
+ static_assert(0u == decltype(subspan)::extent, "");
+ }
+
+ {
+ constexpr auto subspan = span.last<1>();
+ static_assert(span.data() + 2 == subspan.data(), "");
+ static_assert(1u == subspan.size(), "");
+ static_assert(1u == decltype(subspan)::extent, "");
+ static_assert(3 == subspan[0], "");
+ }
+
+ {
+ constexpr auto subspan = span.last<2>();
+ static_assert(span.data() + 1 == subspan.data(), "");
+ static_assert(2u == subspan.size(), "");
+ static_assert(2u == decltype(subspan)::extent, "");
+ static_assert(2 == subspan[0], "");
+ static_assert(3 == subspan[1], "");
+ }
+
+ {
+ constexpr auto subspan = span.last<3>();
+ static_assert(span.data() == subspan.data(), "");
+ static_assert(3u == subspan.size(), "");
+ static_assert(3u == decltype(subspan)::extent, "");
+ static_assert(1 == subspan[0], "");
+ static_assert(2 == subspan[1], "");
+ static_assert(3 == subspan[2], "");
+ }
+}
+
+TEST(SpanTest, TemplatedSubspan) {
+ static constexpr int array[] = {1, 2, 3};
+ constexpr span<const int, 3> span(array);
+
+ {
+ constexpr auto subspan = span.subspan<0>();
+ static_assert(span.data() == subspan.data(), "");
+ static_assert(3u == subspan.size(), "");
+ static_assert(3u == decltype(subspan)::extent, "");
+ static_assert(1 == subspan[0], "");
+ static_assert(2 == subspan[1], "");
+ static_assert(3 == subspan[2], "");
+ }
+
+ {
+ constexpr auto subspan = span.subspan<1>();
+ static_assert(span.data() + 1 == subspan.data(), "");
+ static_assert(2u == subspan.size(), "");
+ static_assert(2u == decltype(subspan)::extent, "");
+ static_assert(2 == subspan[0], "");
+ static_assert(3 == subspan[1], "");
+ }
+
+ {
+ constexpr auto subspan = span.subspan<2>();
+ static_assert(span.data() + 2 == subspan.data(), "");
+ static_assert(1u == subspan.size(), "");
+ static_assert(1u == decltype(subspan)::extent, "");
+ static_assert(3 == subspan[0], "");
+ }
+
+ {
+ constexpr auto subspan = span.subspan<3>();
+ static_assert(span.data() + 3 == subspan.data(), "");
+ static_assert(subspan.empty(), "");
+ static_assert(0u == decltype(subspan)::extent, "");
+ }
+
+ {
+ constexpr auto subspan = span.subspan<0, 0>();
+ static_assert(span.data() == subspan.data(), "");
+ static_assert(subspan.empty(), "");
+ static_assert(0u == decltype(subspan)::extent, "");
+ }
+
+ {
+ constexpr auto subspan = span.subspan<1, 0>();
+ static_assert(span.data() + 1 == subspan.data(), "");
+ static_assert(subspan.empty(), "");
+ static_assert(0u == decltype(subspan)::extent, "");
+ }
+
+ {
+ constexpr auto subspan = span.subspan<2, 0>();
+ static_assert(span.data() + 2 == subspan.data(), "");
+ static_assert(subspan.empty(), "");
+ static_assert(0u == decltype(subspan)::extent, "");
+ }
+
+ {
+ constexpr auto subspan = span.subspan<0, 1>();
+ static_assert(span.data() == subspan.data(), "");
+ static_assert(1u == subspan.size(), "");
+ static_assert(1u == decltype(subspan)::extent, "");
+ static_assert(1 == subspan[0], "");
+ }
+
+ {
+ constexpr auto subspan = span.subspan<1, 1>();
+ static_assert(span.data() + 1 == subspan.data(), "");
+ static_assert(1u == subspan.size(), "");
+ static_assert(1u == decltype(subspan)::extent, "");
+ static_assert(2 == subspan[0], "");
+ }
+
+ {
+ constexpr auto subspan = span.subspan<2, 1>();
+ static_assert(span.data() + 2 == subspan.data(), "");
+ static_assert(1u == subspan.size(), "");
+ static_assert(1u == decltype(subspan)::extent, "");
+ static_assert(3 == subspan[0], "");
+ }
+
+ {
+ constexpr auto subspan = span.subspan<0, 2>();
+ static_assert(span.data() == subspan.data(), "");
+ static_assert(2u == subspan.size(), "");
+ static_assert(2u == decltype(subspan)::extent, "");
+ static_assert(1 == subspan[0], "");
+ static_assert(2 == subspan[1], "");
+ }
+
+ {
+ constexpr auto subspan = span.subspan<1, 2>();
+ static_assert(span.data() + 1 == subspan.data(), "");
+ static_assert(2u == subspan.size(), "");
+ static_assert(2u == decltype(subspan)::extent, "");
+ static_assert(2 == subspan[0], "");
+ static_assert(3 == subspan[1], "");
+ }
+
+ {
+ constexpr auto subspan = span.subspan<0, 3>();
+ static_assert(span.data() == subspan.data(), "");
+ static_assert(3u == subspan.size(), "");
+ static_assert(3u == decltype(subspan)::extent, "");
+ static_assert(1 == subspan[0], "");
+ static_assert(2 == subspan[1], "");
+ static_assert(3 == subspan[2], "");
+ }
+}
+
+TEST(SpanTest, SubscriptedBeginIterator) {
+ int array[] = {1, 2, 3};
+ span<const int> const_span(array);
+ for (size_t i = 0; i < const_span.size(); ++i)
+ EXPECT_EQ(array[i], const_span.begin()[i]);
+
+ span<int> mutable_span(array);
+ for (size_t i = 0; i < mutable_span.size(); ++i)
+ EXPECT_EQ(array[i], mutable_span.begin()[i]);
+}
+
+TEST(SpanTest, TemplatedFirstOnDynamicSpan) {
+ int array[] = {1, 2, 3};
+ span<const int> span(array);
+
+ {
+ auto subspan = span.first<0>();
+ EXPECT_EQ(span.data(), subspan.data());
+ EXPECT_EQ(0u, subspan.size());
+ static_assert(0u == decltype(subspan)::extent, "");
+ }
+
+ {
+ auto subspan = span.first<1>();
+ EXPECT_EQ(span.data(), subspan.data());
+ EXPECT_EQ(1u, subspan.size());
+ static_assert(1u == decltype(subspan)::extent, "");
+ EXPECT_EQ(1, subspan[0]);
+ }
+
+ {
+ auto subspan = span.first<2>();
+ EXPECT_EQ(span.data(), subspan.data());
+ EXPECT_EQ(2u, subspan.size());
+ static_assert(2u == decltype(subspan)::extent, "");
+ EXPECT_EQ(1, subspan[0]);
+ EXPECT_EQ(2, subspan[1]);
+ }
+
+ {
+ auto subspan = span.first<3>();
+ EXPECT_EQ(span.data(), subspan.data());
+ EXPECT_EQ(3u, subspan.size());
+ static_assert(3u == decltype(subspan)::extent, "");
+ EXPECT_EQ(1, subspan[0]);
+ EXPECT_EQ(2, subspan[1]);
+ EXPECT_EQ(3, subspan[2]);
+ }
+}
+
+TEST(SpanTest, TemplatedLastOnDynamicSpan) {
+ int array[] = {1, 2, 3};
+ span<int> span(array);
+
+ {
+ auto subspan = span.last<0>();
+ EXPECT_EQ(span.data() + 3, subspan.data());
+ EXPECT_EQ(0u, subspan.size());
+ static_assert(0u == decltype(subspan)::extent, "");
+ }
+
+ {
+ auto subspan = span.last<1>();
+ EXPECT_EQ(span.data() + 2, subspan.data());
+ EXPECT_EQ(1u, subspan.size());
+ static_assert(1u == decltype(subspan)::extent, "");
+ EXPECT_EQ(3, subspan[0]);
+ }
+
+ {
+ auto subspan = span.last<2>();
+ EXPECT_EQ(span.data() + 1, subspan.data());
+ EXPECT_EQ(2u, subspan.size());
+ static_assert(2u == decltype(subspan)::extent, "");
+ EXPECT_EQ(2, subspan[0]);
+ EXPECT_EQ(3, subspan[1]);
+ }
+
+ {
+ auto subspan = span.last<3>();
+ EXPECT_EQ(span.data(), subspan.data());
+ EXPECT_EQ(3u, subspan.size());
+ static_assert(3u == decltype(subspan)::extent, "");
+ EXPECT_EQ(1, subspan[0]);
+ EXPECT_EQ(2, subspan[1]);
+ EXPECT_EQ(3, subspan[2]);
+ }
+}
+
+TEST(SpanTest, TemplatedSubspanFromDynamicSpan) {
+ int array[] = {1, 2, 3};
+ span<int, 3> span(array);
+
+ {
+ auto subspan = span.subspan<0>();
+ EXPECT_EQ(span.data(), subspan.data());
+ static_assert(3u == decltype(subspan)::extent, "");
+ EXPECT_EQ(3u, subspan.size());
+ EXPECT_EQ(1, subspan[0]);
+ EXPECT_EQ(2, subspan[1]);
+ EXPECT_EQ(3, subspan[2]);
+ }
+
+ {
+ auto subspan = span.subspan<1>();
+ EXPECT_EQ(span.data() + 1, subspan.data());
+ EXPECT_EQ(2u, subspan.size());
+ static_assert(2u == decltype(subspan)::extent, "");
+ EXPECT_EQ(2, subspan[0]);
+ EXPECT_EQ(3, subspan[1]);
+ }
+
+ {
+ auto subspan = span.subspan<2>();
+ EXPECT_EQ(span.data() + 2, subspan.data());
+ EXPECT_EQ(1u, subspan.size());
+ static_assert(1u == decltype(subspan)::extent, "");
+ EXPECT_EQ(3, subspan[0]);
+ }
+
+ {
+ auto subspan = span.subspan<3>();
+ EXPECT_EQ(span.data() + 3, subspan.data());
+ EXPECT_EQ(0u, subspan.size());
+ static_assert(0u == decltype(subspan)::extent, "");
+ }
+
+ {
+ auto subspan = span.subspan<0, 0>();
+ EXPECT_EQ(span.data(), subspan.data());
+ EXPECT_EQ(0u, subspan.size());
+ static_assert(0u == decltype(subspan)::extent, "");
+ }
+
+ {
+ auto subspan = span.subspan<1, 0>();
+ EXPECT_EQ(span.data() + 1, subspan.data());
+ EXPECT_EQ(0u, subspan.size());
+ static_assert(0u == decltype(subspan)::extent, "");
+ }
+
+ {
+ auto subspan = span.subspan<2, 0>();
+ EXPECT_EQ(span.data() + 2, subspan.data());
+ EXPECT_EQ(0u, subspan.size());
+ static_assert(0u == decltype(subspan)::extent, "");
+ }
+
+ {
+ auto subspan = span.subspan<0, 1>();
+ EXPECT_EQ(span.data(), subspan.data());
+ EXPECT_EQ(1u, subspan.size());
+ static_assert(1u == decltype(subspan)::extent, "");
+ EXPECT_EQ(1, subspan[0]);
+ }
+
+ {
+ auto subspan = span.subspan<1, 1>();
+ EXPECT_EQ(span.data() + 1, subspan.data());
+ EXPECT_EQ(1u, subspan.size());
+ static_assert(1u == decltype(subspan)::extent, "");
+ EXPECT_EQ(2, subspan[0]);
+ }
+
+ {
+ auto subspan = span.subspan<2, 1>();
+ EXPECT_EQ(span.data() + 2, subspan.data());
+ EXPECT_EQ(1u, subspan.size());
+ static_assert(1u == decltype(subspan)::extent, "");
+ EXPECT_EQ(3, subspan[0]);
+ }
+
+ {
+ auto subspan = span.subspan<0, 2>();
+ EXPECT_EQ(span.data(), subspan.data());
+ EXPECT_EQ(2u, subspan.size());
+ static_assert(2u == decltype(subspan)::extent, "");
+ EXPECT_EQ(1, subspan[0]);
+ EXPECT_EQ(2, subspan[1]);
+ }
+
+ {
+ auto subspan = span.subspan<1, 2>();
+ EXPECT_EQ(span.data() + 1, subspan.data());
+ EXPECT_EQ(2u, subspan.size());
+ static_assert(2u == decltype(subspan)::extent, "");
+ EXPECT_EQ(2, subspan[0]);
+ EXPECT_EQ(3, subspan[1]);
+ }
+
+ {
+ auto subspan = span.subspan<0, 3>();
+ EXPECT_EQ(span.data(), subspan.data());
+ EXPECT_EQ(3u, subspan.size());
+ static_assert(3u == decltype(subspan)::extent, "");
+ EXPECT_EQ(1, subspan[0]);
+ EXPECT_EQ(2, subspan[1]);
+ EXPECT_EQ(3, subspan[2]);
+ }
+}
+
+TEST(SpanTest, First) {
+ int array[] = {1, 2, 3};
+ span<int> span(array);
+
+ {
+ auto subspan = span.first(0);
+ EXPECT_EQ(span.data(), subspan.data());
+ EXPECT_EQ(0u, subspan.size());
+ }
+
+ {
+ auto subspan = span.first(1);
+ EXPECT_EQ(span.data(), subspan.data());
+ EXPECT_EQ(1u, subspan.size());
+ EXPECT_EQ(1, subspan[0]);
+ }
+
+ {
+ auto subspan = span.first(2);
+ EXPECT_EQ(span.data(), subspan.data());
+ EXPECT_EQ(2u, subspan.size());
+ EXPECT_EQ(1, subspan[0]);
+ EXPECT_EQ(2, subspan[1]);
+ }
+
+ {
+ auto subspan = span.first(3);
+ EXPECT_EQ(span.data(), subspan.data());
+ EXPECT_EQ(3u, subspan.size());
+ EXPECT_EQ(1, subspan[0]);
+ EXPECT_EQ(2, subspan[1]);
+ EXPECT_EQ(3, subspan[2]);
+ }
+}
+
+TEST(SpanTest, Last) {
+ int array[] = {1, 2, 3};
+ span<int> span(array);
+
+ {
+ auto subspan = span.last(0);
+ EXPECT_EQ(span.data() + 3, subspan.data());
+ EXPECT_EQ(0u, subspan.size());
+ }
+
+ {
+ auto subspan = span.last(1);
+ EXPECT_EQ(span.data() + 2, subspan.data());
+ EXPECT_EQ(1u, subspan.size());
+ EXPECT_EQ(3, subspan[0]);
+ }
+
+ {
+ auto subspan = span.last(2);
+ EXPECT_EQ(span.data() + 1, subspan.data());
+ EXPECT_EQ(2u, subspan.size());
+ EXPECT_EQ(2, subspan[0]);
+ EXPECT_EQ(3, subspan[1]);
+ }
+
+ {
+ auto subspan = span.last(3);
+ EXPECT_EQ(span.data(), subspan.data());
+ EXPECT_EQ(3u, subspan.size());
+ EXPECT_EQ(1, subspan[0]);
+ EXPECT_EQ(2, subspan[1]);
+ EXPECT_EQ(3, subspan[2]);
+ }
+}
+
+TEST(SpanTest, Subspan) {
+ int array[] = {1, 2, 3};
+ span<int> span(array);
+
+ {
+ auto subspan = span.subspan(0);
+ EXPECT_EQ(span.data(), subspan.data());
+ EXPECT_EQ(3u, subspan.size());
+ EXPECT_EQ(1, subspan[0]);
+ EXPECT_EQ(2, subspan[1]);
+ EXPECT_EQ(3, subspan[2]);
+ }
+
+ {
+ auto subspan = span.subspan(1);
+ EXPECT_EQ(span.data() + 1, subspan.data());
+ EXPECT_EQ(2u, subspan.size());
+ EXPECT_EQ(2, subspan[0]);
+ EXPECT_EQ(3, subspan[1]);
+ }
+
+ {
+ auto subspan = span.subspan(2);
+ EXPECT_EQ(span.data() + 2, subspan.data());
+ EXPECT_EQ(1u, subspan.size());
+ EXPECT_EQ(3, subspan[0]);
+ }
+
+ {
+ auto subspan = span.subspan(3);
+ EXPECT_EQ(span.data() + 3, subspan.data());
+ EXPECT_EQ(0u, subspan.size());
+ }
+
+ {
+ auto subspan = span.subspan(0, 0);
+ EXPECT_EQ(span.data(), subspan.data());
+ EXPECT_EQ(0u, subspan.size());
+ }
+
+ {
+ auto subspan = span.subspan(1, 0);
+ EXPECT_EQ(span.data() + 1, subspan.data());
+ EXPECT_EQ(0u, subspan.size());
+ }
+
+ {
+ auto subspan = span.subspan(2, 0);
+ EXPECT_EQ(span.data() + 2, subspan.data());
+ EXPECT_EQ(0u, subspan.size());
+ }
+
+ {
+ auto subspan = span.subspan(0, 1);
+ EXPECT_EQ(span.data(), subspan.data());
+ EXPECT_EQ(1u, subspan.size());
+ EXPECT_EQ(1, subspan[0]);
+ }
+
+ {
+ auto subspan = span.subspan(1, 1);
+ EXPECT_EQ(span.data() + 1, subspan.data());
+ EXPECT_EQ(1u, subspan.size());
+ EXPECT_EQ(2, subspan[0]);
+ }
+
+ {
+ auto subspan = span.subspan(2, 1);
+ EXPECT_EQ(span.data() + 2, subspan.data());
+ EXPECT_EQ(1u, subspan.size());
+ EXPECT_EQ(3, subspan[0]);
+ }
+
+ {
+ auto subspan = span.subspan(0, 2);
+ EXPECT_EQ(span.data(), subspan.data());
+ EXPECT_EQ(2u, subspan.size());
+ EXPECT_EQ(1, subspan[0]);
+ EXPECT_EQ(2, subspan[1]);
+ }
+
+ {
+ auto subspan = span.subspan(1, 2);
+ EXPECT_EQ(span.data() + 1, subspan.data());
+ EXPECT_EQ(2u, subspan.size());
+ EXPECT_EQ(2, subspan[0]);
+ EXPECT_EQ(3, subspan[1]);
+ }
+
+ {
+ auto subspan = span.subspan(0, 3);
+ EXPECT_EQ(span.data(), subspan.data());
+ EXPECT_EQ(span.size(), subspan.size());
+ EXPECT_EQ(1, subspan[0]);
+ EXPECT_EQ(2, subspan[1]);
+ EXPECT_EQ(3, subspan[2]);
+ }
+}
+
+TEST(SpanTest, Size) {
+ {
+ span<int> span;
+ EXPECT_EQ(0u, span.size());
+ }
+
+ {
+ int array[] = {1, 2, 3};
+ span<int> span(array);
+ EXPECT_EQ(3u, span.size());
+ }
+}
+
+TEST(SpanTest, SizeBytes) {
+ {
+ span<int> span;
+ EXPECT_EQ(0u, span.size_bytes());
+ }
+
+ {
+ int array[] = {1, 2, 3};
+ span<int> span(array);
+ EXPECT_EQ(3u * sizeof(int), span.size_bytes());
+ }
+}
+
+TEST(SpanTest, Empty) {
+ {
+ span<int> span;
+ EXPECT_TRUE(span.empty());
+ }
+
+ {
+ int array[] = {1, 2, 3};
+ span<int> span(array);
+ EXPECT_FALSE(span.empty());
+ }
+}
+
+TEST(SpanTest, OperatorAt) {
+ static constexpr int kArray[] = {1, 6, 1, 8, 0};
+ constexpr span<const int> span(kArray);
+
+ static_assert(&kArray[0] == &span[0],
+ "span[0] does not refer to the same element as kArray[0]");
+ static_assert(&kArray[1] == &span[1],
+ "span[1] does not refer to the same element as kArray[1]");
+ static_assert(&kArray[2] == &span[2],
+ "span[2] does not refer to the same element as kArray[2]");
+ static_assert(&kArray[3] == &span[3],
+ "span[3] does not refer to the same element as kArray[3]");
+ static_assert(&kArray[4] == &span[4],
+ "span[4] does not refer to the same element as kArray[4]");
+}
+
+TEST(SpanTest, Front) {
+ static constexpr int kArray[] = {1, 6, 1, 8, 0};
+ constexpr span<const int> span(kArray);
+ static_assert(&kArray[0] == &span.front(),
+ "span.front() does not refer to the same element as kArray[0]");
+}
+
+TEST(SpanTest, Back) {
+ static constexpr int kArray[] = {1, 6, 1, 8, 0};
+ constexpr span<const int> span(kArray);
+ static_assert(&kArray[4] == &span.back(),
+ "span.back() does not refer to the same element as kArray[4]");
+}
+
+// Pigweed: This test uses gMock features not yet supported in Pigweed.
+#if 0
+TEST(SpanTest, Iterator) {
+ static constexpr int kArray[] = {1, 6, 1, 8, 0};
+ constexpr span<const int> span(kArray);
+
+ std::vector<int> results;
+ for (int i : span)
+ results.emplace_back(i);
+ EXPECT_THAT(results, ElementsAre(1, 6, 1, 8, 0));
+}
+#endif // 0
+
+TEST(SpanTest, ConstexprIterator) {
+ static constexpr int kArray[] = {1, 6, 1, 8, 0};
+ constexpr span<const int> span(kArray);
+
+ static_assert(
+ constexpr_equal(
+ std::begin(kArray), std::end(kArray), span.begin(), span.end()),
+ "");
+ static_assert(1 == span.begin()[0], "");
+ // Pigweed: These tests assume an iterator object, but Pigweed's span uses a
+ // simple pointer.
+#if 0
+ static_assert(1 == *(span.begin() += 0), "");
+ static_assert(6 == *(span.begin() += 1), "");
+
+ static_assert(1 == *((span.begin() + 1) -= 1), "");
+ static_assert(6 == *((span.begin() + 1) -= 0), "");
+#endif // 0
+}
+
+TEST(SpanTest, ReverseIterator) {
+ static constexpr int kArray[] = {1, 6, 1, 8, 0};
+ constexpr span<const int> span(kArray);
+
+ EXPECT_TRUE(std::equal(
+ std::rbegin(kArray), std::rend(kArray), span.rbegin(), span.rend()));
+ EXPECT_TRUE(std::equal(std::crbegin(kArray),
+ std::crend(kArray),
+ std::crbegin(span),
+ std::crend(span)));
+}
+
+// Pigweed: These are tests for make_span, which is not included in Pigweed's
+// implementation, since class template deduction is available.
+#if 0
+TEST(SpanTest, AsBytes) {
+ {
+ constexpr int kArray[] = {2, 3, 5, 7, 11, 13};
+ span<const uint8_t, sizeof(kArray)> bytes_span =
+ as_bytes(make_span(kArray));
+ EXPECT_EQ(reinterpret_cast<const uint8_t*>(kArray), bytes_span.data());
+ EXPECT_EQ(sizeof(kArray), bytes_span.size());
+ EXPECT_EQ(bytes_span.size(), bytes_span.size_bytes());
+ }
+
+ {
+ std::vector<int> vec = {1, 1, 2, 3, 5, 8};
+ span<int> mutable_span(vec);
+ span<const uint8_t> bytes_span = as_bytes(mutable_span);
+ EXPECT_EQ(reinterpret_cast<const uint8_t*>(vec.data()), bytes_span.data());
+ EXPECT_EQ(sizeof(int) * vec.size(), bytes_span.size());
+ EXPECT_EQ(bytes_span.size(), bytes_span.size_bytes());
+ }
+}
+
+TEST(SpanTest, AsWritableBytes) {
+ std::vector<int> vec = {1, 1, 2, 3, 5, 8};
+ span<int> mutable_span(vec);
+ span<uint8_t> writable_bytes_span = as_writable_bytes(mutable_span);
+ EXPECT_EQ(reinterpret_cast<uint8_t*>(vec.data()), writable_bytes_span.data());
+ EXPECT_EQ(sizeof(int) * vec.size(), writable_bytes_span.size());
+ EXPECT_EQ(writable_bytes_span.size(), writable_bytes_span.size_bytes());
+
+ // Set the first entry of vec to zero while writing through the span.
+ std::fill(writable_bytes_span.data(),
+ writable_bytes_span.data() + sizeof(int), 0);
+ EXPECT_EQ(0, vec[0]);
+}
+
+TEST(SpanTest, MakeSpanFromDataAndSize) {
+ int* nullint = nullptr;
+ auto empty_span = make_span(nullint, 0);
+ EXPECT_TRUE(empty_span.empty());
+ EXPECT_EQ(nullptr, empty_span.data());
+
+ std::vector<int> vector = {1, 1, 2, 3, 5, 8};
+ span<int> expected_span(vector.data(), vector.size());
+ auto made_span = make_span(vector.data(), vector.size());
+ EXPECT_EQ(expected_span.data(), made_span.data());
+ EXPECT_EQ(expected_span.size(), made_span.size());
+ static_assert(decltype(made_span)::extent == dynamic_extent, "");
+ static_assert(
+ std::is_same<decltype(expected_span), decltype(made_span)>::value,
+ "the type of made_span differs from expected_span!");
+}
+
+TEST(SpanTest, MakeSpanFromPointerPair) {
+ int* nullint = nullptr;
+ auto empty_span = make_span(nullint, nullint);
+ EXPECT_TRUE(empty_span.empty());
+ EXPECT_EQ(nullptr, empty_span.data());
+
+ std::vector<int> vector = {1, 1, 2, 3, 5, 8};
+ span<int> expected_span(vector.data(), vector.size());
+ auto made_span = make_span(vector.data(), vector.data() + vector.size());
+ EXPECT_EQ(expected_span.data(), made_span.data());
+ EXPECT_EQ(expected_span.size(), made_span.size());
+ static_assert(decltype(made_span)::extent == dynamic_extent, "");
+ static_assert(
+ std::is_same<decltype(expected_span), decltype(made_span)>::value,
+ "the type of made_span differs from expected_span!");
+}
+
+TEST(SpanTest, MakeSpanFromConstexprArray) {
+ static constexpr int kArray[] = {1, 2, 3, 4, 5};
+ constexpr span<const int, 5> expected_span(kArray);
+ constexpr auto made_span = make_span(kArray);
+ EXPECT_EQ(expected_span.data(), made_span.data());
+ EXPECT_EQ(expected_span.size(), made_span.size());
+ static_assert(decltype(made_span)::extent == 5, "");
+ static_assert(
+ std::is_same<decltype(expected_span), decltype(made_span)>::value,
+ "the type of made_span differs from expected_span!");
+}
+
+TEST(SpanTest, MakeSpanFromStdArray) {
+ const std::array<int, 5> kArray = {{1, 2, 3, 4, 5}};
+ span<const int, 5> expected_span(kArray);
+ auto made_span = make_span(kArray);
+ EXPECT_EQ(expected_span.data(), made_span.data());
+ EXPECT_EQ(expected_span.size(), made_span.size());
+ static_assert(decltype(made_span)::extent == 5, "");
+ static_assert(
+ std::is_same<decltype(expected_span), decltype(made_span)>::value,
+ "the type of made_span differs from expected_span!");
+}
+
+TEST(SpanTest, MakeSpanFromConstContainer) {
+ const std::vector<int> vector = {-1, -2, -3, -4, -5};
+ span<const int> expected_span(vector);
+ auto made_span = make_span(vector);
+ EXPECT_EQ(expected_span.data(), made_span.data());
+ EXPECT_EQ(expected_span.size(), made_span.size());
+ static_assert(decltype(made_span)::extent == dynamic_extent, "");
+ static_assert(
+ std::is_same<decltype(expected_span), decltype(made_span)>::value,
+ "the type of made_span differs from expected_span!");
+}
+
+TEST(SpanTest, MakeStaticSpanFromConstContainer) {
+ const std::vector<int> vector = {-1, -2, -3, -4, -5};
+ span<const int, 5> expected_span(vector.data(), vector.size());
+ auto made_span = make_span<5>(vector);
+ EXPECT_EQ(expected_span.data(), made_span.data());
+ EXPECT_EQ(expected_span.size(), made_span.size());
+ static_assert(decltype(made_span)::extent == 5, "");
+ static_assert(
+ std::is_same<decltype(expected_span), decltype(made_span)>::value,
+ "the type of made_span differs from expected_span!");
+}
+
+TEST(SpanTest, MakeSpanFromContainer) {
+ std::vector<int> vector = {-1, -2, -3, -4, -5};
+ span<int> expected_span(vector);
+ auto made_span = make_span(vector);
+ EXPECT_EQ(expected_span.data(), made_span.data());
+ EXPECT_EQ(expected_span.size(), made_span.size());
+ static_assert(decltype(made_span)::extent == dynamic_extent, "");
+ static_assert(
+ std::is_same<decltype(expected_span), decltype(made_span)>::value,
+ "the type of made_span differs from expected_span!");
+}
+
+TEST(SpanTest, MakeStaticSpanFromContainer) {
+ std::vector<int> vector = {-1, -2, -3, -4, -5};
+ span<int, 5> expected_span(vector.data(), vector.size());
+ auto made_span = make_span<5>(vector);
+ EXPECT_EQ(expected_span.data(), make_span<5>(vector).data());
+ EXPECT_EQ(expected_span.size(), make_span<5>(vector).size());
+ static_assert(decltype(make_span<5>(vector))::extent == 5, "");
+ static_assert(
+ std::is_same<decltype(expected_span), decltype(made_span)>::value,
+ "the type of made_span differs from expected_span!");
+}
+
+TEST(SpanTest, MakeStaticSpanFromConstexprContainer) {
+ constexpr StringPiece str = "Hello, World";
+ constexpr auto made_span = make_span<12>(str);
+ static_assert(str.data() == made_span.data(), "Error: data() does not match");
+ static_assert(str.size() == made_span.size(), "Error: size() does not match");
+ static_assert(std::is_same<decltype(str)::value_type,
+ decltype(made_span)::value_type>::value,
+ "Error: value_type does not match");
+ static_assert(str.size() == decltype(made_span)::extent,
+ "Error: extent does not match");
+}
+
+TEST(SpanTest, MakeSpanFromRValueContainer) {
+ std::vector<int> vector = {-1, -2, -3, -4, -5};
+ span<const int> expected_span(vector);
+ // Note: While static_cast<T&&>(foo) is effectively just a fancy spelling of
+ // std::move(foo), make_span does not actually take ownership of the passed in
+ // container. Writing it this way makes it more obvious that we simply care
+ // about the right behavour when passing rvalues.
+ auto made_span = make_span(static_cast<std::vector<int>&&>(vector));
+ EXPECT_EQ(expected_span.data(), made_span.data());
+ EXPECT_EQ(expected_span.size(), made_span.size());
+ static_assert(decltype(made_span)::extent == dynamic_extent, "");
+ static_assert(
+ std::is_same<decltype(expected_span), decltype(made_span)>::value,
+ "the type of made_span differs from expected_span!");
+}
+
+TEST(SpanTest, MakeStaticSpanFromRValueContainer) {
+ std::vector<int> vector = {-1, -2, -3, -4, -5};
+ span<const int, 5> expected_span(vector.data(), vector.size());
+ // Note: While static_cast<T&&>(foo) is effectively just a fancy spelling of
+ // std::move(foo), make_span does not actually take ownership of the passed in
+ // container. Writing it this way makes it more obvious that we simply care
+ // about the right behavour when passing rvalues.
+ auto made_span = make_span<5>(static_cast<std::vector<int>&&>(vector));
+ EXPECT_EQ(expected_span.data(), made_span.data());
+ EXPECT_EQ(expected_span.size(), made_span.size());
+ static_assert(decltype(made_span)::extent == 5, "");
+ static_assert(
+ std::is_same<decltype(expected_span), decltype(made_span)>::value,
+ "the type of made_span differs from expected_span!");
+}
+
+TEST(SpanTest, MakeSpanFromDynamicSpan) {
+ static constexpr int kArray[] = {1, 2, 3, 4, 5};
+ constexpr span<const int> expected_span(kArray);
+ constexpr auto made_span = make_span(expected_span);
+ static_assert(std::is_same<decltype(expected_span)::element_type,
+ decltype(made_span)::element_type>::value,
+ "make_span(span) should have the same element_type as span");
+
+ static_assert(expected_span.data() == made_span.data(),
+ "make_span(span) should have the same data() as span");
+
+ static_assert(expected_span.size() == made_span.size(),
+ "make_span(span) should have the same size() as span");
+
+ static_assert(decltype(made_span)::extent == decltype(expected_span)::extent,
+ "make_span(span) should have the same extent as span");
+
+ static_assert(
+ std::is_same<decltype(expected_span), decltype(made_span)>::value,
+ "the type of made_span differs from expected_span!");
+}
+
+TEST(SpanTest, MakeSpanFromStaticSpan) {
+ static constexpr int kArray[] = {1, 2, 3, 4, 5};
+ constexpr span<const int, 5> expected_span(kArray);
+ constexpr auto made_span = make_span(expected_span);
+ static_assert(std::is_same<decltype(expected_span)::element_type,
+ decltype(made_span)::element_type>::value,
+ "make_span(span) should have the same element_type as span");
+
+ static_assert(expected_span.data() == made_span.data(),
+ "make_span(span) should have the same data() as span");
+
+ static_assert(expected_span.size() == made_span.size(),
+ "make_span(span) should have the same size() as span");
+
+ static_assert(decltype(made_span)::extent == decltype(expected_span)::extent,
+ "make_span(span) should have the same extent as span");
+
+ static_assert(
+ std::is_same<decltype(expected_span), decltype(made_span)>::value,
+ "the type of made_span differs from expected_span!");
+}
+#endif // 0
+
+TEST(SpanTest, EnsureConstexprGoodness) {
+ static constexpr int kArray[] = {5, 4, 3, 2, 1};
+ constexpr span<const int> constexpr_span(kArray);
+ const size_t size = 2;
+
+ const size_t start = 1;
+ constexpr span<const int> subspan =
+ constexpr_span.subspan(start, start + size);
+ for (size_t i = 0; i < subspan.size(); ++i)
+ EXPECT_EQ(kArray[start + i], subspan[i]);
+
+ constexpr span<const int> firsts = constexpr_span.first(size);
+ for (size_t i = 0; i < firsts.size(); ++i)
+ EXPECT_EQ(kArray[i], firsts[i]);
+
+ constexpr span<const int> lasts = constexpr_span.last(size);
+ for (size_t i = 0; i < lasts.size(); ++i) {
+ const size_t j = (std::size(kArray) - size) + i;
+ EXPECT_EQ(kArray[j], lasts[i]);
+ }
+
+ constexpr int item = constexpr_span[size];
+ EXPECT_EQ(kArray[size], item);
+}
+
+#if 0
+
+// Pigweed: Death tests are not yet supported.
+TEST(SpanTest, OutOfBoundsDeath) {
+ constexpr span<int, 0> kEmptySpan;
+ ASSERT_DEATH_IF_SUPPORTED(kEmptySpan[0], "");
+ ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.first(1), "");
+ ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.last(1), "");
+ ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.subspan(1), "");
+
+ constexpr span<int> kEmptyDynamicSpan;
+ ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan[0], "");
+ ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.front(), "");
+ ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.first(1), "");
+ ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.last(1), "");
+ ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.back(), "");
+ ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.subspan(1), "");
+
+ static constexpr int kArray[] = {0, 1, 2};
+ constexpr span<const int> kNonEmptyDynamicSpan(kArray);
+ EXPECT_EQ(3U, kNonEmptyDynamicSpan.size());
+ ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan[4], "");
+ ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.subspan(10), "");
+ ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.subspan(1, 7), "");
+}
+
+// Pigweed: These tests use CheckedContiguousConstIterator, which isn't used in
+// Pigweed's version.
+TEST(SpanTest, IteratorIsRangeMoveSafe) {
+ static constexpr int kArray[] = {1, 6, 1, 8, 0};
+ const size_t kNumElements = 5;
+ constexpr span<const int> span(kArray);
+
+ static constexpr int kOverlappingStartIndexes[] = {-4, 0, 3, 4};
+ static constexpr int kNonOverlappingStartIndexes[] = {-7, -5, 5, 7};
+
+ // Overlapping ranges.
+ for (const int dest_start_index : kOverlappingStartIndexes) {
+ EXPECT_FALSE(CheckedContiguousIterator<const int>::IsRangeMoveSafe(
+ span.begin(), span.end(),
+ CheckedContiguousIterator<const int>(
+ span.data() + dest_start_index,
+ span.data() + dest_start_index + kNumElements)));
+ EXPECT_FALSE(CheckedContiguousConstIterator<const int>::IsRangeMoveSafe(
+ std::cbegin(span), std::cend(span),
+ CheckedContiguousConstIterator<const int>(
+ span.data() + dest_start_index,
+ span.data() + dest_start_index + kNumElements)));
+ }
+
+ // Non-overlapping ranges.
+ for (const int dest_start_index : kNonOverlappingStartIndexes) {
+ EXPECT_TRUE(CheckedContiguousIterator<const int>::IsRangeMoveSafe(
+ span.begin(), span.end(),
+ CheckedContiguousIterator<const int>(
+ span.data() + dest_start_index,
+ span.data() + dest_start_index + kNumElements)));
+ EXPECT_TRUE(CheckedContiguousConstIterator<const int>::IsRangeMoveSafe(
+ std::cbegin(span), std::cend(span),
+ CheckedContiguousConstIterator<const int>(
+ span.data() + dest_start_index,
+ span.data() + dest_start_index + kNumElements)));
+ }
+
+ // IsRangeMoveSafe is true if the length to be moved is 0.
+ EXPECT_TRUE(CheckedContiguousIterator<const int>::IsRangeMoveSafe(
+ span.begin(), span.begin(),
+ CheckedContiguousIterator<const int>(span.data(), span.data())));
+ EXPECT_TRUE(CheckedContiguousConstIterator<const int>::IsRangeMoveSafe(
+ std::cbegin(span), std::cbegin(span),
+ CheckedContiguousConstIterator<const int>(span.data(), span.data())));
+
+ // IsRangeMoveSafe is false if end < begin.
+ EXPECT_FALSE(CheckedContiguousIterator<const int>::IsRangeMoveSafe(
+ span.end(), span.begin(),
+ CheckedContiguousIterator<const int>(span.data(), span.data())));
+ EXPECT_FALSE(CheckedContiguousConstIterator<const int>::IsRangeMoveSafe(
+ std::cend(span), std::cbegin(span),
+ CheckedContiguousConstIterator<const int>(span.data(), span.data())));
+}
+
+// Pigweed: gMock matchers are not yet supported.
+TEST(SpanTest, Sort) {
+ int array[] = {5, 4, 3, 2, 1};
+
+ span<int> dynamic_span = array;
+ std::sort(dynamic_span.begin(), dynamic_span.end());
+ EXPECT_THAT(array, ElementsAre(1, 2, 3, 4, 5));
+ std::sort(dynamic_span.rbegin(), dynamic_span.rend());
+ EXPECT_THAT(array, ElementsAre(5, 4, 3, 2, 1));
+
+ span<int, 5> static_span = array;
+ std::sort(static_span.rbegin(), static_span.rend(), std::greater<>());
+ EXPECT_THAT(array, ElementsAre(1, 2, 3, 4, 5));
+ std::sort(static_span.begin(), static_span.end(), std::greater<>());
+ EXPECT_THAT(array, ElementsAre(5, 4, 3, 2, 1));
+}
+#endif // 0
+
+TEST(SpanTest, SpanExtentConversions) {
+ // Statically checks that various conversions between spans of dynamic and
+ // static extent are possible or not.
+ static_assert(
+ !std::is_constructible<span<int, 0>, span<int>>::value,
+ "Error: static span should not be constructible from dynamic span");
+
+ static_assert(!std::is_constructible<span<int, 2>, span<int, 1>>::value,
+ "Error: static span should not be constructible from static "
+ "span with different extent");
+
+ static_assert(std::is_convertible<span<int, 0>, span<int>>::value,
+ "Error: static span should be convertible to dynamic span");
+
+ static_assert(std::is_convertible<span<int>, span<int>>::value,
+ "Error: dynamic span should be convertible to dynamic span");
+
+ static_assert(std::is_convertible<span<int, 2>, span<int, 2>>::value,
+ "Error: static span should be convertible to static span");
+}
+
+TEST(SpanTest, IteratorConversions) {
+ static_assert(std::is_convertible<span<int>::iterator,
+ span<const int>::iterator>::value,
+ "Error: iterator should be convertible to const iterator");
+
+ static_assert(!std::is_convertible<span<const int>::iterator,
+ span<int>::iterator>::value,
+ "Error: const iterator should not be convertible to iterator");
+}
+
+} // namespace pw
diff --git a/pw_span/span_test.cc b/pw_span/span_test.cc
index 37d5ee4..e41a006 100644
--- a/pw_span/span_test.cc
+++ b/pw_span/span_test.cc
@@ -25,6 +25,7 @@
#include <algorithm>
#include <cstdint>
#include <memory>
+#include <span>
#include <string>
#include <type_traits>
#include <vector>
@@ -38,7 +39,7 @@
using ::testing::Pointwise;
#endif // 0
-namespace pw {
+namespace std {
namespace {
@@ -58,6 +59,59 @@
} // namespace
+// TODO(hepler): Remove these tests after eliminating pw::span.
+TEST(StdSpanCompatibility, ConstructFromPwSpanToStdSpan) {
+ char array[] = {'a', 'b', 'c', 'd', '\0'};
+
+ ::pw::span pw_span(array);
+ std::span std_span(pw_span);
+
+ std_span[0] = '!';
+ EXPECT_STREQ(std_span.data(), "!bcd");
+
+ EXPECT_EQ(pw_span.data(), std_span.data());
+ EXPECT_EQ(pw_span.size(), std_span.size());
+}
+
+TEST(StdSpanCompatibility, ConstructFromStdSpanToPwSpan) {
+ char array[] = {'a', 'b', 'c', 'd', '\0'};
+
+ std::span std_span(array);
+ ::pw::span pw_span(std_span);
+
+ pw_span[0] = '!';
+ EXPECT_STREQ(pw_span.data(), "!bcd");
+
+ EXPECT_EQ(pw_span.data(), std_span.data());
+ EXPECT_EQ(pw_span.size(), std_span.size());
+}
+
+TEST(StdSpanCompatibility, AssignFromPwSpanToStdSpan) {
+ char array[] = {'a', 'b', 'c', 'd', '\0'};
+
+ auto pw_span = ::pw::span(array);
+ std::span std_span = pw_span;
+
+ std_span[0] = '!';
+ EXPECT_STREQ(std_span.data(), "!bcd");
+
+ EXPECT_EQ(pw_span.data(), std_span.data());
+ EXPECT_EQ(pw_span.size(), std_span.size());
+}
+
+TEST(StdSpanCompatibility, AssignFromStdSpanToPwSpan) {
+ char array[] = {'a', 'b', 'c', 'd', '\0'};
+
+ auto std_span = std::span(array);
+ ::pw::span pw_span = std_span;
+
+ pw_span[0] = '!';
+ EXPECT_STREQ(pw_span.data(), "!bcd");
+
+ EXPECT_EQ(pw_span.data(), std_span.data());
+ EXPECT_EQ(pw_span.size(), std_span.size());
+}
+
TEST(SpanTest, DeductionGuides_MutableArray) {
char array[] = {'a', 'b', 'c', 'd', '\0'};
@@ -1629,4 +1683,4 @@
"Error: const iterator should not be convertible to iterator");
}
-} // namespace pw
+} // namespace std