Skip to content

Commit a1d0091

Browse files
authored
1 parent bd5951b commit a1d0091

File tree

3 files changed

+98
-18
lines changed

3 files changed

+98
-18
lines changed

include/pybind11/stl.h

+74-18
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@
1111

1212
#include "pybind11.h"
1313
#include "detail/common.h"
14+
#include "detail/descr.h"
15+
#include "detail/type_caster_base.h"
1416

1517
#include <deque>
1618
#include <initializer_list>
1719
#include <list>
1820
#include <map>
21+
#include <memory>
1922
#include <ostream>
2023
#include <set>
2124
#include <unordered_map>
@@ -349,36 +352,65 @@ struct type_caster<std::deque<Type, Alloc>> : list_caster<std::deque<Type, Alloc
349352
template <typename Type, typename Alloc>
350353
struct type_caster<std::list<Type, Alloc>> : list_caster<std::list<Type, Alloc>, Type> {};
351354

355+
template <typename ArrayType, typename V, size_t... I>
356+
ArrayType vector_to_array_impl(V &&v, index_sequence<I...>) {
357+
return {{std::move(v[I])...}};
358+
}
359+
360+
// Based on https://en.cppreference.com/w/cpp/container/array/to_array
361+
template <typename ArrayType, size_t N, typename V>
362+
ArrayType vector_to_array(V &&v) {
363+
return vector_to_array_impl<ArrayType, V>(std::forward<V>(v), make_index_sequence<N>{});
364+
}
365+
352366
template <typename ArrayType, typename Value, bool Resizable, size_t Size = 0>
353367
struct array_caster {
354368
using value_conv = make_caster<Value>;
355369

356370
private:
357-
template <bool R = Resizable>
358-
bool require_size(enable_if_t<R, size_t> size) {
359-
if (value.size() != size) {
360-
value.resize(size);
371+
std::unique_ptr<ArrayType> value;
372+
373+
template <bool R = Resizable, enable_if_t<R, int> = 0>
374+
bool convert_elements(handle seq, bool convert) {
375+
auto l = reinterpret_borrow<sequence>(seq);
376+
value.reset(new ArrayType{});
377+
// Using `resize` to preserve the behavior exactly as it was before PR #5305
378+
// For the `resize` to work, `Value` must be default constructible.
379+
// For `std::valarray`, this is a requirement:
380+
// https://en.cppreference.com/w/cpp/named_req/NumericType
381+
value->resize(l.size());
382+
size_t ctr = 0;
383+
for (const auto &it : l) {
384+
value_conv conv;
385+
if (!conv.load(it, convert)) {
386+
return false;
387+
}
388+
(*value)[ctr++] = cast_op<Value &&>(std::move(conv));
361389
}
362390
return true;
363391
}
364-
template <bool R = Resizable>
365-
bool require_size(enable_if_t<!R, size_t> size) {
366-
return size == Size;
367-
}
368392

393+
template <bool R = Resizable, enable_if_t<!R, int> = 0>
369394
bool convert_elements(handle seq, bool convert) {
370395
auto l = reinterpret_borrow<sequence>(seq);
371-
if (!require_size(l.size())) {
396+
if (l.size() != Size) {
372397
return false;
373398
}
374-
size_t ctr = 0;
375-
for (const auto &it : l) {
399+
// The `temp` storage is needed to support `Value` types that are not
400+
// default-constructible.
401+
// Deliberate choice: no template specializations, for simplicity, and
402+
// because the compile time overhead for the specializations is deemed
403+
// more significant than the runtime overhead for the `temp` storage.
404+
std::vector<Value> temp;
405+
temp.reserve(l.size());
406+
for (auto it : l) {
376407
value_conv conv;
377408
if (!conv.load(it, convert)) {
378409
return false;
379410
}
380-
value[ctr++] = cast_op<Value &&>(std::move(conv));
411+
temp.emplace_back(cast_op<Value &&>(std::move(conv)));
381412
}
413+
value.reset(new ArrayType(vector_to_array<ArrayType, Size>(std::move(temp))));
382414
return true;
383415
}
384416

@@ -416,12 +448,36 @@ struct array_caster {
416448
return l.release();
417449
}
418450

419-
PYBIND11_TYPE_CASTER(ArrayType,
420-
const_name<Resizable>(const_name(""), const_name("Annotated["))
421-
+ const_name("list[") + value_conv::name + const_name("]")
422-
+ const_name<Resizable>(const_name(""),
423-
const_name(", FixedSize(")
424-
+ const_name<Size>() + const_name(")]")));
451+
// Code copied from PYBIND11_TYPE_CASTER macro.
452+
// Intentionally preserving the behavior exactly as it was before PR #5305
453+
template <typename T_, enable_if_t<std::is_same<ArrayType, remove_cv_t<T_>>::value, int> = 0>
454+
static handle cast(T_ *src, return_value_policy policy, handle parent) {
455+
if (!src) {
456+
return none().release();
457+
}
458+
if (policy == return_value_policy::take_ownership) {
459+
auto h = cast(std::move(*src), policy, parent);
460+
delete src; // WARNING: Assumes `src` was allocated with `new`.
461+
return h;
462+
}
463+
return cast(*src, policy, parent);
464+
}
465+
466+
// NOLINTNEXTLINE(google-explicit-constructor)
467+
operator ArrayType *() { return &(*value); }
468+
// NOLINTNEXTLINE(google-explicit-constructor)
469+
operator ArrayType &() { return *value; }
470+
// NOLINTNEXTLINE(google-explicit-constructor)
471+
operator ArrayType &&() && { return std::move(*value); }
472+
473+
template <typename T_>
474+
using cast_op_type = movable_cast_op_type<T_>;
475+
476+
static constexpr auto name
477+
= const_name<Resizable>(const_name(""), const_name("Annotated[")) + const_name("list[")
478+
+ value_conv::name + const_name("]")
479+
+ const_name<Resizable>(
480+
const_name(""), const_name(", FixedSize(") + const_name<Size>() + const_name(")]"));
425481
};
426482

427483
template <typename Type, size_t Size>

tests/test_stl.cpp

+17
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,23 @@ TEST_SUBMODULE(stl, m) {
201201
m.def("cast_array", []() { return std::array<int, 2>{{1, 2}}; });
202202
m.def("load_array", [](const std::array<int, 2> &a) { return a[0] == 1 && a[1] == 2; });
203203

204+
struct NoDefaultCtor {
205+
explicit constexpr NoDefaultCtor(int val) : val{val} {}
206+
int val;
207+
};
208+
209+
struct NoDefaultCtorArray {
210+
explicit constexpr NoDefaultCtorArray(int i)
211+
: arr{{NoDefaultCtor(10 + i), NoDefaultCtor(20 + i)}} {}
212+
std::array<NoDefaultCtor, 2> arr;
213+
};
214+
215+
// test_array_no_default_ctor
216+
py::class_<NoDefaultCtor>(m, "NoDefaultCtor").def_readonly("val", &NoDefaultCtor::val);
217+
py::class_<NoDefaultCtorArray>(m, "NoDefaultCtorArray")
218+
.def(py::init<int>())
219+
.def_readwrite("arr", &NoDefaultCtorArray::arr);
220+
204221
// test_valarray
205222
m.def("cast_valarray", []() { return std::valarray<int>{1, 4, 9}; });
206223
m.def("load_valarray", [](const std::valarray<int> &v) {

tests/test_stl.py

+7
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ def test_array(doc):
4848
)
4949

5050

51+
def test_array_no_default_ctor():
52+
lst = m.NoDefaultCtorArray(3)
53+
assert [e.val for e in lst.arr] == [13, 23]
54+
lst.arr = m.NoDefaultCtorArray(4).arr
55+
assert [e.val for e in lst.arr] == [14, 24]
56+
57+
5158
def test_valarray(doc):
5259
"""std::valarray <-> list"""
5360
lst = m.cast_valarray()

0 commit comments

Comments
 (0)