pw_span#

std::span for C++17

Stable C++17 C++20

  • Standardized: pw::span matches C++20’s std::span as closely as possible.

  • Zero-cost: If std::span is available, pw::span is simply an alias of it.

#include <span>

// With pw::span, a buffer is passed as a single argument.
// No need to handle pointer and size arguments.
bool ProcessBuffer(pw::span<uint8_t> buffer);

bool DoStuff() {
  // ...
  ProcessBuffer(c_array);
  ProcessBuffer(array_object);
  ProcessBuffer(pw::span(data_pointer, data_size));
}

pw::span is a convenient abstraction that wraps a pointer and a size. It’s especially useful in APIs. Spans support implicit conversions from C arrays, std::array, or any STL-style container, such as std::string_view.

Get started#

Add @pigweed//pw_span to the deps list in your Bazel target:

cc_library("...") {
  # ...
  deps = [
    # ...
    "@pigweed//pw_span",
    # ...
  ]
}

This assumes that your Bazel WORKSPACE has a repository named @pigweed that points to the upstream Pigweed repository.

Add $dir_pw_span to the deps list in your pw_executable() build target:

pw_executable("...") {
  # ...
  deps = [
    # ...
    "$dir_pw_span",
    # ...
  ]
}

Add pw_span to your pw_add_library or similar CMake target:

pw_add_library(my_library STATIC
  HEADERS
    ...
  PRIVATE_DEPS
    # ...
    pw_span
    # ...
)

There are two ways to use pw_span from a Zephyr project:

  1. Depend on pw_span in your CMake target (see CMake tab). This is Pigweed Team’s suggested approach since it enables precise CMake dependency analysis.

  2. Add CONFIG_PIGWEED_SPAN=y to the Zephyr project’s configuration, which causes pw_span to become a global dependency and have the includes exposed to all targets. Pigweed team does not recommend this approach, though it is the typical Zephyr solution.

Guides#

pw::span as a function parameter#

pw::span objects should be passed by value.

Yes: Accept pw::span object by value:

bool ProcessBuffer(pw::span<uint8_t> buffer);

No: Accept pw::span object by reference:

bool ProcessBuffer(const pw::span<uint8_t>& buffer);

Rationale: Since a pw::span object is essentially a pointer and a size, references to pw::span objects are almost always unnecessary indirection.

See also: https://abseil.io/tips/93#as-function-parameters

Operating on arrays of bytes#

When operating on an array of bytes you typically need pointer and size arguments:

bool ProcessBuffer(char* buffer, size_t buffer_size);

bool DoStuff() {
  ProcessBuffer(c_array, sizeof(c_array));
  ProcessBuffer(array_object.data(), array_object.size());
  ProcessBuffer(data_pointer, data_size);
}

With pw::span you can pass the buffer as a single argument instead:

#include <span>

// With pw::span, a buffer is passed as a single argument.
// No need to handle pointer and size arguments.
bool ProcessBuffer(pw::span<uint8_t> buffer);

bool DoStuff() {
  // ...
  ProcessBuffer(c_array);
  ProcessBuffer(array_object);
  ProcessBuffer(pw::span(data_pointer, data_size));
}

Working with spans of binary data#

Use pw::span<std::byte> or pw::span<const std::byte> to represent spans of binary data. Convert spans to byte spans with pw::as_bytes or pw::as_writable_bytes.

void ProcessData(pw::span<const std::byte> data);

void DoStuff() {
  std::array<AnyType, 7> data = { ... };
  ProcessData(pw::as_bytes(pw::span(data)));
}

pw_bytes/span.h provides ByteSpan and ConstByteSpan aliases for these types.

Convert byte spans back to their original type, or to spans of other 1-byte types using pw::span_cast<T>.

#include "pw_bytes/span.h"
#include "pw_span/cast.h"

void SDK_ReadData(uint8_t* data, size_t size);
void SDK_WriteData(const uint8_t* data, size_t size);

void Write(pw::ConstByteSpan buffer) {
  auto data = pw::span_cast<const uint8_t>(buffer);
  SDK_WriteData(data.data(), data.size());
}

void Read(pw::ByteSpan buffer) {
  auto data = pw::span_cast<uint8_t>(buffer);
  SDK_ReadData(data.data(), data.size());
}

Warning

pw::span_cast<T> is only safe to use if the underlying data is actually of the specified type, or if T is another 1-byte type (e.g. uint8_t). You cannot safely use this function to reinterpret e.g. a raw byte array from malloc() as a span of integers.

References#

pw_span/span.h#

See std::span. The pw::span API is designed to match the std::span API.

pw_span/cast.h#

This header provides functions for casting between pw::span types, and is not part of the std::span interface.

span<T> pw::span_cast(std::span<std::byte> bytes)#

Casts a pw::span<std::byte> (ByteSpan) to a span of a different type.

This function is only safe to use if the underlying data is actually of the specified type. You cannot safely use this function to reinterpret e.g. a raw byte array from malloc() as a span of integers.

This function is essentially the inverse of pw::as_writable_bytes.

If kSourceExtentBytes is dynamic_extent, the returned span also has a dynamic extent. Otherwise, the returned span has a static extent of kSourceExtentBytes / sizeof(ResultT).

span<const T> pw::span_cast(std::span<const std::byte> bytes)#

Casts a pw::span<const std::byte> (ConstByteSpan) to a span of a different const type.

This function is only safe to use if the underlying data is actually of the specified type. You cannot safely use this function to reinterpret e.g. a raw byte array from malloc() as a span of integers.

This function is essentially the inverse of pw::as_bytes.

If kSourceExtentBytes is dynamic_extent, the returned span also has a dynamic extent. Otherwise, the returned span has a static extent of kSourceExtentBytes / sizeof(ResultT).

Configuration options#

The following configurations can be adjusted via compile-time configuration of this module, see the module documentation for more details.

PW_SPAN_ENABLE_ASSERTS#

Controls whether pw_span includes asserts for detecting disallowed span operations at runtime.

For C++20 and later, this replaces std::span with the custom implementation in pw_span to ensure bounds-checking asserts have been enabled.

This defaults to disabled because of the significant increase in code size caused by enabling this feature. It’s strongly recommended to enable this in debug and testing builds.