Skip to content

Commit f14499d

Browse files
committed
Interoperability with other Python binding frameworks
1 parent ce71228 commit f14499d

File tree

15 files changed

+1843
-137
lines changed

15 files changed

+1843
-137
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,11 +186,13 @@ set(PYBIND11_HEADERS
186186
include/pybind11/detail/descr.h
187187
include/pybind11/detail/dynamic_raw_ptr_cast_if_possible.h
188188
include/pybind11/detail/exception_translation.h
189+
include/pybind11/detail/foreign.h
189190
include/pybind11/detail/function_record_pyobject.h
190191
include/pybind11/detail/init.h
191192
include/pybind11/detail/internals.h
192193
include/pybind11/detail/native_enum_data.h
193194
include/pybind11/detail/pybind11_namespace_macros.h
195+
include/pybind11/detail/pymetabind.h
194196
include/pybind11/detail/struct_smart_holder.h
195197
include/pybind11/detail/type_caster_base.h
196198
include/pybind11/detail/typeid.h

include/pybind11/cast.h

Lines changed: 97 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,71 @@ struct holder_helper {
862862
static auto get(const T &p) -> decltype(p.get()) { return p.get(); }
863863
};
864864

865+
struct holder_caster_foreign_helpers {
866+
struct py_deleter {
867+
void operator()(const void *) noexcept {
868+
// Don't run the deleter if the interpreter has been shut down
869+
if (!Py_IsInitialized())
870+
return;
871+
gil_scoped_acquire guard;
872+
Py_DECREF(o);
873+
}
874+
875+
PyObject *o;
876+
};
877+
878+
template <typename type>
879+
static bool try_shared_from_this(std::enable_shared_from_this<type> *value,
880+
std::shared_ptr<type> *holder_out) {
881+
// object derives from enable_shared_from_this;
882+
// try to reuse an existing shared_ptr if one is known
883+
if (auto existing = try_get_shared_from_this(value)) {
884+
*holder_out = existing;
885+
return true;
886+
}
887+
return false;
888+
}
889+
890+
template <typename type>
891+
static bool try_shared_from_this(type *, std::shared_ptr<type> *) {
892+
return false;
893+
}
894+
895+
template <typename type>
896+
static bool set_foreign_holder(handle src, type *value,
897+
std::shared_ptr<type> *holder_out) {
898+
// We only support using std::shared_ptr<T> for foreign T, and
899+
// it's done by creating a new shared_ptr control block that
900+
// owns a reference to the original Python object.
901+
if (value == nullptr) {
902+
*holder_out = {};
903+
return true;
904+
}
905+
if (try_shared_from_this(value, holder_out)) {
906+
return true;
907+
}
908+
*holder_out = std::shared_ptr<type>(value, py_deleter{src.inc_ref().ptr()});
909+
return true;
910+
}
911+
912+
template <typename type>
913+
static bool set_foreign_holder(handle src, type *value,
914+
std::shared_ptr<const type> *holder_out) {
915+
std::shared_ptr<type> holder_mut;
916+
if (set_foreign_holder(src, value, &holder_mut)) {
917+
*holder_out = holder_mut;
918+
return true;
919+
}
920+
return false;
921+
}
922+
923+
template <typename type>
924+
static bool set_foreign_holder(handle, type *, ...) {
925+
throw cast_error("Unable to cast foreign type to held instance -- "
926+
"only std::shared_ptr<T> is supported in this case");
927+
}
928+
};
929+
865930
// SMART_HOLDER_BAKEIN_FOLLOW_ON: Rewrite comment, with reference to shared_ptr specialization.
866931
/// Type caster for holder types like std::shared_ptr, etc.
867932
/// The SFINAE hook is provided to help work around the current lack of support
@@ -906,6 +971,11 @@ struct copyable_holder_caster : public type_caster_base<type> {
906971
}
907972
}
908973

