Skip to content

Commit b42bd11

Browse files
committed
Add a (long) C++ comment for py::potentially_slicing_shared_ptr<>()
1 parent 121c8cf commit b42bd11

File tree

1 file changed

+32
-0
lines changed

1 file changed

+32
-0
lines changed

include/pybind11/cast.h

+32
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,38 @@ class type_caster<std::shared_ptr<T>> : public copyable_holder_caster<T, std::sh
10891089

10901090
PYBIND11_NAMESPACE_END(detail)
10911091

1092+
/// Return a std::shared_ptr with the SAME CONTROL BLOCK as the std::shared_ptr owned by the
1093+
/// class_ holder. For class_-wrapped types with trampolines, the returned std::shared_ptr
1094+
/// does NOT keep any derived Python objects alive (see issue #1333).
1095+
///
1096+
/// For class_-wrapped types using std::shared_ptr as the holder, the following expressions
1097+
/// produce equivalent results (see tests/test_potentially_slicing_shared_ptr.cpp,py):
1098+
///
1099+
/// - obj.cast<std::shared_ptr<T>>()
1100+
/// - py::potentially_slicing_shared_ptr<T>(obj)
1101+
///
1102+
/// For class_-wrapped types with trampolines and using py::smart_holder, obj.cast<>()
1103+
/// produces a std::shared_ptr that keeps any derived Python objects alive for its own lifetime,
1104+
/// but this is achieved by introducing a std::shared_ptr control block that is independent of
1105+
/// the one owned by the py::smart_holder. This can lead to surprising std::weak_ptr behavior
1106+
/// (see issue #5623). An easy solution is to use py::potentially_slicing_shared_ptr<>(obj),
1107+
/// as exercised in tests/test_potentially_slicing_shared_ptr.cpp,py (look for
1108+
/// "set_wp_potentially_slicing"). Note, however, that this reintroduces the inheritance
1109+
/// slicing issue (see issue #1333). The ideal — but usually more involved — solution is to use
1110+
/// a Python weakref to the derived Python object, instead of a C++ base-class std::weak_ptr.
1111+
///
1112+
/// It is not possible (at least no known approach exists at the time of this writing) to
1113+
/// simultaneously achieve both desirable properties:
1114+
///
1115+
/// - the same std::shared_ptr control block as the class_ holder
1116+
/// - automatic lifetime extension of any derived Python objects
1117+
///
1118+
/// The reason is that this would introduce a reference cycle that cannot be garbage collected:
1119+
///
1120+
/// - the derived Python object owns the class_ holder
1121+
/// - the class_ holder owns the std::shared_ptr
1122+
/// - the std::shared_ptr would own a reference to the derived Python object,
1123+
/// completing the cycle
10921124
template <typename T>
10931125
std::shared_ptr<T> potentially_slicing_shared_ptr(handle obj) {
10941126
detail::make_caster<std::shared_ptr<T>> caster;

0 commit comments

Comments
 (0)