Skip to content

Commit 4480b95

Browse files
committed
Disable implicit conversion from 0 to pybind11::handle.
1 parent 0964a90 commit 4480b95

File tree

2 files changed

+33
-1
lines changed

2 files changed

+33
-1
lines changed

include/pybind11/pytypes.h

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,23 @@ class object_api : public pyobject_tag {
190190
bool rich_compare(object_api const &other, int value) const;
191191
};
192192

193+
template <typename T, typename SFINAE = void>
194+
struct has_member_ob_refcnt : std::false_type {};
195+
196+
template <typename T>
197+
struct has_member_ob_refcnt<T, decltype((void) T::ob_refcnt, void())> : std::true_type {};
198+
199+
template <typename T, typename SFINAE = void>
200+
struct is_c_api_py_object_pointer : std::false_type {};
201+
202+
template <typename T>
203+
struct is_c_api_py_object_pointer<
204+
T,
205+
detail::enable_if_t<
206+
std::is_pointer<T>::value
207+
&& detail::has_member_ob_refcnt<typename std::remove_pointer<T>::type>::value>>
208+
: std::true_type {};
209+
193210
PYBIND11_NAMESPACE_END(detail)
194211

195212
#if !defined(PYBIND11_HANDLE_REF_DEBUG) && !defined(NDEBUG)
@@ -212,8 +229,18 @@ class handle : public detail::object_api<handle> {
212229
/// The default constructor creates a handle with a ``nullptr``-valued pointer
213230
handle() = default;
214231
/// Creates a ``handle`` from the given raw Python object pointer
232+
/// Not using ``handle(PyObject *ptr)`` to avoid implicit conversion from ``0``.
233+
template <typename T,
234+
detail::enable_if_t<detail::is_c_api_py_object_pointer<T>::value
235+
|| std::is_same<T, std::nullptr_t>::value,
236+
int> = 0>
215237
// NOLINTNEXTLINE(google-explicit-constructor)
216-
handle(PyObject *ptr) : m_ptr(ptr) {} // Allow implicit conversion from PyObject*
238+
handle(/* PyObject* */ T ptr) : m_ptr(ptr) {} // Allow implicit conversion from PyObject*
239+
240+
handle(const handle &) = default;
241+
handle(handle &&) = default;
242+
handle &operator=(const handle &) = default;
243+
handle &operator=(handle &&) = default;
217244

218245
/// Return the underlying ``PyObject *`` pointer
219246
PyObject *ptr() const { return m_ptr; }

tests/test_pytypes.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ class float_ : public py::object {
3939
};
4040
} // namespace external
4141

42+
namespace implicit_conversion_from_0_to_handle {
43+
// Uncomment to trigger compiler error. Note: Before PR #40XX this used to compile successfully.
44+
py::handle expected_to_trigger_compiler_error() { return 0; }
45+
} // namespace implicit_conversion_from_0_to_handle
46+
4247
TEST_SUBMODULE(pytypes, m) {
4348
// test_bool
4449
m.def("get_bool", [] { return py::bool_(false); });

0 commit comments

Comments
 (0)