pw_span: Introduce pw::span
- pw::span is identical to Pigweed's polyfilled std::span, but in a
different namespace. Implicit converions between std::span and
pw::span are supported inherently.
pw::span and std::span are maintained as separate classes, with shared
code in a .inc file. This is done instead of aliasing since C++17 does
not support class template argument decuction with aliases. Also, it
is simpler to have separate implementations to avoid circular
dependencies while migrating code to pw::span.
- Add DEFINES to the pw_add_test() CMake function to support defining
preprocessor macros for the pw_span test.
- Move '.inc' and '.inl' extensions from CPP_HEADER_EXTS to
CPP_SOURCE_EXTS, since these should be treated like sources not
headers (no include guards).
Bug: b/235237667
Change-Id: I4d76fb373cdaa4d3a5d72a0537bea492b5c06814
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/98160
Commit-Queue: Wyatt Hepler <hepler@google.com>
Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com>
Reviewed-by: Ted Pudlik <tpudlik@google.com>
diff --git a/pw_build/pigweed.cmake b/pw_build/pigweed.cmake
index 212d0eb..3ef02c4 100644
--- a/pw_build/pigweed.cmake
+++ b/pw_build/pigweed.cmake
@@ -540,11 +540,12 @@
# NAME: name to use for the target
# SOURCES: source files for this test
# DEPS: libraries on which this test depends
+# DEFINES: defines to set for the source files in this test
# GROUPS: groups to which to add this test; if none are specified, the test is
# added to the 'default' and 'all' groups
#
function(pw_add_test NAME)
- pw_parse_arguments_strict(pw_add_test 1 "" "" "SOURCES;DEPS;GROUPS")
+ pw_parse_arguments_strict(pw_add_test 1 "" "" "SOURCES;DEPS;DEFINES;GROUPS")
add_executable("${NAME}" EXCLUDE_FROM_ALL ${arg_SOURCES})
target_link_libraries("${NAME}"
@@ -553,6 +554,11 @@
${pw_unit_test_MAIN}
${arg_DEPS}
)
+ target_compile_definitions("${NAME}"
+ PRIVATE
+ ${arg_DEFINES}
+ )
+
# Tests require at least one source file.
if(NOT arg_SOURCES)
target_sources("${NAME}" PRIVATE $<TARGET_PROPERTY:pw_build.empty,SOURCES>)
diff --git a/pw_presubmit/py/pw_presubmit/format_code.py b/pw_presubmit/py/pw_presubmit/format_code.py
index 623b544..e4b617f 100755
--- a/pw_presubmit/py/pw_presubmit/format_code.py
+++ b/pw_presubmit/py/pw_presubmit/format_code.py
@@ -283,9 +283,9 @@
fix: Callable[[Iterable], Dict[Path, str]]
-CPP_HEADER_EXTS = frozenset(
- ('.h', '.hpp', '.hxx', '.h++', '.hh', '.H', '.inc', '.inl'))
-CPP_SOURCE_EXTS = frozenset(('.c', '.cpp', '.cxx', '.c++', '.cc', '.C'))
+CPP_HEADER_EXTS = frozenset(('.h', '.hpp', '.hxx', '.h++', '.hh', '.H'))
+CPP_SOURCE_EXTS = frozenset(
+ ('.c', '.cpp', '.cxx', '.c++', '.cc', '.C', '.inc', '.inl'))
CPP_EXTS = CPP_HEADER_EXTS.union(CPP_SOURCE_EXTS)
C_FORMAT: CodeFormat = CodeFormat('C and C++', CPP_EXTS,
diff --git a/pw_span/BUILD.bazel b/pw_span/BUILD.bazel
index 296a995..ca6e901 100644
--- a/pw_span/BUILD.bazel
+++ b/pw_span/BUILD.bazel
@@ -22,9 +22,22 @@
licenses(["notice"])
+# TODO(b/235237667): Rename this target to pw_span:pw_span.
+pw_cc_library(
+ name = "pigweed_span",
+ srcs = ["public/pw_span/internal/span_common.inc"],
+ hdrs = ["public/pw_span/span.h"],
+ includes = ["public"],
+ deps = ["//pw_polyfill"],
+)
+
+# TODO(b/235237667): Rename this target to pw_span:polyfill.
pw_cc_library(
name = "pw_span",
- srcs = ["public/pw_span/internal/span_common.inc"],
+ srcs = [
+ "public/pw_span/internal/span.h",
+ "public/pw_span/internal/span_common.inc",
+ ],
hdrs = ["public_overrides/span"],
includes = [
"public",
@@ -37,10 +50,27 @@
)
pw_cc_test(
- name = "span_test",
+ name = "polyfill_test",
srcs = ["span_test.cc"],
+ defines = [
+ "PW_SPAN_TEST_INCLUDE=<span>",
+ "PW_SPAN_TEST_NAMESPACE=std",
+ ],
deps = [
":pw_span",
"//pw_unit_test",
],
)
+
+pw_cc_test(
+ name = "pw_span_test",
+ srcs = ["span_test.cc"],
+ defines = [
+ 'PW_SPAN_TEST_INCLUDE=\\"pw_span/span.h\\"',
+ "PW_SPAN_TEST_NAMESPACE=pw",
+ ],
+ deps = [
+ ":pigweed_span",
+ "//pw_unit_test",
+ ],
+)
diff --git a/pw_span/BUILD.gn b/pw_span/BUILD.gn
index 86c769a..e73e05f 100644
--- a/pw_span/BUILD.gn
+++ b/pw_span/BUILD.gn
@@ -29,22 +29,11 @@
visibility = [ ":*" ]
}
-# This source set provides the <span> header, which is accessed only through
-# pw_polyfill.
-pw_source_set("polyfill") {
- remove_public_deps = [ "*" ]
- public_configs = [ ":overrides_config" ]
- public_deps = [ ":pw_span" ]
- public = [ "public_overrides/span" ]
- visibility = [ "$dir_pw_polyfill:*" ]
-}
-
-# This source set provides the internal span.h header included by <span>. This
-# source set is only used by pw_polyfill, so its visibility is restricted.
pw_source_set("pw_span") {
- remove_public_deps = [ "*" ]
+ public = [ "public/pw_span/span.h" ]
public_configs = [ ":public_config" ]
- public_deps = [ "$dir_pw_polyfill" ]
+ public_deps = [ dir_pw_polyfill ]
+ sources = [ "public/pw_span/internal/span_common.inc" ]
# Polyfill <cstddef> (std::byte) and <iterator> (std::size(), std::data) if
# C++17 is not supported.
@@ -54,18 +43,64 @@
"$dir_pw_polyfill:iterator",
]
}
- sources = [ "public/pw_span/internal/span_common.inc" ]
- visibility = [ ":*" ]
+}
+
+# This source set provides the <span> header, which is accessed only through
+# pw_polyfill. This source set provides the internal span.h header included by
+# <span>. This source set is only used by pw_polyfill, so its visibility is
+# restricted.
+pw_source_set("polyfill") {
+ remove_public_deps = [ "*" ]
+ public_configs = [
+ ":public_config",
+ ":overrides_config",
+ ]
+ public_deps = [ dir_pw_polyfill ]
+
+ # Polyfill <cstddef> (std::byte) and <iterator> (std::size(), std::data) if
+ # C++17 is not supported.
+ if (pw_toolchain_CXX_STANDARD < pw_toolchain_STANDARD.CXX17) {
+ public_deps += [
+ "$dir_pw_polyfill:cstddef",
+ "$dir_pw_polyfill:iterator",
+ ]
+ }
+ public = [ "public_overrides/span" ]
+ sources = [
+ "public/pw_span/internal/span.h",
+ "public/pw_span/internal/span_common.inc",
+ ]
+ visibility = [
+ ":*",
+ "$dir_pw_polyfill:*",
+ ]
}
pw_test_group("tests") {
- tests = [ ":test" ]
+ tests = [
+ ":polyfill_test",
+ ":pw_span_test",
+ ]
}
-pw_test("test") {
+pw_test("polyfill_test") {
+ deps = [ ":polyfill" ]
+ remove_configs = [ "$dir_pw_build:extra_strict_warnings" ]
+ sources = [ "span_test.cc" ]
+ defines = [
+ "PW_SPAN_TEST_INCLUDE=<span>",
+ "PW_SPAN_TEST_NAMESPACE=std",
+ ]
+}
+
+pw_test("pw_span_test") {
deps = [ ":pw_span" ]
remove_configs = [ "$dir_pw_build:extra_strict_warnings" ]
sources = [ "span_test.cc" ]
+ defines = [
+ "PW_SPAN_TEST_INCLUDE=\"pw_span/span.h\"",
+ "PW_SPAN_TEST_NAMESPACE=pw",
+ ]
}
pw_doc_group("docs") {
diff --git a/pw_span/CMakeLists.txt b/pw_span/CMakeLists.txt
index a53fd0d..e6f5b5f 100644
--- a/pw_span/CMakeLists.txt
+++ b/pw_span/CMakeLists.txt
@@ -14,12 +14,39 @@
include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
-pw_auto_add_simple_module(pw_span
+pw_add_module_library(pw_span
+ HEADERS
+
+ PUBLIC_INCLUDES
+ public
+ public_overrides
PUBLIC_DEPS
pw_polyfill
pw_polyfill.standard_library
)
-target_include_directories(pw_span INTERFACE public_overrides)
+
+pw_add_test(pw_span.polyfill_test
+ SOURCES
+ span_test.cc
+ DEFINES
+ "PW_SPAN_TEST_INCLUDE=<span>"
+ "PW_SPAN_TEST_NAMESPACE=std"
+ GROUPS
+ modules
+ pw_span
+)
+
+
+pw_add_test(pw_span.pw_span_test
+ SOURCES
+ span_test.cc
+ DEFINES
+ "PW_SPAN_TEST_INCLUDE=\"pw_span/span.h\""
+ "PW_SPAN_TEST_NAMESPACE=pw"
+ GROUPS
+ modules
+ pw_span
+)
if(Zephyr_FOUND AND CONFIG_PIGWEED_SPAN)
zephyr_link_libraries(pw_span)
diff --git a/pw_span/docs.rst b/pw_span/docs.rst
index 63d44ba..65a0466 100644
--- a/pw_span/docs.rst
+++ b/pw_span/docs.rst
@@ -1,27 +1,26 @@
.. _module-pw_span:
--------
+=======
pw_span
--------
-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 this implementation of
-``std::span`` is to exactly match the C++20 standard.
+=======
+The ``pw_span`` module provides :cpp:class:`pw::span`, an implementation of
+C++20's `std::span <https://en.cppreference.com/w/cpp/container/span>`_.
+``std::span`` is a non-owning view of an array of values. The intent is for
+:cpp:class:`pw::span` is to match the C++20 standard as closely as possible.
-The only header provided by the ``pw_span`` module 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.
+.. note::
-``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/``.
+ ``pw_span:polyfill`` provides ``<span>`` and a ``std::span`` class that is
+ identical to :cpp:class:`pw::span`. The ``std::span`` polyfill is DEPRECATED;
+ do NOT use it for new code. Use :cpp:class:`pw::span` instead, or
+ ``std::span`` if you are building with C++20.
-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
+--------------
+Using pw::span
+--------------
+:cpp:class:`pw::span` is a convenient abstraction that wraps a pointer and a
+size. :cpp:class:`pw::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``.
Functions operating on an array of bytes typically accept pointer and size
@@ -37,7 +36,7 @@
ProcessBuffer(data_pointer, data_size);
}
-Pointer and size arguments can be replaced with a ``std::span``:
+Pointer and size arguments can be replaced with a :cpp:class:`pw::span`:
.. code-block:: cpp
@@ -53,9 +52,10 @@
}
.. tip::
- 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.
+
+ Use ``pw::span<std::byte>`` or ``std::span<const std::byte>`` to represent
+ spans of binary data. Use ``pw::as_bytes`` or ``pw::as_writeable_bytes`` to
+ convert any span to a byte span.
.. code-block:: cpp
@@ -66,11 +66,17 @@
ProcessData(std::as_bytes(std::span(data)));
}
-Compatibility
-=============
-Works with C++14, but some features require C++17.
+ ``pw_bytes/span.h`` provides ``ByteSpan`` and ``ConstByteSpan`` aliases for
+ these types.
+-------------
+Compatibility
+-------------
+Works with C++14, but some features require C++17. In C++20, use ``std::span``
+instead.
+
+------
Zephyr
-======
+------
To enable ``pw_span`` for Zephyr add ``CONFIG_PIGWEED_SPAN=y`` to the project's
configuration.
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..3bd8090
--- /dev/null
+++ b/pw_span/public/pw_span/internal/span.h
@@ -0,0 +1,51 @@
+// 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 "pw_polyfill/standard_library/namespace.h"
+
+#ifndef __cpp_lib_span
+#define __cpp_lib_span 202002L
+
+#define _PW_SPAN_COMMON_NAMEPACE_BEGIN _PW_POLYFILL_BEGIN_NAMESPACE_STD
+#define _PW_SPAN_COMMON_NAMEPACE_END _PW_POLYFILL_END_NAMESPACE_STD
+
+#include "pw_span/internal/span_common.inc"
+
+#undef _PW_SPAN_COMMON_NAMEPACE_BEGIN
+#undef _PW_SPAN_COMMON_NAMEPACE_END
+
+#endif // __cpp_lib_span
diff --git a/pw_span/public/pw_span/internal/span_common.inc b/pw_span/public/pw_span/internal/span_common.inc
index 2799bfd..a347ffc 100644
--- a/pw_span/public/pw_span/internal/span_common.inc
+++ b/pw_span/public/pw_span/internal/span_common.inc
@@ -12,8 +12,8 @@
// 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>.
+// This span implementation is a stand-in for C++20's std::span. Do NOT include
+// this header directly; instead, include it as "pw_span/span.h".
//
// 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
@@ -32,11 +32,9 @@
// - 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
-#ifndef __cpp_lib_span
-#define __cpp_lib_span 202002L
+// This file does not have #pragma once! It is intended to be included in
+// multiple header files!
#include <algorithm>
#include <array>
@@ -47,12 +45,16 @@
#include <utility>
#include "pw_polyfill/language_feature_macros.h"
-#include "pw_polyfill/standard_library/namespace.h"
// Pigweed: Disable the asserts from Chromium for now.
#define _PW_SPAN_ASSERT(arg)
-_PW_POLYFILL_BEGIN_NAMESPACE_STD
+// The file that includes this .inc file must define the following macros:
+//
+// * _PW_SPAN_COMMON_NAMEPACE_BEGIN
+// * _PW_SPAN_COMMON_NAMEPACE_END
+
+_PW_SPAN_COMMON_NAMEPACE_BEGIN
// [views.constants]
constexpr size_t dynamic_extent = std::numeric_limits<size_t>::max();
@@ -72,7 +74,7 @@
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> {};
+struct ExtentImpl<span<T, N>> : std::integral_constant<size_t, N> {};
template <typename T>
using Extent = ExtentImpl<std::remove_cv_t<std::remove_reference_t<T>>>;
@@ -473,7 +475,6 @@
#endif // __cpp_deduction_guides
-_PW_POLYFILL_END_NAMESPACE_STD
+_PW_SPAN_COMMON_NAMEPACE_END
#undef _PW_SPAN_ASSERT
-#endif // __cpp_lib_span
diff --git a/pw_span/public/pw_span/span.h b/pw_span/public/pw_span/span.h
new file mode 100644
index 0000000..a5fb5a2
--- /dev/null
+++ b/pw_span/public/pw_span/span.h
@@ -0,0 +1,27 @@
+// Copyright 2022 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.
+
+// pw::span is an implementation of std::span for C++14 or newer. The
+// implementation is shared with the std::span polyfill class.
+#pragma once
+
+// TODO(b/235237667): Make pw::span an alias of std::span when it is available.
+
+#define _PW_SPAN_COMMON_NAMEPACE_BEGIN namespace pw {
+#define _PW_SPAN_COMMON_NAMEPACE_END } // namespace pw
+
+#include "pw_span/internal/span_common.inc"
+
+#undef _PW_SPAN_COMMON_NAMEPACE_BEGIN
+#undef _PW_SPAN_COMMON_NAMEPACE_END
diff --git a/pw_span/public_overrides/span b/pw_span/public_overrides/span
index c22a188..1ac4288 100644
--- a/pw_span/public_overrides/span
+++ b/pw_span/public_overrides/span
@@ -17,4 +17,4 @@
#include_next <span>
#endif // __has_include_next(<span>)
-#include "pw_span/internal/span_common.inc"
+#include "pw_span/internal/span.h"
diff --git a/pw_span/span_test.cc b/pw_span/span_test.cc
index 7277368..5b9ea68 100644
--- a/pw_span/span_test.cc
+++ b/pw_span/span_test.cc
@@ -21,10 +21,18 @@
// In order to minimize changes from the original, this file does NOT fully
// adhere to Pigweed's style guide.
+#ifndef PW_SPAN_TEST_INCLUDE
+#error "The PW_SPAN_TEST_INCLUDE macro must be defined to compile this test."
+#endif // PW_SPAN_TEST_INCLUDE
+
+#ifndef PW_SPAN_TEST_NAMESPACE
+#error "The PW_SPAN_TEST_NAMESPACE macro must be defined to compile this test."
+#endif // PW_SPAN_TEST_NAMESPACE
+
#include <algorithm>
#include <cstdint>
#include <memory>
-#include <span>
+#include PW_SPAN_TEST_INCLUDE
#include <string>
#include <type_traits>
#include <vector>
@@ -39,7 +47,7 @@
using ::testing::Pointwise;
#endif // 0
-namespace std {
+namespace PW_SPAN_TEST_NAMESPACE {
namespace {
@@ -1645,4 +1653,4 @@
"Error: const iterator should not be convertible to iterator");
}
-} // namespace std
+} // namespace PW_SPAN_TEST_NAMESPACE