Skip to content

Commit 61cf177

Browse files
committed
Moved shared_ptr specialization to pybind11/shared_ptr.h.
Addresses C++/Python shared object crash fix pybind#1546 (comment)
1 parent 486e136 commit 61cf177

File tree

2 files changed

+71
-4
lines changed

2 files changed

+71
-4
lines changed

include/pybind11/cast.h

-4
Original file line numberDiff line numberDiff line change
@@ -1510,10 +1510,6 @@ struct copyable_holder_caster : public type_caster_base<type> {
15101510
holder_type holder;
15111511
};
15121512

1513-
/// Specialize for the common std::shared_ptr, so users don't need to
1514-
template <typename T>
1515-
class type_caster<std::shared_ptr<T>> : public copyable_holder_caster<T, std::shared_ptr<T>> { };
1516-
15171513
template <typename type, typename holder_type>
15181514
struct move_only_holder_caster {
15191515
static_assert(std::is_base_of<type_caster_base<type>, type_caster<type>>::value,

include/pybind11/shared_ptr.h

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
pybind11/shared_ptr.h: Partial template specializations to shared_ptr cast
3+
Includes fix due to https://github.com/pybind/pybind11/issues/1546#issuecomment-526712704
4+
5+
Copyright (c) 2020 Andrey Asadchev <[email protected]>
6+
7+
All rights reserved. Use of this source code is governed by a
8+
BSD-style license that can be found in the LICENSE file.
9+
*/
10+
11+
#pragma once
12+
13+
#include "pybind11.h" // gil
14+
#include "cast.h"
15+
#include <memory>
16+
17+
NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
18+
NAMESPACE_BEGIN(detail)
19+
20+
/// Shared object crash fix: https://github.com/pybind/pybind11/issues/1546#issuecomment-526712704
21+
22+
/// Specialize for the common std::shared_ptr, so users don't need to
23+
template <typename T>
24+
class type_caster<std::shared_ptr<T>> {
25+
26+
PYBIND11_TYPE_CASTER (std::shared_ptr<T>, _(PYBIND11_STRING_NAME));
27+
28+
using BaseCaster = copyable_holder_caster<T, std::shared_ptr<T>>;
29+
30+
bool load (pybind11::handle src, bool b)
31+
{
32+
BaseCaster bc;
33+
bool success = bc.load (src, b);
34+
if (!success)
35+
{
36+
return false;
37+
}
38+
39+
auto base_ptr = static_cast<std::shared_ptr<T>> (bc);
40+
auto h = BaseCaster::cast(base_ptr, return_value_policy(), handle());
41+
auto py_obj = reinterpret_borrow<object>(h);
42+
43+
auto py_obj_ptr = std::shared_ptr<object>{
44+
new object{py_obj},
45+
[](auto py_object_ptr) {
46+
// It's possible that when the shared_ptr dies we won't have the
47+
// gil (if the last holder is in a non-Python thread), so we
48+
// make sure to acquire it in the deleter.
49+
gil_scoped_acquire gil;
50+
delete py_object_ptr;
51+
}
52+
};
53+
54+
value = std::shared_ptr<T> (py_obj_ptr, base_ptr.get ());
55+
return true;
56+
}
57+
58+
static handle cast (std::shared_ptr<T> sp,
59+
return_value_policy rvp,
60+
handle h)
61+
{
62+
return BaseCaster::cast (sp, rvp, h);
63+
}
64+
};
65+
66+
// Specialization for shared_ptr:
67+
template <typename base> struct is_holder_type<base, std::shared_ptr<base>> :
68+
std::true_type {};
69+
70+
NAMESPACE_END(detail)
71+
NAMESPACE_END(PYBIND11_NAMESPACE)

0 commit comments

Comments
 (0)