From b71bbfbb0737c18653466601d52de5e1c6727573 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sat, 3 Aug 2024 22:02:10 -0700 Subject: [PATCH] Transfer the `try_as_void_ptr_capsule` feature from smart_holder_type_casters.h to type_caster_base.h Restore the original, complete test_class_sh_void_ptr_capsule.py --- CMakeLists.txt | 1 + include/pybind11/cast.h | 53 +++++++++++- .../try_as_void_ptr_capsule_get_pointer.h | 85 +++++++++++++++++++ include/pybind11/detail/type_caster_base.h | 12 +++ tests/extra_python_package/test_files.py | 1 + tests/test_class_sh_void_ptr_capsule.py | 5 -- 6 files changed, 150 insertions(+), 7 deletions(-) create mode 100644 include/pybind11/detail/try_as_void_ptr_capsule_get_pointer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index bf33f673..f55c0cb0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,6 +156,7 @@ set(PYBIND11_HEADERS include/pybind11/detail/internals.h include/pybind11/detail/native_enum_data.h include/pybind11/detail/smart_holder_poc.h + include/pybind11/detail/try_as_void_ptr_capsule_get_pointer.h include/pybind11/detail/type_caster_base.h include/pybind11/detail/type_caster_odr_guard.h include/pybind11/detail/typeid.h diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 149e5b6e..edbc9152 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -958,6 +958,7 @@ struct copyable_holder_caster : public type_caster_base { } static bool try_direct_conversions(handle) { return false; } + static bool try_as_void_ptr_capsule(handle) { return false; } holder_type holder; }; @@ -998,6 +999,14 @@ struct copyable_holder_caster< explicit operator std::shared_ptr &() { if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) { + if (from_direct_conversion) { + throw cast_error("Unowned pointer from direct conversion cannot be converted to a " + "std::shared_ptr."); + } + if (from_as_void_ptr_capsule) { + throw cast_error("Unowned pointer from a void pointer capsule cannot be converted " + "to a std::shared_ptr."); + } shared_ptr_holder = sh_load_helper.load_as_shared_ptr(value); } return shared_ptr_holder; @@ -1083,10 +1092,26 @@ struct copyable_holder_caster< return false; } - static bool try_direct_conversions(handle) { return false; } + bool try_direct_conversions(handle src) { + if (type_caster_base::try_direct_conversions(src)) { + from_direct_conversion = true; + return true; + } + return false; + } + + bool try_as_void_ptr_capsule(handle src) { + if (type_caster_base::try_as_void_ptr_capsule(src)) { + from_as_void_ptr_capsule = true; + return true; + } + return false; + } std::shared_ptr shared_ptr_holder; smart_holder_type_caster_support::load_helper> sh_load_helper; // Const2Mutbl + bool from_direct_conversion = false; + bool from_as_void_ptr_capsule = false; }; #endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT @@ -1188,6 +1213,14 @@ struct move_only_holder_caster< explicit operator std::unique_ptr() { if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) { + if (from_direct_conversion) { + throw cast_error("Unowned pointer from direct conversion cannot be converted to a " + "std::unique_ptr."); + } + if (from_as_void_ptr_capsule) { + throw cast_error("Unowned pointer from a void pointer capsule cannot be converted " + "to a std::unique_ptr."); + } return sh_load_helper.template load_as_unique_ptr(value); } pybind11_fail("Expected to be UNREACHABLE: " __FILE__ ":" PYBIND11_TOSTRING(__LINE__)); @@ -1210,9 +1243,25 @@ struct move_only_holder_caster< return false; } - static bool try_direct_conversions(handle) { return false; } + bool try_direct_conversions(handle src) { + if (type_caster_base::try_direct_conversions(src)) { + from_direct_conversion = true; + return true; + } + return false; + } + + bool try_as_void_ptr_capsule(handle src) { + if (type_caster_base::try_as_void_ptr_capsule(src)) { + from_as_void_ptr_capsule = true; + return true; + } + return false; + } smart_holder_type_caster_support::load_helper> sh_load_helper; // Const2Mutbl + bool from_direct_conversion = false; + bool from_as_void_ptr_capsule = false; }; #endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT diff --git a/include/pybind11/detail/try_as_void_ptr_capsule_get_pointer.h b/include/pybind11/detail/try_as_void_ptr_capsule_get_pointer.h new file mode 100644 index 00000000..1f4ec30f --- /dev/null +++ b/include/pybind11/detail/try_as_void_ptr_capsule_get_pointer.h @@ -0,0 +1,85 @@ +// Copyright (c) 2024 The pybind Community. + +#pragma once + +#include "../pytypes.h" +#include "common.h" +#include "typeid.h" + +#include + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) + +// Replace all occurrences of substrings in a string. +inline void replace_all(std::string &str, const std::string &from, const std::string &to) { + if (str.empty()) { + return; + } + size_t pos = 0; + while ((pos = str.find(from, pos)) != std::string::npos) { + str.replace(pos, from.length(), to); + pos += to.length(); + } +} + +// Forward declaration needed here: Refactoring opportunity. +extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *); + +inline bool type_is_pybind11_class_(PyTypeObject *type_obj) { +#if defined(PYPY_VERSION) + auto &internals = get_internals(); + return bool(internals.registered_types_py.find(type_obj) + != internals.registered_types_py.end()); +#else + return bool(type_obj->tp_new == pybind11_object_new); +#endif +} + +inline bool is_instance_method_of_type(PyTypeObject *type_obj, PyObject *attr_name) { + PyObject *descr = _PyType_Lookup(type_obj, attr_name); + return bool((descr != nullptr) && PyInstanceMethod_Check(descr)); +} + +inline object try_get_as_capsule_method(PyObject *obj, PyObject *attr_name) { + if (PyType_Check(obj)) { + return object(); + } + PyTypeObject *type_obj = Py_TYPE(obj); + bool known_callable = false; + if (type_is_pybind11_class_(type_obj)) { + if (!is_instance_method_of_type(type_obj, attr_name)) { + return object(); + } + known_callable = true; + } + PyObject *method = PyObject_GetAttr(obj, attr_name); + if (method == nullptr) { + PyErr_Clear(); + return object(); + } + if (!known_callable && PyCallable_Check(method) == 0) { + Py_DECREF(method); + return object(); + } + return reinterpret_steal(method); +} + +inline void *try_as_void_ptr_capsule_get_pointer(handle src, const char *typeid_name) { + std::string suffix = clean_type_id(typeid_name); + replace_all(suffix, "::", "_"); // Convert `a::b::c` to `a_b_c`. + replace_all(suffix, "*", ""); + object as_capsule_method = try_get_as_capsule_method(src.ptr(), str("as_" + suffix).ptr()); + if (as_capsule_method) { + object void_ptr_capsule = as_capsule_method(); + if (isinstance(void_ptr_capsule)) { + return reinterpret_borrow(void_ptr_capsule).get_pointer(); + } + } + return nullptr; +} + +#define PYBIND11_HAS_TRY_AS_VOID_PTR_CAPSULE_GET_POINTER + +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index 3a37aded..b713b72c 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -16,6 +16,7 @@ #include "descr.h" #include "dynamic_raw_ptr_cast_if_possible.h" #include "internals.h" +#include "try_as_void_ptr_capsule_get_pointer.h" #include "typeid.h" #include "using_smart_holder.h" #include "value_and_holder.h" @@ -985,6 +986,13 @@ class type_caster_generic { } return false; } + bool try_as_void_ptr_capsule(handle src) { + value = try_as_void_ptr_capsule_get_pointer(src, cpptype->name()); + if (value != nullptr) { + return true; + } + return false; + } void check_holder_compat() {} PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) { @@ -1116,6 +1124,10 @@ class type_caster_generic { return true; } + if (convert && cpptype && this_.try_as_void_ptr_capsule(src)) { + return true; + } + return false; } diff --git a/tests/extra_python_package/test_files.py b/tests/extra_python_package/test_files.py index e07aead3..82971c9a 100644 --- a/tests/extra_python_package/test_files.py +++ b/tests/extra_python_package/test_files.py @@ -63,6 +63,7 @@ "include/pybind11/detail/internals.h", "include/pybind11/detail/native_enum_data.h", "include/pybind11/detail/smart_holder_poc.h", + "include/pybind11/detail/try_as_void_ptr_capsule_get_pointer.h", "include/pybind11/detail/type_caster_base.h", "include/pybind11/detail/type_caster_odr_guard.h", "include/pybind11/detail/typeid.h", diff --git a/tests/test_class_sh_void_ptr_capsule.py b/tests/test_class_sh_void_ptr_capsule.py index c384cac9..90934c15 100644 --- a/tests/test_class_sh_void_ptr_capsule.py +++ b/tests/test_class_sh_void_ptr_capsule.py @@ -4,11 +4,6 @@ from pybind11_tests import class_sh_void_ptr_capsule as m -pytest.skip( - "PYBIND11K_MERGE_SH_AFTER_PR5257_WIP: void_ptr_capsule feature needs to be re-integrated", - allow_module_level=True, -) - class Valid: def __init__(self):