@@ -1524,6 +1524,39 @@ struct holder_helper {
15241524 static auto get (const T &p) -> decltype(p.get()) { return p.get (); }
15251525};
15261526
1527+ // / Another helper class for holders that helps construct derivative holders from
1528+ // / the original holder
1529+ template <typename T>
1530+ struct holder_retriever {
1531+ static auto get_derivative_holder (const value_and_holder &v_h) -> decltype(v_h.template holder<T>()) {
1532+ return v_h.template holder <T>();
1533+ }
1534+ };
1535+
1536+ template <typename T>
1537+ struct holder_retriever <std::shared_ptr<T>> {
1538+ struct shared_ptr_deleter {
1539+ // Note: deleter destructor fails on MSVC 2015 and GCC 4.8, so we manually
1540+ // call dec_ref here instead
1541+ handle ref;
1542+ void operator ()(T *) { ref.dec_ref (); }
1543+ };
1544+
1545+ static auto get_derivative_holder (const value_and_holder &v_h) -> std::shared_ptr<T> {
1546+ // The shared_ptr is always given to C++ code, so construct a new shared_ptr
1547+ // that is given a custom deleter. The custom deleter increments the python
1548+ // reference count to bind the python instance lifetime with the lifetime
1549+ // of the shared_ptr.
1550+ //
1551+ // This enables things like passing the last python reference of a subclass to a
1552+ // C++ function without the python reference dying.
1553+ //
1554+ // Reference cycles will cause a leak, but this is a limitation of shared_ptr
1555+ return std::shared_ptr<T>((T*)v_h.value_ptr (),
1556+ shared_ptr_deleter{handle ((PyObject*)v_h.inst ).inc_ref ()});
1557+ }
1558+ };
1559+
15271560// / Type caster for holder types like std::shared_ptr, etc.
15281561// / The SFINAE hook is provided to help work around the current lack of support
15291562// / for smart-pointer interoperability. Please consider it an implementation
@@ -1566,7 +1599,7 @@ struct copyable_holder_caster : public type_caster_base<type> {
15661599 bool load_value (value_and_holder &&v_h) {
15671600 if (v_h.holder_constructed ()) {
15681601 value = v_h.value_ptr ();
1569- holder = v_h. template holder <holder_type>( );
1602+ holder = holder_retriever <holder_type>:: get_derivative_holder (v_h );
15701603 return true ;
15711604 } else {
15721605 throw cast_error (" Unable to cast from non-held to held instance (T& to Holder<T>) "
0 commit comments