Skip to content

Commit c473392

Browse files
committed
Add py::reinterpret_borrow<T>()/steal<T>() for low-level unchecked casts
The pytype converting constructors are convenient and safe for user code, but for library internals the additional type checks and possible conversions are sometimes not desired. `reinterpret_borrow<T>()` and `reinterpret_steal<T>()` serve as the low-level unsafe counterparts of `cast<T>()`. This deprecates the `object(handle, bool)` constructor. Renamed `borrowed` parameter to `is_borrowed` to avoid shadowing warnings on MSVC.
1 parent 294832e commit c473392

File tree

8 files changed

+178
-136
lines changed

8 files changed

+178
-136
lines changed

include/pybind11/cast.h

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ class type_caster_generic {
216216
auto const &type_dict = get_internals().registered_types_py;
217217
bool new_style_class = PyType_Check(tobj);
218218
if (type_dict.find(tobj) == type_dict.end() && new_style_class && tobj->tp_bases) {
219-
tuple parents(tobj->tp_bases, true);
219+
auto parents = reinterpret_borrow<tuple>(tobj->tp_bases);
220220
for (handle parent : parents) {
221221
bool result = load(src, convert, (PyTypeObject *) parent.ptr());
222222
if (result)
@@ -237,7 +237,7 @@ class type_caster_generic {
237237
/* Perform an implicit conversion */
238238
if (convert) {
239239
for (auto &converter : typeinfo->implicit_conversions) {
240-
temp = object(converter(src.ptr(), typeinfo->type), false);
240+
temp = reinterpret_steal<object>(converter(src.ptr(), typeinfo->type));
241241
if (load(temp, false))
242242
return true;
243243
}
@@ -284,7 +284,7 @@ class type_caster_generic {
284284
return handle((PyObject *) it_i->second).inc_ref();
285285
}
286286

287-
object inst(PyType_GenericAlloc(tinfo->type, 0), false);
287+
auto inst = reinterpret_steal<object>(PyType_GenericAlloc(tinfo->type, 0));
288288

289289
auto wrapper = (instance<void> *) inst.ptr();
290290

@@ -487,9 +487,9 @@ struct type_caster<T, enable_if_t<std::is_arithmetic<T>::value>> {
487487
#endif
488488
PyErr_Clear();
489489
if (type_error && PyNumber_Check(src.ptr())) {
490-
object tmp(std::is_floating_point<T>::value
491-
? PyNumber_Float(src.ptr())
492-
: PyNumber_Long(src.ptr()), true);
490+
auto tmp = reinterpret_borrow<object>(std::is_floating_point<T>::value
491+
? PyNumber_Float(src.ptr())
492+
: PyNumber_Long(src.ptr()));
493493
PyErr_Clear();
494494
return load(tmp, false);
495495
}
@@ -544,7 +544,7 @@ template <> class type_caster<void> : public type_caster<void_type> {
544544

545545
/* Check if this is a capsule */
546546
if (isinstance<capsule>(h)) {
547-
value = capsule(h, true);
547+
value = reinterpret_borrow<capsule>(h);
548548
return true;
549549
}
550550

@@ -596,7 +596,7 @@ template <> class type_caster<std::string> {
596596
if (!src) {
597597
return false;
598598
} else if (PyUnicode_Check(load_src.ptr())) {
599-
temp = object(PyUnicode_AsUTF8String(load_src.ptr()), false);
599+
temp = reinterpret_steal<object>(PyUnicode_AsUTF8String(load_src.ptr()));
600600
if (!temp) { PyErr_Clear(); return false; } // UnicodeEncodeError
601601
load_src = temp;
602602
}
@@ -637,7 +637,7 @@ template <> class type_caster<std::wstring> {
637637
if (!src) {
638638
return false;
639639
} else if (!PyUnicode_Check(load_src.ptr())) {
640-
temp = object(PyUnicode_FromObject(load_src.ptr()), false);
640+
temp = reinterpret_steal<object>(PyUnicode_FromObject(load_src.ptr()));
641641
if (!temp) { PyErr_Clear(); return false; }
642642
load_src = temp;
643643
}
@@ -646,10 +646,10 @@ template <> class type_caster<std::wstring> {
646646
#if PY_MAJOR_VERSION >= 3
647647
buffer = PyUnicode_AsWideCharString(load_src.ptr(), &length);
648648
#else
649-
temp = object(
649+
temp = reinterpret_steal<object>(
650650
sizeof(wchar_t) == sizeof(short)
651651
? PyUnicode_AsUTF16String(load_src.ptr())
652-
: PyUnicode_AsUTF32String(load_src.ptr()), false);
652+
: PyUnicode_AsUTF32String(load_src.ptr()));
653653
if (temp) {
654654
int err = PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), (char **) &buffer, &length);
655655
if (err == -1) { buffer = nullptr; } // TypeError
@@ -730,8 +730,8 @@ template <typename T1, typename T2> class type_caster<std::pair<T1, T2>> {
730730
}
731731

732732
static handle cast(const type &src, return_value_policy policy, handle parent) {
733-
object o1 = object(make_caster<T1>::cast(src.first, policy, parent), false);
734-
object o2 = object(make_caster<T2>::cast(src.second, policy, parent), false);
733+
auto o1 = reinterpret_steal<object>(make_caster<T1>::cast(src.first, policy, parent));
734+
auto o2 = reinterpret_steal<object>(make_caster<T2>::cast(src.second, policy, parent));
735735
if (!o1 || !o2)
736736
return handle();
737737
tuple result(2);
@@ -846,7 +846,7 @@ template <typename... Tuple> class type_caster<std::tuple<Tuple...>> {
846846
/* Implementation: Convert a C++ tuple into a Python tuple */
847847
template <size_t ... Indices> static handle cast(const type &src, return_value_policy policy, handle parent, index_sequence<Indices...>) {
848848
std::array<object, size> entries {{
849-
object(make_caster<Tuple>::cast(std::get<Indices>(src), policy, parent), false)...
849+
reinterpret_steal<object>(make_caster<Tuple>::cast(std::get<Indices>(src), policy, parent))...
850850
}};
851851
for (const auto &entry: entries)
852852
if (!entry)
@@ -905,7 +905,7 @@ template <typename type, typename holder_type> class type_caster_holder : public
905905
auto const &type_dict = get_internals().registered_types_py;
906906
bool new_style_class = PyType_Check(tobj);
907907
if (type_dict.find(tobj) == type_dict.end() && new_style_class && tobj->tp_bases) {
908-
tuple parents(tobj->tp_bases, true);
908+
auto parents = reinterpret_borrow<tuple>(tobj->tp_bases);
909909
for (handle parent : parents) {
910910
bool result = load(src, convert, (PyTypeObject *) parent.ptr());
911911
if (result)
@@ -919,7 +919,7 @@ template <typename type, typename holder_type> class type_caster_holder : public
919919

920920
if (convert) {
921921
for (auto &converter : typeinfo->implicit_conversions) {
922-
temp = object(converter(src.ptr(), typeinfo->type), false);
922+
temp = reinterpret_steal<object>(converter(src.ptr(), typeinfo->type));
923923
if (load(temp, false))
924924
return true;
925925
}
@@ -1000,7 +1000,7 @@ struct pyobject_caster {
10001000
bool load(handle src, bool /* convert */) {
10011001
if (!isinstance<type>(src))
10021002
return false;
1003-
value = type(src, true);
1003+
value = reinterpret_borrow<type>(src);
10041004
return true;
10051005
}
10061006

@@ -1070,6 +1070,7 @@ template <typename T> make_caster<T> load_type(const handle &handle) {
10701070

10711071
NAMESPACE_END(detail)
10721072

1073+
// pytype -> C++ type
10731074
template <typename T, detail::enable_if_t<!detail::is_pyobject<T>::value, int> = 0>
10741075
T cast(const handle &handle) {
10751076
static_assert(!detail::cast_is_temporary_value_reference<T>::value,
@@ -1078,17 +1079,19 @@ T cast(const handle &handle) {
10781079
return detail::load_type<T>(handle).operator typename type_caster::template cast_op_type<T>();
10791080
}
10801081

1082+
// pytype -> pytype (calls converting constructor)
10811083
template <typename T, detail::enable_if_t<detail::is_pyobject<T>::value, int> = 0>
1082-
T cast(const handle &handle) { return {handle, true}; }
1084+
T cast(const handle &handle) { return T(reinterpret_borrow<object>(handle)); }
10831085

1086+
// C++ type -> py::object
10841087
template <typename T, detail::enable_if_t<!detail::is_pyobject<T>::value, int> = 0>
10851088
object cast(const T &value, return_value_policy policy = return_value_policy::automatic_reference,
10861089
handle parent = handle()) {
10871090
if (policy == return_value_policy::automatic)
10881091
policy = std::is_pointer<T>::value ? return_value_policy::take_ownership : return_value_policy::copy;
10891092
else if (policy == return_value_policy::automatic_reference)
10901093
policy = std::is_pointer<T>::value ? return_value_policy::reference : return_value_policy::copy;
1091-
return object(detail::make_caster<T>::cast(value, policy, parent), false);
1094+
return reinterpret_steal<object>(detail::make_caster<T>::cast(value, policy, parent));
10921095
}
10931096

10941097
template <typename T> T handle::cast() const { return pybind11::cast<T>(*this); }
@@ -1162,8 +1165,8 @@ template <return_value_policy policy = return_value_policy::automatic_reference,
11621165
typename... Args> tuple make_tuple(Args&&... args_) {
11631166
const size_t size = sizeof...(Args);
11641167
std::array<object, size> args {
1165-
{ object(detail::make_caster<Args>::cast(
1166-
std::forward<Args>(args_), policy, nullptr), false)... }
1168+
{ reinterpret_steal<object>(detail::make_caster<Args>::cast(
1169+
std::forward<Args>(args_), policy, nullptr))... }
11671170
};
11681171
for (auto &arg_value : args) {
11691172
if (!arg_value) {
@@ -1195,7 +1198,9 @@ struct arg_v : arg {
11951198
template <typename T>
11961199
arg_v(const char *name, T &&x, const char *descr = nullptr)
11971200
: arg(name),
1198-
value(detail::make_caster<T>::cast(x, return_value_policy::automatic, handle()), false),
1201+
value(reinterpret_steal<object>(
1202+
detail::make_caster<T>::cast(x, return_value_policy::automatic, {})
1203+
)),
11991204
descr(descr)
12001205
#if !defined(NDEBUG)
12011206
, type(type_id<T>())
@@ -1256,10 +1261,10 @@ class simple_collector {
12561261

12571262
/// Call a Python function and pass the collected arguments
12581263
object call(PyObject *ptr) const {
1259-
auto result = object(PyObject_CallObject(ptr, m_args.ptr()), false);
1264+
PyObject *result = PyObject_CallObject(ptr, m_args.ptr());
12601265
if (!result)
12611266
throw error_already_set();
1262-
return result;
1267+
return reinterpret_steal<object>(result);
12631268
}
12641269

12651270
private:
@@ -1289,16 +1294,16 @@ class unpacking_collector {
12891294

12901295
/// Call a Python function and pass the collected arguments
12911296
object call(PyObject *ptr) const {
1292-
auto result = object(PyObject_Call(ptr, m_args.ptr(), m_kwargs.ptr()), false);
1297+
PyObject *result = PyObject_Call(ptr, m_args.ptr(), m_kwargs.ptr());
12931298
if (!result)
12941299
throw error_already_set();
1295-
return result;
1300+
return reinterpret_steal<object>(result);
12961301
}
12971302

12981303
private:
12991304
template <typename T>
13001305
void process(list &args_list, T &&x) {
1301-
auto o = object(detail::make_caster<T>::cast(std::forward<T>(x), policy, nullptr), false);
1306+
auto o = reinterpret_steal<object>(detail::make_caster<T>::cast(std::forward<T>(x), policy, {}));
13021307
if (!o) {
13031308
#if defined(NDEBUG)
13041309
argument_cast_error();
@@ -1335,7 +1340,7 @@ class unpacking_collector {
13351340
void process(list &/*args_list*/, detail::kwargs_proxy kp) {
13361341
if (!kp)
13371342
return;
1338-
for (const auto &k : dict(kp, true)) {
1343+
for (const auto &k : reinterpret_borrow<dict>(kp)) {
13391344
if (m_kwargs.contains(k.first)) {
13401345
#if defined(NDEBUG)
13411346
multiple_values_error();

include/pybind11/eigen.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
182182
if (!src)
183183
return false;
184184

185-
object obj(src, true);
185+
auto obj = reinterpret_borrow<object>(src);
186186
object sparse_module = module::import("scipy.sparse");
187187
object matrix_type = sparse_module.attr(
188188
rowMajor ? "csr_matrix" : "csc_matrix");

include/pybind11/eval.h

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ enum eval_mode {
3131
template <eval_mode mode = eval_expr>
3232
object eval(str expr, object global = object(), object local = object()) {
3333
if (!global) {
34-
global = object(PyEval_GetGlobals(), true);
34+
global = reinterpret_borrow<object>(PyEval_GetGlobals());
3535
if (!global)
3636
global = dict();
3737
}
@@ -50,17 +50,16 @@ object eval(str expr, object global = object(), object local = object()) {
5050
default: pybind11_fail("invalid evaluation mode");
5151
}
5252

53-
object result(PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr()), false);
54-
53+
PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr());
5554
if (!result)
5655
throw error_already_set();
57-
return result;
56+
return reinterpret_steal<object>(result);
5857
}
5958

6059
template <eval_mode mode = eval_statements>
6160
object eval_file(str fname, object global = object(), object local = object()) {
6261
if (!global) {
63-
global = object(PyEval_GetGlobals(), true);
62+
global = reinterpret_borrow<object>(PyEval_GetGlobals());
6463
if (!global)
6564
global = dict();
6665
}
@@ -83,9 +82,9 @@ object eval_file(str fname, object global = object(), object local = object()) {
8382
FILE *f = _Py_fopen(fname.ptr(), "r");
8483
#else
8584
/* No unicode support in open() :( */
86-
object fobj(PyFile_FromString(
85+
auto fobj = reinterpret_steal<object>(PyFile_FromString(
8786
const_cast<char *>(fname_str.c_str()),
88-
const_cast<char*>("r")), false);
87+
const_cast<char*>("r")));
8988
FILE *f = nullptr;
9089
if (fobj)
9190
f = PyFile_AsFile(fobj.ptr());
@@ -96,14 +95,11 @@ object eval_file(str fname, object global = object(), object local = object()) {
9695
pybind11_fail("File \"" + fname_str + "\" could not be opened!");
9796
}
9897

99-
object result(PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(),
100-
local.ptr(), closeFile),
101-
false);
102-
98+
PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(),
99+
local.ptr(), closeFile);
103100
if (!result)
104101
throw error_already_set();
105-
106-
return result;
102+
return reinterpret_steal<object>(result);
107103
}
108104

109105
NAMESPACE_END(pybind11)

include/pybind11/functional.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ template <typename Return, typename... Args> struct type_caster<std::function<Re
3636
captured variables), in which case the roundtrip can be avoided.
3737
*/
3838
if (PyCFunction_Check(src_.ptr())) {
39-
capsule c(PyCFunction_GetSelf(src_.ptr()), true);
39+
auto c = reinterpret_borrow<capsule>(PyCFunction_GetSelf(src_.ptr()));
4040
auto rec = (function_record *) c;
4141
using FunctionType = Return (*) (Args...);
4242

@@ -47,7 +47,7 @@ template <typename Return, typename... Args> struct type_caster<std::function<Re
4747
}
4848
}
4949

50-
object src(src_, true);
50+
auto src = reinterpret_borrow<object>(src_);
5151
value = [src](Args... args) -> Return {
5252
gil_scoped_acquire acq;
5353
object retval(src(std::move(args)...));

0 commit comments

Comments
 (0)