diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 16a399ea02..efdc5536be 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1367,6 +1367,12 @@ template class type_caster> template struct holder_helper { static auto get(const T &p) -> decltype(p.get()) { return p.get(); } + + // Specialize move-only holder semantics to address #1138. + template + static auto get(T &&p, enable_if_t::value>* = nullptr) -> decltype(p.get()) { + return p.release(); + } }; /// Type caster for holder types like std::shared_ptr, etc. @@ -1456,8 +1462,11 @@ struct move_only_holder_caster { "Holder classes are only supported for custom types"); static handle cast(holder_type &&src, return_value_policy, handle) { - auto *ptr = holder_helper::get(src); - return type_caster_base::cast_holder(ptr, &src); + // Move `src` so that `holder_helper<>::get()` can call `release` if need be. + // That way, if we mix `holder_type`s, we don't have to worry about `existing_holder` + // from being mistakenly reinterpret_cast'd to `shared_ptr` (#1138). + auto *ptr = holder_helper::get(std::move(src)); + return type_caster_base::cast_holder(ptr, nullptr); } static constexpr auto name = type_caster_base::name; }; diff --git a/tests/test_smart_ptr.cpp b/tests/test_smart_ptr.cpp index dccb1e9be5..cbe8f93478 100644 --- a/tests/test_smart_ptr.cpp +++ b/tests/test_smart_ptr.cpp @@ -40,7 +40,8 @@ template class huge_unique_ptr { uint64_t padding[10]; public: huge_unique_ptr(T *p) : ptr(p) {}; - T *get() { return ptr.get(); } + T *get() const { return ptr.get(); } + T* release() { return ptr.release(); } }; PYBIND11_DECLARE_HOLDER_TYPE(T, huge_unique_ptr); @@ -51,7 +52,7 @@ class custom_unique_ptr { public: custom_unique_ptr(T* p) : impl(p) { } T* get() const { return impl.get(); } - T* release_ptr() { return impl.release(); } + T* release() { return impl.release(); } }; PYBIND11_DECLARE_HOLDER_TYPE(T, custom_unique_ptr);