Skip to content

Commit 23c3edc

Browse files
When determining if a shared_ptr already exists, use a test on the we… (#2819)
* When determining if a shared_ptr already exists, use a test on the weak_ptr instead of a try/catch block. * When determining if a shared_ptr already exists, use a test on the weak_ptr instead of a try/catch block. * weak_from_this is only available in C++17 and later * Switch to use feature flag instead of C++ version flag. * Add Microsoft-specific check. * Avoid undefined preprocessor macro warning treated as error. * Simplify shared_from_this in init_holder * Include <version> in detail/common.h (~stolen~ borrowed from @bstaletic's #2816) * Move class_::get_shared_from_this to detail::try_get_shared_from_this * Simplify try_get_shared_from_this by using weak_ptr::lock() Co-authored-by: Yannick Jadoul <[email protected]>
1 parent 0432ae7 commit 23c3edc

File tree

2 files changed

+30
-8
lines changed

2 files changed

+30
-8
lines changed

include/pybind11/detail/common.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,11 @@
162162
#include <memory>
163163
#include <typeindex>
164164
#include <type_traits>
165+
#if defined(__has_include)
166+
# if __has_include(<version>)
167+
# include <version>
168+
# endif
169+
#endif
165170

166171
// #define PYBIND11_STR_LEGACY_PERMISSIVE
167172
// If DEFINED, pybind11::str can hold PyUnicodeObject or PyBytesObject
@@ -870,5 +875,23 @@ class any_container {
870875
// Forward-declaration; see detail/class.h
871876
std::string get_fully_qualified_tp_name(PyTypeObject*);
872877

878+
template <typename T>
879+
inline static std::shared_ptr<T> try_get_shared_from_this(std::enable_shared_from_this<T> *holder_value_ptr) {
880+
// Pre C++17, this code path exploits undefined behavior, but is known to work on many platforms.
881+
// Use at your own risk!
882+
// See also https://en.cppreference.com/w/cpp/memory/enable_shared_from_this, and in particular
883+
// the `std::shared_ptr<Good> gp1 = not_so_good.getptr();` and `try`-`catch` parts of the example.
884+
#if defined(__cpp_lib_enable_shared_from_this) && (!defined(_MSC_VER) || _MSC_VER >= 1912)
885+
return holder_value_ptr->weak_from_this().lock();
886+
#else
887+
try {
888+
return holder_value_ptr->shared_from_this();
889+
}
890+
catch (const std::bad_weak_ptr &) {
891+
return nullptr;
892+
}
893+
#endif
894+
}
895+
873896
PYBIND11_NAMESPACE_END(detail)
874897
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

include/pybind11/pybind11.h

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,14 +1505,13 @@ class class_ : public detail::generic_type {
15051505
template <typename T>
15061506
static void init_holder(detail::instance *inst, detail::value_and_holder &v_h,
15071507
const holder_type * /* unused */, const std::enable_shared_from_this<T> * /* dummy */) {
1508-
try {
1509-
auto sh = std::dynamic_pointer_cast<typename holder_type::element_type>(
1510-
v_h.value_ptr<type>()->shared_from_this());
1511-
if (sh) {
1512-
new (std::addressof(v_h.holder<holder_type>())) holder_type(std::move(sh));
1513-
v_h.set_holder_constructed();
1514-
}
1515-
} catch (const std::bad_weak_ptr &) {}
1508+
1509+
auto sh = std::dynamic_pointer_cast<typename holder_type::element_type>(
1510+
detail::try_get_shared_from_this(v_h.value_ptr<type>()));
1511+
if (sh) {
1512+
new (std::addressof(v_h.holder<holder_type>())) holder_type(std::move(sh));
1513+
v_h.set_holder_constructed();
1514+
}
15161515

15171516
if (!v_h.holder_constructed() && inst->owned) {
15181517
new (std::addressof(v_h.holder<holder_type>())) holder_type(v_h.value_ptr<type>());

0 commit comments

Comments
 (0)