@@ -50,6 +50,7 @@ High-level aspects:
50
50
51
51
#include " pybind11_namespace_macros.h"
52
52
53
+ #include < cstring>
53
54
#include < functional>
54
55
#include < memory>
55
56
#include < stdexcept>
@@ -58,19 +59,6 @@ High-level aspects:
58
59
#include < typeinfo>
59
60
#include < utility>
60
61
61
- // IMPORTANT: This code block must stay BELOW the #include <stdexcept> above.
62
- // This is only required on some builds with libc++ (one of three implementations
63
- // in
64
- // https://github.com/llvm/llvm-project/blob/a9b64bb3180dab6d28bf800a641f9a9ad54d2c0c/libcxx/include/typeinfo#L271-L276
65
- // require it)
66
- #if !defined(PYBIND11_EXPORT_GUARDED_DELETE)
67
- # if defined(_LIBCPP_VERSION) && !defined(WIN32) && !defined(_WIN32)
68
- # define PYBIND11_EXPORT_GUARDED_DELETE __attribute__ ((visibility(" default" )))
69
- # else
70
- # define PYBIND11_EXPORT_GUARDED_DELETE
71
- # endif
72
- #endif
73
-
74
62
PYBIND11_NAMESPACE_BEGIN (PYBIND11_NAMESPACE)
75
63
PYBIND11_NAMESPACE_BEGIN(memory)
76
64
@@ -91,7 +79,8 @@ static constexpr bool type_has_shared_from_this(const void *) {
91
79
return false ;
92
80
}
93
81
94
- struct PYBIND11_EXPORT_GUARDED_DELETE guarded_delete {
82
+ struct guarded_delete {
83
+ // NOTE: PYBIND11_INTERNALS_VERSION needs to be bumped if changes are made to this struct.
95
84
std::weak_ptr<void > released_ptr; // Trick to keep the smart_holder memory footprint small.
96
85
std::function<void (void *)> del_fun; // Rare case.
97
86
void (*del_ptr)(void *); // Common case.
@@ -113,26 +102,33 @@ struct PYBIND11_EXPORT_GUARDED_DELETE guarded_delete {
113
102
}
114
103
};
115
104
105
+ inline guarded_delete *get_guarded_delete (const std::shared_ptr<void > &ptr) {
106
+ return std::get_deleter<guarded_delete>(ptr);
107
+ }
108
+
109
+ using get_guarded_delete_fn = guarded_delete *(*) (const std::shared_ptr<void > &);
110
+
116
111
template <typename T, typename std::enable_if<std::is_destructible<T>::value, int >::type = 0 >
117
- inline void builtin_delete_if_destructible (void *raw_ptr) {
112
+ inline void std_default_delete_if_destructible (void *raw_ptr) {
118
113
std::default_delete<T>{}(static_cast <T *>(raw_ptr));
119
114
}
120
115
121
116
template <typename T, typename std::enable_if<!std::is_destructible<T>::value, int >::type = 0 >
122
- inline void builtin_delete_if_destructible (void *) {
117
+ inline void std_default_delete_if_destructible (void *) {
123
118
// This noop operator is needed to avoid a compilation error (for `delete raw_ptr;`), but
124
119
// throwing an exception from a destructor will std::terminate the process. Therefore the
125
120
// runtime check for lifetime-management correctness is implemented elsewhere (in
126
121
// ensure_pointee_is_destructible()).
127
122
}
128
123
129
124
template <typename T>
130
- guarded_delete make_guarded_builtin_delete (bool armed_flag) {
131
- return guarded_delete (builtin_delete_if_destructible <T>, armed_flag);
125
+ guarded_delete make_guarded_std_default_delete (bool armed_flag) {
126
+ return guarded_delete (std_default_delete_if_destructible <T>, armed_flag);
132
127
}
133
128
134
129
template <typename T, typename D>
135
130
struct custom_deleter {
131
+ // NOTE: PYBIND11_INTERNALS_VERSION needs to be bumped if changes are made to this struct.
136
132
D deleter;
137
133
explicit custom_deleter (D &&deleter) : deleter{std::forward<D>(deleter)} {}
138
134
void operator ()(void *raw_ptr) { deleter (static_cast <T *>(raw_ptr)); }
@@ -144,17 +140,25 @@ guarded_delete make_guarded_custom_deleter(D &&uqp_del, bool armed_flag) {
144
140
std::function<void (void *)>(custom_deleter<T, D>(std::forward<D>(uqp_del))), armed_flag);
145
141
}
146
142
147
- template <typename T>
148
- inline bool is_std_default_delete (const std::type_info &rtti_deleter) {
149
- return rtti_deleter == typeid (std::default_delete<T>)
150
- || rtti_deleter == typeid (std::default_delete<T const >);
143
+ template <typename T, typename D>
144
+ constexpr bool uqp_del_is_std_default_delete () {
145
+ return std::is_same<D, std::default_delete<T>>::value
146
+ || std::is_same<D, std::default_delete<T const >>::value;
147
+ }
148
+
149
+ inline bool type_info_equal_across_dso_boundaries (const std::type_info &a,
150
+ const std::type_info &b) {
151
+ // RTTI pointer comparison may fail across DSOs (e.g., macOS libc++).
152
+ // Fallback to name comparison, which is generally safe and ABI-stable enough for our use.
153
+ return a == b || std::strcmp (a.name (), b.name ()) == 0 ;
151
154
}
152
155
153
156
struct smart_holder {
157
+ // NOTE: PYBIND11_INTERNALS_VERSION needs to be bumped if changes are made to this struct.
154
158
const std::type_info *rtti_uqp_del = nullptr ;
155
159
std::shared_ptr<void > vptr;
156
160
bool vptr_is_using_noop_deleter : 1 ;
157
- bool vptr_is_using_builtin_delete : 1 ;
161
+ bool vptr_is_using_std_default_delete : 1 ;
158
162
bool vptr_is_external_shared_ptr : 1 ;
159
163
bool is_populated : 1 ;
160
164
bool is_disowned : 1 ;
@@ -166,7 +170,7 @@ struct smart_holder {
166
170
smart_holder &operator =(const smart_holder &) = delete ;
167
171
168
172
smart_holder ()
169
- : vptr_is_using_noop_deleter{false }, vptr_is_using_builtin_delete {false },
173
+ : vptr_is_using_noop_deleter{false }, vptr_is_using_std_default_delete {false },
170
174
vptr_is_external_shared_ptr{false }, is_populated{false }, is_disowned{false } {}
171
175
172
176
bool has_pointee () const { return vptr != nullptr ; }
@@ -191,7 +195,7 @@ struct smart_holder {
191
195
}
192
196
}
193
197
194
- void ensure_vptr_is_using_builtin_delete (const char *context) const {
198
+ void ensure_vptr_is_using_std_default_delete (const char *context) const {
195
199
if (vptr_is_external_shared_ptr) {
196
200
throw std::invalid_argument (std::string (" Cannot disown external shared_ptr (" )
197
201
+ context + " )." );
@@ -200,24 +204,26 @@ struct smart_holder {
200
204
throw std::invalid_argument (std::string (" Cannot disown non-owning holder (" ) + context
201
205
+ " )." );
202
206
}
203
- if (!vptr_is_using_builtin_delete ) {
207
+ if (!vptr_is_using_std_default_delete ) {
204
208
throw std::invalid_argument (std::string (" Cannot disown custom deleter (" ) + context
205
209
+ " )." );
206
210
}
207
211
}
208
212
209
213
template <typename T, typename D>
210
- void ensure_compatible_rtti_uqp_del (const char *context) const {
211
- const std::type_info *rtti_requested = &typeid (D);
214
+ void ensure_compatible_uqp_del (const char *context) const {
212
215
if (!rtti_uqp_del) {
213
- if (!is_std_default_delete<T>(*rtti_requested )) {
216
+ if (!uqp_del_is_std_default_delete<T, D>( )) {
214
217
throw std::invalid_argument (std::string (" Missing unique_ptr deleter (" ) + context
215
218
+ " )." );
216
219
}
217
- ensure_vptr_is_using_builtin_delete (context);
218
- } else if (!(*rtti_requested == *rtti_uqp_del)
219
- && !(vptr_is_using_builtin_delete
220
- && is_std_default_delete<T>(*rtti_requested))) {
220
+ ensure_vptr_is_using_std_default_delete (context);
221
+ return ;
222
+ }
223
+ if (uqp_del_is_std_default_delete<T, D>() && vptr_is_using_std_default_delete) {
224
+ return ;
225
+ }
226
+ if (!type_info_equal_across_dso_boundaries (typeid (D), *rtti_uqp_del)) {
221
227
throw std::invalid_argument (std::string (" Incompatible unique_ptr deleter (" ) + context
222
228
+ " )." );
223
229
}
@@ -244,19 +250,20 @@ struct smart_holder {
244
250
}
245
251
}
246
252
247
- void reset_vptr_deleter_armed_flag (bool armed_flag) const {
248
- auto *vptr_del_ptr = std::get_deleter<guarded_delete> (vptr);
249
- if (vptr_del_ptr == nullptr ) {
253
+ void reset_vptr_deleter_armed_flag (const get_guarded_delete_fn ggd_fn, bool armed_flag) const {
254
+ auto *gd = ggd_fn (vptr);
255
+ if (gd == nullptr ) {
250
256
throw std::runtime_error (
251
257
" smart_holder::reset_vptr_deleter_armed_flag() called in an invalid context." );
252
258
}
253
- vptr_del_ptr ->armed_flag = armed_flag;
259
+ gd ->armed_flag = armed_flag;
254
260
}
255
261
256
- // Caller is responsible for precondition: ensure_compatible_rtti_uqp_del <T, D>() must succeed.
262
+ // Caller is responsible for precondition: ensure_compatible_uqp_del <T, D>() must succeed.
257
263
template <typename T, typename D>
258
- std::unique_ptr<D> extract_deleter (const char *context) const {
259
- const auto *gd = std::get_deleter<guarded_delete>(vptr);
264
+ std::unique_ptr<D> extract_deleter (const char *context,
265
+ const get_guarded_delete_fn ggd_fn) const {
266
+ auto *gd = ggd_fn (vptr);
260
267
if (gd && gd->use_del_fun ) {
261
268
const auto &custom_deleter_ptr = gd->del_fun .template target <custom_deleter<T, D>>();
262
269
if (custom_deleter_ptr == nullptr ) {
@@ -288,28 +295,28 @@ struct smart_holder {
288
295
static smart_holder from_raw_ptr_take_ownership (T *raw_ptr, bool void_cast_raw_ptr = false ) {
289
296
ensure_pointee_is_destructible<T>(" from_raw_ptr_take_ownership" );
290
297
smart_holder hld;
291
- auto gd = make_guarded_builtin_delete <T>(true );
298
+ auto gd = make_guarded_std_default_delete <T>(true );
292
299
if (void_cast_raw_ptr) {
293
300
hld.vptr .reset (static_cast <void *>(raw_ptr), std::move (gd));
294
301
} else {
295
302
hld.vptr .reset (raw_ptr, std::move (gd));
296
303
}
297
- hld.vptr_is_using_builtin_delete = true ;
304
+ hld.vptr_is_using_std_default_delete = true ;
298
305
hld.is_populated = true ;
299
306
return hld;
300
307
}
301
308
302
309
// Caller is responsible for ensuring the complex preconditions
303
310
// (see `smart_holder_type_caster_support::load_helper`).
304
- void disown () {
305
- reset_vptr_deleter_armed_flag (false );
311
+ void disown (const get_guarded_delete_fn ggd_fn ) {
312
+ reset_vptr_deleter_armed_flag (ggd_fn, false );
306
313
is_disowned = true ;
307
314
}
308
315
309
316
// Caller is responsible for ensuring the complex preconditions
310
317
// (see `smart_holder_type_caster_support::load_helper`).
311
- void reclaim_disowned () {
312
- reset_vptr_deleter_armed_flag (true );
318
+ void reclaim_disowned (const get_guarded_delete_fn ggd_fn ) {
319
+ reset_vptr_deleter_armed_flag (ggd_fn, true );
313
320
is_disowned = false ;
314
321
}
315
322
@@ -319,14 +326,14 @@ struct smart_holder {
319
326
320
327
void ensure_can_release_ownership (const char *context = " ensure_can_release_ownership" ) const {
321
328
ensure_is_not_disowned (context);
322
- ensure_vptr_is_using_builtin_delete (context);
329
+ ensure_vptr_is_using_std_default_delete (context);
323
330
ensure_use_count_1 (context);
324
331
}
325
332
326
333
// Caller is responsible for ensuring the complex preconditions
327
334
// (see `smart_holder_type_caster_support::load_helper`).
328
- void release_ownership () {
329
- reset_vptr_deleter_armed_flag (false );
335
+ void release_ownership (const get_guarded_delete_fn ggd_fn ) {
336
+ reset_vptr_deleter_armed_flag (ggd_fn, false );
330
337
release_disowned ();
331
338
}
332
339
@@ -335,10 +342,10 @@ struct smart_holder {
335
342
void *void_ptr = nullptr ) {
336
343
smart_holder hld;
337
344
hld.rtti_uqp_del = &typeid (D);
338
- hld.vptr_is_using_builtin_delete = is_std_default_delete<T>(*hld. rtti_uqp_del );
345
+ hld.vptr_is_using_std_default_delete = uqp_del_is_std_default_delete<T, D>( );
339
346
guarded_delete gd{nullptr , false };
340
- if (hld.vptr_is_using_builtin_delete ) {
341
- gd = make_guarded_builtin_delete <T>(true );
347
+ if (hld.vptr_is_using_std_default_delete ) {
348
+ gd = make_guarded_std_default_delete <T>(true );
342
349
} else {
343
350
gd = make_guarded_custom_deleter<T, D>(std::move (unq_ptr.get_deleter ()), true );
344
351
}
0 commit comments