Skip to content

Commit 2e5a699

Browse files
committed
Add T cast(object &&obj) overload as suggested by @Skylion007
The original suggestion leads to `error: call to 'cast' is ambiguous` (full error message below), therefore SFINAE guarding is needed. ``` clang++ -o pybind11/tests/test_type_caster_pyobject_ptr.os -c -std=c++17 -fPIC -fvisibility=hidden -O0 -g -Wall -Wextra -Wconversion -Wcast-qual -Wdeprecated -Wundef -Wnon-virtual-dtor -Wunused-result -Werror -isystem /usr/include/python3.10 -isystem /usr/include/eigen3 -DPYBIND11_STRICT_ASSERTS_CLASS_HOLDER_VS_TYPE_CASTER_MIX -DPYBIND11_ENABLE_TYPE_CASTER_ODR_GUARD_IF_AVAILABLE -DPYBIND11_TEST_BOOST -Ipybind11/include -I/usr/local/google/home/rwgk/forked/pybind11/include -I/usr/local/google/home/rwgk/clone/pybind11/include /usr/local/google/home/rwgk/forked/pybind11/tests/test_type_caster_pyobject_ptr.cpp In file included from /usr/local/google/home/rwgk/forked/pybind11/tests/test_type_caster_pyobject_ptr.cpp:1: In file included from /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/functional.h:12: In file included from /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/pybind11.h:13: In file included from /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/class.h:12: In file included from /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/attr.h:14: /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/cast.h:1165:12: error: call to 'cast' is ambiguous return pybind11::cast<T>(std::move(*this)); ^~~~~~~~~~~~~~~~~ /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/functional.h:109:70: note: in instantiation of function template specialization 'pybind11::object::cast<_object *>' requested here return hfunc.f(std::forward<Args>(args)...).template cast<Return>(); ^ /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/functional.h:103:16: note: in instantiation of member function 'pybind11::detail::type_caster<std::function<_object *(int)>>::load(pybind11::handle, bool)::func_wrapper::operator()' requested here struct func_wrapper { ^ /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/cast.h:1456:47: note: in instantiation of member function 'pybind11::detail::type_caster<std::function<_object *(int)>>::load' requested here if ((... || !std::get<Is>(argcasters).load(call.args[Is], call.args_convert[Is]))) { ^ /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/cast.h:1434:50: note: in instantiation of function template specialization 'pybind11::detail::argument_loader<const std::function<_object *(int)> &, int>::load_impl_sequence<0UL, 1UL>' requested here bool load_args(function_call &call) { return load_impl_sequence(call, indices{}); } ^ /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/pybind11.h:227:33: note: in instantiation of member function 'pybind11::detail::argument_loader<const std::function<_object *(int)> &, int>::load_args' requested here if (!args_converter.load_args(call)) { ^ /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/pybind11.h:101:9: note: in instantiation of function template specialization 'pybind11::cpp_function::initialize<(lambda at /usr/local/google/home/rwgk/forked/pybind11/tests/test_type_caster_pyobject_ptr.cpp:50:9), _object *, const std::function<_object *(int)> &, int, pybind11::name, pybind11::scope, pybind11::sibling, pybind11::return_value_policy>' requested here initialize( ^ /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/pybind11.h:1163:22: note: in instantiation of function template specialization 'pybind11::cpp_function::cpp_function<(lambda at /usr/local/google/home/rwgk/forked/pybind11/tests/test_type_caster_pyobject_ptr.cpp:50:9), pybind11::name, pybind11::scope, pybind11::sibling, pybind11::return_value_policy, void>' requested here cpp_function func(std::forward<Func>(f), ^ /usr/local/google/home/rwgk/forked/pybind11/tests/test_type_caster_pyobject_ptr.cpp:48:7: note: in instantiation of function template specialization 'pybind11::module_::def<(lambda at /usr/local/google/home/rwgk/forked/pybind11/tests/test_type_caster_pyobject_ptr.cpp:50:9), pybind11::return_value_policy>' requested here m.def( ^ /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/cast.h:1077:3: note: candidate function [with T = _object *, $1 = 0] T cast(object &&obj) { ^ /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/cast.h:1149:1: note: candidate function [with T = _object *] cast(object &&object) { ^ 1 error generated. ```
1 parent 6afa757 commit 2e5a699

