Files
pybind11/tests/pure_cpp/smart_holder_poc.h
Ralf W. Grosse-Kunstleve 365d41a4ba Eliminate cross-DSO RTTI reliance in smart_holder functionality (for platforms like macOS). (#5728)
* Revert PR #5700 production code change (pybind11/detail/struct_smart_holder.h).

```
git checkout b19489145b2c7a117138632d624809dfb3b380bb~1 include/pybind11/detail/struct_smart_holder.h
```

* Introduce `get_internals().get_memory_guarded_delete()`

* [skip ci] Only pass around `memory::get_guarded_delete` function pointer.

* [skip ci] Change a variable name for internal consistency. Add 3 x NOTE: PYBIND11_INTERNALS_VERSION needs to be bumped if changes are made to this struct.

* Add comment: get_internals().get_memory_guarded_delete does not need with_internals()

* Traverse all DSOs to find memory::guarded_delete with matching RTTI.

* Add nullptr check to dynamic_cast overload.

Suggested by ChatGPT for these reasons:

* Prevents runtime RTTI lookups on nullptr.

* Helps avoid undefined behavior if users pass in nulls from failed casts or optional paths.

* Ensures consistent return value semantics and no accidental access to vtable.

* Improve smart_holder unique_ptr deleter compatibility checks across DSOs:

* Replace RTTI-based detection of std::default_delete<T> with a constexpr check to avoid RTTI reliance

* Add type_info_equal_across_dso_boundaries() fallback using type_info::name() for RTTI equality across macOS DSOs

* Rename related flags and functions for clarity (e.g., builtin → std_default)

* Improves ABI robustness and clarity of ownership checks in smart_holder

* Trivial renaming for internal consistency: builtin_delete → std_default_delete

* Add get_trampoline_self_life_support to detail::type_info (passes local testing).

* Polish previous commit slightly.

* [skip ci] Store memory::get_guarded_delete in `detail::type_info` instead of `detail::internals` (no searching across DSOs required).

* Revert change suggested by ChatGPT. After double-checking, ChatGPT agrees this isn't needed.

* Minor polishing.
2025-06-17 12:14:50 -07:00

57 lines
2.0 KiB
C++

// Copyright (c) 2020-2024 The Pybind Development Team.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#pragma once
#include "pybind11/detail/struct_smart_holder.h"
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(memory)
PYBIND11_NAMESPACE_BEGIN(smart_holder_poc) // Proof-of-Concept implementations.
// NOLINTNEXTLINE(bugprone-incorrect-enable-shared-from-this)
struct PrivateESFT : private std::enable_shared_from_this<PrivateESFT> {};
static_assert(!type_has_shared_from_this(static_cast<PrivateESFT *>(nullptr)),
"should detect inaccessible base");
template <typename T>
T &as_lvalue_ref(const smart_holder &hld) {
static const char *context = "as_lvalue_ref";
hld.ensure_is_populated(context);
hld.ensure_has_pointee(context);
return *hld.as_raw_ptr_unowned<T>();
}
template <typename T>
T &&as_rvalue_ref(const smart_holder &hld) {
static const char *context = "as_rvalue_ref";
hld.ensure_is_populated(context);
hld.ensure_has_pointee(context);
return std::move(*hld.as_raw_ptr_unowned<T>());
}
template <typename T>
T *as_raw_ptr_release_ownership(smart_holder &hld,
const char *context = "as_raw_ptr_release_ownership") {
hld.ensure_can_release_ownership(context);
T *raw_ptr = hld.as_raw_ptr_unowned<T>();
hld.release_ownership(get_guarded_delete);
return raw_ptr;
}
template <typename T, typename D = std::default_delete<T>>
std::unique_ptr<T, D> as_unique_ptr(smart_holder &hld) {
static const char *context = "as_unique_ptr";
hld.ensure_compatible_uqp_del<T, D>(context);
hld.ensure_use_count_1(context);
T *raw_ptr = hld.as_raw_ptr_unowned<T>();
hld.release_ownership(get_guarded_delete);
// KNOWN DEFECT (see PR #4850): Does not copy the deleter.
return std::unique_ptr<T, D>(raw_ptr);
}
PYBIND11_NAMESPACE_END(smart_holder_poc)
PYBIND11_NAMESPACE_END(memory)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)