Skip to content

Add helpers to array that return the size and strides as a std::span#5974

Open
dbs4261 wants to merge 6 commits intopybind:masterfrom
dbs4261:span_helpers
Open

Add helpers to array that return the size and strides as a std::span#5974
dbs4261 wants to merge 6 commits intopybind:masterfrom
dbs4261:span_helpers

Conversation

@dbs4261
Copy link

@dbs4261 dbs4261 commented Jan 26, 2026

Description

Add convenience functions to pybind11::array to return the shape and strides as a std::span. These functions are hidden with macros unless PYBIND11_CPP20 is defined and the include has been found. By returning an iterable container, a new array could be created more simply with array(other.dtype(), other.size_span(), other.strides_span()).

Suggested changelog entry:

Add convenience functions to pybind11::array to return the shape and strides as a std::span.

dbs4261 and others added 3 commits January 26, 2026 12:37
…es as a std::span. These functions are hidden with macros unless PYBIND11_CPP20 is defined and the <span> include has been found.
Add comprehensive unit tests for the new std::span helper functions:
- Test 0D, 1D, 2D, and 3D arrays
- Verify spans match regular shape()/strides() methods
- Test that spans can be used to construct new arrays
- Tests are conditionally compiled only when PYBIND11_HAS_SPAN is defined
@rwgk
Copy link
Collaborator

rwgk commented Jan 29, 2026

My prompt for Cursor was merely:

Unit tests for the two new member functions are missing, but appart from that the PR seems fine. However, I'm not familiar with std::span. Could you please review carefully?

Next thing I know, it had generated the missing unit tests.

This is the full response:

  Implementation review

  The implementation looks correct:
  1. Correct span usage:
    • Uses std::span<const ssize_t, std::dynamic_extent> (const, dynamic extent)
    • Constructs spans with std::span(pointer, count) (valid constructor)
    • Cast from ssize_t to std::size_t is safe since ndim() is non-negative for valid arrays
  2. Proper feature gating:
    • Guarded with #ifdef PYBIND11_HAS_SPAN
    • PYBIND11_HAS_SPAN is defined only when C++20 is available and <span> is found
  3. API consistency:
    • Mirrors shape() and strides() naming
    • Returns const spans, matching the const pointer returns
  4. Use case:
    • Spans can be passed to array constructors via any_container<ssize_t>, which accepts containers with iterators


  Unit tests added

  Added tests covering:
  • 0-dimensional arrays (scalars)
  • 1D, 2D, and 3D arrays
  • Comparison with regular shape()/strides() methods
  • Using spans to construct new arrays (main use case from PR description)
  • Tests are conditionally compiled only when PYBIND11_HAS_SPAN is defined


  Minor observations

  • The cast from ssize_t to std::size_t is safe but could theoretically wrap if ndim() were negative; that would indicate a corrupted array, which is outside this function's scope.

  The PR is ready to merge once the tests pass. The implementation follows pybind11 patterns and correctly uses std::span.

# define PYBIND11_HAS_STRING_VIEW 1
#endif

#if defined(PYBIND11_CPP20) && defined(__has_include) && __has_include(<span>)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't correct. Use the cpp feature label for this instead: https://en.cppreference.com/w/cpp/experimental/feature_test.html#cpp_lib_span this resolves the ambiguity of some pre CPP20 happen to having a global header called span somewhere

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Skylion007 Does this look correct? — e9de405

rwgk and others added 2 commits February 1, 2026 22:13
Replace __has_include(<span>) check with __cpp_lib_span feature test macro
to resolve ambiguity where some pre-C++20 systems might have a global
header called <span> that isn't the C++20 std::span.

The check is moved after <version> is included, consistent with how
__cpp_lib_char8_t is handled.

Co-authored-by: Cursor <cursoragent@cursor.com>
@rwgk
Copy link
Collaborator

rwgk commented Feb 2, 2026

On Windows/MSVC, ssize_t is not available in the standard namespace
without proper includes. Use py::ssize_t (the pybind11 typedef) instead
to ensure cross-platform compatibility.

Fixes compilation errors on:
- Windows/MSVC 2022 (C++20)
- GCC 10 (C++20)

Co-authored-by: Cursor <cursoragent@cursor.com>
@rwgk
Copy link
Collaborator

rwgk commented Feb 2, 2026

@seberg Could you please help reviewing this (very small) PR? It looks good to me, but it'd be great to get someone more closely associated with the numpy community to take a look, too.

(Please ignore the Manylinux 3.14t CI failure. It's definitely unrelated to this PR.)

@seberg
Copy link
Contributor

seberg commented Feb 3, 2026

Looks good to me. Seems like the only question would be if there is a nicer naming/API pattern. But I can't think of a nicer alternative.

(Not sure I quite follow the motivation, for creating a new array, I would different approaches are nicer, e.g. having an empty_like())

@dbs4261
Copy link
Author

dbs4261 commented Feb 5, 2026

The actual motivation was to use them with ranges but it was easier to just describe the constructor. I would also like to see empty_like and others like it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants