@@ -50,6 +50,7 @@ High-level aspects:
5050
5151#include " pybind11_namespace_macros.h"
5252
53+ #include < cstring>
5354#include < functional>
5455#include < memory>
5556#include < stdexcept>
@@ -58,19 +59,6 @@ High-level aspects:
5859#include < typeinfo>
5960#include < utility>
6061
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-
7462PYBIND11_NAMESPACE_BEGIN (PYBIND11_NAMESPACE)
7563PYBIND11_NAMESPACE_BEGIN(memory)
7664
@@ -91,7 +79,8 @@ static constexpr bool type_has_shared_from_this(const void *) {
9179 return false ;
9280}
9381
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.
9584 std::weak_ptr<void > released_ptr; // Trick to keep the smart_holder memory footprint small.
9685 std::function<void (void *)> del_fun; // Rare case.
9786 void (*del_ptr)(void *); // Common case.
@@ -113,26 +102,33 @@ struct PYBIND11_EXPORT_GUARDED_DELETE guarded_delete {
113102 }
114103};
115104
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+
116111template <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) {
118113 std::default_delete<T>{}(static_cast <T *>(raw_ptr));
119114}
120115
121116template <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 *) {
123118 // This noop operator is needed to avoid a compilation error (for `delete raw_ptr;`), but
124119 // throwing an exception from a destructor will std::terminate the process. Therefore the
125120 // runtime check for lifetime-management correctness is implemented elsewhere (in
126121 // ensure_pointee_is_destructible()).
127122}
128123
129124template <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);
132127}
133128
134129template <typename T, typename D>
135130struct custom_deleter {
131+ // NOTE: PYBIND11_INTERNALS_VERSION needs to be bumped if changes are made to this struct.
136132 D deleter;
137133 explicit custom_deleter (D &&deleter) : deleter{std::forward<D>(deleter)} {}
138134 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) {
144140 std::function<void (void *)>(custom_deleter<T, D>(std::forward<D>(uqp_del))), armed_flag);
145141}
146142
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 ;
151154}
152155
153156struct smart_holder {
157+ // NOTE: PYBIND11_INTERNALS_VERSION needs to be bumped if changes are made to this struct.
154158 const std::type_info *rtti_uqp_del = nullptr ;
155159 std::shared_ptr<void > vptr;
156160 bool vptr_is_using_noop_deleter : 1 ;
157- bool vptr_is_using_builtin_delete : 1 ;
161+ bool vptr_is_using_std_default_delete : 1 ;
158162 bool vptr_is_external_shared_ptr : 1 ;
159163 bool is_populated : 1 ;
160164 bool is_disowned : 1 ;
@@ -166,7 +170,7 @@ struct smart_holder {
166170 smart_holder &operator =(const smart_holder &) = delete ;
167171
168172 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 },
170174 vptr_is_external_shared_ptr{false }, is_populated{false }, is_disowned{false } {}
171175
172176 bool has_pointee () const { return vptr != nullptr ; }
@@ -191,7 +195,7 @@ struct smart_holder {
191195 }
192196 }
193197
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 {
195199 if (vptr_is_external_shared_ptr) {
196200 throw std::invalid_argument (std::string (" Cannot disown external shared_ptr (" )
197201 + context + " )." );
@@ -200,24 +204,26 @@ struct smart_holder {
200204 throw std::invalid_argument (std::string (" Cannot disown non-owning holder (" ) + context
201205 + " )." );
202206 }
203- if (!vptr_is_using_builtin_delete ) {
207+ if (!vptr_is_using_std_default_delete ) {
204208 throw std::invalid_argument (std::string (" Cannot disown custom deleter (" ) + context
205209 + " )." );
206210 }
207211 }
208212
209213 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 {
212215 if (!rtti_uqp_del) {
213- if (!is_std_default_delete<T>(*rtti_requested )) {
216+ if (!uqp_del_is_std_default_delete<T, D>( )) {
214217 throw std::invalid_argument (std::string (" Missing unique_ptr deleter (" ) + context
215218 + " )." );
216219 }
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)) {
221227 throw std::invalid_argument (std::string (" Incompatible unique_ptr deleter (" ) + context
222228 + " )." );
223229 }
@@ -244,19 +250,20 @@ struct smart_holder {
244250 }
245251 }
246252
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 ) {
250256 throw std::runtime_error (
251257 " smart_holder::reset_vptr_deleter_armed_flag() called in an invalid context." );
252258 }
253- vptr_del_ptr ->armed_flag = armed_flag;
259+ gd ->armed_flag = armed_flag;
254260 }
255261
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.
257263 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);
260267 if (gd && gd->use_del_fun ) {
261268 const auto &custom_deleter_ptr = gd->del_fun .template target <custom_deleter<T, D>>();
262269 if (custom_deleter_ptr == nullptr ) {
@@ -288,28 +295,28 @@ struct smart_holder {
288295 static smart_holder from_raw_ptr_take_ownership (T *raw_ptr, bool void_cast_raw_ptr = false ) {
289296 ensure_pointee_is_destructible<T>(" from_raw_ptr_take_ownership" );
290297 smart_holder hld;
291- auto gd = make_guarded_builtin_delete <T>(true );
298+ auto gd = make_guarded_std_default_delete <T>(true );
292299 if (void_cast_raw_ptr) {
293300 hld.vptr .reset (static_cast <void *>(raw_ptr), std::move (gd));
294301 } else {
295302 hld.vptr .reset (raw_ptr, std::move (gd));
296303 }
297- hld.vptr_is_using_builtin_delete = true ;
304+ hld.vptr_is_using_std_default_delete = true ;
298305 hld.is_populated = true ;
299306 return hld;
300307 }
301308
302309 // Caller is responsible for ensuring the complex preconditions
303310 // (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 );
306313 is_disowned = true ;
307314 }
308315
309316 // Caller is responsible for ensuring the complex preconditions
310317 // (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 );
313320 is_disowned = false ;
314321 }
315322
@@ -319,14 +326,14 @@ struct smart_holder {
319326
320327 void ensure_can_release_ownership (const char *context = " ensure_can_release_ownership" ) const {
321328 ensure_is_not_disowned (context);
322- ensure_vptr_is_using_builtin_delete (context);
329+ ensure_vptr_is_using_std_default_delete (context);
323330 ensure_use_count_1 (context);
324331 }
325332
326333 // Caller is responsible for ensuring the complex preconditions
327334 // (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 );
330337 release_disowned ();
331338 }
332339
@@ -335,10 +342,10 @@ struct smart_holder {
335342 void *void_ptr = nullptr ) {
336343 smart_holder hld;
337344 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>( );
339346 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 );
342349 } else {
343350 gd = make_guarded_custom_deleter<T, D>(std::move (unq_ptr.get_deleter ()), true );
344351 }
0 commit comments