mirror of
https://github.com/pybind/pybind11.git
synced 2026-04-22 15:59:12 +00:00
Fix build failure when shared_ptr<T> points to private std::enable_shared_from_this base (#5590)
* Squashed private_esft/manuscript — 3f2b1201b830d9e431448bd8f5fe577afaa02dbf — 2025-03-30 12:38:30 -0700 [Browse private_esft/manuscript tree](3f2b1201b8) [Browse private_esft/manuscript commits](3f2b1201b8/) * Remove mention of ChatGPT Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com> --------- Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
This commit is contained in:
committed by
GitHub
parent
e7e5d6e5bb
commit
8726ed22d8
@@ -62,13 +62,23 @@ Details:
|
|||||||
namespace pybindit {
|
namespace pybindit {
|
||||||
namespace memory {
|
namespace memory {
|
||||||
|
|
||||||
|
// Default fallback.
|
||||||
static constexpr bool type_has_shared_from_this(...) { return false; }
|
static constexpr bool type_has_shared_from_this(...) { return false; }
|
||||||
|
|
||||||
|
// This overload uses SFINAE to skip enable_shared_from_this checks when the
|
||||||
|
// base is inaccessible (e.g. private inheritance).
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static constexpr bool type_has_shared_from_this(const std::enable_shared_from_this<T> *) {
|
static auto type_has_shared_from_this(const T *ptr)
|
||||||
|
-> decltype(static_cast<const std::enable_shared_from_this<T> *>(ptr), true) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inaccessible base → substitution failure → fallback overload selected
|
||||||
|
template <typename T>
|
||||||
|
static constexpr bool type_has_shared_from_this(const void *) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
struct guarded_delete {
|
struct guarded_delete {
|
||||||
std::weak_ptr<void> released_ptr; // Trick to keep the smart_holder memory footprint small.
|
std::weak_ptr<void> released_ptr; // Trick to keep the smart_holder memory footprint small.
|
||||||
std::function<void(void *)> del_fun; // Rare case.
|
std::function<void(void *)> del_fun; // Rare case.
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ namespace pybindit {
|
|||||||
namespace memory {
|
namespace memory {
|
||||||
namespace smart_holder_poc { // Proof-of-Concept implementations.
|
namespace smart_holder_poc { // Proof-of-Concept implementations.
|
||||||
|
|
||||||
|
struct PrivateESFT : private std::enable_shared_from_this<PrivateESFT> {};
|
||||||
|
static_assert(!pybindit::memory::type_has_shared_from_this(static_cast<PrivateESFT *>(nullptr)),
|
||||||
|
"should detect inaccessible base");
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T &as_lvalue_ref(const smart_holder &hld) {
|
T &as_lvalue_ref(const smart_holder &hld) {
|
||||||
static const char *context = "as_lvalue_ref";
|
static const char *context = "as_lvalue_ref";
|
||||||
|
|||||||
@@ -473,4 +473,13 @@ TEST_SUBMODULE(smart_ptr, m) {
|
|||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
class PrivateESFT : /* implicit private */ std::enable_shared_from_this<PrivateESFT> {};
|
||||||
|
struct ContainerUsingPrivateESFT {
|
||||||
|
std::shared_ptr<PrivateESFT> ptr;
|
||||||
|
};
|
||||||
|
py::class_<ContainerUsingPrivateESFT>(m, "ContainerUsingPrivateESFT")
|
||||||
|
.def(py::init<>())
|
||||||
|
.def_readwrite("ptr",
|
||||||
|
&ContainerUsingPrivateESFT::ptr); // <- access ESFT through shared_ptr
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -326,3 +326,15 @@ def test_shared_ptr_gc():
|
|||||||
pytest.gc_collect()
|
pytest.gc_collect()
|
||||||
for i, v in enumerate(el.get()):
|
for i, v in enumerate(el.get()):
|
||||||
assert i == v.value()
|
assert i == v.value()
|
||||||
|
|
||||||
|
|
||||||
|
def test_private_esft_tolerance():
|
||||||
|
# Regression test: binding a shared_ptr<T> member where T privately inherits
|
||||||
|
# enable_shared_from_this<T> must not cause a C++ compile error.
|
||||||
|
c = m.ContainerUsingPrivateESFT()
|
||||||
|
# The ptr member is not actually usable in any way, but this is how the
|
||||||
|
# pybind11 v2 release series worked.
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
_ = c.ptr # getattr
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
c.ptr = None # setattr
|
||||||
|
|||||||
Reference in New Issue
Block a user