974+
bool set_foreign_holder(handle src) {
975+
return holder_caster_foreign_helpers::set_foreign_holder(
976+
src, (type *) value, &holder);
977+
}
978+
909979
void load_value(value_and_holder &&v_h) {
910980
if (v_h.holder_constructed()) {
911981
value = v_h.value_ptr();
@@ -976,22 +1046,22 @@ struct copyable_holder_caster<
9761046
}
9771047

9781048
explicit operator std::shared_ptr<type> *() {
979-
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
1049+
if (sh_load_helper.was_populated) {
9801050
pybind11_fail("Passing `std::shared_ptr<T> *` from Python to C++ is not supported "
9811051
"(inherently unsafe).");
9821052
}
9831053
return std::addressof(shared_ptr_storage);
9841054
}
9851055

9861056
explicit operator std::shared_ptr<type> &() {
987-
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
1057+
if (sh_load_helper.was_populated) {
9881058
shared_ptr_storage = sh_load_helper.load_as_shared_ptr(typeinfo, value);
9891059
}
9901060
return shared_ptr_storage;
9911061
}
9921062

9931063
std::weak_ptr<type> potentially_slicing_weak_ptr() {
994-
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
1064+
if (sh_load_helper.was_populated) {
9951065
// Reusing shared_ptr code to minimize code complexity.
9961066
shared_ptr_storage
9971067
= sh_load_helper.load_as_shared_ptr(typeinfo,
@@ -1005,15 +1075,12 @@ struct copyable_holder_caster<
10051075
static handle
10061076
cast(const std::shared_ptr<type> &src, return_value_policy policy, handle parent) {
10071077
const auto *ptr = src.get();
1008-
auto st = type_caster_base<type>::src_and_type(ptr);
1009-
if (st.second == nullptr) {
1010-
return handle(); // no type info: error will be set already
1011-
}
1012-
if (st.second->holder_enum_v == detail::holder_enum_t::smart_holder) {
1078+
typename type_caster_base<type>::cast_sources srcs{ptr};
1079+
if (srcs.creates_smart_holder()) {
10131080
return smart_holder_type_caster_support::smart_holder_from_shared_ptr(
1014-
src, policy, parent, st);
1081+
src, policy, parent, srcs.result);
10151082
}
1016-
return type_caster_base<type>::cast_holder(ptr, &src);
1083+
return type_caster_base<type>::cast_holder(srcs, &src);
10171084
}
10181085

10191086
// This function will succeed even if the `responsible_parent` does not own the
@@ -1040,6 +1107,11 @@ struct copyable_holder_caster<
10401107
}
10411108
}
10421109

1110+
bool set_foreign_holder(handle src) {
1111+
return holder_caster_foreign_helpers::set_foreign_holder(
1112+
src, (type *) value, &shared_ptr_storage);
1113+
}
1114+
10431115
void load_value(value_and_holder &&v_h) {
10441116
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
10451117
sh_load_helper.loaded_v_h = v_h;
@@ -1077,6 +1149,7 @@ struct copyable_holder_caster<
10771149
value = cast.second(sub_caster.value);
10781150
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
10791151
sh_load_helper.loaded_v_h = sub_caster.sh_load_helper.loaded_v_h;
1152+
sh_load_helper.was_populated = true;
10801153
} else {
10811154
shared_ptr_storage
10821155
= std::shared_ptr<type>(sub_caster.shared_ptr_storage, (type *) value);
@@ -1183,21 +1256,12 @@ struct move_only_holder_caster<
11831256
static handle
11841257
cast(std::unique_ptr<type, deleter> &&src, return_value_policy policy, handle parent) {
11851258
auto *ptr = src.get();
1186-
auto st = type_caster_base<type>::src_and_type(ptr);
1187-
if (st.second == nullptr) {
1188-
return handle(); // no type info: error will be set already
1189-
}
1190-
if (st.second->holder_enum_v == detail::holder_enum_t::smart_holder) {
1259+
typename type_caster_base<type>::cast_sources srcs{ptr};
1260+
if (srcs.creates_smart_holder()) {
11911261
return smart_holder_type_caster_support::smart_holder_from_unique_ptr(
1192-
std::move(src), policy, parent, st);
1262+
std::move(src), policy, parent, srcs.result);
11931263
}
1194-
return type_caster_generic::cast(st.first,
1195-
return_value_policy::take_ownership,
1196-
{},
1197-
st.second,
1198-
nullptr,
1199-
nullptr,
1200-
std::addressof(src));
1264+
return type_caster_base<type>::cast_holder(srcs, &src);
12011265
}
12021266

12031267
static handle
@@ -1223,6 +1287,12 @@ struct move_only_holder_caster<
12231287
return false;
12241288
}
12251289

1290+
bool set_foreign_holder(handle) {
1291+
throw cast_error("Foreign types cannot be converted to std::unique_ptr "
1292+
"because we don't know how to make them relinquish "
1293+
"ownership");
1294+
}
1295+
12261296
void load_value(value_and_holder &&v_h) {
12271297
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
12281298
sh_load_helper.loaded_v_h = v_h;
@@ -1281,6 +1351,7 @@ struct move_only_holder_caster<
12811351
value = cast.second(sub_caster.value);
12821352
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
12831353
sh_load_helper.loaded_v_h = sub_caster.sh_load_helper.loaded_v_h;
1354+
sh_load_helper.was_populated = true;
12841355
} else {
12851356
pybind11_fail("Expected to be UNREACHABLE: " __FILE__
12861357
":" PYBIND11_TOSTRING(__LINE__));
@@ -2338,11 +2409,11 @@ object object_api<Derived>::call(Args &&...args) const {
23382409
PYBIND11_NAMESPACE_END(detail)
23392410

23402411
template <typename T>
2341-
handle type::handle_of() {
2412+
handle type::handle_of(bool foreign_ok) {
23422413
static_assert(std::is_base_of<detail::type_caster_generic, detail::make_caster<T>>::value,
2343-
"py::type::of<T> only supports the case where T is a registered C++ types.");
2414+
"py::type::of<T> only supports the case where T is a registered C++ type.");
23442415

2345-
return detail::get_type_handle(typeid(T), true);
2416+
return detail::get_type_handle(typeid(T), true, foreign_ok);
23462417
}
23472418

23482419
#define PYBIND11_MAKE_OPAQUE(...) \

include/pybind11/detail/class.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
226226
} else {
227227
internals.registered_types_cpp.erase(tindex);
228228
}
229+
get_foreign_internals().copy_move_ctors.erase(tindex);
229230
internals.registered_types_py.erase(tinfo->type);
230231

231232
// Actually just `std::erase_if`, but that's only available in C++20

include/pybind11/detail/common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@
348348
#define PYBIND11_ENSURE_INTERNALS_READY \
349349
{ \
350350
pybind11::detail::get_internals_pp_manager().unref(); \
351+
pybind11::detail::get_foreign_internals_pp_manager().unref(); \
351352
pybind11::detail::get_internals(); \
352353
}
353354

0 commit comments

Comments
 (0)