@@ -111,6 +111,53 @@ PYBIND11_NOINLINE inline internals &get_internals() {
111
111
return *internals_ptr;
112
112
}
113
113
114
+ // / A life support system for temporary objects created by `type_caster::load()`.
115
+ // / Adding a patient will keep it alive up until the enclosing function returns.
116
+ class loader_life_support {
117
+ public:
118
+ // / A new patient frame is created when a function is entered
119
+ loader_life_support () {
120
+ get_internals ().loader_patient_stack .push_back (nullptr );
121
+ }
122
+
123
+ // / ... and destroyed after it returns
124
+ ~loader_life_support () {
125
+ auto &stack = get_internals ().loader_patient_stack ;
126
+ if (stack.empty ())
127
+ pybind11_fail (" loader_life_support: internal error" );
128
+
129
+ auto ptr = stack.back ();
130
+ stack.pop_back ();
131
+ Py_CLEAR (ptr);
132
+
133
+ // A heuristic to reduce the stack's capacity (e.g. after long recursive calls)
134
+ if (stack.capacity () > 16 && stack.size () != 0 && stack.capacity () / stack.size () > 2 )
135
+ stack.shrink_to_fit ();
136
+ }
137
+
138
+ // / This can only be used inside a pybind11-bound function, either by `argument_loader`
139
+ // / at argument preparation time or by `py::cast()` at execution time.
140
+ PYBIND11_NOINLINE static void add_patient (handle h) {
141
+ auto &stack = get_internals ().loader_patient_stack ;
142
+ if (stack.empty ())
143
+ throw cast_error (" When called outside a bound function, py::cast() cannot "
144
+ " do Python -> C++ conversions which require the creation "
145
+ " of temporary values" );
146
+
147
+ auto &list_ptr = stack.back ();
148
+ if (list_ptr == nullptr ) {
149
+ list_ptr = PyList_New (1 );
150
+ if (!list_ptr)
151
+ pybind11_fail (" loader_life_support: error allocating list" );
152
+ PyList_SET_ITEM (list_ptr, 0 , h.inc_ref ().ptr ());
153
+ } else {
154
+ auto result = PyList_Append (list_ptr, h.ptr ());
155
+ if (result == -1 )
156
+ pybind11_fail (" loader_life_support: error adding patient" );
157
+ }
158
+ }
159
+ };
160
+
114
161
// Gets the cache entry for the given type, creating it if necessary. The return value is the pair
115
162
// returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was
116
163
// just created.
@@ -643,9 +690,11 @@ class type_caster_generic {
643
690
// Perform an implicit conversion
644
691
if (convert) {
645
692
for (auto &converter : typeinfo->implicit_conversions ) {
646
- temp = reinterpret_steal<object>(converter (src.ptr (), typeinfo->type ));
647
- if (load_impl<ThisT>(temp, false ))
693
+ auto temp = reinterpret_steal<object>(converter (src.ptr (), typeinfo->type ));
694
+ if (load_impl<ThisT>(temp, false )) {
695
+ loader_life_support::add_patient (temp);
648
696
return true ;
697
+ }
649
698
}
650
699
if (this_.try_direct_conversions (src))
651
700
return true ;
@@ -674,7 +723,6 @@ class type_caster_generic {
674
723
675
724
const type_info *typeinfo = nullptr ;
676
725
void *value = nullptr ;
677
- object temp;
678
726
};
679
727
680
728
/* *
@@ -1064,7 +1112,7 @@ template <typename StringType, bool IsView = false> struct string_caster {
1064
1112
1065
1113
// If we're loading a string_view we need to keep the encoded Python object alive:
1066
1114
if (IsView)
1067
- view_into = std::move (utfNbytes);
1115
+ loader_life_support::add_patient (utfNbytes);
1068
1116
1069
1117
return true ;
1070
1118
}
@@ -1080,8 +1128,6 @@ template <typename StringType, bool IsView = false> struct string_caster {
1080
1128
PYBIND11_TYPE_CASTER (StringType, _(PYBIND11_STRING_NAME));
1081
1129
1082
1130
private:
1083
- object view_into;
1084
-
1085
1131
static handle decode_utfN (const char *buffer, ssize_t nbytes) {
1086
1132
#if !defined(PYPY_VERSION)
1087
1133
return
@@ -1336,7 +1382,6 @@ struct copyable_holder_caster : public type_caster_base<type> {
1336
1382
using base::cast;
1337
1383
using base::typeinfo;
1338
1384
using base::value;
1339
- using base::temp;
1340
1385
1341
1386
bool load (handle src, bool convert) {
1342
1387
return base::template load_impl<copyable_holder_caster<type, holder_type>>(src, convert);
0 commit comments