Skip to content
This repository was archived by the owner on Nov 1, 2023. It is now read-only.

Commit 7022310

Browse files
authored
Merge pull request #44 from robotpy/fixed-size-span
Add support for fixed/dynamic sized spans
2 parents 60de173 + 9be6bdd commit 7022310

File tree

3 files changed

+89
-7
lines changed

3 files changed

+89
-7
lines changed

tests/cpp/wpiutil_test/module.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ std::span<std::vector<std::string>> load_span_vector(std::span<std::vector<std::
5252
return ref;
5353
}
5454

55+
std::span<const double, 3> load_span_fixed_double(std::span<const double, 3> ref) {
56+
return ref;
57+
}
58+
59+
5560
std::span<int> cast_span() {
5661
static std::vector<int> vec{1, 2, 3};
5762
return vec;
@@ -148,6 +153,7 @@ RPYBUILD_PYBIND11_MODULE(m) {
148153
// span
149154
m.def("load_span_int", &load_span_int);
150155
m.def("load_span_bool", &load_span_bool);
156+
m.def("load_span_fixed_double", &load_span_fixed_double);
151157
m.def("load_span_string", &load_span_string);
152158
m.def("load_span_string_const", &load_span_string_const);
153159
m.def("load_span_string_view", &load_span_string_view);

tests/test_span.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,10 @@ def test_span_cast():
6666

6767
def test_string_span():
6868
assert module.cast_string_span() == ["hi", "there"]
69+
70+
71+
def test_fixed_double_span():
72+
assert module.load_span_fixed_double([1, 2, 3]) == (1, 2, 3)
73+
74+
with pytest.raises(TypeError):
75+
assert module.load_span_fixed_double([1, 2, 3, 4])

wpiutil/src/type_casters/wpi_span_type_caster.h

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,77 @@
1010
namespace pybind11 {
1111
namespace detail {
1212

13-
template <typename Type> struct type_caster<std::span<Type>> {
13+
template <size_t N>
14+
struct span_name_maker {
15+
template <typename T>
16+
static constexpr auto make(const T &t) {
17+
return concat(t, span_name_maker<N-1>::make(t));
18+
}
19+
};
20+
21+
template <>
22+
struct span_name_maker<1> {
23+
template <typename T>
24+
static constexpr auto make(const T &t) {
25+
return t;
26+
}
27+
};
28+
29+
// span with fixed size converts to a tuple
30+
template <typename Type, size_t Extent> struct type_caster<std::span<Type, Extent>> {
31+
using span_type = typename std::span<Type, Extent>;
32+
using value_conv = make_caster<Type>;
33+
using value_type = typename std::remove_cv<Type>::type;
34+
35+
value_type backing_array[Extent] = {};
36+
37+
PYBIND11_TYPE_CASTER(span_type, _("Tuple[") + span_name_maker<Extent>::make(value_conv::name) + _("]"));
38+
39+
type_caster() : value(backing_array) {}
40+
41+
bool load(handle src, bool convert) {
42+
if (!isinstance<sequence>(src) || isinstance<str>(src))
43+
return false;
44+
auto s = reinterpret_borrow<sequence>(src);
45+
if (s.size() != Extent)
46+
return false;
47+
size_t i = 0;
48+
for (auto it : s) {
49+
value_conv conv;
50+
if (!conv.load(it, convert))
51+
return false;
52+
backing_array[i] = cast_op<Type &&>(std::move(conv));
53+
i++;
54+
}
55+
return true;
56+
}
57+
58+
public:
59+
template <typename T>
60+
static handle cast(T &&src, return_value_policy policy, handle parent) {
61+
if (!std::is_lvalue_reference<T>::value)
62+
policy = return_value_policy_override<Type>::policy(policy);
63+
tuple l(Extent);
64+
size_t index = 0;
65+
for (auto &&value : src) {
66+
auto value_ = reinterpret_steal<object>(
67+
value_conv::cast(forward_like<T>(value), policy, parent));
68+
if (!value_)
69+
return handle();
70+
PyTuple_SET_ITEM(l.ptr(), (ssize_t)index++,
71+
value_.release().ptr()); // steals a reference
72+
}
73+
return l.release();
74+
}
75+
};
76+
77+
78+
// span with dynamic extent
79+
template <typename Type> struct type_caster<std::span<Type, std::dynamic_extent>> {
80+
using span_type = typename std::span<Type, std::dynamic_extent>;
1481
using value_conv = make_caster<Type>;
1582
using value_type = typename std::remove_cv<Type>::type;
16-
PYBIND11_TYPE_CASTER(std::span<Type>, _("List[") + value_conv::name + _("]"));
83+
PYBIND11_TYPE_CASTER(span_type, _("List[") + value_conv::name + _("]"));
1784

1885
wpi::SmallVector<value_type, 32> vec;
1986
bool load(handle src, bool convert) {
@@ -27,7 +94,7 @@ template <typename Type> struct type_caster<std::span<Type>> {
2794
return false;
2895
vec.push_back(cast_op<Type &&>(std::move(conv)));
2996
}
30-
value = std::span<Type>(std::data(vec), std::size(vec));
97+
value = span_type(std::data(vec), std::size(vec));
3198
return true;
3299
}
33100

@@ -51,8 +118,9 @@ template <typename Type> struct type_caster<std::span<Type>> {
51118
};
52119

53120
// span specialization: accepts any readonly buffers
54-
template <> struct type_caster<std::span<const uint8_t>> {
55-
PYBIND11_TYPE_CASTER(std::span<const uint8_t>, _("buffer"));
121+
template <> struct type_caster<std::span<const uint8_t, std::dynamic_extent>> {
122+
using span_type = typename std::span<const uint8_t, std::dynamic_extent>;
123+
PYBIND11_TYPE_CASTER(span_type, _("buffer"));
56124

57125
bool load(handle src, bool convert) {
58126
if (!isinstance<buffer>(src))
@@ -63,7 +131,7 @@ template <> struct type_caster<std::span<const uint8_t>> {
63131
return false;
64132
}
65133

66-
value = std::span<const uint8_t>((const uint8_t*)req.ptr, req.size*req.itemsize);
134+
value = span_type((const uint8_t*)req.ptr, req.size*req.itemsize);
67135
return true;
68136
}
69137

@@ -75,7 +143,8 @@ template <> struct type_caster<std::span<const uint8_t>> {
75143
};
76144

77145
// span specialization: writeable buffer
78-
template <> struct type_caster<std::span<uint8_t>> {
146+
template <> struct type_caster<std::span<uint8_t, std::dynamic_extent>> {
147+
using span_type = typename std::span<const uint8_t, std::dynamic_extent>;
79148
PYBIND11_TYPE_CASTER(std::span<uint8_t>, _("buffer"));
80149

81150
bool load(handle src, bool convert) {

0 commit comments

Comments
 (0)