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",
    # ...
  ]
}

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.

API reference#

Moved: pw_span

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.