File tree

3 files changed

+48
-5
lines changed

3 files changed

+48
-5
lines changed

include/pybind11/cast.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,10 +1068,24 @@ T cast(const handle &handle) {
10681068
// It is the responsibility of the caller to ensure that the reference count
10691069
// is decremented.
10701070
template <typename T,
1071-
detail::enable_if_t<detail::is_same_ignoring_cvref<T, PyObject *>::value, int> = 0>
1072-
T cast(const handle &handle) {
1071+
typename Handle,
1072+
detail::enable_if_t<detail::is_same_ignoring_cvref<T, PyObject *>::value
1073+
&& detail::is_same_ignoring_cvref<Handle, handle>::value,
1074+
int>
1075+
= 0>
1076+
T cast(Handle &&handle) {
10731077
return handle.inc_ref().ptr();
10741078
}
1079+
// To optimize way an inc_ref/dec_ref cycle:
1080+
template <typename T,
1081+
typename Object,
1082+
detail::enable_if_t<detail::is_same_ignoring_cvref<T, PyObject *>::value
1083+
&& detail::is_same_ignoring_cvref<Object, object>::value,
1084+
int>
1085+
= 0>
1086+
T cast(Object &&obj) {
1087+
return obj.release().ptr();
1088+
}
10751089

10761090
// C++ type -> py::object
10771091
template <typename T, detail::enable_if_t<!detail::is_pyobject<T>::value, int> = 0>

tests/test_type_caster_pyobject_ptr.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ TEST_SUBMODULE(type_caster_pyobject_ptr, m) {
2525
PyObject *ptr = PyLong_FromLongLong(6758L);
2626
return py::cast(ptr, py::return_value_policy::take_ownership);
2727
});
28-
m.def("cast_to_pyobject_ptr", [](py::handle obj) {
28+
m.def("cast_handle_to_pyobject_ptr", [](py::handle obj) {
2929
auto rc1 = obj.ref_count();
3030
auto *ptr = py::cast<PyObject *>(obj);
3131
auto rc2 = obj.ref_count();
@@ -34,6 +34,27 @@ TEST_SUBMODULE(type_caster_pyobject_ptr, m) {
3434
}
3535
return 100 - py::reinterpret_steal<py::object>(ptr).attr("value").cast<int>();
3636
});
37+
m.def("cast_object_to_pyobject_ptr", [](py::object obj) {
38+
py::handle hdl = obj;
39+
auto rc1 = hdl.ref_count();
40+
auto *ptr = py::cast<PyObject *>(std::move(obj));
41+
auto rc2 = hdl.ref_count();
42+
if (rc2 != rc1) {
43+
return -1;
44+
}
45+
return 300 - py::reinterpret_steal<py::object>(ptr).attr("value").cast<int>();
46+
});
47+
m.def("cast_list_to_pyobject_ptr", [](py::list lst) {
48+
// This is to cover types implicitly convertible to object.
49+
py::handle hdl = lst;
50+
auto rc1 = hdl.ref_count();
51+
auto *ptr = py::cast<PyObject *>(std::move(lst));
52+
auto rc2 = hdl.ref_count();
53+
if (rc2 != rc1) {
54+
return -1;
55+
}
56+
return 400 - static_cast<int>(py::len(py::reinterpret_steal<py::list>(ptr)));
57+
});
3758

3859
m.def(
3960
"return_pyobject_ptr",

tests/test_type_caster_pyobject_ptr.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,16 @@ def test_cast_from_pyobject_ptr():
1313
assert m.cast_from_pyobject_ptr() == 6758
1414

1515

16-
def test_cast_to_pyobject_ptr():
17-
assert m.cast_to_pyobject_ptr(ValueHolder(24)) == 76
16+
def test_cast_handle_to_pyobject_ptr():
17+
assert m.cast_handle_to_pyobject_ptr(ValueHolder(24)) == 76
18+
19+
20+
def test_cast_object_to_pyobject_ptr():
21+
assert m.cast_object_to_pyobject_ptr(ValueHolder(43)) == 257
22+
23+
24+
def test_cast_list_to_pyobject_ptr():
25+
assert m.cast_list_to_pyobject_ptr([1, 2, 3, 4, 5]) == 395
1826

1927

2028
def test_return_pyobject_ptr():

0 commit comments

Comments
 (0)