Skip to content

Commit 1f6fa1d

Browse files
committed
Fully emulating type_caster_base-related behavior: trying shared_from_this also for unowned pointees.
1 parent 576d668 commit 1f6fa1d

File tree

3 files changed

+31
-38
lines changed

3 files changed

+31
-38
lines changed

include/pybind11/detail/smart_holder_poc.h

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -94,16 +94,6 @@ inline bool is_std_default_delete(const std::type_info &rtti_deleter) {
9494
|| rtti_deleter == typeid(std::default_delete<T const>);
9595
}
9696

97-
inline void enable_shared_from_this_from_raw_ptr_take_ownership_guard(...) {}
98-
99-
template <typename AnyBaseOfT>
100-
inline void enable_shared_from_this_from_raw_ptr_take_ownership_guard(
101-
const std::enable_shared_from_this<AnyBaseOfT> *) {
102-
// This static_assert will always trigger if this template function is instantiated.
103-
static_assert(!std::is_base_of<std::enable_shared_from_this<AnyBaseOfT>, AnyBaseOfT>::value,
104-
"Ownership must not be transferred via a raw pointer.");
105-
}
106-
10797
struct smart_holder {
10898
const std::type_info *rtti_uqp_del = nullptr;
10999
std::shared_ptr<bool> vptr_deleter_armed_flag_ptr;
@@ -236,7 +226,6 @@ struct smart_holder {
236226

237227
template <typename T>
238228
static smart_holder from_raw_ptr_take_ownership(T *raw_ptr) {
239-
enable_shared_from_this_from_raw_ptr_take_ownership_guard(raw_ptr);
240229
ensure_pointee_is_destructible<T>("from_raw_ptr_take_ownership");
241230
smart_holder hld(true);
242231
hld.vptr.reset(raw_ptr, guarded_builtin_delete<T>(hld.vptr_deleter_armed_flag_ptr));

include/pybind11/detail/smart_holder_type_casters.h

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -269,25 +269,22 @@ struct smart_holder_type_caster_class_hooks : smart_holder_type_caster_base_tag
269269
using holder_type = pybindit::memory::smart_holder;
270270

271271
template <typename WrappedType>
272-
static void from_raw_pointer_take_ownership_or_shared_from_this(
273-
holder_type *uninitialized_location, WrappedType *value_ptr, ...) {
274-
new (uninitialized_location)
275-
holder_type(holder_type::from_raw_ptr_take_ownership(value_ptr));
272+
static bool try_initialization_using_shared_from_this(holder_type *, WrappedType *, ...) {
273+
return false;
276274
}
277275

278276
template <typename WrappedType, typename AnyBaseOfWrappedType>
279-
static void from_raw_pointer_take_ownership_or_shared_from_this(
277+
static bool try_initialization_using_shared_from_this(
280278
holder_type *uninitialized_location,
281-
WrappedType *value_ptr,
279+
WrappedType *value_ptr_w_t,
282280
const std::enable_shared_from_this<AnyBaseOfWrappedType> *) {
283-
auto shd_ptr
284-
= std::dynamic_pointer_cast<WrappedType>(detail::try_get_shared_from_this(value_ptr));
285-
if (shd_ptr) {
286-
new (uninitialized_location) holder_type(holder_type::from_shared_ptr(shd_ptr));
287-
} else {
288-
new (uninitialized_location)
289-
holder_type(holder_type::from_shared_ptr(std::shared_ptr<WrappedType>(value_ptr)));
290-
}
281+
auto shd_ptr = std::dynamic_pointer_cast<WrappedType>(
282+
detail::try_get_shared_from_this(value_ptr_w_t));
283+
if (!shd_ptr)
284+
return false;
285+
// Note: inst->owned ignored.
286+
new (uninitialized_location) holder_type(holder_type::from_shared_ptr(shd_ptr));
287+
return true;
291288
}
292289

293290
template <typename WrappedType, typename AliasType>
@@ -301,21 +298,26 @@ struct smart_holder_type_caster_class_hooks : smart_holder_type_caster_base_tag
301298
register_instance(inst, v_h.value_ptr(), v_h.type);
302299
v_h.set_instance_registered();
303300
}
301+
auto uninitialized_location = std::addressof(v_h.holder<holder_type>());
302+
auto value_ptr_w_t = v_h.value_ptr<WrappedType>();
304303
if (holder_void_ptr) {
305304
// Note: inst->owned ignored.
306305
auto holder_ptr = static_cast<holder_type *>(holder_void_ptr);
307-
new (std::addressof(v_h.holder<holder_type>())) holder_type(std::move(*holder_ptr));
308-
} else if (inst->owned) {
309-
from_raw_pointer_take_ownership_or_shared_from_this(
310-
std::addressof(v_h.holder<holder_type>()),
311-
v_h.value_ptr<WrappedType>(),
312-
v_h.value_ptr<WrappedType>());
306+
new (uninitialized_location) holder_type(std::move(*holder_ptr));
313307
} else {
314-
new (std::addressof(v_h.holder<holder_type>()))
315-
holder_type(holder_type::from_raw_ptr_unowned(v_h.value_ptr<WrappedType>()));
308+
if (!try_initialization_using_shared_from_this(
309+
uninitialized_location, value_ptr_w_t, value_ptr_w_t)) {
310+
if (inst->owned) {
311+
new (uninitialized_location)
312+
holder_type(holder_type::from_raw_ptr_take_ownership(value_ptr_w_t));
313+
} else {
314+
new (uninitialized_location)
315+
holder_type(holder_type::from_raw_ptr_unowned(value_ptr_w_t));
316+
}
317+
}
316318
}
317319
v_h.holder<holder_type>().pointee_depends_on_holder_owner
318-
= dynamic_raw_ptr_cast_if_possible<AliasType>(v_h.value_ptr<WrappedType>()) != nullptr;
320+
= dynamic_raw_ptr_cast_if_possible<AliasType>(value_ptr_w_t) != nullptr;
319321
v_h.set_holder_constructed();
320322
}
321323

@@ -390,6 +392,9 @@ struct smart_holder_type_caster_load {
390392
shared_ptr_dec_ref_deleter{
391393
handle((PyObject *) load_impl.loaded_v_h.inst).inc_ref()});
392394
}
395+
if (holder().vptr_is_using_noop_deleter) {
396+
throw std::runtime_error("Non-owning holder (loaded_as_shared_ptr).");
397+
}
393398
std::shared_ptr<void> void_shd_ptr = holder().template as_shared_ptr<void>();
394399
return std::shared_ptr<T>(void_shd_ptr, type_raw_ptr);
395400
}

tests/test_class_sh_shared_from_this.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# -*- coding: utf-8 -*-
2-
# import pytest
2+
import pytest
33

44
from pybind11_tests import class_sh_shared_from_this as m
55
from pybind11_tests import ConstructorStats
@@ -53,10 +53,9 @@ def test_shared_from_this_bad_wp():
5353
bad_wp = s.bad_wp # init_holder_helper(holder_ptr=false, owned=false, bad_wp=true)
5454
assert stats.alive() == 2
5555
assert s.set_ref(bad_wp)
56-
# with pytest.raises(RuntimeError) as excinfo:
57-
if 1: # XXX XXX XXX
56+
with pytest.raises(RuntimeError) as excinfo:
5857
assert s.set_holder(bad_wp)
59-
# assert "Unable to cast from non-held to held instance" in str(excinfo.value)
58+
assert str(excinfo.value) == "Non-owning holder (loaded_as_shared_ptr)."
6059
del bad_wp, s
6160
assert stats.alive() == 0
6261

0 commit comments

Comments
 (0)